diff --git a/src/web_executor/js/function.rs b/src/web_executor/js/function.rs index 4976df9b9..c8ee8e8e6 100644 --- a/src/web_executor/js/function.rs +++ b/src/web_executor/js/function.rs @@ -1,5 +1,6 @@ use crate::types::Result; use crate::web_executor::js::{JSContext, JSError, JSObject, JSRuntime, JSValue}; +use core::fmt::Display; struct Function(pub T); @@ -31,6 +32,8 @@ pub trait JSFunctionCallBack { self.len() == 0 } + fn error(&mut self, error: impl Display); + fn ret(&mut self, value: Self::Value); } @@ -77,6 +80,8 @@ pub trait JSFunctionCallBackVariadic { self.len() == 0 } + fn error(&mut self, error: impl Display); + fn ret(&mut self, value: Self::Value); } diff --git a/src/web_executor/js/object.rs b/src/web_executor/js/object.rs index 432d62a0b..e99f2cb42 100644 --- a/src/web_executor/js/object.rs +++ b/src/web_executor/js/object.rs @@ -1,4 +1,5 @@ use crate::web_executor::js::{JSContext, JSFunction, JSFunctionVariadic, JSValue}; +use core::fmt::Display; pub trait JSObject { type Value: JSValue; @@ -35,6 +36,9 @@ pub trait JSGetterCallback { type Context: JSContext; fn context(&mut self) -> &mut Self::Context; + + fn error(&mut self, error: impl Display); + fn ret(&mut self, value: Self::Value); } @@ -43,5 +47,8 @@ pub trait JSSetterCallback { type Context: JSContext; fn context(&mut self) -> &mut Self::Context; + + fn error(&mut self, error: impl Display); + fn value(&mut self) -> &Self::Value; } diff --git a/src/web_executor/js/v8.rs b/src/web_executor/js/v8.rs index 54cc68d69..d681913e0 100644 --- a/src/web_executor/js/v8.rs +++ b/src/web_executor/js/v8.rs @@ -21,7 +21,6 @@ mod compile; mod context; mod function; mod object; -mod utils; mod value; // status of the V8 engine @@ -43,15 +42,23 @@ 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; } @@ -108,24 +115,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(); diff --git a/src/web_executor/js/v8/function.rs b/src/web_executor/js/v8/function.rs index c8b7e13f2..fbbc86a1e 100644 --- a/src/web_executor/js/v8/function.rs +++ b/src/web_executor/js/v8/function.rs @@ -1,4 +1,5 @@ use alloc::rc::Rc; +use core::fmt::Display; use v8::{ CallbackScope, External, Function, FunctionBuilder, FunctionCallbackArguments, @@ -105,6 +106,16 @@ impl<'a> JSFunctionCallBack for V8FunctionCallBack<'a> { fn ret(&mut self, value: Self::Value) { self.ret = Ok(value.value); } + + fn error(&mut self, error: impl Display) { + let scope = self.ctx.borrow_mut().scope(); + let err = error.to_string(); + let Some(e) = v8::String::new(scope, &err) else { + eprintln!("failed to create exception string\nexception was: {}", err); + return; + }; + scope.throw_exception(Local::from(e)); + } } impl<'a> V8Function<'a> { @@ -142,7 +153,7 @@ impl<'a> V8Function<'a> { { exception.into() } else { - eprintln!("failed to create exception string\nexception was: {e}"); //TODO: replace with our own logger + eprintln!("failed to create exception string\nexception was: {e}"); v8::undefined(ctx.borrow_mut().scope()).into() }; @@ -161,7 +172,7 @@ extern "C" fn callback(info: *const FunctionCallbackInfo) { Ok(external) => external, Err(e) => { 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 + eprintln!("failed to create exception string\nexception was: {e}"); return; }; scope.throw_exception(Local::from(e)); @@ -179,7 +190,7 @@ extern "C" fn callback(info: *const FunctionCallbackInfo) { 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 + eprintln!("failed to create exception string\nexception was: {e}"); return; }; scope.throw_exception(Local::from(e)); @@ -252,8 +263,6 @@ impl<'a> JSFunction for V8Function<'a> { } } -//TODO: maybe move both implementations into a macro, so we have less code duplication - pub struct V8FunctionVariadic<'a> { pub(super) ctx: V8Context<'a>, pub(super) function: Local<'a, Function>, @@ -373,6 +382,16 @@ impl<'a> JSFunctionCallBackVariadic for V8FunctionCallBackVariadic<'a> { self.args.len() } + fn error(&mut self, error: impl Display) { + let scope = self.ctx.borrow_mut().scope(); + let err = error.to_string(); + let Some(e) = v8::String::new(scope, &err) else { + eprintln!("failed to create exception string\nexception was: {}", err); + return; + }; + scope.throw_exception(Local::from(e)); + } + fn ret(&mut self, value: Self::Value) { self.ret = Ok(value.value); } @@ -413,7 +432,7 @@ impl<'a> V8FunctionVariadic<'a> { { exception.into() } else { - eprintln!("failed to create exception string\nexception was: {e}"); //TODO: replace with our own logger + eprintln!("failed to create exception string\nexception was: {e}"); v8::undefined(ctx.borrow_mut().scope()).into() }; @@ -432,7 +451,7 @@ extern "C" fn callback_variadic(info: *const FunctionCallbackInfo) { Ok(external) => external, Err(e) => { 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 + eprintln!("failed to create exception string\nexception was: {e}"); return; }; scope.throw_exception(Local::from(e)); @@ -447,7 +466,7 @@ extern "C" fn callback_variadic(info: *const FunctionCallbackInfo) { 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 + eprintln!("failed to create exception string\nexception was: {e}"); return; }; scope.throw_exception(Local::from(e)); diff --git a/src/web_executor/js/v8/object.rs b/src/web_executor/js/v8/object.rs index af428186b..6d7e03a16 100644 --- a/src/web_executor/js/v8/object.rs +++ b/src/web_executor/js/v8/object.rs @@ -1,3 +1,4 @@ +use core::fmt::Display; use std::ffi::c_void; use v8::{ @@ -31,6 +32,16 @@ impl<'a> JSGetterCallback for GetterCallback<'a, '_> { &mut self.ctx } + fn error(&mut self, error: impl Display) { + let scope = self.ctx.borrow_mut().scope(); + let err = error.to_string(); + let Some(e) = v8::String::new(scope, &err) else { + eprintln!("failed to create exception string\nexception was: {}", err); + return; + }; + scope.throw_exception(Local::from(e)); + } + fn ret(&mut self, value: Self::Value) { *self.ret = value; } @@ -49,6 +60,16 @@ impl<'a, 'v> JSSetterCallback for SetterCallback<'a, 'v> { &mut self.ctx } + fn error(&mut self, error: impl Display) { + let scope = self.ctx.borrow_mut().scope(); + let err = error.to_string(); + let Some(e) = v8::String::new(scope, &err) else { + eprintln!("failed to create exception string\nexception was: {}", err); + return; + }; + scope.throw_exception(Local::from(e)); + } + fn value(&mut self) -> &'v Self::Value { self.value } @@ -217,7 +238,7 @@ impl<'a> JSObject for V8Object<'a> { Ok(external) => external, Err(e) => { 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 + eprintln!("failed to create exception string\nexception was: {e}"); return; }; scope.throw_exception(Local::from(e)); @@ -234,7 +255,7 @@ impl<'a> JSObject for V8Object<'a> { 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 + eprintln!("failed to create exception string\nexception was: {e}"); return; }; scope.throw_exception(Local::from(e)); @@ -247,7 +268,7 @@ impl<'a> JSObject for V8Object<'a> { Err(e) => { let scope = ctx.borrow_mut().scope(); 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 + eprintln!("failed to create exception string\nexception was: {e}"); return; }; scope.throw_exception(Local::from(e)); @@ -272,7 +293,7 @@ impl<'a> JSObject for V8Object<'a> { Ok(external) => external, Err(e) => { 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 + eprintln!("failed to create exception string\nexception was: {e}"); return; }; scope.throw_exception(Local::from(e)); @@ -289,7 +310,7 @@ impl<'a> JSObject for V8Object<'a> { 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 + eprintln!("failed to create exception string\nexception was: {e}"); return; }; scope.throw_exception(Local::from(e)); diff --git a/src/web_executor/js/v8/utils.rs b/src/web_executor/js/v8/utils.rs deleted file mode 100644 index 926eddc02..000000000 --- a/src/web_executor/js/v8/utils.rs +++ /dev/null @@ -1,191 +0,0 @@ -use crate::web_executor::js::v8::{V8Context, V8Function, V8FunctionCallBack}; -use std::marker::PhantomData; -use std::mem::size_of; -use v8::{ - CallbackScope, FunctionCallback, FunctionCallbackArguments, FunctionCallbackInfo, ReturnValue, -}; - -// --- https://github.com/denoland/rusty_v8/blob/ff2a50ccdf7d5f7091e2bfbdedf0927101e2844c/src/support.rs#L562 --- -pub trait UnitType -where - Self: Copy + Sized, -{ - #[inline(always)] - fn get() -> Self { - UnitValue::::get() - } -} - -impl UnitType for T where T: Copy + Sized {} - -#[derive(Copy, Clone, Debug)] -struct UnitValue(PhantomData) -where - Self: Sized; - -impl UnitValue -where - Self: Copy + Sized, -{ - const SELF: Self = Self::new_checked(); - - const fn new_checked() -> Self { - // Statically assert that T is indeed a unit type. - let size_must_be_0 = size_of::(); - let s = Self(PhantomData::); - [s][size_must_be_0] - } - - #[inline(always)] - fn get_checked(self) -> T { - // This run-time check serves just as a backup for the compile-time - // check when Self::SELF is initialized. - assert_eq!(size_of::(), 0); - unsafe { std::mem::MaybeUninit::::zeroed().assume_init() } - } - - #[inline(always)] - pub fn get() -> T { - // Accessing the Self::SELF is necessary to make the compile-time type check - // work. - Self::SELF.get_checked() - } -} - -#[derive(Debug)] -pub struct DefaultTag; - -#[derive(Debug)] -pub struct IdenticalConversionTag; - -pub trait MapFnFrom<'a, F, Tag = DefaultTag> -where - F: UnitType, - Self: Sized, -{ - fn mapping(ctx: &V8Context<'a>) -> Self; - - #[inline(always)] - fn map_fn_from(ctx: &V8Context<'a>, _: F) -> Self { - Self::mapping(ctx) - } -} - -impl<'a, F> MapFnFrom<'a, F, IdenticalConversionTag> for F -where - Self: UnitType, -{ - #[inline(always)] - fn mapping(ctx: &V8Context<'a>) -> Self { - Self::get() - } -} - -pub trait MapFnTo<'a, T, Tag = DefaultTag> -where - Self: UnitType, - T: Sized, -{ - fn mapping(ctx: &V8Context<'a>) -> T; - - #[inline(always)] - fn map_fn_to(self, ctx: &V8Context<'a>) -> T { - Self::mapping(ctx) - } -} - -impl<'a, F, T, Tag> MapFnTo<'a, T, Tag> for F -where - Self: UnitType, - T: MapFnFrom<'a, F, Tag>, -{ - #[inline(always)] - fn mapping(ctx: &V8Context<'a>) -> T { - T::map_fn_from(ctx, F::get()) - } -} - -pub trait CFnFrom -where - Self: Sized, - F: UnitType, -{ - fn mapping() -> Self; - - #[inline(always)] - fn c_fn_from(_: F) -> Self { - Self::mapping() - } -} - -macro_rules! impl_c_fn_from { - ($($arg:ident: $ty:ident),*) => { - impl CFnFrom for extern "C" fn($($ty),*) -> R - where - F: UnitType + Fn($($ty),*) -> R, - { - #[inline(always)] - fn mapping() -> Self { - extern "C" fn c_fn($($arg: $ty),*) -> R - where - F: UnitType + Fn($($ty),*) -> R, - { - (F::get())($($arg),*) - } - c_fn:: - } - } - }; -} - -impl_c_fn_from!(); -impl_c_fn_from!(a0: A0); -impl_c_fn_from!(a0: A0, a1: A1); -impl_c_fn_from!(a0: A0, a1: A1, a2: A2); -impl_c_fn_from!(a0: A0, a1: A1, a2: A2, a3: A3); -impl_c_fn_from!(a0: A0, a1: A1, a2: A2, a3: A3, a4: A4); -impl_c_fn_from!(a0: A0, a1: A1, a2: A2, a3: A3, a4: A4, a5: A5); -impl_c_fn_from!(a0: A0, a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6); - -pub trait ToCFn -where - Self: UnitType, - T: Sized, -{ - fn mapping() -> T; - - #[inline(always)] - fn to_c_fn(self) -> T { - Self::mapping() - } -} - -impl ToCFn for F -where - Self: UnitType, - T: CFnFrom, -{ - #[inline(always)] - fn mapping() -> T { - T::c_fn_from(F::get()) - } -} - -// --- copy end --- - -impl<'a, F> MapFnFrom<'a, F> for FunctionCallback -where - F: UnitType + Fn(&mut V8FunctionCallBack<'a>), -{ - fn mapping(ctx: &V8Context<'a>) -> Self { - let f = |info: *const FunctionCallbackInfo| { - let info = unsafe { &*info }; - let scope = &mut unsafe { CallbackScope::new(info) }; - let args = FunctionCallbackArguments::from_function_callback_info(info); - let rv = ReturnValue::from_function_callback_info(info); - - // V8Function::callback(ctx, scope, args, rv, F::get()); - }; - f.to_c_fn() - } -} diff --git a/src/web_executor/test.rs b/src/web_executor/test.rs index 44787ab1c..3e1f73992 100644 --- a/src/web_executor/test.rs +++ b/src/web_executor/test.rs @@ -7,7 +7,6 @@ use std::ops::Add; use crate::types::Result; use crate::web_executor::js::v8::{ GetterCallback, SetterCallback, V8Context, V8Function, V8FunctionVariadic, V8Value, - V8VariadicArgsInternal, }; use crate::web_executor::js::{ Args, JSContext, JSFunction, JSFunctionCallBack, JSFunctionCallBackVariadic, @@ -137,7 +136,7 @@ impl Test2 { let value = match value.to_js_value(ctx.clone()) { Ok(value) => value, Err(e) => { - // cb.error(e); TODO + cb.error(e); return; } }; @@ -153,7 +152,7 @@ impl Test2 { let value = match value.as_number() { Ok(value) => value, Err(e) => { - // cb.error(e); TODO + cb.error(e); return; } }; @@ -178,7 +177,7 @@ impl Test2 { let value = match value.to_js_value(ctx.clone()) { Ok(value) => value, Err(e) => { - // cb.error(e); TODO + cb.error(e); return; } }; @@ -194,7 +193,7 @@ impl Test2 { let value = match value.as_string() { Ok(value) => value, Err(e) => { - // cb.error(e); TODO + cb.error(e); return; } }; @@ -211,18 +210,20 @@ impl Test2 { let cool_fn = { let s = Rc::clone(&s); V8Function::new(ctx.clone(), move |cb| { - //TODO: add R::Function::new let num_args = 0; //function.arguments.len(); if num_args != cb.len() { - // cb.error("wrong number of arguments"); //TODO + cb.error("wrong number of arguments"); return; } let ctx = cb.context(); - let Ok(ret) = s.borrow().cool_fn().to_js_value(ctx.clone()) else { - // cb.error(e); //TODO - return; + let ret = match s.borrow().cool_fn().to_js_value(ctx.clone()) { + Ok(ret) => ret, + Err(e) => { + cb.error(e); + return; + } }; cb.ret(ret); @@ -236,7 +237,7 @@ impl Test2 { V8Function::new(ctx.clone(), move |cb| { let num_args = 1; //function.arguments.len(); if num_args != cb.len() { - // cb.error("wrong number of arguments"); //TODO + cb.error("wrong number of arguments"); return; } @@ -245,12 +246,12 @@ impl Test2 { let args = cb.args(); let Some(arg0) = cb.args().get(0, ctx.clone()) else { - // cb.error("failed to get argument"); //TODO + cb.error("failed to get argument"); return; }; let Ok(arg0) = arg0.as_number() else { - // cb.error("failed to convert argument"); //TODO + cb.error("failed to convert argument"); return; }; @@ -271,7 +272,7 @@ impl Test2 { V8Function::new(ctx.clone(), move |cb| { let num_args = 1; //function.arguments.len(); if num_args != cb.len() { - // cb.error("wrong number of arguments"); //TODO + cb.error("wrong number of arguments"); return; } @@ -280,12 +281,12 @@ impl Test2 { let args = cb.args(); let Some(arg0) = cb.args().get(0, ctx.clone()) else { - // cb.error("failed to get argument"); //TODO + cb.error("failed to get argument"); return; }; let Ok(arg0) = arg0.as_string() else { - // cb.error("failed to convert argument"); //TODO + cb.error("failed to convert argument"); return; }; @@ -301,7 +302,7 @@ impl Test2 { V8Function::new(ctx.clone(), move |cb| { let num_args = 1; //function.arguments.len(); if num_args != cb.len() { - // cb.error("wrong number of arguments"); //TODO + cb.error("wrong number of arguments"); return; } @@ -310,12 +311,12 @@ impl Test2 { let args = cb.args(); let Some(arg0) = cb.args().get(0, ctx.clone()) else { - // cb.error("failed to get argument"); //TODO + cb.error("failed to get argument"); return; }; let Ok(arg0) = arg0.as_string() else { - // cb.error("failed to convert argument"); //TODO + cb.error("failed to convert argument"); return; };