From f106ab5b10e0e5cfa64fb4713d382040643a5541 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Wed, 4 Dec 2019 14:12:27 +0100 Subject: [PATCH] Running a minimal script (#32) --- BUILD.gn | 2 ++ src/context.cc | 24 ++++++++++++++++++++++ src/context.rs | 32 +++++++++++++++++++++++++++++ src/handle_scope.rs | 6 +++--- src/lib.rs | 7 +++++++ src/local.rs | 29 +++++++++++++++++++-------- src/number.rs | 6 +++--- src/script.cc | 20 ++++++++++++++++++ src/script.rs | 49 +++++++++++++++++++++++++++++++++++++++++++++ src/string.cc | 2 +- src/string.rs | 2 +- src/support.h | 24 ++++++++++++++++++++-- tests/test_api.rs | 29 ++++++++++++++++++++++++++- 13 files changed, 213 insertions(+), 19 deletions(-) create mode 100644 src/context.cc create mode 100644 src/context.rs create mode 100644 src/script.cc create mode 100644 src/script.rs diff --git a/BUILD.gn b/BUILD.gn index d6eb5e51..afede21a 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -5,6 +5,7 @@ v8_static_library("rusty_v8") { sources = [ "src/V8.cc", "src/array_buffer.cc", + "src/context.cc", "src/handle_scope.cc", "src/inspector/channel.cc", "src/inspector/client.cc", @@ -14,6 +15,7 @@ v8_static_library("rusty_v8") { "src/number.cc", "src/platform/mod.cc", "src/platform/task.cc", + "src/script.cc", "src/string.cc", ] deps = [ diff --git a/src/context.cc b/src/context.cc new file mode 100644 index 00000000..471a9c4d --- /dev/null +++ b/src/context.cc @@ -0,0 +1,24 @@ +#include + +#include "v8/include/v8.h" + +using namespace v8; + +extern "C" { +Context* v8__Context__New(Isolate* isolate) { + // TODO: optional arguments. + return *Context::New(isolate); +} + +void v8__Context__Enter(Context& self) { + self.Enter(); +} + +void v8__Context__Exit(Context& self) { + self.Exit(); +} + +Isolate* v8__Context__GetIsolate(Context& self) { + return self.GetIsolate(); +} +} diff --git a/src/context.rs b/src/context.rs new file mode 100644 index 00000000..1bf51436 --- /dev/null +++ b/src/context.rs @@ -0,0 +1,32 @@ +use crate::isolate::CxxIsolate; +use crate::isolate::LockedIsolate; +use crate::support::Opaque; +use crate::HandleScope; +use crate::Local; + +extern "C" { + fn v8__Context__New(isolate: *mut CxxIsolate) -> *mut Context; + fn v8__Context__Enter(this: &mut Context); + fn v8__Context__Exit(this: &mut Context); + fn v8__Context__GetIsolate(this: &mut Context) -> *mut CxxIsolate; +} + +#[repr(C)] +pub struct Context(Opaque); + +impl Context { + pub fn new<'sc>(scope: &mut HandleScope<'sc>) -> Local<'sc, Context> { + // TODO: optional arguments; + unsafe { Local::from_raw(v8__Context__New(scope.cxx_isolate())).unwrap() } + } + + pub fn enter(&mut self) { + // TODO: enter/exit should be controlled by a scope. + unsafe { v8__Context__Enter(self) }; + } + + pub fn exit(&mut self) { + // TODO: enter/exit should be controlled by a scope. + unsafe { v8__Context__Exit(self) }; + } +} diff --git a/src/handle_scope.rs b/src/handle_scope.rs index cf0b4504..6bc5204f 100644 --- a/src/handle_scope.rs +++ b/src/handle_scope.rs @@ -25,10 +25,10 @@ impl<'sc> HandleScope<'sc> { { let mut scope: MaybeUninit = MaybeUninit::uninit(); unsafe { v8__HandleScope__CONSTRUCT(&mut scope, parent.cxx_isolate()) }; - let mut scope = unsafe { &mut *(&mut scope as *mut _ as *mut HandleScope) }; - f(&mut scope); + let scope = unsafe { &mut *(scope.as_mut_ptr()) }; + f(scope); - unsafe { v8__HandleScope__DESTRUCT(&mut scope) }; + unsafe { v8__HandleScope__DESTRUCT(scope) }; } } diff --git a/src/lib.rs b/src/lib.rs index e0596d3d..36dd4ba1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,11 +11,13 @@ extern crate bitflags; extern crate lazy_static; extern crate libc; +mod context; mod handle_scope; mod isolate; mod local; mod locker; mod number; +mod script; mod string; mod support; @@ -27,10 +29,15 @@ pub mod platform; #[allow(non_snake_case)] pub mod V8; +pub use context::Context; pub use handle_scope::HandleScope; pub use isolate::Isolate; pub use local::Local; pub use locker::Locker; pub use number::{Integer, Number}; +pub use script::{Script, ScriptOrigin}; pub use string::NewStringType; pub use string::String; + +#[repr(C)] +pub struct Value(support::Opaque); diff --git a/src/local.rs b/src/local.rs index 3356248e..5662d211 100644 --- a/src/local.rs +++ b/src/local.rs @@ -1,20 +1,33 @@ +use std::marker::PhantomData; use std::ops::Deref; +use std::ops::DerefMut; +use std::ptr::NonNull; -pub struct Local<'sc, T>(&'sc T); +pub struct Local<'sc, T>(NonNull, PhantomData<&'sc ()>); + +impl<'sc, T> Copy for Local<'sc, T> {} + +impl<'sc, T> Clone for Local<'sc, T> { + fn clone(&self) -> Self { + Self(self.0, self.1) + } +} impl<'sc, T> Local<'sc, T> { - pub unsafe fn from_raw(ptr: *const T) -> Option { - if ptr.is_null() { - None - } else { - Some(Self(&*ptr)) - } + pub(crate) unsafe fn from_raw(ptr: *mut T) -> Option { + Some(Self(NonNull::new(ptr)?, PhantomData)) } } impl<'sc, T> Deref for Local<'sc, T> { type Target = T; fn deref(&self) -> &T { - &self.0 + unsafe { self.0.as_ref() } + } +} + +impl<'sc, T> DerefMut for Local<'sc, T> { + fn deref_mut(&mut self) -> &mut T { + unsafe { self.0.as_mut() } } } diff --git a/src/number.rs b/src/number.rs index d2413dc5..4aea9c11 100644 --- a/src/number.rs +++ b/src/number.rs @@ -7,13 +7,13 @@ use crate::HandleScope; use crate::Local; extern "C" { - fn v8__Number__New(isolate: &mut CxxIsolate, value: f64) -> *const Number; + fn v8__Number__New(isolate: &mut CxxIsolate, value: f64) -> *mut Number; fn v8__Number__Value(this: &Number) -> f64; - fn v8__Integer__New(isolate: &mut CxxIsolate, value: i32) -> *const Integer; + fn v8__Integer__New(isolate: &mut CxxIsolate, value: i32) -> *mut Integer; fn v8__Integer__NewFromUnsigned( isolate: &mut CxxIsolate, value: u32, - ) -> *const Integer; + ) -> *mut Integer; fn v8__Integer__Value(this: &Integer) -> i64; } diff --git a/src/script.cc b/src/script.cc new file mode 100644 index 00000000..40a1dfa6 --- /dev/null +++ b/src/script.cc @@ -0,0 +1,20 @@ +#include + +#include "support.h" +#include "v8/include/v8.h" + +using namespace v8; +using namespace support; + +extern "C" { +Script* v8__Script__Compile(Context* context, + String* source, + ScriptOrigin* origin) { + return maybe_local_to_ptr( + Script::Compile(ptr_to_local(context), ptr_to_local(source), origin)); +} + +Value* v8__Script__Run(Script& script, Context* context) { + return maybe_local_to_ptr(script.Run(ptr_to_local(context))); +} +} diff --git a/src/script.rs b/src/script.rs new file mode 100644 index 00000000..11951e50 --- /dev/null +++ b/src/script.rs @@ -0,0 +1,49 @@ +use std::ptr::null; + +use crate::support::Opaque; +use crate::Context; +use crate::HandleScope; +use crate::Local; +use crate::String; +use crate::Value; + +extern "C" { + fn v8__Script__Compile( + context: *mut Context, + source: *mut String, + origin: *const ScriptOrigin, + ) -> *mut Script; + fn v8__Script__Run(this: &mut Script, context: *mut Context) -> *mut Value; +} + +#[repr(C)] +pub struct Script(Opaque); +#[repr(C)] +pub struct ScriptOrigin(Opaque); + +impl Script { + pub fn compile<'sc>( + _scope: &mut HandleScope<'sc>, + mut context: Local<'_, Context>, + mut source: Local<'_, String>, + origin: Option<&'_ ScriptOrigin>, + ) -> Option> { + // TODO: use the type system to enforce that a Context has been entered. + // TODO: `context` and `source` probably shouldn't be mut. + unsafe { + Local::from_raw(v8__Script__Compile( + &mut *context, + &mut *source, + origin.map(|r| r as *const _).unwrap_or(null()), + )) + } + } + + pub fn run<'sc>( + &mut self, + _scope: &mut HandleScope<'sc>, + mut context: Local<'_, Context>, + ) -> Option> { + unsafe { Local::from_raw(v8__Script__Run(self, &mut *context)) } + } +} diff --git a/src/string.cc b/src/string.cc index 3539bf18..b88bd210 100644 --- a/src/string.cc +++ b/src/string.cc @@ -11,7 +11,7 @@ String* v8__String__NewFromUtf8(Isolate* isolate, const char* data, NewStringType type, int length) { - return maybe_local_ptr(String::NewFromUtf8(isolate, data, type, length)); + return maybe_local_to_ptr(String::NewFromUtf8(isolate, data, type, length)); } int v8__String__Utf8Length(const String& self, Isolate* isolate) { diff --git a/src/string.rs b/src/string.rs index 9b3564e9..d873d735 100644 --- a/src/string.rs +++ b/src/string.rs @@ -17,7 +17,7 @@ extern "C" { data: *const char, new_type: NewStringType, length: int, - ) -> *const String; + ) -> *mut String; fn v8__String__Utf8Length(this: &String, isolate: *mut CxxIsolate) -> int; diff --git a/src/support.h b/src/support.h index fc362b54..501b9b65 100644 --- a/src/support.h +++ b/src/support.h @@ -35,9 +35,29 @@ void construct_in_place(uninit_t& buf, Args... args) { } } // namespace support + template -inline static T* maybe_local_ptr(v8::MaybeLocal value) { - return *value.FromMaybe(v8::Local()); +inline static T* local_to_ptr(v8::Local local) { + return *local; +} + +template +inline static v8::Local ptr_to_local(T* ptr) { + static_assert(sizeof(v8::Local) == sizeof(T*), ""); + auto local = *reinterpret_cast*>(&ptr); + assert(*local == ptr); + return local; +} + +template +inline static T* maybe_local_to_ptr(v8::MaybeLocal local) { + return *local.FromMaybe(v8::Local()); +} + +template +inline static v8::MaybeLocal ptr_to_maybe_local(T* ptr) { + static_assert(sizeof(v8::MaybeLocal) == sizeof(T*), ""); + return *reinterpret_cast*>(&ptr); } #endif // SUPPORT_H_ diff --git a/tests/test_api.rs b/tests/test_api.rs index c4c4ad30..115baa2e 100644 --- a/tests/test_api.rs +++ b/tests/test_api.rs @@ -3,6 +3,7 @@ extern crate lazy_static; use rusty_v8 as v8; +use std::default::Default; use std::sync::Mutex; lazy_static! { @@ -25,7 +26,7 @@ impl Drop for TestGuard { } } -fn setup() -> TestGuard { +pub(crate) fn setup() -> TestGuard { let mut g = INIT_LOCK.lock().unwrap(); *g += 1; if *g == 1 { @@ -104,6 +105,32 @@ fn isolate_new() { drop(g); } +#[test] +fn script_compile_and_run() { + setup(); + let mut params = v8::Isolate::create_params(); + params.set_array_buffer_allocator( + v8::array_buffer::Allocator::new_default_allocator(), + ); + let mut isolate = v8::Isolate::new(params); + let mut locker = v8::Locker::new(&mut isolate); + + v8::HandleScope::enter(&mut locker, |s| { + let mut context = v8::Context::new(s); + context.enter(); + let source = + v8::String::new(s, "'Hello ' + 13 + 'th planet'", Default::default()) + .unwrap(); + let mut script = v8::Script::compile(s, context, source, None).unwrap(); + source.to_rust_string_lossy(s); + let result = script.run(s, context).unwrap(); + // TODO: safer casts. + let result: v8::Local = + unsafe { std::mem::transmute_copy(&result) }; + assert_eq!(result.to_rust_string_lossy(s), "Hello 13th planet"); + }); +} + #[test] fn get_version() { assert!(v8::V8::get_version().len() > 3);