Skip to content

Commit

Permalink
add support for js getters and setters
Browse files Browse the repository at this point in the history
  • Loading branch information
Sharktheone committed Feb 2, 2024
1 parent b4526b7 commit a57858a
Show file tree
Hide file tree
Showing 5 changed files with 304 additions and 47 deletions.
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
47 changes: 47 additions & 0 deletions src/web_executor/js/object.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use crate::web_executor::js::{JSContext, JSFunction, JSFunctionVariadic, JSValue};

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 ret(&mut self, value: Self::Value);
}

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

fn context(&mut self) -> &mut Self::Context;
fn value(&mut self) -> &Self::Value;
}
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
17 changes: 11 additions & 6 deletions src/web_executor/js/v8/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,9 +171,13 @@ extern "C" fn callback(info: *const FunctionCallbackInfo) {

let data = unsafe { &mut *(external.value() as *mut CallbackWrapper) };

let ctx = match ctx_from_function_callback_info(unsafe { CallbackScope::new(info) }) {
let ctx = match ctx_from_function_callback_info(
unsafe { CallbackScope::new(info) },
data.ctx.borrow().isolate,
) {
Ok(scope) => scope,
Err(e) => {
Err((mut st, e)) => {
let scope = st.get();
let Some(e) = v8::String::new(scope, &e.to_string()) else {
eprintln!("failed to create exception string\nexception was: {e}"); //TODO: replace with our own logger
return;
Expand Down Expand Up @@ -421,13 +425,13 @@ impl<'a> V8FunctionVariadic<'a> {

extern "C" fn callback_variadic(info: *const FunctionCallbackInfo) {
let info = unsafe { &*info };
let scope = &mut unsafe { CallbackScope::new(info) };
let mut scope = unsafe { CallbackScope::new(info) };
let args = FunctionCallbackArguments::from_function_callback_info(info);
let rv = ReturnValue::from_function_callback_info(info);
let external = match <Local<External>>::try_from(args.data()) {
Ok(external) => external,
Err(e) => {
let Some(e) = v8::String::new(scope, &e.to_string()) else {
let Some(e) = v8::String::new(&mut scope, &e.to_string()) else {
eprintln!("failed to create exception string\nexception was: {e}"); //TODO: replace with our own logger
return;
};
Expand All @@ -438,9 +442,10 @@ extern "C" fn callback_variadic(info: *const FunctionCallbackInfo) {

let data = unsafe { &mut *(external.value() as *mut CallbackWrapperVariadic) };

let ctx = match ctx_from_function_callback_info(unsafe { CallbackScope::new(info) }) {
let ctx = match ctx_from_function_callback_info(scope, data.ctx.borrow().isolate) {
Ok(scope) => scope,
Err(e) => {
Err((mut st, e)) => {
let scope = st.get();
let Some(e) = v8::String::new(scope, &e.to_string()) else {
eprintln!("failed to create exception string\nexception was: {e}"); //TODO: replace with our own logger
return;
Expand Down
Loading

0 comments on commit a57858a

Please sign in to comment.