[oden] Grab my incomplete QuickJS wrapper
This commit is contained in:
parent
aa70df41a3
commit
898b1fe129
114 changed files with 244181 additions and 0 deletions
180
oden-js/src/callback.rs
Normal file
180
oden-js/src/callback.rs
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
use crate::{Class, ClassID, ContextRef, Error, Runtime, ValueRef, ValueResult};
|
||||
use oden_js_sys as sys;
|
||||
use std::ffi::{c_int, CString};
|
||||
use std::marker;
|
||||
use std::panic::catch_unwind;
|
||||
|
||||
pub trait Callback<'rt>:
|
||||
Fn(&ContextRef<'rt>, &ValueRef<'rt>, &[&ValueRef<'rt>]) -> ValueResult<'rt>
|
||||
{
|
||||
}
|
||||
|
||||
impl<'rt, T> Callback<'rt> for T where
|
||||
T: Fn(&ContextRef<'rt>, &ValueRef<'rt>, &[&ValueRef<'rt>]) -> ValueResult<'rt>
|
||||
{
|
||||
}
|
||||
|
||||
struct CallbackObject<'rt, T: Callback<'rt>> {
|
||||
callback: T,
|
||||
_phantom: marker::PhantomData<&'rt Runtime>,
|
||||
}
|
||||
|
||||
impl<'rt, T: Callback<'rt>> CallbackObject<'rt, T> {
|
||||
fn new(callback: T, context: &ContextRef<'rt>) -> ValueResult<'rt> {
|
||||
let obj = CallbackObject {
|
||||
callback,
|
||||
_phantom: marker::PhantomData,
|
||||
};
|
||||
obj.into_value(context)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'rt, T: Callback<'rt>> Class for CallbackObject<'rt, T> {
|
||||
fn class_id() -> &'static ClassID {
|
||||
static ID: ClassID = ClassID::new("CallbackObject");
|
||||
&ID
|
||||
}
|
||||
}
|
||||
|
||||
fn throw_string<'rt>(context: &ContextRef<'rt>, 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<'rt, F>(
|
||||
ctx: *mut sys::JSContext,
|
||||
_this: sys::JSValue,
|
||||
argc: c_int,
|
||||
argv: *mut sys::JSValue,
|
||||
_magic: c_int,
|
||||
data: *mut sys::JSValue,
|
||||
) -> sys::JSValue
|
||||
where
|
||||
F: Callback<'rt>,
|
||||
{
|
||||
let context: ContextRef<'_> = ContextRef::from_raw(ctx);
|
||||
let this: ValueRef = ValueRef::from_raw(_this, &context);
|
||||
|
||||
let mut actual_args = Vec::new();
|
||||
unsafe {
|
||||
for arg in std::slice::from_raw_parts(argv, argc.try_into().unwrap()) {
|
||||
actual_args.push(ValueRef::from_raw(*arg, &context));
|
||||
}
|
||||
}
|
||||
|
||||
let mut args = Vec::new();
|
||||
for aa in &actual_args {
|
||||
args.push(aa);
|
||||
}
|
||||
|
||||
// Grab the callback that we stashed in the value.
|
||||
let data_ref = unsafe { ValueRef::from_raw(*data, &context) };
|
||||
let result = match CallbackObject::<F>::try_from_value(&data_ref) {
|
||||
Ok(closure) => (closure.callback)(&context, &this, &args),
|
||||
Err(e) => Err(e),
|
||||
};
|
||||
|
||||
match result {
|
||||
Ok(v) => {
|
||||
// `result` is owned; make sure to dup the value on the way out. Ideally
|
||||
// we would just have a `take` or something on `Value` but I don't quite
|
||||
// know how to implement it correctly.
|
||||
unsafe {
|
||||
let ret = &v.val;
|
||||
sys::JS_DupValue(ctx, *ret);
|
||||
*ret
|
||||
}
|
||||
}
|
||||
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)
|
||||
},
|
||||
Err(err) => throw_string(&context, err.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn trampoline<'rt, F>(
|
||||
ctx: *mut sys::JSContext,
|
||||
_this: sys::JSValue,
|
||||
argc: c_int,
|
||||
argv: *mut sys::JSValue,
|
||||
_magic: c_int,
|
||||
data: *mut sys::JSValue,
|
||||
) -> sys::JSValue
|
||||
where
|
||||
F: Callback<'rt>,
|
||||
{
|
||||
match catch_unwind(|| callback_impl::<'rt, F>(ctx, _this, argc, argv, _magic, data)) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
let message = if let Some(e) = e.downcast_ref::<&'static str>() {
|
||||
format!("{e}")
|
||||
} else {
|
||||
format!("Unknown error")
|
||||
};
|
||||
|
||||
let context: ContextRef<'_> = ContextRef::from_raw(ctx);
|
||||
throw_string(&context, message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get an unsafe pointer to the trampoline for a closure. We need a generic
|
||||
/// function here to intuit the type of the closure, otherwise otherwise
|
||||
/// there's nothing we can put in the `<>` for `trampoline`. This also
|
||||
/// ensures that the rust compiler actually generates the trampoline.
|
||||
fn get_trampoline<'rt, F>(_closure: &F) -> sys::JSCFunctionData
|
||||
where
|
||||
F: Callback<'rt>,
|
||||
{
|
||||
Some(trampoline::<F>)
|
||||
}
|
||||
|
||||
/// Construct a new value that wraps a closure.
|
||||
pub(crate) fn new_fn<'rt, F>(ctx: &ContextRef<'rt>, func: F) -> sys::JSValue
|
||||
where
|
||||
F: Callback<'rt>,
|
||||
{
|
||||
let closure = func;
|
||||
let callback = get_trampoline(&closure);
|
||||
let value = CallbackObject::new(closure, ctx).expect("Unable to create");
|
||||
|
||||
unsafe {
|
||||
// Just some silly pointer magic; they want an array but we only have
|
||||
// one value so.
|
||||
let mut val = value.val;
|
||||
sys::JS_NewCFunctionData(ctx.ctx, callback, 0, 0, 1, &mut val)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue