Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for JS getters and setters #358

Merged
merged 3 commits into from
Feb 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 2 additions & 16 deletions src/web_executor/js.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::web_executor::js::v8::V8Engine;
pub use compile::*;
pub use context::*;
pub use function::*;
pub use object::*;
pub use runtime::*;
pub use value::*;
pub use value_conversion::*;
Expand All @@ -15,6 +16,7 @@ use crate::types::Result;
mod compile;
mod context;
mod function;
mod object;
mod runtime;
pub mod v8;
mod value;
Expand Down Expand Up @@ -45,22 +47,6 @@ lazy_static! {
pub static ref RUNTIME: Mutex<V8Engine<'static>> = Mutex::new(V8Engine::new());
}

pub trait JSObject {
type Value: JSValue;
type Function: JSFunction;
type FunctionVariadic: JSFunctionVariadic;

fn set_property(&self, name: &str, value: &Self::Value) -> Result<()>;

fn get_property(&self, name: &str) -> Result<Self::Value>;

fn call_method(&self, name: &str, args: &[&Self::Value]) -> Result<Self::Value>;

fn set_method(&self, name: &str, func: &Self::Function) -> Result<()>;

fn set_method_variadic(&self, name: &str, func: &Self::FunctionVariadic) -> Result<()>;
}

pub trait JSArray {
type Value: JSValue;

Expand Down
5 changes: 5 additions & 0 deletions src/web_executor/js/function.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::types::Result;
use crate::web_executor::js::{JSContext, JSError, JSObject, JSRuntime, JSValue};
use core::fmt::Display;

struct Function<T: JSFunction>(pub T);

Expand Down Expand Up @@ -31,6 +32,8 @@ pub trait JSFunctionCallBack {
self.len() == 0
}

fn error(&mut self, error: impl Display);

fn ret(&mut self, value: Self::Value);
}

Expand Down Expand Up @@ -77,6 +80,8 @@ pub trait JSFunctionCallBackVariadic {
self.len() == 0
}

fn error(&mut self, error: impl Display);

fn ret(&mut self, value: Self::Value);
}

Expand Down
54 changes: 54 additions & 0 deletions src/web_executor/js/object.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use crate::web_executor::js::{JSContext, JSFunction, JSFunctionVariadic, JSValue};
use core::fmt::Display;

pub trait JSObject {
type Value: JSValue;
type Function: JSFunction;
type FunctionVariadic: JSFunctionVariadic;
type GetterCB: JSGetterCallback;
type SetterCB: JSSetterCallback;

fn set_property(&self, name: &str, value: &Self::Value) -> crate::types::Result<()>;

fn get_property(&self, name: &str) -> crate::types::Result<Self::Value>;

fn call_method(&self, name: &str, args: &[&Self::Value]) -> crate::types::Result<Self::Value>;

fn set_method(&self, name: &str, func: &Self::Function) -> crate::types::Result<()>;

fn set_method_variadic(
&self,
name: &str,
func: &Self::FunctionVariadic,
) -> crate::types::Result<()>;

#[allow(clippy::type_complexity)]
fn set_property_accessor(
&self,
name: &str,
getter: Box<dyn Fn(&mut Self::GetterCB)>,
setter: Box<dyn Fn(&mut Self::SetterCB)>,
) -> crate::types::Result<()>;
}

pub trait JSGetterCallback {
type Value: JSValue;
type Context: JSContext;

fn context(&mut self) -> &mut Self::Context;

fn error(&mut self, error: impl Display);

fn ret(&mut self, value: Self::Value);
}

pub trait JSSetterCallback {
type Value: JSValue;
type Context: JSContext;

fn context(&mut self) -> &mut Self::Context;

fn error(&mut self, error: impl Display);

fn value(&mut self) -> &Self::Value;
}
30 changes: 11 additions & 19 deletions src/web_executor/js/v8.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ mod compile;
mod context;
mod function;
mod object;
mod utils;
mod value;

// status of the V8 engine
Expand All @@ -43,15 +42,26 @@ impl Default for V8Engine<'_> {
}
}

const MAX_V8_INIT_SECONDS: u64 = 10;

impl V8Engine<'_> {
pub fn initialize() {
if PLATFORM_INITIALIZED.load(Ordering::SeqCst) {
return;
}

let mut wait_time = MAX_V8_INIT_SECONDS * 1000;

if PLATFORM_INITIALIZING.load(Ordering::SeqCst) {
while !PLATFORM_INITIALIZED.load(Ordering::SeqCst) {
std::thread::sleep(std::time::Duration::from_millis(10));
wait_time -= 10;
if wait_time <= 9 {
panic!(
"V8 initialization timed out after {} seconds",
MAX_V8_INIT_SECONDS
);
}
}
return;
}
Expand Down Expand Up @@ -108,24 +118,6 @@ mod tests {
assert!(PLATFORM_INITIALIZED.load(Ordering::SeqCst));
}

// #[test]
// fn v8_bindings_test() {
// let platform = v8::new_default_platform(0, false).make_shared();
// v8::V8::initialize_platform(platform);
// v8::V8::initialize();
//
// let isolate = &mut v8::Isolate::new(Default::default());
// let hs = &mut v8::HandleScope::new(isolate);
// let c = v8::Context::new(hs);
// let s = &mut v8::ContextScope::new(hs, c);
//
// let code = v8::String::new(s, "console.log(\"Hello World!\"); 1234").unwrap();
//
// let value = v8::Script::compile(s, code, None).unwrap().run(s).unwrap();
//
// println!("{}", value.to_rust_string_lossy(s));
// }

#[test]
fn v8_js_execution() {
let mut engine = crate::web_executor::js::v8::V8Engine::new();
Expand Down
83 changes: 62 additions & 21 deletions src/web_executor/js/v8/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ use crate::web_executor::js::{JSContext, JSError, JSRuntime};
/// SAFETY: This is NOT thread safe, as the rest of the engine is not thread safe.
/// This struct uses `NonNull` internally to store pointers to the V8Context "values" in one struct.
pub struct V8Ctx<'a> {
isolate: NonNull<OwnedIsolate>,
handle_scope: NonNull<HandleScopeType<'a>>,
ctx: NonNull<Local<'a, v8::Context>>,
context_scope: NonNull<ContextScope<'a, HandleScope<'a>>>,
pub(crate) isolate: NonNull<OwnedIsolate>,
pub(crate) handle_scope: NonNull<HandleScopeType<'a>>,
pub(crate) ctx: NonNull<Local<'a, v8::Context>>,
pub(crate) context_scope: NonNull<ContextScope<'a, HandleScope<'a>>>,
copied: Copied,
}

Expand All @@ -41,8 +41,9 @@ impl Copied {
}
}

enum HandleScopeType<'a> {
pub(crate) enum HandleScopeType<'a> {
WithContext(HandleScope<'a>),
WithContextRef(&'a mut HandleScope<'a>),
WithoutContext(HandleScope<'a, ()>),
CallbackScope(CallbackScope<'a>),
}
Expand All @@ -52,11 +53,12 @@ impl<'a> HandleScopeType<'a> {
Self::WithoutContext(HandleScope::new(isolate))
}

fn get(&mut self) -> &mut HandleScope<'a, ()> {
pub(crate) fn get(&mut self) -> &mut HandleScope<'a, ()> {
match self {
Self::WithContext(scope) => scope,
Self::WithoutContext(scope) => scope,
Self::CallbackScope(scope) => scope,
Self::WithContextRef(scope) => scope,
}
}
}
Expand Down Expand Up @@ -149,9 +151,13 @@ impl<'a> V8Ctx<'a> {
}
}

