2020-04-23 03:34:28 -04:00
|
|
|
// These tests mock out an organizational pattern that we hope to use in Deno.
|
|
|
|
// There we want to wrap v8::Isolate to provide extra functionality at multiple
|
|
|
|
// layers: v8::Isolate -> CoreIsolate -> EsIsolate
|
|
|
|
// This demonstrates how this can be done in a safe way.
|
|
|
|
|
|
|
|
use rusty_v8 as v8;
|
|
|
|
use std::ops::Deref;
|
|
|
|
use std::ops::DerefMut;
|
|
|
|
use std::rc::Rc;
|
|
|
|
use std::sync::atomic::AtomicUsize;
|
|
|
|
use std::sync::atomic::Ordering;
|
|
|
|
use std::sync::Once;
|
|
|
|
|
|
|
|
struct CoreIsolate(v8::OwnedIsolate);
|
|
|
|
|
|
|
|
struct CoreIsolateState {
|
|
|
|
drop_count: Rc<AtomicUsize>,
|
|
|
|
i: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for CoreIsolateState {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
self.drop_count.fetch_add(1, Ordering::SeqCst);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl CoreIsolate {
|
|
|
|
fn new(drop_count: Rc<AtomicUsize>) -> CoreIsolate {
|
|
|
|
static START: Once = Once::new();
|
|
|
|
START.call_once(|| {
|
|
|
|
v8::V8::initialize_platform(v8::new_default_platform().unwrap());
|
|
|
|
v8::V8::initialize();
|
|
|
|
});
|
|
|
|
let mut isolate = v8::Isolate::new(Default::default());
|
|
|
|
let state = CoreIsolateState { drop_count, i: 0 };
|
|
|
|
isolate.set_slot(state);
|
|
|
|
CoreIsolate(isolate)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns false if there was an error.
|
|
|
|
fn execute(&mut self, code: &str) -> bool {
|
2020-06-03 01:38:34 -04:00
|
|
|
let scope = &mut v8::HandleScope::new(&mut self.0);
|
2020-04-23 03:34:28 -04:00
|
|
|
let context = v8::Context::new(scope);
|
2020-06-03 01:38:34 -04:00
|
|
|
let scope = &mut v8::ContextScope::new(scope, context);
|
2020-04-23 03:34:28 -04:00
|
|
|
let source = v8::String::new(scope, code).unwrap();
|
2020-06-26 08:47:13 -04:00
|
|
|
let script = v8::Script::compile(scope, source, None).unwrap();
|
2020-06-25 20:56:24 -04:00
|
|
|
let r = script.run(scope);
|
2020-04-23 03:34:28 -04:00
|
|
|
r.is_some()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_i(&self) -> usize {
|
|
|
|
let s = self.0.get_slot::<CoreIsolateState>().unwrap();
|
|
|
|
s.i
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_i(&self, i: usize) {
|
|
|
|
let mut s = self.0.get_slot_mut::<CoreIsolateState>().unwrap();
|
|
|
|
s.i = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Deref for CoreIsolate {
|
|
|
|
type Target = v8::Isolate;
|
|
|
|
|
|
|
|
fn deref(&self) -> &v8::Isolate {
|
|
|
|
&self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DerefMut for CoreIsolate {
|
|
|
|
fn deref_mut(&mut self) -> &mut v8::Isolate {
|
|
|
|
&mut self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct EsIsolate(CoreIsolate);
|
|
|
|
|
|
|
|
struct EsIsolateState {
|
|
|
|
drop_count: Rc<AtomicUsize>,
|
|
|
|
x: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for EsIsolateState {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
self.drop_count.fetch_add(1, Ordering::SeqCst);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl EsIsolate {
|
|
|
|
fn new(drop_count: Rc<AtomicUsize>) -> Self {
|
|
|
|
let mut core_isolate = CoreIsolate::new(drop_count.clone());
|
|
|
|
let state = EsIsolateState {
|
|
|
|
drop_count,
|
|
|
|
x: false,
|
|
|
|
};
|
|
|
|
core_isolate.set_slot(state);
|
|
|
|
EsIsolate(core_isolate)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_x(&self) -> bool {
|
|
|
|
let state = self.0.get_slot::<EsIsolateState>().unwrap();
|
|
|
|
state.x
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_x(&self, x: bool) {
|
|
|
|
let mut state = self.0.get_slot_mut::<EsIsolateState>().unwrap();
|
|
|
|
state.x = x;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Deref for EsIsolate {
|
|
|
|
type Target = CoreIsolate;
|
|
|
|
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
&self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DerefMut for EsIsolate {
|
|
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
|
|
&mut self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn slots_layer1() {
|
|
|
|
let drop_count = Rc::new(AtomicUsize::new(0));
|
|
|
|
let mut core_isolate = CoreIsolate::new(drop_count.clone());
|
2020-04-23 13:48:07 -04:00
|
|
|
// The existence of a IsolateHandle that outlives the isolate should not
|
|
|
|
// inhibit dropping of slot contents.
|
|
|
|
let isolate_handle = core_isolate.thread_safe_handle();
|
2020-04-23 03:34:28 -04:00
|
|
|
assert!(core_isolate.execute("1 + 1"));
|
|
|
|
assert!(!core_isolate.execute("throw 'foo'"));
|
|
|
|
assert_eq!(0, core_isolate.get_i());
|
|
|
|
core_isolate.set_i(123);
|
|
|
|
assert_eq!(123, core_isolate.get_i());
|
|
|
|
assert_eq!(drop_count.load(Ordering::SeqCst), 0);
|
|
|
|
// Check that we can deref CoreIsolate by running a random v8::Isolate method
|
2020-08-23 10:25:16 -04:00
|
|
|
core_isolate.perform_microtask_checkpoint();
|
2020-04-23 03:34:28 -04:00
|
|
|
drop(core_isolate);
|
|
|
|
assert_eq!(drop_count.load(Ordering::SeqCst), 1);
|
2020-04-23 13:48:07 -04:00
|
|
|
drop(isolate_handle);
|
2020-04-23 03:34:28 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn slots_layer2() {
|
|
|
|
let drop_count = Rc::new(AtomicUsize::new(0));
|
|
|
|
let mut es_isolate = EsIsolate::new(drop_count.clone());
|
|
|
|
// We can deref to CoreIsolate and use execute...
|
|
|
|
assert!(es_isolate.execute("1 + 1"));
|
|
|
|
assert!(!es_isolate.execute("throw 'bar'"));
|
|
|
|
// We can use get_x set_x
|
|
|
|
assert!(!es_isolate.get_x());
|
|
|
|
es_isolate.set_x(true);
|
|
|
|
assert!(es_isolate.get_x());
|
|
|
|
// Check that we can deref all the way to a v8::Isolate method
|
2020-08-23 10:25:16 -04:00
|
|
|
es_isolate.perform_microtask_checkpoint();
|
2020-04-23 03:34:28 -04:00
|
|
|
|
|
|
|
// When we drop, both CoreIsolateState and EsIsolateState should be dropped.
|
|
|
|
assert_eq!(drop_count.load(Ordering::SeqCst), 0);
|
|
|
|
drop(es_isolate);
|
|
|
|
assert_eq!(drop_count.load(Ordering::SeqCst), 2);
|
|
|
|
}
|