mirror of
https://github.com/denoland/deno.git
synced 2025-01-03 12:58:54 -05:00
core: Abstract out Behavior from Isolate (#1904)
Move v8_set_flags and v8_version to core. (The idea is that src/ should not depend on libdeno.rs anymore. This is a step towards that.)
This commit is contained in:
parent
830ce93785
commit
72f9a2e20d
7 changed files with 883 additions and 572 deletions
84
core/flags.rs
Normal file
84
core/flags.rs
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||||
|
//! This module wraps libdeno::deno_set_v8_flags
|
||||||
|
use crate::libdeno::deno_set_v8_flags;
|
||||||
|
use libc::c_char;
|
||||||
|
use libc::c_int;
|
||||||
|
use std::ffi::CStr;
|
||||||
|
use std::ffi::CString;
|
||||||
|
use std::mem;
|
||||||
|
use std::vec::Vec;
|
||||||
|
|
||||||
|
/// Pass the command line arguments to v8.
|
||||||
|
/// Returns a vector of command line arguments that V8 did not understand.
|
||||||
|
/// Translates --v8-options into a --help flag for V8.
|
||||||
|
pub fn v8_set_flags(args: Vec<String>) -> Vec<String> {
|
||||||
|
// deno_set_v8_flags(int* argc, char** argv) mutates argc and argv to remove
|
||||||
|
// flags that v8 understands.
|
||||||
|
// First parse core args, then convert to a vector of C strings.
|
||||||
|
let (args, rest) = v8_set_flags_preprocess(args);
|
||||||
|
|
||||||
|
// Make a new array, that can be modified by V8::SetFlagsFromCommandLine(),
|
||||||
|
// containing mutable raw pointers to the individual command line args.
|
||||||
|
let mut raw_argv = args
|
||||||
|
.iter()
|
||||||
|
.map(|arg| CString::new(arg.as_str()).unwrap().into_bytes_with_nul())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let mut c_argv = raw_argv
|
||||||
|
.iter_mut()
|
||||||
|
.map(|arg| arg.as_mut_ptr() as *mut c_char)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// Store the length of the c_argv array in a local variable. We'll pass
|
||||||
|
// a pointer to this local variable to deno_set_v8_flags(), which then
|
||||||
|
// updates its value.
|
||||||
|
let mut c_argv_len = c_argv.len() as c_int;
|
||||||
|
// Let v8 parse the arguments it recognizes and remove them from c_argv.
|
||||||
|
unsafe { deno_set_v8_flags(&mut c_argv_len, c_argv.as_mut_ptr()) };
|
||||||
|
// If c_argv_len was updated we have to change the length of c_argv to match.
|
||||||
|
c_argv.truncate(c_argv_len as usize);
|
||||||
|
// Copy the modified arguments list into a proper rust vec and return it.
|
||||||
|
c_argv
|
||||||
|
.iter()
|
||||||
|
.map(|ptr| unsafe {
|
||||||
|
let cstr = CStr::from_ptr(*ptr as *const c_char);
|
||||||
|
let slice = cstr.to_str().unwrap();
|
||||||
|
slice.to_string()
|
||||||
|
}).chain(rest.into_iter())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns args passed to V8, followed by args passed to JS
|
||||||
|
fn v8_set_flags_preprocess(args: Vec<String>) -> (Vec<String>, Vec<String>) {
|
||||||
|
let (rest, mut v8_args) =
|
||||||
|
args.into_iter().partition(|ref a| a.as_str() == "--help");
|
||||||
|
|
||||||
|
// Replace args being sent to V8
|
||||||
|
for a in &mut v8_args {
|
||||||
|
if a == "--v8-options" {
|
||||||
|
mem::swap(a, &mut String::from("--help"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(v8_args, rest)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_v8_set_flags_preprocess_1() {
|
||||||
|
let js_args = v8_set_flags_preprocess(vec![
|
||||||
|
"deno".to_string(),
|
||||||
|
"--v8-options".to_string(),
|
||||||
|
]);
|
||||||
|
assert_eq!(
|
||||||
|
js_args,
|
||||||
|
(vec!["deno".to_string(), "--help".to_string()], vec![])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_v8_set_flags_preprocess_2() {
|
||||||
|
let js_args =
|
||||||
|
v8_set_flags_preprocess(vec!["deno".to_string(), "--help".to_string()]);
|
||||||
|
assert_eq!(
|
||||||
|
js_args,
|
||||||
|
(vec!["deno".to_string()], vec!["--help".to_string()])
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,24 +1,57 @@
|
||||||
// This is not a real HTTP server. We read blindly one time into 'requestBuf',
|
// This is not a real HTTP server. We read blindly one time into 'requestBuf',
|
||||||
// then write this fixed 'responseBuf'. The point of this benchmark is to
|
// then write this fixed 'responseBuf'. The point of this benchmark is to
|
||||||
// exercise the event loop in a simple yet semi-realistic way.
|
// exercise the event loop in a simple yet semi-realistic way.
|
||||||
const shared32 = new Int32Array(libdeno.shared);
|
|
||||||
|
|
||||||
const INDEX_NUM_RECORDS = 0;
|
|
||||||
const INDEX_RECORDS = 1;
|
|
||||||
const RECORD_OFFSET_PROMISE_ID = 0;
|
|
||||||
const RECORD_OFFSET_OP = 1;
|
|
||||||
const RECORD_OFFSET_ARG = 2;
|
|
||||||
const RECORD_OFFSET_RESULT = 3;
|
|
||||||
const RECORD_SIZE = 4;
|
|
||||||
const OP_LISTEN = 1;
|
const OP_LISTEN = 1;
|
||||||
const OP_ACCEPT = 2;
|
const OP_ACCEPT = 2;
|
||||||
const OP_READ = 3;
|
const OP_READ = 3;
|
||||||
const OP_WRITE = 4;
|
const OP_WRITE = 4;
|
||||||
const OP_CLOSE = 5;
|
const OP_CLOSE = 5;
|
||||||
|
const INDEX_START = 0;
|
||||||
|
const INDEX_END = 1;
|
||||||
|
const NUM_RECORDS = 128;
|
||||||
|
const RECORD_SIZE = 4;
|
||||||
|
|
||||||
const NUM_RECORDS = (shared32.length - INDEX_RECORDS) / RECORD_SIZE;
|
const shared32 = new Int32Array(libdeno.shared);
|
||||||
if (NUM_RECORDS != 100) {
|
|
||||||
throw Error("expected 100 entries");
|
function idx(i, off) {
|
||||||
|
return 2 + i * RECORD_SIZE + off;
|
||||||
|
}
|
||||||
|
|
||||||
|
function recordsPush(promiseId, opId, arg, result) {
|
||||||
|
let i = shared32[INDEX_END];
|
||||||
|
if (i >= NUM_RECORDS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
shared32[idx(i, 0)] = promiseId;
|
||||||
|
shared32[idx(i, 1)] = opId;
|
||||||
|
shared32[idx(i, 2)] = arg;
|
||||||
|
shared32[idx(i, 3)] = result;
|
||||||
|
shared32[INDEX_END]++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function recordsShift() {
|
||||||
|
if (shared32[INDEX_START] == shared32[INDEX_END]) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const i = shared32[INDEX_START];
|
||||||
|
const record = {
|
||||||
|
promiseId: shared32[idx(i, 0)],
|
||||||
|
opId: shared32[idx(i, 1)],
|
||||||
|
arg: shared32[idx(i, 2)],
|
||||||
|
result: shared32[idx(i, 3)]
|
||||||
|
};
|
||||||
|
shared32[INDEX_START]++;
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
function recordsReset() {
|
||||||
|
shared32[INDEX_START] = 0;
|
||||||
|
shared32[INDEX_END] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function recordsSize() {
|
||||||
|
return shared32[INDEX_END] - shared32[INDEX_START];
|
||||||
}
|
}
|
||||||
|
|
||||||
const requestBuf = new Uint8Array(64 * 1024);
|
const requestBuf = new Uint8Array(64 * 1024);
|
||||||
|
@ -39,51 +72,35 @@ function createResolvable() {
|
||||||
return Object.assign(promise, methods);
|
return Object.assign(promise, methods);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setRecord(i, off, value) {
|
|
||||||
if (i >= NUM_RECORDS) {
|
|
||||||
throw Error("out of range");
|
|
||||||
}
|
|
||||||
shared32[INDEX_RECORDS + RECORD_SIZE * i + off] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRecord(i, off) {
|
|
||||||
if (i >= NUM_RECORDS) {
|
|
||||||
throw Error("out of range");
|
|
||||||
}
|
|
||||||
return shared32[INDEX_RECORDS + RECORD_SIZE * i + off];
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns Promise<number> */
|
/** Returns Promise<number> */
|
||||||
function sendAsync(op, arg, zeroCopyData) {
|
function sendAsync(opId, arg, zeroCopyData) {
|
||||||
const id = nextPromiseId++;
|
const promiseId = nextPromiseId++;
|
||||||
const p = createResolvable();
|
const p = createResolvable();
|
||||||
shared32[INDEX_NUM_RECORDS] = 1;
|
recordsReset();
|
||||||
setRecord(0, RECORD_OFFSET_PROMISE_ID, id);
|
recordsPush(promiseId, opId, arg, -1);
|
||||||
setRecord(0, RECORD_OFFSET_OP, op);
|
promiseMap.set(promiseId, p);
|
||||||
setRecord(0, RECORD_OFFSET_ARG, arg);
|
|
||||||
setRecord(0, RECORD_OFFSET_RESULT, -1);
|
|
||||||
promiseMap.set(id, p);
|
|
||||||
libdeno.send(null, zeroCopyData);
|
libdeno.send(null, zeroCopyData);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns u32 number */
|
/** Returns u32 number */
|
||||||
function sendSync(op, arg) {
|
function sendSync(opId, arg) {
|
||||||
shared32[INDEX_NUM_RECORDS] = 1;
|
recordsReset();
|
||||||
setRecord(0, RECORD_OFFSET_PROMISE_ID, 0);
|
recordsPush(0, opId, arg, -1);
|
||||||
setRecord(0, RECORD_OFFSET_OP, op);
|
|
||||||
setRecord(0, RECORD_OFFSET_ARG, arg);
|
|
||||||
setRecord(0, RECORD_OFFSET_RESULT, -1);
|
|
||||||
libdeno.send();
|
libdeno.send();
|
||||||
return getRecord(0, RECORD_OFFSET_RESULT);
|
if (recordsSize() != 1) {
|
||||||
|
throw Error("Expected sharedSimple to have size 1");
|
||||||
|
}
|
||||||
|
let { result } = recordsShift();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleAsyncMsgFromRust() {
|
function handleAsyncMsgFromRust() {
|
||||||
for (let i = 0; i < shared32[INDEX_NUM_RECORDS]; i++) {
|
while (recordsSize() > 0) {
|
||||||
let id = getRecord(i, RECORD_OFFSET_PROMISE_ID);
|
const { promiseId, result } = recordsShift();
|
||||||
const p = promiseMap.get(id);
|
const p = promiseMap.get(promiseId);
|
||||||
promiseMap.delete(id);
|
promiseMap.delete(promiseId);
|
||||||
p.resolve(getRecord(i, RECORD_OFFSET_RESULT));
|
p.resolve(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,7 +149,7 @@ async function serve(rid) {
|
||||||
async function main() {
|
async function main() {
|
||||||
libdeno.recv(handleAsyncMsgFromRust);
|
libdeno.recv(handleAsyncMsgFromRust);
|
||||||
|
|
||||||
libdeno.print("http_bench.js start");
|
libdeno.print("http_bench.js start\n");
|
||||||
|
|
||||||
const listenerRid = listen();
|
const listenerRid = listen();
|
||||||
libdeno.print(`listening http://127.0.0.1:4544/ rid = ${listenerRid}`);
|
libdeno.print(`listening http://127.0.0.1:4544/ rid = ${listenerRid}`);
|
||||||
|
|
|
@ -12,18 +12,11 @@ extern crate log;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
|
|
||||||
use deno_core::deno_buf;
|
use deno_core::*;
|
||||||
use deno_core::AsyncResult;
|
|
||||||
use deno_core::Isolate;
|
|
||||||
use deno_core::JSError;
|
|
||||||
use deno_core::Op;
|
|
||||||
use deno_core::RECORD_OFFSET_ARG;
|
|
||||||
use deno_core::RECORD_OFFSET_OP;
|
|
||||||
use deno_core::RECORD_OFFSET_PROMISE_ID;
|
|
||||||
use deno_core::RECORD_OFFSET_RESULT;
|
|
||||||
use futures::future::lazy;
|
use futures::future::lazy;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
use std::mem;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::sync::atomic::AtomicUsize;
|
use std::sync::atomic::AtomicUsize;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
@ -36,11 +29,148 @@ const OP_READ: i32 = 3;
|
||||||
const OP_WRITE: i32 = 4;
|
const OP_WRITE: i32 = 4;
|
||||||
const OP_CLOSE: i32 = 5;
|
const OP_CLOSE: i32 = 5;
|
||||||
|
|
||||||
|
const INDEX_START: usize = 0;
|
||||||
|
const INDEX_END: usize = 1;
|
||||||
|
|
||||||
|
const NUM_RECORDS: usize = 128;
|
||||||
|
const RECORD_SIZE: usize = 4;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Record {
|
||||||
|
pub promise_id: i32,
|
||||||
|
pub op_id: i32,
|
||||||
|
pub arg: i32,
|
||||||
|
pub result: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type HttpBenchOp = dyn Future<Item = i32, Error = std::io::Error> + Send;
|
||||||
|
|
||||||
|
struct HttpBench {
|
||||||
|
shared32: Vec<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HttpBench {
|
||||||
|
fn new() -> Self {
|
||||||
|
let mut shared32 = Vec::<i32>::new();
|
||||||
|
let n = 2 + 4 * NUM_RECORDS;
|
||||||
|
shared32.resize(n, 0);
|
||||||
|
shared32[INDEX_START] = 0;
|
||||||
|
shared32[INDEX_END] = 0;
|
||||||
|
Self { shared32 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn idx(i: usize, off: usize) -> usize {
|
||||||
|
2 + i * RECORD_SIZE + off
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Behavior<Record> for HttpBench {
|
||||||
|
fn startup_snapshot(&mut self) -> Option<deno_buf> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn startup_shared(&mut self) -> Option<deno_buf> {
|
||||||
|
let ptr = self.shared32.as_ptr() as *const u8;
|
||||||
|
let len = mem::size_of::<i32>() * self.shared32.len();
|
||||||
|
Some(unsafe { deno_buf::from_raw_parts(ptr, len) })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve(&mut self, _specifier: &str, _referrer: deno_mod) -> deno_mod {
|
||||||
|
// HttpBench doesn't do ES modules.
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn recv(
|
||||||
|
&mut self,
|
||||||
|
record: Record,
|
||||||
|
zero_copy_buf: deno_buf,
|
||||||
|
) -> (bool, Box<Op<Record>>) {
|
||||||
|
let is_sync = record.promise_id == 0;
|
||||||
|
let http_bench_op = match record.op_id {
|
||||||
|
OP_LISTEN => {
|
||||||
|
assert!(is_sync);
|
||||||
|
op_listen()
|
||||||
|
}
|
||||||
|
OP_CLOSE => {
|
||||||
|
assert!(is_sync);
|
||||||
|
let rid = record.arg;
|
||||||
|
op_close(rid)
|
||||||
|
}
|
||||||
|
OP_ACCEPT => {
|
||||||
|
assert!(!is_sync);
|
||||||
|
let listener_rid = record.arg;
|
||||||
|
op_accept(listener_rid)
|
||||||
|
}
|
||||||
|
OP_READ => {
|
||||||
|
assert!(!is_sync);
|
||||||
|
let rid = record.arg;
|
||||||
|
op_read(rid, zero_copy_buf)
|
||||||
|
}
|
||||||
|
OP_WRITE => {
|
||||||
|
assert!(!is_sync);
|
||||||
|
let rid = record.arg;
|
||||||
|
op_write(rid, zero_copy_buf)
|
||||||
|
}
|
||||||
|
_ => panic!("bad op {}", record.op_id),
|
||||||
|
};
|
||||||
|
let mut record_a = record.clone();
|
||||||
|
let mut record_b = record.clone();
|
||||||
|
|
||||||
|
let op = Box::new(
|
||||||
|
http_bench_op
|
||||||
|
.and_then(move |result| {
|
||||||
|
record_a.result = result;
|
||||||
|
Ok(record_a)
|
||||||
|
}).or_else(|err| -> Result<Record, ()> {
|
||||||
|
eprintln!("unexpected err {}", err);
|
||||||
|
record_b.result = -1;
|
||||||
|
Ok(record_b)
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
(is_sync, op)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn records_reset(&mut self) {
|
||||||
|
self.shared32[INDEX_START] = 0;
|
||||||
|
self.shared32[INDEX_END] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn records_push(&mut self, record: Record) -> bool {
|
||||||
|
debug!("push {:?}", record);
|
||||||
|
let i = self.shared32[INDEX_END] as usize;
|
||||||
|
if i >= NUM_RECORDS {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
self.shared32[idx(i, 0)] = record.promise_id;
|
||||||
|
self.shared32[idx(i, 1)] = record.op_id;
|
||||||
|
self.shared32[idx(i, 2)] = record.arg;
|
||||||
|
self.shared32[idx(i, 3)] = record.result;
|
||||||
|
self.shared32[INDEX_END] += 1;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn records_shift(&mut self) -> Option<Record> {
|
||||||
|
let i = self.shared32[INDEX_START] as usize;
|
||||||
|
if i == self.shared32[INDEX_END] as usize {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let record = Record {
|
||||||
|
promise_id: self.shared32[idx(i, 0)],
|
||||||
|
op_id: self.shared32[idx(i, 1)],
|
||||||
|
arg: self.shared32[idx(i, 2)],
|
||||||
|
result: self.shared32[idx(i, 3)],
|
||||||
|
};
|
||||||
|
self.shared32[INDEX_START] += 1;
|
||||||
|
Some(record)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let js_source = include_str!("http_bench.js");
|
let js_source = include_str!("http_bench.js");
|
||||||
let isolate = deno_core::Isolate::new(recv_cb);
|
|
||||||
|
|
||||||
let main_future = lazy(move || {
|
let main_future = lazy(move || {
|
||||||
|
let isolate = deno_core::Isolate::new(HttpBench::new());
|
||||||
|
|
||||||
// TODO currently isolate.execute() must be run inside tokio, hence the
|
// TODO currently isolate.execute() must be run inside tokio, hence the
|
||||||
// lazy(). It would be nice to not have that contraint. Probably requires
|
// lazy(). It would be nice to not have that contraint. Probably requires
|
||||||
// using v8::MicrotasksPolicy::kExplicit
|
// using v8::MicrotasksPolicy::kExplicit
|
||||||
|
@ -77,69 +207,7 @@ fn new_rid() -> i32 {
|
||||||
rid as i32
|
rid as i32
|
||||||
}
|
}
|
||||||
|
|
||||||
fn recv_cb(isolate: &mut Isolate, zero_copy_buf: deno_buf) {
|
fn op_accept(listener_rid: i32) -> Box<HttpBenchOp> {
|
||||||
isolate.test_send_counter += 1; // TODO ideally store this in isolate.state?
|
|
||||||
|
|
||||||
let promise_id = isolate.shared.get_record(0, RECORD_OFFSET_PROMISE_ID);
|
|
||||||
let op_id = isolate.shared.get_record(0, RECORD_OFFSET_OP);
|
|
||||||
let arg = isolate.shared.get_record(0, RECORD_OFFSET_ARG);
|
|
||||||
|
|
||||||
// dbg!(promise_id);
|
|
||||||
// dbg!(op_id);
|
|
||||||
// dbg!(arg);
|
|
||||||
|
|
||||||
let is_sync = promise_id == 0;
|
|
||||||
|
|
||||||
if is_sync {
|
|
||||||
// sync ops
|
|
||||||
match op_id {
|
|
||||||
OP_CLOSE => {
|
|
||||||
debug!("close");
|
|
||||||
assert!(is_sync);
|
|
||||||
let mut table = RESOURCE_TABLE.lock().unwrap();
|
|
||||||
let r = table.remove(&arg);
|
|
||||||
isolate.shared.set_record(
|
|
||||||
0,
|
|
||||||
RECORD_OFFSET_RESULT,
|
|
||||||
if r.is_some() { 0 } else { -1 },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
OP_LISTEN => {
|
|
||||||
debug!("listen");
|
|
||||||
assert!(is_sync);
|
|
||||||
|
|
||||||
let addr = "127.0.0.1:4544".parse::<SocketAddr>().unwrap();
|
|
||||||
let listener = tokio::net::TcpListener::bind(&addr).unwrap();
|
|
||||||
let rid = new_rid();
|
|
||||||
isolate.shared.set_record(0, RECORD_OFFSET_RESULT, rid);
|
|
||||||
let mut guard = RESOURCE_TABLE.lock().unwrap();
|
|
||||||
guard.insert(rid, Repr::TcpListener(listener));
|
|
||||||
}
|
|
||||||
_ => panic!("bad op"),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// async ops
|
|
||||||
let zero_copy_id = zero_copy_buf.zero_copy_id;
|
|
||||||
let op = match op_id {
|
|
||||||
OP_ACCEPT => {
|
|
||||||
let listener_rid = arg;
|
|
||||||
op_accept(listener_rid)
|
|
||||||
}
|
|
||||||
OP_READ => {
|
|
||||||
let rid = arg;
|
|
||||||
op_read(rid, zero_copy_buf)
|
|
||||||
}
|
|
||||||
OP_WRITE => {
|
|
||||||
let rid = arg;
|
|
||||||
op_write(rid, zero_copy_buf)
|
|
||||||
}
|
|
||||||
_ => panic!("bad op"),
|
|
||||||
};
|
|
||||||
isolate.add_op(promise_id, op, zero_copy_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn op_accept(listener_rid: i32) -> Box<Op> {
|
|
||||||
debug!("accept {}", listener_rid);
|
debug!("accept {}", listener_rid);
|
||||||
Box::new(
|
Box::new(
|
||||||
futures::future::poll_fn(move || {
|
futures::future::poll_fn(move || {
|
||||||
|
@ -147,7 +215,7 @@ fn op_accept(listener_rid: i32) -> Box<Op> {
|
||||||
let maybe_repr = table.get_mut(&listener_rid);
|
let maybe_repr = table.get_mut(&listener_rid);
|
||||||
match maybe_repr {
|
match maybe_repr {
|
||||||
Some(Repr::TcpListener(ref mut listener)) => listener.poll_accept(),
|
Some(Repr::TcpListener(ref mut listener)) => listener.poll_accept(),
|
||||||
_ => panic!("bad rid"),
|
_ => panic!("bad rid {}", listener_rid),
|
||||||
}
|
}
|
||||||
}).and_then(move |(stream, addr)| {
|
}).and_then(move |(stream, addr)| {
|
||||||
debug!("accept success {}", addr);
|
debug!("accept success {}", addr);
|
||||||
|
@ -156,12 +224,36 @@ fn op_accept(listener_rid: i32) -> Box<Op> {
|
||||||
let mut guard = RESOURCE_TABLE.lock().unwrap();
|
let mut guard = RESOURCE_TABLE.lock().unwrap();
|
||||||
guard.insert(rid, Repr::TcpStream(stream));
|
guard.insert(rid, Repr::TcpStream(stream));
|
||||||
|
|
||||||
Ok(AsyncResult { result: rid })
|
Ok(rid as i32)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_read(rid: i32, mut zero_copy_buf: deno_buf) -> Box<Op> {
|
fn op_listen() -> Box<HttpBenchOp> {
|
||||||
|
debug!("listen");
|
||||||
|
|
||||||
|
Box::new(lazy(move || {
|
||||||
|
let addr = "127.0.0.1:4544".parse::<SocketAddr>().unwrap();
|
||||||
|
let listener = tokio::net::TcpListener::bind(&addr).unwrap();
|
||||||
|
let rid = new_rid();
|
||||||
|
|
||||||
|
let mut guard = RESOURCE_TABLE.lock().unwrap();
|
||||||
|
guard.insert(rid, Repr::TcpListener(listener));
|
||||||
|
futures::future::ok(rid)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn op_close(rid: i32) -> Box<HttpBenchOp> {
|
||||||
|
debug!("close");
|
||||||
|
Box::new(lazy(move || {
|
||||||
|
let mut table = RESOURCE_TABLE.lock().unwrap();
|
||||||
|
let r = table.remove(&rid);
|
||||||
|
let result = if r.is_some() { 0 } else { -1 };
|
||||||
|
futures::future::ok(result)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn op_read(rid: i32, mut zero_copy_buf: deno_buf) -> Box<HttpBenchOp> {
|
||||||
debug!("read rid={}", rid);
|
debug!("read rid={}", rid);
|
||||||
Box::new(
|
Box::new(
|
||||||
futures::future::poll_fn(move || {
|
futures::future::poll_fn(move || {
|
||||||
|
@ -175,14 +267,12 @@ fn op_read(rid: i32, mut zero_copy_buf: deno_buf) -> Box<Op> {
|
||||||
}
|
}
|
||||||
}).and_then(move |nread| {
|
}).and_then(move |nread| {
|
||||||
debug!("read success {}", nread);
|
debug!("read success {}", nread);
|
||||||
Ok(AsyncResult {
|
Ok(nread as i32)
|
||||||
result: nread as i32,
|
|
||||||
})
|
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_write(rid: i32, zero_copy_buf: deno_buf) -> Box<Op> {
|
fn op_write(rid: i32, zero_copy_buf: deno_buf) -> Box<HttpBenchOp> {
|
||||||
debug!("write rid={}", rid);
|
debug!("write rid={}", rid);
|
||||||
Box::new(
|
Box::new(
|
||||||
futures::future::poll_fn(move || {
|
futures::future::poll_fn(move || {
|
||||||
|
@ -196,9 +286,7 @@ fn op_write(rid: i32, zero_copy_buf: deno_buf) -> Box<Op> {
|
||||||
}
|
}
|
||||||
}).and_then(move |nwritten| {
|
}).and_then(move |nwritten| {
|
||||||
debug!("write success {}", nwritten);
|
debug!("write success {}", nwritten);
|
||||||
Ok(AsyncResult {
|
Ok(nwritten as i32)
|
||||||
result: nwritten as i32,
|
|
||||||
})
|
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
548
core/isolate.rs
Normal file
548
core/isolate.rs
Normal file
|
@ -0,0 +1,548 @@
|
||||||
|
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
|
||||||
|
use crate::js_errors::JSError;
|
||||||
|
use crate::libdeno;
|
||||||
|
use crate::libdeno::deno_buf;
|
||||||
|
use crate::libdeno::deno_mod;
|
||||||
|
use futures::Async;
|
||||||
|
use futures::Future;
|
||||||
|
use futures::Poll;
|
||||||
|
use libc::c_void;
|
||||||
|
use std::ffi::CStr;
|
||||||
|
use std::ffi::CString;
|
||||||
|
use std::sync::{Once, ONCE_INIT};
|
||||||
|
|
||||||
|
pub type Op<R> = dyn Future<Item = R, Error = ()> + Send;
|
||||||
|
|
||||||
|
struct PendingOp<R> {
|
||||||
|
op: Box<Op<R>>,
|
||||||
|
polled_recently: bool,
|
||||||
|
zero_copy_id: usize, // non-zero if associated zero-copy buffer.
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> Future for PendingOp<R> {
|
||||||
|
type Item = R;
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn poll(&mut self) -> Poll<R, ()> {
|
||||||
|
// Do not call poll on ops we've already polled this turn.
|
||||||
|
if self.polled_recently {
|
||||||
|
Ok(Async::NotReady)
|
||||||
|
} else {
|
||||||
|
self.polled_recently = true;
|
||||||
|
let op = &mut self.op;
|
||||||
|
op.poll().map_err(|()| {
|
||||||
|
// Ops should not error. If an op experiences an error it needs to
|
||||||
|
// encode that error into the record R, so it can be returned to JS.
|
||||||
|
panic!("ops should not error")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Behavior<R> {
|
||||||
|
fn startup_snapshot(&mut self) -> Option<deno_buf>;
|
||||||
|
fn startup_shared(&mut self) -> Option<deno_buf>;
|
||||||
|
|
||||||
|
fn resolve(&mut self, specifier: &str, referrer: deno_mod) -> deno_mod;
|
||||||
|
|
||||||
|
fn recv(&mut self, record: R, zero_copy_buf: deno_buf) -> (bool, Box<Op<R>>);
|
||||||
|
|
||||||
|
/// Clears the shared buffer.
|
||||||
|
fn records_reset(&mut self);
|
||||||
|
|
||||||
|
/// Returns false if not enough room.
|
||||||
|
fn records_push(&mut self, record: R) -> bool;
|
||||||
|
|
||||||
|
/// Returns none if empty.
|
||||||
|
fn records_shift(&mut self) -> Option<R>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Isolate<R, B: Behavior<R>> {
|
||||||
|
libdeno_isolate: *const libdeno::isolate,
|
||||||
|
behavior: B,
|
||||||
|
pending_ops: Vec<PendingOp<R>>,
|
||||||
|
polled_recently: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<R, B: Behavior<R>> Send for Isolate<R, B> {}
|
||||||
|
|
||||||
|
impl<R, B: Behavior<R>> Drop for Isolate<R, B> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe { libdeno::deno_delete(self.libdeno_isolate) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static DENO_INIT: Once = ONCE_INIT;
|
||||||
|
|
||||||
|
impl<R, B: Behavior<R>> Isolate<R, B> {
|
||||||
|
pub fn new(mut behavior: B) -> Self {
|
||||||
|
DENO_INIT.call_once(|| {
|
||||||
|
unsafe { libdeno::deno_init() };
|
||||||
|
});
|
||||||
|
|
||||||
|
let config = libdeno::deno_config {
|
||||||
|
will_snapshot: 0,
|
||||||
|
load_snapshot: match behavior.startup_snapshot() {
|
||||||
|
Some(s) => s,
|
||||||
|
None => libdeno::deno_buf::empty(),
|
||||||
|
},
|
||||||
|
shared: match behavior.startup_shared() {
|
||||||
|
Some(s) => s,
|
||||||
|
None => libdeno::deno_buf::empty(),
|
||||||
|
},
|
||||||
|
recv_cb: Self::pre_dispatch,
|
||||||
|
};
|
||||||
|
let libdeno_isolate = unsafe { libdeno::deno_new(config) };
|
||||||
|
|
||||||
|
Self {
|
||||||
|
libdeno_isolate,
|
||||||
|
behavior,
|
||||||
|
pending_ops: Vec::new(),
|
||||||
|
polled_recently: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn pre_dispatch(
|
||||||
|
user_data: *mut c_void,
|
||||||
|
control_buf: deno_buf,
|
||||||
|
zero_copy_buf: deno_buf,
|
||||||
|
) {
|
||||||
|
let isolate = unsafe { Isolate::<R, B>::from_raw_ptr(user_data) };
|
||||||
|
assert_eq!(control_buf.len(), 0);
|
||||||
|
let zero_copy_id = zero_copy_buf.zero_copy_id;
|
||||||
|
|
||||||
|
let req_record = isolate.behavior.records_shift().unwrap();
|
||||||
|
|
||||||
|
isolate.behavior.records_reset();
|
||||||
|
|
||||||
|
let (is_sync, op) = isolate.behavior.recv(req_record, zero_copy_buf);
|
||||||
|
if is_sync {
|
||||||
|
let res_record = op.wait().unwrap();
|
||||||
|
let push_success = isolate.behavior.records_push(res_record);
|
||||||
|
assert!(push_success);
|
||||||
|
// TODO check that if JSError thrown during respond(), that it will be
|
||||||
|
// picked up.
|
||||||
|
let _ = isolate.respond();
|
||||||
|
} else {
|
||||||
|
isolate.pending_ops.push(PendingOp {
|
||||||
|
op,
|
||||||
|
polled_recently: false,
|
||||||
|
zero_copy_id,
|
||||||
|
});
|
||||||
|
isolate.polled_recently = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn zero_copy_release(&self, zero_copy_id: usize) {
|
||||||
|
unsafe {
|
||||||
|
libdeno::deno_zero_copy_release(self.libdeno_isolate, zero_copy_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn from_raw_ptr<'a>(ptr: *const c_void) -> &'a mut Self {
|
||||||
|
let ptr = ptr as *mut _;
|
||||||
|
&mut *ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn as_raw_ptr(&self) -> *const c_void {
|
||||||
|
self as *const _ as *const c_void
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn execute(
|
||||||
|
&self,
|
||||||
|
js_filename: &str,
|
||||||
|
js_source: &str,
|
||||||
|
) -> Result<(), JSError> {
|
||||||
|
let filename = CString::new(js_filename).unwrap();
|
||||||
|
let source = CString::new(js_source).unwrap();
|
||||||
|
unsafe {
|
||||||
|
libdeno::deno_execute(
|
||||||
|
self.libdeno_isolate,
|
||||||
|
self.as_raw_ptr(),
|
||||||
|
filename.as_ptr(),
|
||||||
|
source.as_ptr(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if let Some(err) = self.last_exception() {
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn last_exception(&self) -> Option<JSError> {
|
||||||
|
let ptr = unsafe { libdeno::deno_last_exception(self.libdeno_isolate) };
|
||||||
|
if ptr.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let cstr = unsafe { CStr::from_ptr(ptr) };
|
||||||
|
let v8_exception = cstr.to_str().unwrap();
|
||||||
|
debug!("v8_exception\n{}\n", v8_exception);
|
||||||
|
let js_error = JSError::from_v8_exception(v8_exception).unwrap();
|
||||||
|
Some(js_error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_promise_errors(&self) {
|
||||||
|
unsafe {
|
||||||
|
libdeno::deno_check_promise_errors(self.libdeno_isolate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn respond(&mut self) -> Result<(), JSError> {
|
||||||
|
let buf = deno_buf::empty();
|
||||||
|
unsafe {
|
||||||
|
libdeno::deno_respond(self.libdeno_isolate, self.as_raw_ptr(), buf)
|
||||||
|
}
|
||||||
|
if let Some(err) = self.last_exception() {
|
||||||
|
Err(err)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Low-level module creation.
|
||||||
|
/// You probably want to use IsolateState::mod_execute instead.
|
||||||
|
pub fn mod_new(
|
||||||
|
&self,
|
||||||
|
main: bool,
|
||||||
|
name: &str,
|
||||||
|
source: &str,
|
||||||
|
) -> Result<deno_mod, JSError> {
|
||||||
|
let name_ = CString::new(name.to_string()).unwrap();
|
||||||
|
let name_ptr = name_.as_ptr() as *const libc::c_char;
|
||||||
|
|
||||||
|
let source_ = CString::new(source.to_string()).unwrap();
|
||||||
|
let source_ptr = source_.as_ptr() as *const libc::c_char;
|
||||||
|
|
||||||
|
let id = unsafe {
|
||||||
|
libdeno::deno_mod_new(self.libdeno_isolate, main, name_ptr, source_ptr)
|
||||||
|
};
|
||||||
|
if let Some(js_error) = self.last_exception() {
|
||||||
|
assert_eq!(id, 0);
|
||||||
|
return Err(js_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mod_get_imports(&self, id: deno_mod) -> Vec<String> {
|
||||||
|
let len =
|
||||||
|
unsafe { libdeno::deno_mod_imports_len(self.libdeno_isolate, id) };
|
||||||
|
let mut out = Vec::new();
|
||||||
|
for i in 0..len {
|
||||||
|
let specifier_ptr =
|
||||||
|
unsafe { libdeno::deno_mod_imports_get(self.libdeno_isolate, id, i) };
|
||||||
|
let specifier_c: &CStr = unsafe { CStr::from_ptr(specifier_ptr) };
|
||||||
|
let specifier: &str = specifier_c.to_str().unwrap();
|
||||||
|
|
||||||
|
out.push(specifier.to_string());
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mod_instantiate(&self, id: deno_mod) -> Result<(), JSError> {
|
||||||
|
unsafe {
|
||||||
|
libdeno::deno_mod_instantiate(
|
||||||
|
self.libdeno_isolate,
|
||||||
|
self.as_raw_ptr(),
|
||||||
|
id,
|
||||||
|
Self::resolve_cb,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if let Some(js_error) = self.last_exception() {
|
||||||
|
return Err(js_error);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mod_evaluate(&self, id: deno_mod) -> Result<(), JSError> {
|
||||||
|
unsafe {
|
||||||
|
libdeno::deno_mod_evaluate(self.libdeno_isolate, self.as_raw_ptr(), id)
|
||||||
|
};
|
||||||
|
if let Some(js_error) = self.last_exception() {
|
||||||
|
return Err(js_error);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called during mod_instantiate() only.
|
||||||
|
extern "C" fn resolve_cb(
|
||||||
|
user_data: *mut libc::c_void,
|
||||||
|
specifier_ptr: *const libc::c_char,
|
||||||
|
referrer: deno_mod,
|
||||||
|
) -> deno_mod {
|
||||||
|
let isolate = unsafe { Isolate::<R, B>::from_raw_ptr(user_data) };
|
||||||
|
let specifier_c: &CStr = unsafe { CStr::from_ptr(specifier_ptr) };
|
||||||
|
let specifier: &str = specifier_c.to_str().unwrap();
|
||||||
|
isolate.behavior.resolve(specifier, referrer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LockerScope {
|
||||||
|
libdeno_isolate: *const libdeno::isolate,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LockerScope {
|
||||||
|
fn new(libdeno_isolate: *const libdeno::isolate) -> LockerScope {
|
||||||
|
unsafe { libdeno::deno_lock(libdeno_isolate) }
|
||||||
|
LockerScope { libdeno_isolate }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for LockerScope {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe { libdeno::deno_unlock(self.libdeno_isolate) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R, B: Behavior<R>> Future for Isolate<R, B> {
|
||||||
|
type Item = ();
|
||||||
|
type Error = JSError;
|
||||||
|
|
||||||
|
fn poll(&mut self) -> Poll<(), JSError> {
|
||||||
|
// Lock the current thread for V8.
|
||||||
|
let _locker = LockerScope::new(self.libdeno_isolate);
|
||||||
|
|
||||||
|
// Clear poll_recently state both on the Isolate itself and
|
||||||
|
// on the pending ops.
|
||||||
|
self.polled_recently = false;
|
||||||
|
for pending in self.pending_ops.iter_mut() {
|
||||||
|
pending.polled_recently = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while !self.polled_recently {
|
||||||
|
let mut completed_count = 0;
|
||||||
|
|
||||||
|
debug!("poll loop");
|
||||||
|
|
||||||
|
self.polled_recently = true;
|
||||||
|
|
||||||
|
self.behavior.records_reset();
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
while i != self.pending_ops.len() {
|
||||||
|
let pending = &mut self.pending_ops[i];
|
||||||
|
match pending.poll() {
|
||||||
|
Err(()) => panic!("unexpectd error"),
|
||||||
|
Ok(Async::NotReady) => {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
Ok(Async::Ready(record)) => {
|
||||||
|
let completed = self.pending_ops.remove(i);
|
||||||
|
completed_count += 1;
|
||||||
|
|
||||||
|
if completed.zero_copy_id > 0 {
|
||||||
|
self.zero_copy_release(completed.zero_copy_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.behavior.records_push(record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if completed_count > 0 {
|
||||||
|
debug!("respond");
|
||||||
|
self.respond()?;
|
||||||
|
debug!("after respond");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.check_promise_errors();
|
||||||
|
if let Some(err) = self.last_exception() {
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're idle if pending_ops is empty.
|
||||||
|
if self.pending_ops.is_empty() {
|
||||||
|
Ok(futures::Async::Ready(()))
|
||||||
|
} else {
|
||||||
|
Ok(futures::Async::NotReady)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
fn js_check(r: Result<(), JSError>) {
|
||||||
|
if let Err(e) = r {
|
||||||
|
panic!(e.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TestBehavior {
|
||||||
|
recv_count: usize,
|
||||||
|
resolve_count: usize,
|
||||||
|
push_count: usize,
|
||||||
|
shift_count: usize,
|
||||||
|
reset_count: usize,
|
||||||
|
mod_map: HashMap<String, deno_mod>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestBehavior {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
recv_count: 0,
|
||||||
|
resolve_count: 0,
|
||||||
|
push_count: 0,
|
||||||
|
shift_count: 0,
|
||||||
|
reset_count: 0,
|
||||||
|
mod_map: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register(&mut self, name: &str, id: deno_mod) {
|
||||||
|
self.mod_map.insert(name.to_string(), id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Behavior<()> for TestBehavior {
|
||||||
|
fn startup_snapshot(&mut self) -> Option<deno_buf> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn startup_shared(&mut self) -> Option<deno_buf> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn recv(
|
||||||
|
&mut self,
|
||||||
|
_record: (),
|
||||||
|
_zero_copy_buf: deno_buf,
|
||||||
|
) -> (bool, Box<Op<()>>) {
|
||||||
|
self.recv_count += 1;
|
||||||
|
(false, Box::new(futures::future::ok(())))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve(&mut self, specifier: &str, _referrer: deno_mod) -> deno_mod {
|
||||||
|
self.resolve_count += 1;
|
||||||
|
match self.mod_map.get(specifier) {
|
||||||
|
Some(id) => *id,
|
||||||
|
None => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn records_reset(&mut self) {
|
||||||
|
self.reset_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn records_push(&mut self, _record: ()) -> bool {
|
||||||
|
self.push_count += 1;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn records_shift(&mut self) -> Option<()> {
|
||||||
|
self.shift_count += 1;
|
||||||
|
Some(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_recv() {
|
||||||
|
let behavior = TestBehavior::new();
|
||||||
|
let isolate = Isolate::new(behavior);
|
||||||
|
js_check(isolate.execute(
|
||||||
|
"filename.js",
|
||||||
|
r#"
|
||||||
|
libdeno.send();
|
||||||
|
async function main() {
|
||||||
|
libdeno.send();
|
||||||
|
}
|
||||||
|
main();
|
||||||
|
"#,
|
||||||
|
));
|
||||||
|
assert_eq!(isolate.behavior.recv_count, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mods() {
|
||||||
|
let behavior = TestBehavior::new();
|
||||||
|
let mut isolate = Isolate::new(behavior);
|
||||||
|
let mod_a = isolate
|
||||||
|
.mod_new(
|
||||||
|
true,
|
||||||
|
"a.js",
|
||||||
|
r#"
|
||||||
|
import { b } from 'b.js'
|
||||||
|
if (b() != 'b') throw Error();
|
||||||
|
libdeno.send();
|
||||||
|
"#,
|
||||||
|
).unwrap();
|
||||||
|
assert_eq!(isolate.behavior.recv_count, 0);
|
||||||
|
assert_eq!(isolate.behavior.resolve_count, 0);
|
||||||
|
|
||||||
|
let imports = isolate.mod_get_imports(mod_a);
|
||||||
|
assert_eq!(imports, vec!["b.js".to_string()]);
|
||||||
|
|
||||||
|
let mod_b = isolate
|
||||||
|
.mod_new(false, "b.js", "export function b() { return 'b' }")
|
||||||
|
.unwrap();
|
||||||
|
let imports = isolate.mod_get_imports(mod_b);
|
||||||
|
assert_eq!(imports.len(), 0);
|
||||||
|
|
||||||
|
js_check(isolate.mod_instantiate(mod_b));
|
||||||
|
assert_eq!(isolate.behavior.recv_count, 0);
|
||||||
|
assert_eq!(isolate.behavior.resolve_count, 0);
|
||||||
|
|
||||||
|
isolate.behavior.register("b.js", mod_b);
|
||||||
|
js_check(isolate.mod_instantiate(mod_a));
|
||||||
|
assert_eq!(isolate.behavior.recv_count, 0);
|
||||||
|
assert_eq!(isolate.behavior.resolve_count, 1);
|
||||||
|
|
||||||
|
js_check(isolate.mod_evaluate(mod_a));
|
||||||
|
assert_eq!(isolate.behavior.recv_count, 1);
|
||||||
|
assert_eq!(isolate.behavior.resolve_count, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_poll_async_immediate_ops() {
|
||||||
|
let behavior = TestBehavior::new();
|
||||||
|
let mut isolate = Isolate::new(behavior);
|
||||||
|
|
||||||
|
js_check(isolate.execute(
|
||||||
|
"setup.js",
|
||||||
|
r#"
|
||||||
|
let nrecv = 0;
|
||||||
|
libdeno.recv(() => {
|
||||||
|
nrecv++;
|
||||||
|
});
|
||||||
|
function assertEq(actual, expected) {
|
||||||
|
if (expected != actual) {
|
||||||
|
throw Error(`actual ${actual} expected ${expected} `);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
));
|
||||||
|
assert_eq!(isolate.behavior.recv_count, 0);
|
||||||
|
js_check(isolate.execute(
|
||||||
|
"check1.js",
|
||||||
|
r#"
|
||||||
|
assertEq(nrecv, 0);
|
||||||
|
libdeno.send();
|
||||||
|
assertEq(nrecv, 0);
|
||||||
|
"#,
|
||||||
|
));
|
||||||
|
assert_eq!(isolate.behavior.recv_count, 1);
|
||||||
|
assert_eq!(Ok(Async::Ready(())), isolate.poll());
|
||||||
|
assert_eq!(isolate.behavior.recv_count, 1);
|
||||||
|
js_check(isolate.execute(
|
||||||
|
"check2.js",
|
||||||
|
r#"
|
||||||
|
assertEq(nrecv, 1);
|
||||||
|
libdeno.send();
|
||||||
|
assertEq(nrecv, 1);
|
||||||
|
"#,
|
||||||
|
));
|
||||||
|
assert_eq!(isolate.behavior.recv_count, 2);
|
||||||
|
assert_eq!(Ok(Async::Ready(())), isolate.poll());
|
||||||
|
js_check(isolate.execute("check3.js", "assertEq(nrecv, 2)"));
|
||||||
|
assert_eq!(isolate.behavior.recv_count, 2);
|
||||||
|
// We are idle, so the next poll should be the last.
|
||||||
|
assert_eq!(Ok(Async::Ready(())), isolate.poll());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
364
core/lib.rs
364
core/lib.rs
|
@ -1,364 +1,28 @@
|
||||||
|
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
extern crate futures;
|
extern crate futures;
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
|
|
||||||
|
mod flags;
|
||||||
|
mod isolate;
|
||||||
mod js_errors;
|
mod js_errors;
|
||||||
mod libdeno;
|
mod libdeno;
|
||||||
mod shared;
|
|
||||||
|
|
||||||
|
pub use crate::flags::v8_set_flags;
|
||||||
|
pub use crate::isolate::*;
|
||||||
pub use crate::js_errors::*;
|
pub use crate::js_errors::*;
|
||||||
pub use crate::libdeno::deno_buf;
|
pub use crate::libdeno::deno_buf;
|
||||||
pub use crate::shared::*;
|
pub use crate::libdeno::deno_mod;
|
||||||
use futures::Async;
|
|
||||||
use futures::Future;
|
|
||||||
use futures::Poll;
|
|
||||||
use libc::c_void;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::ffi::CStr;
|
|
||||||
use std::ffi::CString;
|
|
||||||
use std::sync::{Once, ONCE_INIT};
|
|
||||||
|
|
||||||
pub struct Isolate {
|
pub fn v8_version() -> &'static str {
|
||||||
libdeno_isolate: *const libdeno::isolate,
|
use std::ffi::CStr;
|
||||||
pending_ops: HashMap<i32, PendingOp>, // promise_id -> op
|
let version = unsafe { libdeno::deno_v8_version() };
|
||||||
polled_recently: bool,
|
let c_str = unsafe { CStr::from_ptr(version) };
|
||||||
recv_cb: RecvCallback,
|
c_str.to_str().unwrap()
|
||||||
|
|
||||||
pub shared: Shared,
|
|
||||||
pub test_send_counter: u32, // TODO only used for testing- REMOVE.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type RecvCallback = fn(isolate: &mut Isolate, zero_copy_buf: deno_buf);
|
#[test]
|
||||||
|
fn test_v8_version() {
|
||||||
pub const NUM_RECORDS: usize = 100;
|
assert!(v8_version().len() > 3);
|
||||||
|
|
||||||
// TODO rename to AsyncResult
|
|
||||||
pub struct AsyncResult {
|
|
||||||
pub result: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Op = dyn Future<Item = AsyncResult, Error = std::io::Error> + Send;
|
|
||||||
|
|
||||||
struct PendingOp {
|
|
||||||
op: Box<Op>,
|
|
||||||
polled_recently: bool,
|
|
||||||
zero_copy_id: usize, // non-zero if associated zero-copy buffer.
|
|
||||||
}
|
|
||||||
|
|
||||||
static DENO_INIT: Once = ONCE_INIT;
|
|
||||||
|
|
||||||
unsafe impl Send for Isolate {}
|
|
||||||
|
|
||||||
impl Isolate {
|
|
||||||
pub fn new(recv_cb: RecvCallback) -> Self {
|
|
||||||
DENO_INIT.call_once(|| {
|
|
||||||
unsafe { libdeno::deno_init() };
|
|
||||||
});
|
|
||||||
|
|
||||||
// Allocate unmanaged memory for the shared buffer by creating a Vec<u8>,
|
|
||||||
// grabbing the raw pointer, and then leaking the Vec so it is never freed.
|
|
||||||
let mut shared = Shared::new();
|
|
||||||
let shared_deno_buf = shared.as_deno_buf();
|
|
||||||
|
|
||||||
let config = libdeno::deno_config {
|
|
||||||
will_snapshot: 0,
|
|
||||||
load_snapshot: deno_buf::empty(), // TODO
|
|
||||||
shared: shared_deno_buf,
|
|
||||||
recv_cb: pre_dispatch,
|
|
||||||
};
|
|
||||||
let libdeno_isolate = unsafe { libdeno::deno_new(config) };
|
|
||||||
|
|
||||||
Self {
|
|
||||||
pending_ops: HashMap::new(),
|
|
||||||
polled_recently: false,
|
|
||||||
libdeno_isolate,
|
|
||||||
test_send_counter: 0,
|
|
||||||
recv_cb,
|
|
||||||
shared,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn zero_copy_release(&self, zero_copy_id: usize) {
|
|
||||||
unsafe {
|
|
||||||
libdeno::deno_zero_copy_release(self.libdeno_isolate, zero_copy_id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_op(
|
|
||||||
self: &mut Self,
|
|
||||||
promise_id: i32,
|
|
||||||
op: Box<Op>,
|
|
||||||
zero_copy_id: usize,
|
|
||||||
) {
|
|
||||||
debug!("add_op {}", zero_copy_id);
|
|
||||||
self.pending_ops.insert(
|
|
||||||
promise_id,
|
|
||||||
PendingOp {
|
|
||||||
op,
|
|
||||||
polled_recently: false,
|
|
||||||
zero_copy_id,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
self.polled_recently = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub unsafe fn from_raw_ptr<'a>(ptr: *const c_void) -> &'a mut Self {
|
|
||||||
let ptr = ptr as *mut _;
|
|
||||||
&mut *ptr
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn as_raw_ptr(&self) -> *const c_void {
|
|
||||||
self as *const _ as *const c_void
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn execute(
|
|
||||||
&self,
|
|
||||||
js_filename: &str,
|
|
||||||
js_source: &str,
|
|
||||||
) -> Result<(), JSError> {
|
|
||||||
let filename = CString::new(js_filename).unwrap();
|
|
||||||
let source = CString::new(js_source).unwrap();
|
|
||||||
unsafe {
|
|
||||||
libdeno::deno_execute(
|
|
||||||
self.libdeno_isolate,
|
|
||||||
self.as_raw_ptr(),
|
|
||||||
filename.as_ptr(),
|
|
||||||
source.as_ptr(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
if let Some(err) = self.last_exception() {
|
|
||||||
return Err(err);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn last_exception(&self) -> Option<JSError> {
|
|
||||||
let ptr = unsafe { libdeno::deno_last_exception(self.libdeno_isolate) };
|
|
||||||
if ptr.is_null() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
let cstr = unsafe { CStr::from_ptr(ptr) };
|
|
||||||
let v8_exception = cstr.to_str().unwrap();
|
|
||||||
debug!("v8_exception\n{}\n", v8_exception);
|
|
||||||
let js_error = JSError::from_v8_exception(v8_exception).unwrap();
|
|
||||||
Some(js_error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_promise_errors(&self) {
|
|
||||||
unsafe {
|
|
||||||
libdeno::deno_check_promise_errors(self.libdeno_isolate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn respond(&mut self) -> Result<(), JSError> {
|
|
||||||
let buf = deno_buf::empty();
|
|
||||||
unsafe {
|
|
||||||
libdeno::deno_respond(self.libdeno_isolate, self.as_raw_ptr(), buf)
|
|
||||||
}
|
|
||||||
if let Some(err) = self.last_exception() {
|
|
||||||
Err(err)
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LockerScope {
|
|
||||||
libdeno_isolate: *const libdeno::isolate,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LockerScope {
|
|
||||||
fn new(isolate: &Isolate) -> LockerScope {
|
|
||||||
let libdeno_isolate = isolate.libdeno_isolate;
|
|
||||||
unsafe { libdeno::deno_lock(libdeno_isolate) }
|
|
||||||
LockerScope { libdeno_isolate }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for LockerScope {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe { libdeno::deno_unlock(self.libdeno_isolate) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Future for Isolate {
|
|
||||||
type Item = ();
|
|
||||||
type Error = JSError;
|
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<(), JSError> {
|
|
||||||
// Lock the current thread for V8.
|
|
||||||
let _locker = LockerScope::new(self);
|
|
||||||
|
|
||||||
// Clear
|
|
||||||
self.polled_recently = false;
|
|
||||||
for (_, pending) in self.pending_ops.iter_mut() {
|
|
||||||
pending.polled_recently = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
while !self.polled_recently {
|
|
||||||
let mut complete = HashMap::<i32, AsyncResult>::new();
|
|
||||||
|
|
||||||
self.polled_recently = true;
|
|
||||||
for (promise_id, pending) in self.pending_ops.iter_mut() {
|
|
||||||
// Do not call poll on futures we've already polled this turn.
|
|
||||||
if pending.polled_recently {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
pending.polled_recently = true;
|
|
||||||
|
|
||||||
let promise_id = *promise_id;
|
|
||||||
let op = &mut pending.op;
|
|
||||||
match op.poll() {
|
|
||||||
Err(op_err) => {
|
|
||||||
eprintln!("op err {:?}", op_err);
|
|
||||||
complete.insert(promise_id, AsyncResult { result: -1 });
|
|
||||||
debug!("pending op {} complete err", promise_id);
|
|
||||||
}
|
|
||||||
Ok(Async::Ready(async_result)) => {
|
|
||||||
complete.insert(promise_id, async_result);
|
|
||||||
debug!("pending op {} complete ready", promise_id);
|
|
||||||
}
|
|
||||||
Ok(Async::NotReady) => {
|
|
||||||
debug!("pending op {} not ready", promise_id);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.shared.set_num_records(complete.len() as i32);
|
|
||||||
if complete.len() > 0 {
|
|
||||||
// self.zero_copy_release() and self.respond() need Locker.
|
|
||||||
let mut i = 0;
|
|
||||||
for (promise_id, async_result) in complete.iter_mut() {
|
|
||||||
let pending = self.pending_ops.remove(promise_id).unwrap();
|
|
||||||
|
|
||||||
if pending.zero_copy_id > 0 {
|
|
||||||
self.zero_copy_release(pending.zero_copy_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
self
|
|
||||||
.shared
|
|
||||||
.set_record(i, RECORD_OFFSET_PROMISE_ID, *promise_id);
|
|
||||||
self
|
|
||||||
.shared
|
|
||||||
.set_record(i, RECORD_OFFSET_RESULT, async_result.result);
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
self.respond()?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.check_promise_errors();
|
|
||||||
if let Some(err) = self.last_exception() {
|
|
||||||
return Err(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We're idle if pending_ops is empty.
|
|
||||||
if self.pending_ops.is_empty() {
|
|
||||||
Ok(futures::Async::Ready(()))
|
|
||||||
} else {
|
|
||||||
Ok(futures::Async::NotReady)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" fn pre_dispatch(
|
|
||||||
user_data: *mut c_void,
|
|
||||||
control_buf: deno_buf,
|
|
||||||
zero_copy_buf: deno_buf,
|
|
||||||
) {
|
|
||||||
let isolate = unsafe { Isolate::from_raw_ptr(user_data) };
|
|
||||||
assert_eq!(control_buf.len(), 0);
|
|
||||||
(isolate.recv_cb)(isolate, zero_copy_buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
fn inc_counter(isolate: &mut Isolate, zero_copy_buf: deno_buf) {
|
|
||||||
assert_eq!(zero_copy_buf.len(), 0);
|
|
||||||
isolate.test_send_counter += 1; // TODO ideally store this in isolate.state?
|
|
||||||
}
|
|
||||||
|
|
||||||
fn js_check(r: Result<(), JSError>) {
|
|
||||||
if let Err(e) = r {
|
|
||||||
panic!(e.to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_execute() {
|
|
||||||
let isolate = Isolate::new(inc_counter);
|
|
||||||
js_check(isolate.execute(
|
|
||||||
"filename.js",
|
|
||||||
r#"
|
|
||||||
libdeno.send();
|
|
||||||
async function main() {
|
|
||||||
libdeno.send();
|
|
||||||
}
|
|
||||||
main();
|
|
||||||
"#,
|
|
||||||
));
|
|
||||||
// We expect that main is executed even tho we didn't poll.
|
|
||||||
assert_eq!(isolate.test_send_counter, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn async_immediate(isolate: &mut Isolate, zero_copy_buf: deno_buf) {
|
|
||||||
assert_eq!(zero_copy_buf.len(), 0);
|
|
||||||
isolate.test_send_counter += 1; // TODO ideally store this in isolate.state?
|
|
||||||
|
|
||||||
let promise_id = 0;
|
|
||||||
let op = Box::new(futures::future::ok(AsyncResult { result: 0 }));
|
|
||||||
isolate.add_op(promise_id, op, zero_copy_buf.zero_copy_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_poll_async_immediate_ops() {
|
|
||||||
let mut isolate = Isolate::new(async_immediate);
|
|
||||||
js_check(isolate.execute(
|
|
||||||
"setup.js",
|
|
||||||
r#"
|
|
||||||
let nrecv = 0;
|
|
||||||
libdeno.recv(() => {
|
|
||||||
nrecv++;
|
|
||||||
});
|
|
||||||
function assertEq(actual, expected) {
|
|
||||||
if (expected != actual) {
|
|
||||||
throw Error(`actual ${actual} expected ${expected} `);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
));
|
|
||||||
assert_eq!(isolate.test_send_counter, 0);
|
|
||||||
js_check(isolate.execute(
|
|
||||||
"check1.js",
|
|
||||||
r#"
|
|
||||||
assertEq(nrecv, 0);
|
|
||||||
libdeno.send();
|
|
||||||
assertEq(nrecv, 0);
|
|
||||||
"#,
|
|
||||||
));
|
|
||||||
assert_eq!(isolate.test_send_counter, 1);
|
|
||||||
assert_eq!(Ok(Async::Ready(())), isolate.poll());
|
|
||||||
assert_eq!(isolate.test_send_counter, 1);
|
|
||||||
js_check(isolate.execute(
|
|
||||||
"check2.js",
|
|
||||||
r#"
|
|
||||||
assertEq(nrecv, 1);
|
|
||||||
libdeno.send();
|
|
||||||
assertEq(nrecv, 1);
|
|
||||||
"#,
|
|
||||||
));
|
|
||||||
assert_eq!(isolate.test_send_counter, 2);
|
|
||||||
assert_eq!(Ok(Async::Ready(())), isolate.poll());
|
|
||||||
js_check(isolate.execute("check3.js", "assertEq(nrecv, 2)"));
|
|
||||||
assert_eq!(isolate.test_send_counter, 2);
|
|
||||||
// We are idle, so the next poll should be the last.
|
|
||||||
assert_eq!(Ok(Async::Ready(())), isolate.poll());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
86
src/flags.rs
86
src/flags.rs
|
@ -1,14 +1,7 @@
|
||||||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||||
use crate::libdeno;
|
use deno_core::v8_set_flags;
|
||||||
|
|
||||||
use getopts;
|
use getopts;
|
||||||
use getopts::Options;
|
use getopts::Options;
|
||||||
use libc::c_char;
|
|
||||||
use libc::c_int;
|
|
||||||
use std::ffi::CStr;
|
|
||||||
use std::ffi::CString;
|
|
||||||
use std::mem;
|
|
||||||
use std::vec::Vec;
|
|
||||||
|
|
||||||
// Creates vector of strings, Vec<String>
|
// Creates vector of strings, Vec<String>
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -291,80 +284,3 @@ fn test_set_flags_8() {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns args passed to V8, followed by args passed to JS
|
|
||||||
fn v8_set_flags_preprocess(args: Vec<String>) -> (Vec<String>, Vec<String>) {
|
|
||||||
let (rest, mut v8_args) =
|
|
||||||
args.into_iter().partition(|ref a| a.as_str() == "--help");
|
|
||||||
|
|
||||||
// Replace args being sent to V8
|
|
||||||
for a in &mut v8_args {
|
|
||||||
if a == "--v8-options" {
|
|
||||||
mem::swap(a, &mut String::from("--help"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(v8_args, rest)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_v8_set_flags_preprocess_1() {
|
|
||||||
let js_args = v8_set_flags_preprocess(vec![
|
|
||||||
"deno".to_string(),
|
|
||||||
"--v8-options".to_string(),
|
|
||||||
]);
|
|
||||||
assert_eq!(
|
|
||||||
js_args,
|
|
||||||
(vec!["deno".to_string(), "--help".to_string()], vec![])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_v8_set_flags_preprocess_2() {
|
|
||||||
let js_args =
|
|
||||||
v8_set_flags_preprocess(vec!["deno".to_string(), "--help".to_string()]);
|
|
||||||
assert_eq!(
|
|
||||||
js_args,
|
|
||||||
(vec!["deno".to_string()], vec!["--help".to_string()])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pass the command line arguments to v8.
|
|
||||||
// Returns a vector of command line arguments that v8 did not understand.
|
|
||||||
#[cfg_attr(feature = "cargo-clippy", allow(stutter))]
|
|
||||||
pub fn v8_set_flags(args: Vec<String>) -> Vec<String> {
|
|
||||||
// deno_set_v8_flags(int* argc, char** argv) mutates argc and argv to remove
|
|
||||||
// flags that v8 understands.
|
|
||||||
// First parse core args, then convert to a vector of C strings.
|
|
||||||
let (args, rest) = v8_set_flags_preprocess(args);
|
|
||||||
|
|
||||||
// Make a new array, that can be modified by V8::SetFlagsFromCommandLine(),
|
|
||||||
// containing mutable raw pointers to the individual command line args.
|
|
||||||
let mut raw_argv = args
|
|
||||||
.iter()
|
|
||||||
.map(|arg| CString::new(arg.as_str()).unwrap().into_bytes_with_nul())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
let mut c_argv = raw_argv
|
|
||||||
.iter_mut()
|
|
||||||
.map(|arg| arg.as_mut_ptr() as *mut c_char)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
// Store the length of the c_argv array in a local variable. We'll pass
|
|
||||||
// a pointer to this local variable to deno_set_v8_flags(), which then
|
|
||||||
// updates its value.
|
|
||||||
let mut c_argv_len = c_argv.len() as c_int;
|
|
||||||
// Let v8 parse the arguments it recognizes and remove them from c_argv.
|
|
||||||
unsafe {
|
|
||||||
libdeno::deno_set_v8_flags(&mut c_argv_len, c_argv.as_mut_ptr());
|
|
||||||
};
|
|
||||||
// If c_argv_len was updated we have to change the length of c_argv to match.
|
|
||||||
c_argv.truncate(c_argv_len as usize);
|
|
||||||
// Copy the modified arguments list into a proper rust vec and return it.
|
|
||||||
c_argv
|
|
||||||
.iter()
|
|
||||||
.map(|ptr| unsafe {
|
|
||||||
let cstr = CStr::from_ptr(*ptr as *const c_char);
|
|
||||||
let slice = cstr.to_str().unwrap();
|
|
||||||
slice.to_string()
|
|
||||||
}).chain(rest.into_iter())
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,12 +1,6 @@
|
||||||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||||
use crate::libdeno;
|
|
||||||
|
|
||||||
use std::ffi::CStr;
|
|
||||||
|
|
||||||
pub const DENO: &str = env!("CARGO_PKG_VERSION");
|
pub const DENO: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
pub fn v8() -> &'static str {
|
pub fn v8() -> &'static str {
|
||||||
let version = unsafe { libdeno::deno_v8_version() };
|
deno_core::v8_version()
|
||||||
let c_str = unsafe { CStr::from_ptr(version) };
|
|
||||||
c_str.to_str().unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue