[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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ impl TryIntoValue for Error {
|
|||
Error::RustFunctionError(e) => Err(Error::RustFunctionError(e)),
|
||||
Error::Exception(v, d) => Err(Error::Exception(v.dup(ctx), d)),
|
||||
Error::OutOfMemory => Err(Error::OutOfMemory),
|
||||
Error::IOError(e) => Err(Error::IOError(e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ mod value;
|
|||
|
||||
pub use atom::{Atom, AtomRef};
|
||||
pub use class::{Class, ClassID};
|
||||
pub use context::{Context, ContextRef, EvalFlags, EvalType};
|
||||
pub use context::{Context, ContextRef, EvalFlags};
|
||||
pub use conversion::*;
|
||||
pub use runtime::Runtime;
|
||||
pub use value::{Value, ValueRef, ValueType};
|
||||
|
|
@ -43,6 +43,8 @@ pub enum Error {
|
|||
Exception(Value, String),
|
||||
#[error("out of memory")]
|
||||
OutOfMemory,
|
||||
#[error("an io error occurred: {0}")]
|
||||
IOError(std::io::Error),
|
||||
}
|
||||
|
||||
impl From<NulError> for Error {
|
||||
|
|
@ -51,10 +53,26 @@ impl From<NulError> for Error {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for Error {
|
||||
fn from(e: std::io::Error) -> Self {
|
||||
Error::IOError(e)
|
||||
}
|
||||
}
|
||||
|
||||
pub type Result<T> = core::result::Result<T, Error>;
|
||||
pub type ValueResult = core::result::Result<Value, Error>;
|
||||
|
||||
pub fn throw_string(context: &ContextRef, message: String) -> sys::JSValue {
|
||||
pub(crate) fn throw_error(context: &ContextRef, error: Error) -> sys::JSValue {
|
||||
match error {
|
||||
Error::Exception(v, _) => unsafe {
|
||||
sys::JS_DupValue(context.ctx, v.val);
|
||||
sys::JS_Throw(context.ctx, v.val)
|
||||
},
|
||||
other => throw_string(context, other.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn throw_string(context: &ContextRef, message: String) -> sys::JSValue {
|
||||
let ctx = context.ctx;
|
||||
match context.new_string(&message) {
|
||||
Ok(e) => unsafe {
|
||||
|
|
|
|||
53
oden-js/src/module/loader.rs
Normal file
53
oden-js/src/module/loader.rs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
use super::ModuleRef;
|
||||
use crate::{throw_error, ContextRef, Error, Result};
|
||||
use oden_js_sys as sys;
|
||||
use std::path::Path;
|
||||
|
||||
pub enum ModuleSource<'a> {
|
||||
Native(&'a ModuleRef),
|
||||
JavaScript(String),
|
||||
}
|
||||
|
||||
pub trait ModuleLoader {
|
||||
fn load(&mut self, context: &ContextRef, name: &str) -> Result<ModuleSource>;
|
||||
}
|
||||
|
||||
pub struct DefaultModuleLoader {}
|
||||
|
||||
impl DefaultModuleLoader {
|
||||
pub fn new() -> DefaultModuleLoader {
|
||||
DefaultModuleLoader {}
|
||||
}
|
||||
}
|
||||
|
||||
impl ModuleLoader for DefaultModuleLoader {
|
||||
fn load(&mut self, _context: &ContextRef, name: &str) -> Result<ModuleSource> {
|
||||
// Attempt to open the file.
|
||||
let path = Path::new(name);
|
||||
match std::fs::read_to_string(path) {
|
||||
Ok(str) => Ok(ModuleSource::JavaScript(str)),
|
||||
Err(e) => Err(Error::IOError(e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn load_module(
|
||||
context: &ContextRef,
|
||||
name: &str,
|
||||
loader: &mut Box<dyn ModuleLoader>,
|
||||
) -> *mut sys::JSModuleDef {
|
||||
match loader.load(context, name) {
|
||||
Ok(ModuleSource::Native(native)) => native.module,
|
||||
Ok(ModuleSource::JavaScript(js)) => match context.eval_module(&js, name) {
|
||||
Ok(v) => v.module,
|
||||
Err(e) => {
|
||||
throw_error(context, e);
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
throw_error(context, e);
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
}
|
||||
}
|
||||
76
oden-js/src/module/mod.rs
Normal file
76
oden-js/src/module/mod.rs
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
use crate::{ContextRef, Result, Runtime, Value};
|
||||
use oden_js_sys as sys;
|
||||
use std::ffi::CString;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
pub mod loader;
|
||||
pub mod native;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ModuleRef {
|
||||
module: *mut sys::JSModuleDef,
|
||||
}
|
||||
|
||||
impl ModuleRef {
|
||||
pub(crate) fn from_raw(module: *mut sys::JSModuleDef) -> ModuleRef {
|
||||
ModuleRef { module }
|
||||
}
|
||||
|
||||
fn eval_self(&self, context: &ContextRef) -> Result<()> {
|
||||
let result = unsafe {
|
||||
let v = sys::JSValue {
|
||||
u: sys::JSValueUnion {
|
||||
ptr: self.module as *mut _,
|
||||
},
|
||||
tag: sys::JS_TAG_MODULE as i64,
|
||||
};
|
||||
|
||||
sys::JS_DupValue(context.ctx, v);
|
||||
sys::JS_EvalFunction(context.ctx, v)
|
||||
};
|
||||
context.check_exception(result)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_export(&self, context: &ContextRef, export: &str) -> Result<Value> {
|
||||
self.eval_self(context)?;
|
||||
let c_value = CString::new(export)?;
|
||||
unsafe {
|
||||
context.check_exception(sys::JS_GetModuleExport(
|
||||
context.ctx,
|
||||
self.module,
|
||||
c_value.as_ptr(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Module {
|
||||
m: ModuleRef,
|
||||
|
||||
#[allow(dead_code)]
|
||||
runtime: Runtime,
|
||||
}
|
||||
|
||||
impl Module {
|
||||
pub(crate) fn from_raw(module: *mut sys::JSModuleDef, runtime: Runtime) -> Module {
|
||||
Module {
|
||||
m: ModuleRef::from_raw(module),
|
||||
runtime,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Module {
|
||||
type Target = ModuleRef;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.m
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Module {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.m
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
use super::Module;
|
||||
use crate::{
|
||||
throw_string, Class, ClassID, ContextRef, Error, Result, TryIntoValue, Value, ValueRef,
|
||||
};
|
||||
|
|
@ -186,7 +187,7 @@ pub fn define_native_module<T: NativeModule>(
|
|||
ctx: &ContextRef,
|
||||
name: &str,
|
||||
module: T,
|
||||
) -> Result<()> {
|
||||
) -> Result<Module> {
|
||||
let c_name = CString::new(name)?;
|
||||
let m = unsafe { sys::JS_NewCModule(ctx.ctx, c_name.as_ptr(), Some(init_func::<T>)) };
|
||||
if m.is_null() {
|
||||
|
|
@ -202,7 +203,7 @@ pub fn define_native_module<T: NativeModule>(
|
|||
let mut import_meta = ctx.check_exception(unsafe { sys::JS_GetImportMeta(ctx.ctx, m) })?;
|
||||
import_meta.set_property(ctx, "native_module", &native_value)?;
|
||||
|
||||
Ok(())
|
||||
Ok(Module::from_raw(m, ctx.get_runtime()))
|
||||
}
|
||||
|
||||
struct GenericNativeModule {
|
||||
|
|
@ -256,7 +257,7 @@ impl<'ctx> NativeModuleBuilder<'ctx> {
|
|||
}
|
||||
|
||||
/// Finish constructing the native module.
|
||||
pub fn build(&self, name: &str) -> Result<()> {
|
||||
pub fn build(&self, name: &str) -> Result<Module> {
|
||||
let context = self.exports.context;
|
||||
let mut exports = Vec::new();
|
||||
for export in self.exports.exports.iter() {
|
||||
|
|
@ -298,7 +299,7 @@ mod tests {
|
|||
.expect("Module load should succeed");
|
||||
|
||||
let js_module = context
|
||||
.load_module(
|
||||
.eval_module(
|
||||
r#"
|
||||
import { foo } from "the_test";
|
||||
export const my_foo = foo;
|
||||
|
|
@ -308,7 +309,7 @@ export const my_foo = foo;
|
|||
.expect("Evaluation of the test script should succeed");
|
||||
|
||||
let my_foo = js_module
|
||||
.get_module_export(&context, "my_foo")
|
||||
.get_export(&context, "my_foo")
|
||||
.expect("Retrieving JS export should succeed");
|
||||
|
||||
assert_eq!(my_foo.to_string(&context).unwrap(), String::from("23"));
|
||||
|
|
@ -328,7 +329,7 @@ export const my_foo = foo;
|
|||
.expect("Module build should succeed");
|
||||
|
||||
let js_module = context
|
||||
.load_module(
|
||||
.eval_module(
|
||||
r#"
|
||||
import { foo, bar } from "the_test";
|
||||
export const my_foo = foo + bar;
|
||||
|
|
@ -338,7 +339,7 @@ export const my_foo = foo + bar;
|
|||
.expect("Evaluation of the test script should succeed");
|
||||
|
||||
let my_foo = js_module
|
||||
.get_module_export(&context, "my_foo")
|
||||
.get_export(&context, "my_foo")
|
||||
.expect("Retrieving JS export should succeed");
|
||||
|
||||
assert_eq!(my_foo.to_string(&context).unwrap(), String::from("444"));
|
||||
|
|
@ -1,8 +1,29 @@
|
|||
use crate::module::loader::{load_module, DefaultModuleLoader, ModuleLoader};
|
||||
use crate::ContextRef;
|
||||
use oden_js_sys as sys;
|
||||
use std::cell::RefCell;
|
||||
use std::ffi::CStr;
|
||||
|
||||
struct PrivateState {
|
||||
refs: u64,
|
||||
loader: Box<dyn ModuleLoader>,
|
||||
}
|
||||
|
||||
impl PrivateState {
|
||||
unsafe extern "C" fn module_loader(
|
||||
ctx: *mut sys::JSContext,
|
||||
path: *const i8,
|
||||
opaque: *mut std::os::raw::c_void,
|
||||
) -> *mut sys::JSModuleDef {
|
||||
let path = match CStr::from_ptr(path).to_str() {
|
||||
Ok(s) => s,
|
||||
Err(_) => return std::ptr::null_mut(),
|
||||
};
|
||||
|
||||
let state = opaque as *mut PrivateState;
|
||||
let context = ContextRef::from_raw(ctx);
|
||||
load_module(&context, path, &mut (*state).loader)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -12,10 +33,19 @@ pub struct Runtime {
|
|||
|
||||
impl Runtime {
|
||||
pub fn new() -> Runtime {
|
||||
let state = Box::new(RefCell::new(PrivateState { refs: 1 }));
|
||||
Self::with_loader(DefaultModuleLoader::new())
|
||||
}
|
||||
|
||||
pub fn with_loader<TLoader: ModuleLoader + 'static>(loader: TLoader) -> Runtime {
|
||||
let state = Box::new(RefCell::new(PrivateState {
|
||||
refs: 1,
|
||||
loader: Box::new(loader),
|
||||
}));
|
||||
let rt = unsafe {
|
||||
let rt = sys::JS_NewRuntime();
|
||||
sys::JS_SetRuntimeOpaque(rt, Box::into_raw(state) as *mut _);
|
||||
let state = Box::into_raw(state) as *mut _;
|
||||
sys::JS_SetRuntimeOpaque(rt, state);
|
||||
sys::JS_SetModuleLoaderFunc(rt, None, Some(PrivateState::module_loader), state);
|
||||
rt
|
||||
};
|
||||
Runtime { rt }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{AtomRef, ContextRef, Error, Result, Runtime, RustFunction};
|
||||
use oden_js_sys as sys;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::ffi::CStr;
|
||||
use std::fmt;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
|
|
@ -312,21 +312,6 @@ impl ValueRef {
|
|||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn get_module_export(&self, ctx: &ContextRef, export: &str) -> Result<Value> {
|
||||
if self.value_type() != ValueType::Module {
|
||||
return Err(Error::InvalidType {
|
||||
expected: ValueType::Bool,
|
||||
found: self.value_type(),
|
||||
});
|
||||
}
|
||||
|
||||
let c_value = CString::new(export)?;
|
||||
unsafe {
|
||||
let module = sys::JS_VALUE_GET_PTR(self.val) as *mut sys::JSModuleDef;
|
||||
ctx.check_exception(sys::JS_GetModuleExport(ctx.ctx, module, c_value.as_ptr()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call(&self, ctx: &ContextRef) -> Result<Value> {
|
||||
unsafe {
|
||||
ctx.check_exception(sys::JS_Call(
|
||||
|
|
@ -414,7 +399,7 @@ impl fmt::Debug for Value {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{Context, EvalFlags, EvalType, Runtime};
|
||||
use crate::{Context, EvalFlags, Runtime};
|
||||
|
||||
#[test]
|
||||
fn value_type() {
|
||||
|
|
@ -438,9 +423,7 @@ mod tests {
|
|||
];
|
||||
|
||||
for (expr, expected) in tests.into_iter() {
|
||||
let val = ctx
|
||||
.eval(expr, "script", EvalType::Global, EvalFlags::STRICT)
|
||||
.unwrap();
|
||||
let val = ctx.eval(expr, "script", EvalFlags::STRICT).unwrap();
|
||||
|
||||
assert_eq!(*expected, val.value_type(), "for {}", expr);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue