summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/flags.rs84
-rw-r--r--core/http_bench.js111
-rw-r--r--core/http_bench.rs254
-rw-r--r--core/isolate.rs548
-rw-r--r--core/lib.rs364
-rw-r--r--src/flags.rs86
-rw-r--r--src/version.rs8
7 files changed, 883 insertions, 572 deletions
diff --git a/core/flags.rs b/core/flags.rs
new file mode 100644
index 000000000..2da35734d
--- /dev/null
+++ b/core/flags.rs
@@ -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()])
+ );
+}
diff --git a/core/http_bench.js b/core/http_bench.js
index 29a79c436..aae15a72c 100644
--- a/core/http_bench.js
+++ b/core/http_bench.js
@@ -1,24 +1,57 @@
// 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
// 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_ACCEPT = 2;
const OP_READ = 3;
const OP_WRITE = 4;
const OP_CLOSE = 5;
+const INDEX_START = 0;
+const INDEX_END = 1;
+const NUM_RECORDS = 128;
+const RECORD_SIZE = 4;
+
+const shared32 = new Int32Array(libdeno.shared);
+
+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;
+}
-const NUM_RECORDS = (shared32.length - INDEX_RECORDS) / RECORD_SIZE;
-if (NUM_RECORDS != 100) {
- throw Error("expected 100 entries");
+function recordsSize() {
+ return shared32[INDEX_END] - shared32[INDEX_START];
}
const requestBuf = new Uint8Array(64 * 1024);
@@ -39,51 +72,35 @@ function createResolvable() {
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> */
-function sendAsync(op, arg, zeroCopyData) {
- const id = nextPromiseId++;
+function sendAsync(opId, arg, zeroCopyData) {
+ const promiseId = nextPromiseId++;
const p = createResolvable();
- shared32[INDEX_NUM_RECORDS] = 1;
- setRecord(0, RECORD_OFFSET_PROMISE_ID, id);
- setRecord(0, RECORD_OFFSET_OP, op);
- setRecord(0, RECORD_OFFSET_ARG, arg);
- setRecord(0, RECORD_OFFSET_RESULT, -1);
- promiseMap.set(id, p);
+ recordsReset();
+ recordsPush(promiseId, opId, arg, -1);
+ promiseMap.set(promiseId, p);
libdeno.send(null, zeroCopyData);
return p;
}
/** Returns u32 number */
-function sendSync(op, arg) {
- shared32[INDEX_NUM_RECORDS] = 1;
- setRecord(0, RECORD_OFFSET_PROMISE_ID, 0);
- setRecord(0, RECORD_OFFSET_OP, op);
- setRecord(0, RECORD_OFFSET_ARG, arg);
- setRecord(0, RECORD_OFFSET_RESULT, -1);
+function sendSync(opId, arg) {
+ recordsReset();
+ recordsPush(0, opId, arg, -1);
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() {
- for (let i = 0; i < shared32[INDEX_NUM_RECORDS]; i++) {
- let id = getRecord(i, RECORD_OFFSET_PROMISE_ID);
- const p = promiseMap.get(id);
- promiseMap.delete(id);
- p.resolve(getRecord(i, RECORD_OFFSET_RESULT));
+ while (recordsSize() > 0) {
+ const { promiseId, result } = recordsShift();
+ const p = promiseMap.get(promiseId);
+ promiseMap.delete(promiseId);
+ p.resolve(result);
}
}
@@ -132,7 +149,7 @@ async function serve(rid) {
async function main() {
libdeno.recv(handleAsyncMsgFromRust);
- libdeno.print("http_bench.js start");
+ libdeno.print("http_bench.js start\n");
const listenerRid = listen();
libdeno.print(`listening http://127.0.0.1:4544/ rid = ${listenerRid}`);
diff --git a/core/http_bench.rs b/core/http_bench.rs
index 3da30433a..8e5a0e10c 100644
--- a/core/http_bench.rs
+++ b/core/http_bench.rs
@@ -12,18 +12,11 @@ extern crate log;
#[macro_use]
extern crate lazy_static;
-use deno_core::deno_buf;
-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 deno_core::*;
use futures::future::lazy;
use std::collections::HashMap;
use std::env;
+use std::mem;
use std::net::SocketAddr;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
@@ -36,11 +29,148 @@ const OP_READ: i32 = 3;
const OP_WRITE: i32 = 4;
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() {
let js_source = include_str!("http_bench.js");
- let isolate = deno_core::Isolate::new(recv_cb);
let main_future = lazy(move || {
+ let isolate = deno_core::Isolate::new(HttpBench::new());
+
// TODO currently isolate.execute() must be run inside tokio, hence the
// lazy(). It would be nice to not have that contraint. Probably requires
// using v8::MicrotasksPolicy::kExplicit
@@ -77,69 +207,7 @@ fn new_rid() -> i32 {
rid as i32
}
-fn recv_cb(isolate: &mut Isolate, zero_copy_buf: deno_buf) {
- 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> {
+fn op_accept(listener_rid: i32) -> Box<HttpBenchOp> {
debug!("accept {}", listener_rid);
Box::new(
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);
match maybe_repr {
Some(Repr::TcpListener(ref mut listener)) => listener.poll_accept(),
- _ => panic!("bad rid"),
+ _ => panic!("bad rid {}", listener_rid),
}
}).and_then(move |(stream, addr)| {
debug!("accept success {}", addr);
@@ -156,12 +224,36 @@ fn op_accept(listener_rid: i32) -> Box<Op> {
let mut guard = RESOURCE_TABLE.lock().unwrap();
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);
Box::new(
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| {
debug!("read success {}", nread);
- Ok(AsyncResult {
- result: nread as i32,
- })
+ Ok(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);
Box::new(
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| {
debug!("write success {}", nwritten);
- Ok(AsyncResult {
- result: nwritten as i32,
- })
+ Ok(nwritten as i32)
}),
)
}
diff --git a/core/isolate.rs b/core/isolate.rs
new file mode 100644
index 000000000..cdda7b815
--- /dev/null
+++ b/core/isolate.rs
@@ -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());
+ }
+
+}
diff --git a/core/lib.rs b/core/lib.rs
index 6416704e2..66233ddfc 100644
--- a/core/lib.rs
+++ b/core/lib.rs
@@ -1,364 +1,28 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
#[macro_use]
extern crate log;
extern crate futures;
extern crate libc;
+mod flags;
+mod isolate;
mod js_errors;
mod libdeno;
-mod shared;
+pub use crate::flags::v8_set_flags;
+pub use crate::isolate::*;
pub use crate::js_errors::*;
pub use crate::libdeno::deno_buf;
-pub use crate::shared::*;
-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 use crate::libdeno::deno_mod;
-pub struct Isolate {
- libdeno_isolate: *const libdeno::isolate,
- pending_ops: HashMap<i32, PendingOp>, // promise_id -> op
- polled_recently: bool,
- recv_cb: RecvCallback,
-
- 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);
-
-pub const NUM_RECORDS: usize = 100;
-
-// 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,
+pub fn v8_version() -> &'static str {
+ use std::ffi::CStr;
+ let version = unsafe { libdeno::deno_v8_version() };
+ let c_str = unsafe { CStr::from_ptr(version) };
+ c_str.to_str().unwrap()
}
-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());
- }
+#[test]
+fn test_v8_version() {
+ assert!(v8_version().len() > 3);
}
diff --git a/src/flags.rs b/src/flags.rs
index 376e0ab95..471fc58ed 100644
--- a/src/flags.rs
+++ b/src/flags.rs
@@ -1,14 +1,7 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
-use crate::libdeno;
-
+use deno_core::v8_set_flags;
use getopts;
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>
#[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()
-}
diff --git a/src/version.rs b/src/version.rs
index 1cf082257..e6ec9008b 100644
--- a/src/version.rs
+++ b/src/version.rs
@@ -1,12 +1,6 @@
// 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 fn v8() -> &'static str {
- let version = unsafe { libdeno::deno_v8_version() };
- let c_str = unsafe { CStr::from_ptr(version) };
- c_str.to_str().unwrap()
+ deno_core::v8_version()
}