[oden] Native Modules

This commit is contained in:
John Doty 2023-06-21 06:19:14 -07:00
parent 3b02faf9b4
commit c574fd8cb8
8 changed files with 453 additions and 79 deletions

View file

@ -52,7 +52,9 @@ extern "C" {
fn JS_MakeException_real() -> JSValue; fn JS_MakeException_real() -> JSValue;
fn JS_MakeNull_real() -> JSValue; fn JS_MakeNull_real() -> JSValue;
fn JS_MakeUndefined_real() -> JSValue; fn JS_MakeUndefined_real() -> JSValue;
fn JS_ValueGetPtr_real(v: JSValue) -> *mut ::std::os::raw::c_void; fn JS_VALUE_GET_PTR_real(v: JSValue) -> *mut ::std::os::raw::c_void;
fn JS_VALUE_GET_INT_real(v: JSValue) -> ::std::os::raw::c_int;
fn JS_VALUE_GET_BOOL_real(v: JSValue) -> ::std::os::raw::c_int;
} }
pub unsafe fn JS_ValueGetTag(v: JSValue) -> i32 { pub unsafe fn JS_ValueGetTag(v: JSValue) -> i32 {
@ -228,6 +230,14 @@ pub unsafe fn JS_MakeUndefined() -> JSValue {
JS_MakeUndefined_real() JS_MakeUndefined_real()
} }
pub unsafe fn JS_ValueGetPtr(v: JSValue) -> *mut ::std::os::raw::c_void { pub unsafe fn JS_VALUE_GET_PTR(v: JSValue) -> *mut ::std::os::raw::c_void {
JS_ValueGetPtr_real(v) JS_VALUE_GET_PTR_real(v)
}
pub unsafe fn JS_VALUE_GET_INT(v: JSValue) -> ::std::os::raw::c_int {
JS_VALUE_GET_INT_real(v)
}
pub unsafe fn JS_VALUE_GET_BOOL(v: JSValue) -> ::std::os::raw::c_int {
JS_VALUE_GET_BOOL_real(v)
} }

View file

@ -137,6 +137,15 @@ JSValue JS_MakeUndefined_real() {
return JS_UNDEFINED; return JS_UNDEFINED;
} }
void *JS_ValueGetPtr_real(JSValue val) { void *JS_VALUE_GET_PTR_real(JSValue val) {
return JS_VALUE_GET_PTR(val); return JS_VALUE_GET_PTR(val);
} }
int JS_VALUE_GET_INT_real(JSValue v) {
return JS_VALUE_GET_INT(v);
}
int JS_VALUE_GET_BOOL_real(JSValue v) {
return JS_VALUE_GET_BOOL(v);
}

View file

@ -1,6 +1,6 @@
use crate::{Class, ClassID, ContextRef, Error, ValueRef, ValueResult}; use crate::{throw_string, Class, ClassID, ContextRef, Error, ValueRef, ValueResult};
use oden_js_sys as sys; use oden_js_sys as sys;
use std::ffi::{c_int, CString}; use std::ffi::c_int;
use std::panic::catch_unwind; use std::panic::catch_unwind;
pub trait Callback: Fn(&ContextRef, &ValueRef, &[&ValueRef]) -> ValueResult {} pub trait Callback: Fn(&ContextRef, &ValueRef, &[&ValueRef]) -> ValueResult {}
@ -25,40 +25,6 @@ impl<T: Callback> Class for CallbackObject<T> {
} }
} }
fn throw_string(context: &ContextRef, message: String) -> sys::JSValue {
let ctx = context.ctx;
match context.new_string(&message) {
Ok(e) => unsafe {
// Because context.new_string yields an owned Value, and will
// clean it up on the way out, we need to explicitly DupValue a
// reference for the `Throw` to own.
let err = sys::JS_NewError(ctx);
if sys::JS_ValueGetTag(err) == sys::JS_TAG_EXCEPTION {
// GIVE UP; this is out of memory anyway things probably went
// wrong because of that.
return err;
}
sys::JS_DupValue(ctx, e.val); // SetProperty takes ownership.
let prop = CString::new("message").unwrap();
if sys::JS_SetPropertyStr(ctx, err, prop.as_ptr(), e.val) == -1 {
// Also an out of memory but we need to free the error object
// on our way out.
sys::JS_FreeValue(ctx, err);
return sys::JS_MakeException(); // JS_EXCEPTION
}
sys::JS_Throw(ctx, err)
},
Err(_) => unsafe {
sys::JS_Throw(
ctx,
sys::JS_NewString(ctx, "Errors within errors: embedded nulls in the description of the error that occurred".as_bytes().as_ptr() as *const i8),
)
},
}
}
fn callback_impl<F>( fn callback_impl<F>(
ctx: *mut sys::JSContext, ctx: *mut sys::JSContext,
_this: sys::JSValue, _this: sys::JSValue,
@ -103,7 +69,7 @@ where
*ret *ret
} }
} }
Err(Error::Exception(e)) => unsafe { Err(Error::Exception(e, _)) => unsafe {
// If we returned `Error::Exception` then we're propagating an // If we returned `Error::Exception` then we're propagating an
// exception through the JS stack, just flip it. // exception through the JS stack, just flip it.
let exc = &e.val; let exc = &e.val;

View file

@ -106,6 +106,7 @@ impl ContextRef {
unsafe { sys::JS_EnableBignumExt(self.ctx, if enable { 1 } else { 0 }) } 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> { pub fn load_module(&self, input: &str, filename: &str) -> Result<Value> {
let val = self.eval(input, filename, EvalType::Module, EvalFlags::COMPILE_ONLY)?; let val = self.eval(input, filename, EvalType::Module, EvalFlags::COMPILE_ONLY)?;
assert!(val.is_module()); assert!(val.is_module());
@ -122,14 +123,8 @@ impl ContextRef {
eval_type: EvalType, eval_type: EvalType,
flags: EvalFlags, flags: EvalFlags,
) -> ValueResult { ) -> ValueResult {
let c_input = match CString::new(input) { let c_input = CString::new(input)?;
Ok(cs) => Ok(cs), let c_filename = CString::new(filename)?;
Err(_) => Err(Error::UnexpectedNul),
}?;
let c_filename = match CString::new(filename) {
Ok(cs) => Ok(cs),
Err(_) => Err(Error::UnexpectedNul),
}?;
let eval_type = match eval_type { let eval_type = match eval_type {
EvalType::Global => sys::JS_EVAL_TYPE_GLOBAL, EvalType::Global => sys::JS_EVAL_TYPE_GLOBAL,
@ -150,14 +145,11 @@ impl ContextRef {
/// Construct a new string atom. /// Construct a new string atom.
pub fn new_atom(&self, value: &str) -> Result<Atom> { pub fn new_atom(&self, value: &str) -> Result<Atom> {
let c_value = match CString::new(value) { let c_value = CString::new(value)?;
Ok(cs) => Ok(cs),
Err(_) => Err(Error::UnexpectedNul),
}?;
let atom = unsafe { sys::JS_NewAtomLen(self.ctx, c_value.into_raw(), value.len()) }; let atom = unsafe { sys::JS_NewAtomLen(self.ctx, c_value.as_ptr(), value.len()) };
if atom == sys::JS_ATOM_NULL { if atom == sys::JS_ATOM_NULL {
return Err(Error::Exception(self.exception())); return Err(self.exception_error());
} }
Ok(Atom::from_raw(atom, self)) Ok(Atom::from_raw(atom, self))
@ -241,13 +233,9 @@ impl ContextRef {
/// Construct a new value from a string. /// Construct a new value from a string.
pub fn new_string(&self, value: &str) -> ValueResult { pub fn new_string(&self, value: &str) -> ValueResult {
let c_value = match CString::new(value) { let c_value = CString::new(value)?;
Ok(cs) => Ok(cs),
Err(_) => Err(Error::UnexpectedNul),
}?;
self.check_exception(unsafe { self.check_exception(unsafe {
sys::JS_NewStringLen(self.ctx, c_value.into_raw(), value.len()) sys::JS_NewStringLen(self.ctx, c_value.as_ptr(), value.len())
}) })
} }
@ -283,7 +271,7 @@ impl ContextRef {
/// error value. Otherwise, just return success with the value, wrapped. /// error value. Otherwise, just return success with the value, wrapped.
pub(crate) fn check_exception(&self, val: sys::JSValue) -> ValueResult { pub(crate) fn check_exception(&self, val: sys::JSValue) -> ValueResult {
if unsafe { sys::JS_ValueGetTag(val) } == sys::JS_TAG_EXCEPTION { if unsafe { sys::JS_ValueGetTag(val) } == sys::JS_TAG_EXCEPTION {
Err(Error::Exception(self.exception())) Err(self.exception_error())
} else { } else {
Ok(Value::from_raw(val, self)) Ok(Value::from_raw(val, self))
} }
@ -296,6 +284,16 @@ impl ContextRef {
pub(crate) fn exception(&self) -> Value { pub(crate) fn exception(&self) -> Value {
Value::from_raw(unsafe { sys::JS_GetException(self.ctx) }, self) Value::from_raw(unsafe { sys::JS_GetException(self.ctx) }, self)
} }
/// Fetch the exception value from the context, if any. This is not
/// public because anything that might raise an exception should be
/// returning a Result<> instead, to separate the exception flow from the
/// value flow.
pub(crate) fn exception_error(&self) -> Error {
let exc = self.exception();
let desc = exc.to_string(&self).unwrap_or_else(|_| String::new());
Error::Exception(exc, desc)
}
} }
#[derive(Debug)] #[derive(Debug)]

View file

@ -116,7 +116,8 @@ impl TryIntoValue for Error {
} }
Error::ConversionError(e) => Err(Error::ConversionError(e)), Error::ConversionError(e) => Err(Error::ConversionError(e)),
Error::RustFunctionError(e) => Err(Error::RustFunctionError(e)), Error::RustFunctionError(e) => Err(Error::RustFunctionError(e)),
Error::Exception(v) => Err(Error::Exception(v.dup(ctx))), Error::Exception(v, d) => Err(Error::Exception(v.dup(ctx), d)),
Error::OutOfMemory => Err(Error::OutOfMemory),
} }
} }
} }

View file

@ -1,3 +1,5 @@
use oden_js_sys as sys;
use std::ffi::{CString, NulError};
use thiserror::Error; use thiserror::Error;
mod atom; mod atom;
@ -5,6 +7,7 @@ mod callback;
mod class; mod class;
mod context; mod context;
mod conversion; mod conversion;
pub mod module;
mod runtime; mod runtime;
mod value; mod value;
@ -36,8 +39,51 @@ pub enum Error {
ConversionError(String), ConversionError(String),
#[error("an error occurred calling a rust function: {0}")] #[error("an error occurred calling a rust function: {0}")]
RustFunctionError(String), RustFunctionError(String),
#[error("an exception was thrown during evaluation")] #[error("an exception was thrown during evaluation: {1}")]
Exception(Value), Exception(Value, String),
#[error("out of memory")]
OutOfMemory,
} }
impl From<NulError> for Error {
fn from(_: NulError) -> Self {
Error::UnexpectedNul
}
}
pub type Result<T> = core::result::Result<T, Error>; pub type Result<T> = core::result::Result<T, Error>;
pub type ValueResult = core::result::Result<Value, Error>; pub type ValueResult = core::result::Result<Value, Error>;
pub fn throw_string(context: &ContextRef, message: String) -> sys::JSValue {
let ctx = context.ctx;
match context.new_string(&message) {
Ok(e) => unsafe {
// Because context.new_string yields an owned Value, and will
// clean it up on the way out, we need to explicitly DupValue a
// reference for the `Throw` to own.
let err = sys::JS_NewError(ctx);
if sys::JS_ValueGetTag(err) == sys::JS_TAG_EXCEPTION {
// GIVE UP; this is out of memory anyway things probably went
// wrong because of that.
return err;
}
sys::JS_DupValue(ctx, e.val); // SetProperty takes ownership.
let prop = CString::new("message").unwrap();
if sys::JS_SetPropertyStr(ctx, err, prop.as_ptr(), e.val) == -1 {
// Also an out of memory but we need to free the error object
// on our way out.
sys::JS_FreeValue(ctx, err);
return sys::JS_MakeException(); // JS_EXCEPTION
}
sys::JS_Throw(ctx, err)
},
Err(_) => unsafe {
sys::JS_Throw(
ctx,
sys::JS_NewString(ctx, "Errors within errors: embedded nulls in the description of the error that occurred".as_bytes().as_ptr() as *const i8),
)
},
}
}

346
oden-js/src/module.rs Normal file
View file

@ -0,0 +1,346 @@
use crate::{
throw_string, Class, ClassID, ContextRef, Error, Result, TryIntoValue, Value, ValueRef,
};
use oden_js_sys as sys;
use std::collections::HashSet;
use std::ffi::CString;
/// A helper structure for declaring values. Use this in the implementation
/// of a NativeModule. (See the documentation for `NativeModule` for more
/// information.)
pub struct Declarations {
declarations: HashSet<CString>,
}
impl Declarations {
pub(crate) fn new() -> Self {
Declarations {
declarations: HashSet::new(),
}
}
/// Declare that a native module will eventually export a value named
/// `name`.
pub fn declare<S>(&mut self, name: S) -> Result<&mut Self>
where
S: Into<Vec<u8>>,
{
self.declarations.insert(CString::new(name)?);
Ok(self)
}
/// Apply all the declarations to the module. (Don't let user code see
/// the JSModuleDef, right?)
pub(crate) unsafe fn apply(self, ctx: &ContextRef, m: *mut sys::JSModuleDef) -> Result<()> {
for k in self.declarations {
let res = unsafe { sys::JS_AddModuleExport(ctx.ctx, m, k.into_raw()) };
if res < 0 {
return Err(Error::OutOfMemory);
}
}
Ok(())
}
}
struct Export {
name: CString,
value: Value,
}
/// A helper structure for exporting values. Use this in the implementation
/// of a NativeModule. (See the documentation for `NativeModule` for more
/// information.)
pub struct Exports<'ctx> {
context: &'ctx ContextRef,
exports: Vec<Export>,
}
impl<'ctx> Exports<'ctx> {
pub(crate) fn new(context: &'ctx ContextRef) -> Self {
Exports {
context,
exports: Vec::new(),
}
}
/// Export a value named `name` from the current module. You *must* have
/// provided the same name in a previous call to `Declarations::declare`.
pub fn export<N: Into<Vec<u8>>, T: TryIntoValue>(
&mut self,
name: N,
value: T,
) -> Result<&mut Self> {
let name = CString::new(name.into())?;
let value = value.try_into_value(&self.context)?;
self.export_value(name, &value)
}
/// Export a value named `name` from the current module. You *must* have
/// provided the same name in a previous call to `Declarations::declare`.
pub fn export_value(&mut self, name: CString, value: &ValueRef) -> Result<&mut Self> {
self.exports.push(Export {
name,
value: value.dup(self.context),
});
Ok(self)
}
/// Actually export the values in the module. (Don't let user code see
/// the JSModuleDef!)
pub(crate) fn apply(self, module: *mut sys::JSModuleDef) -> Result<()> {
for export in self.exports {
let name = export.name;
let value = export.value;
let res = unsafe {
// Ownership of name is retained
// Ownership of value is transfered.
sys::JS_DupValue(self.context.ctx, value.val);
sys::JS_SetModuleExport(self.context.ctx, module, name.as_ref().as_ptr(), value.val)
};
if res < 0 {
return Err(Error::OutOfMemory);
}
}
Ok(())
}
}
/// Implement this trait to implement a JavaScript module in Rust.
///
/// JavaScript modules proceed in two phases. First, we resolve all the
/// imports, and once the import graph has been determined, then "execute"
/// the body of the modules to actually generate values. We reflect these
/// two phases here, in the `declare` and `define` methods.
///
/// (You might find the `NativeModuleBuilder` structure easier to use.)
pub trait NativeModule {
/// Phase 1: Declare all the names you're going to export. Call
/// `declarations.declare` once for each value you will eventually export
/// in phase 2.
fn declare(&self, declarations: &mut Declarations) -> Result<()>;
/// Phase 2: Define all the values you're going to define. Call
/// `exports.export` once for each value you declared in phase 1.
fn define<'ctx>(&self, context: &'ctx ContextRef, exports: &mut Exports<'ctx>) -> Result<()>;
}
struct NativeModuleState<T: NativeModule> {
module: T,
}
impl<T: NativeModule> NativeModuleState<T> {
fn new(module: T) -> Self {
NativeModuleState { module }
}
fn define(context: &ContextRef, m: *mut sys::JSModuleDef) -> Result<()> {
let import_meta =
context.check_exception(unsafe { sys::JS_GetImportMeta(context.ctx, m) })?;
let native_value = import_meta.get_property(context, "native_module")?;
let native = Self::try_from_value(&native_value)?;
let mut exports = Exports::new(context);
native
.module
.define(context, &mut exports)
.and_then(|_| exports.apply(m))
}
}
impl<T: NativeModule> Class for NativeModuleState<T> {
fn class_id() -> &'static ClassID {
static ID: ClassID = ClassID::new("NativeModuleState");
&ID
}
}
unsafe extern "C" fn init_func<T: NativeModule>(
ctx: *mut sys::JSContext,
m: *mut sys::JSModuleDef,
) -> std::os::raw::c_int {
let context = ContextRef::from_raw(ctx);
match NativeModuleState::<T>::define(&context, m) {
Ok(_) => 0,
Err(Error::Exception(e, _)) => unsafe {
// If we returned `Error::Exception` then we're propagating an
// exception through the JS stack, just flip it.
let exc = &e.val;
sys::JS_DupValue(ctx, *exc);
sys::JS_Throw(ctx, *exc);
-1
},
Err(err) => {
throw_string(&context, err.to_string());
-1
}
}
}
/// Define a new native module, with the provided name and
/// implementation. Once this succeeds, the specified module is ready to be
/// consumed by javascript, although the `define` method will not be called
/// until the first time it gets successfully imported somewhere.
pub fn define_native_module<T: NativeModule>(
ctx: &ContextRef,
name: &str,
module: T,
) -> Result<()> {
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() {
return Err(ctx.exception_error());
}
let mut declarations = Declarations::new();
module
.declare(&mut declarations)
.and_then(|_| unsafe { declarations.apply(&ctx, m) })?;
let native_value = NativeModuleState::new(module).into_value(ctx)?;
let mut import_meta = ctx.check_exception(unsafe { sys::JS_GetImportMeta(ctx.ctx, m) })?;
import_meta.set_property(ctx, "native_module", &native_value)?;
Ok(())
}
struct GenericNativeModule {
exports: Vec<Export>,
}
impl GenericNativeModule {
fn new(exports: Vec<Export>) -> Self {
GenericNativeModule { exports }
}
}
impl NativeModule for GenericNativeModule {
fn declare(&self, declarations: &mut Declarations) -> Result<()> {
for e in self.exports.iter() {
declarations.declare(e.name.clone())?;
}
Ok(())
}
fn define<'ctx>(&self, _: &'ctx ContextRef, exports: &mut Exports<'ctx>) -> Result<()> {
for e in self.exports.iter() {
exports.export_value(e.name.clone(), &e.value)?;
}
Ok(())
}
}
/// A helper to define a native module, by defining it export-by-export.
pub struct NativeModuleBuilder<'ctx> {
exports: Exports<'ctx>,
}
impl<'ctx> NativeModuleBuilder<'ctx> {
/// Construct a new native module builder, which will use the given
/// context to define members.
pub fn new(context: &'ctx ContextRef) -> Self {
NativeModuleBuilder {
exports: Exports::new(context),
}
}
/// Define a new export that will be in the native module.
pub fn export<N: Into<Vec<u8>>, T: TryIntoValue>(
&mut self,
name: N,
value: T,
) -> Result<&mut Self> {
self.exports.export(name, value)?;
Ok(self)
}
/// Finish constructing the native module.
pub fn build(&self, name: &str) -> Result<()> {
let context = self.exports.context;
let mut exports = Vec::new();
for export in self.exports.exports.iter() {
exports.push(Export {
name: export.name.clone(),
value: export.value.dup(context),
});
}
let generic_module = GenericNativeModule::new(exports);
define_native_module(context, name, generic_module)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Context, Runtime};
struct TestingNativeModule {}
impl NativeModule for TestingNativeModule {
fn declare(&self, declarations: &mut Declarations) -> Result<()> {
declarations.declare("foo")?;
Ok(())
}
fn define<'ctx>(&self, _: &'ctx ContextRef, exports: &mut Exports<'ctx>) -> Result<()> {
exports.export("foo", 23)?;
Ok(())
}
}
#[test]
fn test_define_native_module() {
let runtime = Runtime::new();
let context = Context::new(runtime);
define_native_module(&context, "the_test", TestingNativeModule {})
.expect("Module load should succeed");
let js_module = context
.load_module(
r#"
import { foo } from "the_test";
export const my_foo = foo;
"#,
"test",
)
.expect("Evaluation of the test script should succeed");
let my_foo = js_module
.get_module_export(&context, "my_foo")
.expect("Retrieving JS export should succeed");
assert_eq!(my_foo.to_string(&context).unwrap(), String::from("23"));
}
#[test]
fn test_native_module_builder() {
let runtime = Runtime::new();
let context = Context::new(runtime);
NativeModuleBuilder::new(&context)
.export("foo", 123)
.expect("define should succeed")
.export("bar", 321)
.expect("define should succeed")
.build("the_test")
.expect("Module build should succeed");
let js_module = context
.load_module(
r#"
import { foo, bar } from "the_test";
export const my_foo = foo + bar;
"#,
"test",
)
.expect("Evaluation of the test script should succeed");
let my_foo = js_module
.get_module_export(&context, "my_foo")
.expect("Retrieving JS export should succeed");
assert_eq!(my_foo.to_string(&context).unwrap(), String::from("444"));
}
}

View file

@ -125,7 +125,7 @@ impl ValueRef {
let mut res: i32 = 0; let mut res: i32 = 0;
let ret = sys::JS_ToInt32(ctx.ctx, &mut res, self.val); let ret = sys::JS_ToInt32(ctx.ctx, &mut res, self.val);
if ret < 0 { if ret < 0 {
Err(Error::Exception(ctx.exception())) Err(ctx.exception_error())
} else { } else {
Ok(res) Ok(res)
} }
@ -137,7 +137,9 @@ impl ValueRef {
let mut res: u32 = 0; let mut res: u32 = 0;
let ret = sys::JS_ToUint32(ctx.ctx, &mut res, self.val); let ret = sys::JS_ToUint32(ctx.ctx, &mut res, self.val);
if ret < 0 { if ret < 0 {
Err(Error::Exception(ctx.exception())) let exc = ctx.exception();
let desc = exc.to_string(&ctx).unwrap_or_else(|_| String::new());
Err(Error::Exception(exc, desc))
} else { } else {
Ok(res) Ok(res)
} }
@ -149,7 +151,7 @@ impl ValueRef {
let mut res: i64 = 0; let mut res: i64 = 0;
let ret = sys::JS_ToInt64(ctx.ctx, &mut res, self.val); let ret = sys::JS_ToInt64(ctx.ctx, &mut res, self.val);
if ret < 0 { if ret < 0 {
Err(Error::Exception(ctx.exception())) Err(ctx.exception_error())
} else { } else {
Ok(res) Ok(res)
} }
@ -189,7 +191,7 @@ impl ValueRef {
let mut res: f64 = 0.0; let mut res: f64 = 0.0;
let ret = sys::JS_ToFloat64(ctx.ctx, &mut res, self.val); let ret = sys::JS_ToFloat64(ctx.ctx, &mut res, self.val);
if ret < 0 { if ret < 0 {
Err(Error::Exception(ctx.exception())) Err(ctx.exception_error())
} else { } else {
Ok(res) Ok(res)
} }
@ -257,7 +259,7 @@ impl ValueRef {
sys::JS_DupValue(ctx.ctx, val.val); sys::JS_DupValue(ctx.ctx, val.val);
let result = sys::JS_SetProperty(ctx.ctx, self.val, prop.atom, val.val); let result = sys::JS_SetProperty(ctx.ctx, self.val, prop.atom, val.val);
if result == -1 { if result == -1 {
Err(Error::Exception(ctx.exception())) Err(ctx.exception_error())
} else { } else {
Ok(()) Ok(())
} }
@ -290,7 +292,7 @@ impl ValueRef {
let cstr = unsafe { let cstr = unsafe {
let ptr = sys::JS_ToCStringLen2(ctx.ctx, std::ptr::null_mut(), self.val, 0); let ptr = sys::JS_ToCStringLen2(ctx.ctx, std::ptr::null_mut(), self.val, 0);
if ptr.is_null() { if ptr.is_null() {
return Err(Error::Exception(ctx.exception())); return Err(ctx.exception_error());
} }
CStr::from_ptr(ptr) CStr::from_ptr(ptr)
}; };
@ -318,14 +320,10 @@ impl ValueRef {
}); });
} }
let c_value = match CString::new(export) { let c_value = CString::new(export)?;
Ok(cs) => Ok(cs),
Err(_) => Err(Error::UnexpectedNul),
}?;
unsafe { unsafe {
let module = sys::JS_ValueGetPtr(self.val) as *mut sys::JSModuleDef; let module = sys::JS_VALUE_GET_PTR(self.val) as *mut sys::JSModuleDef;
ctx.check_exception(sys::JS_GetModuleExport(ctx.ctx, module, c_value.into_raw())) ctx.check_exception(sys::JS_GetModuleExport(ctx.ctx, module, c_value.as_ptr()))
} }
} }
@ -342,7 +340,7 @@ impl ValueRef {
} }
} }
impl<'ctx> fmt::Debug for ValueRef { impl fmt::Debug for ValueRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Value") f.debug_struct("Value")
.field("v", &self.val) .field("v", &self.val)