[oden][oden-js] Rework modules
Damn this is a lot
This commit is contained in:
parent
aa90cea4a3
commit
db8a5f8eed
12 changed files with 280 additions and 105 deletions
|
|
@ -1,24 +1,12 @@
|
|||
use crate::{
|
||||
callback::new_fn, conversion::RustFunction, Atom, ClassID, Error, Result, Runtime, Value,
|
||||
ValueRef, ValueResult,
|
||||
callback::new_fn, conversion::RustFunction, module::Module, Atom, ClassID, Error, Result,
|
||||
Runtime, Value, ValueRef, ValueResult,
|
||||
};
|
||||
use bitflags::bitflags;
|
||||
use oden_js_sys as sys;
|
||||
use std::ffi::CString;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
/// Different ways to evaluate JavaScript. See the various `eval` methods on
|
||||
/// `ContextRef`.
|
||||
pub enum EvalType {
|
||||
/// Global code, i.e., just plain old JavaScript running in the most
|
||||
/// boring, traditional context.
|
||||
Global,
|
||||
|
||||
/// Module code, i.e., code running under the context of an `import`, or
|
||||
/// referenced in HTML as `type=module`.
|
||||
Module,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct EvalFlags: u32 {
|
||||
const NONE = 0;
|
||||
|
|
@ -31,8 +19,8 @@ bitflags! {
|
|||
const STRIP = sys::JS_EVAL_FLAG_STRIP;
|
||||
|
||||
/// Compile but do not run. The result is a value with a value type
|
||||
/// of `ValueType::Module` or `ValueType::FunctionBytecode`, and
|
||||
/// which can be executed with `value.eval_function`.
|
||||
/// of `ValueType::FunctionBytecode`, and which can be executed with
|
||||
/// `value.eval_function`.
|
||||
const COMPILE_ONLY = sys::JS_EVAL_FLAG_COMPILE_ONLY;
|
||||
|
||||
/// Don't include the stack frames before this eval in the Error()
|
||||
|
|
@ -51,6 +39,10 @@ impl ContextRef {
|
|||
ContextRef { ctx }
|
||||
}
|
||||
|
||||
pub fn get_runtime(&self) -> Runtime {
|
||||
Runtime::from_raw(unsafe { sys::JS_GetRuntime(self.ctx) })
|
||||
}
|
||||
|
||||
pub fn is_registered_class(&self, id: &ClassID) -> bool {
|
||||
let id = id.get();
|
||||
let is_registered = unsafe { sys::JS_IsRegisteredClass(sys::JS_GetRuntime(self.ctx), id) };
|
||||
|
|
@ -106,30 +98,42 @@ impl ContextRef {
|
|||
unsafe { sys::JS_EnableBignumExt(self.ctx, if enable { 1 } else { 0 }) }
|
||||
}
|
||||
|
||||
/// Evaluate the specified JavaScript code as a module.
|
||||
pub fn load_module(&self, input: &str, filename: &str) -> Result<Value> {
|
||||
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, input: &str, filename: &str, flags: EvalFlags) -> ValueResult {
|
||||
self.eval_internal(input, filename, sys::JS_EVAL_TYPE_GLOBAL, flags)
|
||||
}
|
||||
|
||||
/// Evaluate the specified JavaScript code.
|
||||
pub fn eval(
|
||||
/// Evaluate the specified JavaScript code as a module.
|
||||
pub fn eval_module(&self, input: &str, filename: &str) -> Result<Module> {
|
||||
let val = self.eval_internal(
|
||||
input,
|
||||
filename,
|
||||
sys::JS_EVAL_TYPE_MODULE,
|
||||
EvalFlags::COMPILE_ONLY,
|
||||
)?;
|
||||
assert!(val.is_module());
|
||||
unsafe {
|
||||
// NOTE: This might be stupid but we're trying to
|
||||
// intentionally leak the value here; since the module
|
||||
// itself has a lifetime unconstrained by traditional value
|
||||
// semantics. (This is a weird edge in QuickJS.)
|
||||
sys::JS_DupValue(self.ctx, val.val);
|
||||
Ok(Module::from_raw(
|
||||
sys::JS_VALUE_GET_PTR(val.val) as *mut sys::JSModuleDef,
|
||||
self.get_runtime(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_internal(
|
||||
&self,
|
||||
input: &str,
|
||||
filename: &str,
|
||||
eval_type: EvalType,
|
||||
eval_type: u32,
|
||||
flags: EvalFlags,
|
||||
) -> ValueResult {
|
||||
let c_input = CString::new(input)?;
|
||||
let c_filename = CString::new(filename)?;
|
||||
|
||||
let eval_type = match eval_type {
|
||||
EvalType::Global => sys::JS_EVAL_TYPE_GLOBAL,
|
||||
EvalType::Module => sys::JS_EVAL_TYPE_MODULE,
|
||||
};
|
||||
let flags_bits: i32 = (eval_type | flags.bits).try_into().unwrap();
|
||||
|
||||
unsafe {
|
||||
|
|
@ -143,6 +147,18 @@ impl ContextRef {
|
|||
}
|
||||
}
|
||||
|
||||
/// Import a module by name.
|
||||
pub fn import_module(&self, name: &str, base: &str) -> Result<Module> {
|
||||
let name = CString::new(name)?;
|
||||
let base = CString::new(base)?;
|
||||
let module = unsafe { sys::JS_RunModule(self.ctx, name.as_ptr(), base.as_ptr()) };
|
||||
if module.is_null() {
|
||||
return Err(self.exception_error());
|
||||
}
|
||||
|
||||
Ok(Module::from_raw(module, self.get_runtime()))
|
||||
}
|
||||
|
||||
/// Construct a new string atom.
|
||||
pub fn new_atom(&self, value: &str) -> Result<Atom> {
|
||||
let c_value = CString::new(value)?;
|
||||
|
|
@ -377,9 +393,7 @@ mod tests {
|
|||
let rt = Runtime::new();
|
||||
let ctx = Context::new(rt);
|
||||
|
||||
let val = ctx
|
||||
.eval("1+1", "script", EvalType::Global, EvalFlags::NONE)
|
||||
.unwrap();
|
||||
let val = ctx.eval("1+1", "script", EvalFlags::NONE).unwrap();
|
||||
|
||||
assert_eq!(String::from("2"), val.to_string(&ctx).unwrap());
|
||||
}
|
||||
|
|
@ -390,7 +404,7 @@ mod tests {
|
|||
let ctx = Context::new(rt);
|
||||
|
||||
let compiled = ctx
|
||||
.eval("1 + 1", "script", EvalType::Global, EvalFlags::COMPILE_ONLY)
|
||||
.eval("1 + 1", "script", EvalFlags::COMPILE_ONLY)
|
||||
.expect("Unable to compile code");
|
||||
let result = compiled
|
||||
.eval_function(&ctx)
|
||||
|
|
@ -410,9 +424,7 @@ mod tests {
|
|||
let val = ctx.new_i32(12).unwrap();
|
||||
go.set_property(&ctx, "foo", &val).unwrap();
|
||||
|
||||
let result = ctx
|
||||
.eval("foo + 3", "script", EvalType::Global, EvalFlags::NONE)
|
||||
.unwrap();
|
||||
let result = ctx.eval("foo + 3", "script", EvalFlags::NONE).unwrap();
|
||||
assert_eq!(String::from("15"), result.to_string(&ctx).unwrap());
|
||||
}
|
||||
|
||||
|
|
@ -454,9 +466,7 @@ mod tests {
|
|||
let val = vr.as_ref().unwrap();
|
||||
go.set_property(&ctx, "val", val).unwrap();
|
||||
|
||||
let result = ctx
|
||||
.eval(expr, "script", EvalType::Global, EvalFlags::NONE)
|
||||
.unwrap();
|
||||
let result = ctx.eval(expr, "script", EvalFlags::NONE).unwrap();
|
||||
assert_eq!(String::from(*expected), result.to_string(&ctx).unwrap());
|
||||
}
|
||||
}
|
||||
|
|
@ -479,12 +489,7 @@ mod tests {
|
|||
}
|
||||
|
||||
let result = ctx
|
||||
.eval(
|
||||
"foo().toUpperCase()",
|
||||
"script",
|
||||
EvalType::Global,
|
||||
EvalFlags::NONE,
|
||||
)
|
||||
.eval("foo().toUpperCase()", "script", EvalFlags::NONE)
|
||||
.unwrap();
|
||||
assert_eq!(String::from("UNSAFE"), result.to_string(&ctx).unwrap());
|
||||
}
|
||||
|
|
@ -493,17 +498,16 @@ mod tests {
|
|||
fn modules_with_exports() {
|
||||
let ctx = Context::new(Runtime::new());
|
||||
let module = ctx
|
||||
.load_module("const foo = 123; export { foo };", "main.js")
|
||||
.eval_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")
|
||||
.get_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")
|
||||
.get_export(&ctx, "bar")
|
||||
.expect("Could not get export");
|
||||
assert!(bar.is_undefined());
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue