use oden_js_sys as sys; use std::ffi::{CString, NulError}; use thiserror::Error; mod atom; mod callback; mod class; mod context; mod conversion; pub mod module; mod runtime; mod value; pub use atom::{Atom, AtomRef}; pub use class::{Class, ClassID}; pub use context::{Context, ContextRef, EvalFlags}; pub use conversion::*; pub use runtime::Runtime; pub use value::{Value, ValueRef, ValueType}; #[derive(Debug, Error)] pub enum Error { #[error("too many classes have been registered")] TooManyClasses, #[error("the specified value is not an instance of the class {0}")] WrongClass(String), #[error("input script contained an embedded NUL byte")] UnexpectedNul, #[error("the target context is from a different runtime")] DifferentRuntime, #[error("the specified value had the wrong type (expected {expected:?}, found {found:?})")] InvalidType { expected: ValueType, found: ValueType, }, #[error("argument count mismatch, expected {expected} but received {received}")] ArgumentCountMismatch { expected: usize, received: usize }, #[error("a conversion error occurred: {0}")] ConversionError(String), #[error("an error occurred calling a rust function: {0}")] RustFunctionError(String), #[error("an exception was thrown during evaluation: {1}\nStack: {2}")] Exception(Value, String, String), #[error("out of memory")] OutOfMemory, #[error("an io error occurred: {0}")] IOError(std::io::Error), #[error("one or more errors occurred parsing {0}: {1}")] ParseError(String, String), } impl From for Error { fn from(_: NulError) -> Self { Error::UnexpectedNul } } impl From for Error { fn from(e: std::io::Error) -> Self { Error::IOError(e) } } pub type Result = core::result::Result; pub type ValueResult = core::result::Result; 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 { // 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), ) }, } }