diff --git a/oden-js-sys/quickjs/quickjs.c b/oden-js-sys/quickjs/quickjs.c index 48aeffc6..94ed0e54 100644 --- a/oden-js-sys/quickjs/quickjs.c +++ b/oden-js-sys/quickjs/quickjs.c @@ -27125,7 +27125,7 @@ static int add_req_module_entry(JSContext *ctx, JSModuleDef *m, return i; } -static JSExportEntry *find_export_entry(JSContext *ctx, JSModuleDef *m, +static JSExportEntry *find_export_entry(JSContext *ctx, const JSModuleDef *m, JSAtom export_name) { JSExportEntry *me; @@ -27241,6 +27241,37 @@ int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name, return -1; } +JSValueConst JS_GetModuleExport(JSContext *ctx, const JSModuleDef *m, const char *export_name) { + JSExportEntry *me; + JSAtom name; + name = JS_NewAtom(ctx, export_name); + if (name == JS_ATOM_NULL) + goto fail; + me = find_export_entry(ctx, m, name); + JS_FreeAtom(ctx, name); + if (!me) + goto fail; + return JS_DupValue(ctx, me->u.local.var_ref->value); + fail: + return JS_UNDEFINED; +} + +int JS_CountModuleExport(JSContext *ctx, const JSModuleDef *m) { + return m->export_entries_count; +} + +JSAtom JS_GetModuleExportName(JSContext *ctx, const JSModuleDef *m, int idx) { + if (idx >= m->export_entries_count || idx < 0) + return JS_ATOM_NULL; + return JS_DupAtom(ctx, m->export_entries[idx].export_name); +} + +JSValueConst JS_GetModuleExportValue(JSContext *ctx, const JSModuleDef *m, int idx) { + if (idx >= m->export_entries_count || idx < 0) + return JS_UNDEFINED; + return JS_DupValue(ctx, m->export_entries[idx].u.local.var_ref->value); +} + void JS_SetModuleLoaderFunc(JSRuntime *rt, JSModuleNormalizeFunc *module_normalize, JSModuleLoaderFunc *module_loader, void *opaque) diff --git a/oden-js-sys/quickjs/quickjs.h b/oden-js-sys/quickjs/quickjs.h index d4a5cd31..2b3b6205 100644 --- a/oden-js-sys/quickjs/quickjs.h +++ b/oden-js-sys/quickjs/quickjs.h @@ -1038,6 +1038,11 @@ int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name, JSValue val); int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m, const JSCFunctionListEntry *tab, int len); +/* can only be called after the module is initialized */ +JSValueConst JS_GetModuleExport(JSContext *ctx, const JSModuleDef *m, const char *export_name); +int JS_CountModuleExport(JSContext *ctx, const JSModuleDef *m); +JSAtom JS_GetModuleExportName(JSContext *ctx, const JSModuleDef *m, int idx); +JSValueConst JS_GetModuleExportValue(JSContext *ctx, const JSModuleDef *m, int idx); #undef js_unlikely #undef js_force_inline diff --git a/oden-js-sys/src/static-functions.rs b/oden-js-sys/src/static-functions.rs index 21a7f957..6419d46e 100644 --- a/oden-js-sys/src/static-functions.rs +++ b/oden-js-sys/src/static-functions.rs @@ -50,7 +50,9 @@ extern "C" { magic: ::std::os::raw::c_int, ) -> JSValue; fn JS_MakeException_real() -> JSValue; - + fn JS_MakeNull_real() -> JSValue; + fn JS_MakeUndefined_real() -> JSValue; + fn JS_ValueGetPtr_real(v: JSValue) -> *mut ::std::os::raw::c_void; } pub unsafe fn JS_ValueGetTag(v: JSValue) -> i32 { @@ -217,3 +219,15 @@ pub unsafe fn JS_NewCFunctionMagic( pub unsafe fn JS_MakeException() -> JSValue { JS_MakeException_real() } + +pub unsafe fn JS_MakeNull() -> JSValue { + JS_MakeNull_real() +} + +pub unsafe fn JS_MakeUndefined() -> JSValue { + JS_MakeUndefined_real() +} + +pub unsafe fn JS_ValueGetPtr(v: JSValue) -> *mut ::std::os::raw::c_void { + JS_ValueGetPtr_real(v) +} diff --git a/oden-js-sys/static-functions.c b/oden-js-sys/static-functions.c index 5185ecb1..26922beb 100644 --- a/oden-js-sys/static-functions.c +++ b/oden-js-sys/static-functions.c @@ -128,3 +128,15 @@ JSValue JS_NewCFunctionMagic_real(JSContext *ctx, JSCFunctionMagic *func, const JSValue JS_MakeException_real() { return JS_EXCEPTION; } + +JSValue JS_MakeNull_real() { + return JS_NULL; +} + +JSValue JS_MakeUndefined_real() { + return JS_UNDEFINED; +} + +void *JS_ValueGetPtr_real(JSValue val) { + return JS_VALUE_GET_PTR(val); +} diff --git a/oden-js/src/context.rs b/oden-js/src/context.rs index 00aa6247..8a748755 100644 --- a/oden-js/src/context.rs +++ b/oden-js/src/context.rs @@ -106,6 +106,14 @@ impl ContextRef { unsafe { sys::JS_EnableBignumExt(self.ctx, if enable { 1 } else { 0 }) } } + pub fn load_module(&self, input: &str, filename: &str) -> Result { + let val = self.eval(input, filename, EvalType::Module, EvalFlags::COMPILE_ONLY)?; + assert!(val.is_module()); + val.eval_function(self)?; + + Ok(val) + } + /// Evaluate the specified JavaScript code. pub fn eval( &self, @@ -482,4 +490,23 @@ mod tests { .unwrap(); assert_eq!(String::from("UNSAFE"), result.to_string(&ctx).unwrap()); } + + #[test] + fn modules_with_exports() { + let ctx = Context::new(Runtime::new()); + let module = ctx + .load_module("const foo = 123; export { foo };", "main.js") + .expect("Could not load!"); + assert_eq!(module.value_type(), ValueType::Module); + + let foo = module + .get_module_export(&ctx, "foo") + .expect("Could not get export"); + assert_eq!(String::from("123"), foo.to_string(&ctx).unwrap()); + + let bar = module + .get_module_export(&ctx, "bar") + .expect("Could not get export"); + assert!(bar.is_undefined()); + } } diff --git a/oden-js/src/value.rs b/oden-js/src/value.rs index 90257483..b6d88405 100644 --- a/oden-js/src/value.rs +++ b/oden-js/src/value.rs @@ -1,6 +1,6 @@ use crate::{AtomRef, ContextRef, Error, Result, Runtime, RustFunction}; use oden_js_sys as sys; -use std::ffi::CStr; +use std::ffi::{CStr, CString}; use std::fmt; use std::ops::{Deref, DerefMut}; @@ -309,6 +309,37 @@ impl ValueRef { Ok(result) } + + pub fn get_module_export(&self, ctx: &ContextRef, export: &str) -> Result { + if self.value_type() != ValueType::Module { + return Err(Error::InvalidType { + expected: ValueType::Bool, + found: self.value_type(), + }); + } + + let c_value = match CString::new(export) { + Ok(cs) => Ok(cs), + Err(_) => Err(Error::UnexpectedNul), + }?; + + unsafe { + let module = sys::JS_ValueGetPtr(self.val) as *mut sys::JSModuleDef; + ctx.check_exception(sys::JS_GetModuleExport(ctx.ctx, module, c_value.into_raw())) + } + } + + pub fn call(&self, ctx: &ContextRef) -> Result { + unsafe { + ctx.check_exception(sys::JS_Call( + ctx.ctx, + self.val, + sys::JS_MakeUndefined(), + 0, + std::ptr::null_mut(), + )) + } + } } impl<'ctx> fmt::Debug for ValueRef { @@ -338,6 +369,20 @@ impl Value { rt: Runtime::from_raw(unsafe { sys::JS_GetRuntime(ctx.ctx) }), } } + + pub fn null(ctx: &ContextRef) -> Self { + Value { + value: ValueRef::from_raw(unsafe { sys::JS_MakeNull() }), + rt: Runtime::from_raw(unsafe { sys::JS_GetRuntime(ctx.ctx) }), + } + } + + pub fn undefined(ctx: &ContextRef) -> Self { + Value { + value: ValueRef::from_raw(unsafe { sys::JS_MakeUndefined() }), + rt: Runtime::from_raw(unsafe { sys::JS_GetRuntime(ctx.ctx) }), + } + } } impl Deref for Value { diff --git a/src/lib.rs b/src/lib.rs index 5314c44b..562f9e35 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -356,6 +356,8 @@ pub async fn run() { let mut state = State::new(window).await; let context = script::ScriptContext::new(); + context.init(); + event_loop.run(move |event, _, control_flow| { control_flow.set_poll(); diff --git a/src/main.js b/src/main.js new file mode 100644 index 00000000..7b985b1f --- /dev/null +++ b/src/main.js @@ -0,0 +1,9 @@ +function init() { + // console.log("Hello world!"); +} + +function update() {} + +function draw() {} + +export { init, update, draw } diff --git a/src/script.rs b/src/script.rs index 2ed49e87..4391e0d2 100644 --- a/src/script.rs +++ b/src/script.rs @@ -1,7 +1,10 @@ use oden_js as js; pub struct ScriptContext { - _context: js::Context, + context: js::Context, + init: js::Value, + update: js::Value, + draw: js::Value, } impl ScriptContext { @@ -13,10 +16,40 @@ impl ScriptContext { context.add_intrinsic_bigdecimal(); context.add_intrinsic_operators(); - ScriptContext { _context: context } + let js = include_str!("main.js"); + let module = context + .load_module(js, "main.js") + .expect("Unable to load main"); + + let init = module + .get_module_export(&context, "init") + .expect("Unable to fetch init"); + let update = module + .get_module_export(&context, "update") + .expect("Unable to fetch update"); + let draw = module + .get_module_export(&context, "draw") + .expect("Unable to fetch draw"); + + ScriptContext { + context, + init, + update, + draw, + } } - pub fn update(&self) {} + pub fn init(&self) { + self.init.call(&self.context).expect("Exception in init"); + } - pub fn render(&self) {} + pub fn update(&self) { + self.update + .call(&self.context) + .expect("Exception in update"); + } + + pub fn render(&self) { + self.draw.call(&self.context).expect("Exception in draw"); + } }