pub(crate) fn ctx_from_function_callback_info(mut scope: CallbackScope) -> Result<V8Context> {
pub(crate) fn ctx_from_scope_isolate<'a>(
scope: HandleScopeType<'a>,
ctx: Local<'a, v8::Context>,
isolate: NonNull<OwnedIsolate>,
) -> std::result::Result<V8Context<'a>, (HandleScopeType<'a>, Error)> {
let mut v8_ctx = V8Ctx {
isolate: NonNull::dangling(),
isolate,
handle_scope: NonNull::dangling(),
ctx: NonNull::dangling(),
context_scope: NonNull::dangling(),
Expand All @@ -160,25 +166,30 @@ pub(crate) fn ctx_from_function_callback_info(mut scope: CallbackScope) -> Resul
handle_scope: false,
ctx: false,
context_scope: false,
}, //TODO: figure out what to deallocate
},
};

let ctx = Box::new(scope.get_current_context());
let ctx = Box::new(ctx);

let Some(ctx) = NonNull::new(Box::into_raw(ctx)) else {
return Err(Error::JS(JSError::Compile(
"Failed to create context".to_owned(),
)));
return Err((
scope,
Error::JS(JSError::Compile("Failed to create context".to_owned())),
));
};

v8_ctx.ctx = ctx;

let scope = Box::new(HandleScopeType::CallbackScope(scope));
let scope = Box::new(scope);

let Some(scope) = NonNull::new(Box::into_raw(scope)) else {
return Err(Error::JS(JSError::Compile(
"Failed to create handle scope".to_owned(),
)));
let raw_scope = Box::into_raw(scope);
let Some(scope) = NonNull::new(raw_scope) else {
//SAFETY: we just created this, so it is safe to convert it back to a Box
let scope = unsafe { Box::from_raw(raw_scope) };
return Err((
*scope,
Error::JS(JSError::Compile("Failed to create handle scope".to_owned())),
));
};

v8_ctx.handle_scope = scope;
Expand All @@ -189,9 +200,15 @@ pub(crate) fn ctx_from_function_callback_info(mut scope: CallbackScope) -> Resul
));

let Some(ctx_scope) = NonNull::new(Box::into_raw(ctx_scope)) else {
return Err(Error::JS(JSError::Compile(
"Failed to create context scope".to_owned(),
)));
//SAFETY: we just created this, so it is safe to convert it back to a Box
let scope = unsafe { Box::from_raw(v8_ctx.handle_scope.as_ptr()) };

return Err((
*scope,
Error::JS(JSError::Compile(
"Failed to create context scope".to_owned(),
)),
));
};

v8_ctx.context_scope = ctx_scope;
Expand All @@ -200,6 +217,30 @@ pub(crate) fn ctx_from_function_callback_info(mut scope: CallbackScope) -> Resul
// Ok(v8_ctx)
}

pub(crate) fn ctx_from_function_callback_info(
mut scope: CallbackScope,
isolate: NonNull<OwnedIsolate>,
) -> std::result::Result<V8Context, (HandleScopeType, Error)> {
let ctx = scope.get_current_context();
let scope = HandleScopeType::CallbackScope(scope);

ctx_from_scope_isolate(scope, ctx, isolate)
}

pub(crate) fn ctx_from<'a>(
scope: &'a mut HandleScope,
isolate: NonNull<OwnedIsolate>,
) -> std::result::Result<V8Context<'a>, (HandleScopeType<'a>, Error)> {
let ctx = scope.get_current_context();

//SAFETY: This can only shorten the lifetime of the scope, which is fine. (we borrow it for 'a and it is '2, which will always be longer than 'a)
let scope = unsafe { std::mem::transmute(scope) };

let scope = HandleScopeType::WithContextRef(scope);

ctx_from_scope_isolate(scope, ctx, isolate)
}

impl Drop for V8Ctx<'_> {
fn drop(&mut self) {
// order is important here: context scope, then handle scope (and ctx), then isolate
Expand Down
Loading
Loading