[oden] The big lifetime removal

It turns out that rust can't really reason about the relationship
between the runtime lifetime and the context lifetime in a way that is
actually usable. This removes the lifetime stuff in favor of reference
counting the runtime itself, via a block that we embed in the
pointer. This, I think, it the least worst option here.
This commit is contained in:
John Doty 2023-06-19 08:28:26 -07:00
parent 898b1fe129
commit 9f808cea31
10 changed files with 269 additions and 312 deletions

View file

@ -1,42 +1,31 @@
use crate::{Class, ClassID, ContextRef, Error, Runtime, ValueRef, ValueResult};
use crate::{Class, ClassID, ContextRef, Error, 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>
{
}
pub trait Callback: Fn(&ContextRef, &ValueRef, &[&ValueRef]) -> ValueResult {}
impl<'rt, T> Callback<'rt> for T where
T: Fn(&ContextRef<'rt>, &ValueRef<'rt>, &[&ValueRef<'rt>]) -> ValueResult<'rt>
{
}
impl<T> Callback for T where T: Fn(&ContextRef, &ValueRef, &[&ValueRef]) -> ValueResult {}
struct CallbackObject<'rt, T: Callback<'rt>> {
struct CallbackObject<T: Callback> {
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,
};
impl<T: Callback> CallbackObject<T> {
fn new(callback: T, context: &ContextRef) -> ValueResult {
let obj = CallbackObject { callback };
obj.into_value(context)
}
}
impl<'rt, T: Callback<'rt>> Class for CallbackObject<'rt, T> {
impl<T: Callback> Class for CallbackObject<T> {
fn class_id() -> &'static ClassID {
static ID: ClassID = ClassID::new("CallbackObject");
&ID
}
}
fn throw_string<'rt>(context: &ContextRef<'rt>, message: String) -> sys::JSValue {
fn throw_string(context: &ContextRef, message: String) -> sys::JSValue {
let ctx = context.ctx;
match context.new_string(&message) {
Ok(e) => unsafe {
@ -70,7 +59,7 @@ fn throw_string<'rt>(context: &ContextRef<'rt>, message: String) -> sys::JSValue
}
}
fn callback_impl<'rt, F>(
fn callback_impl<F>(
ctx: *mut sys::JSContext,
_this: sys::JSValue,
argc: c_int,
@ -79,15 +68,15 @@ fn callback_impl<'rt, F>(
data: *mut sys::JSValue,
) -> sys::JSValue
where
F: Callback<'rt>,
F: Callback,
{
let context: ContextRef<'_> = ContextRef::from_raw(ctx);
let this: ValueRef = ValueRef::from_raw(_this, &context);
let context: ContextRef = ContextRef::from_raw(ctx);
let this: ValueRef = ValueRef::from_raw(_this);
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));
actual_args.push(ValueRef::from_raw(*arg));
}
}
@ -97,7 +86,7 @@ where
}
// Grab the callback that we stashed in the value.
let data_ref = unsafe { ValueRef::from_raw(*data, &context) };
let data_ref = unsafe { ValueRef::from_raw(*data) };
let result = match CallbackObject::<F>::try_from_value(&data_ref) {
Ok(closure) => (closure.callback)(&context, &this, &args),
Err(e) => Err(e),
@ -125,7 +114,7 @@ where
}
}
unsafe extern "C" fn trampoline<'rt, F>(
unsafe extern "C" fn trampoline<F>(
ctx: *mut sys::JSContext,
_this: sys::JSValue,
argc: c_int,
@ -134,9 +123,9 @@ unsafe extern "C" fn trampoline<'rt, F>(
data: *mut sys::JSValue,
) -> sys::JSValue
where
F: Callback<'rt>,
F: Callback,
{
match catch_unwind(|| callback_impl::<'rt, F>(ctx, _this, argc, argv, _magic, data)) {
match catch_unwind(|| callback_impl::<F>(ctx, _this, argc, argv, _magic, data)) {
Ok(r) => r,
Err(e) => {
let message = if let Some(e) = e.downcast_ref::<&'static str>() {
@ -145,7 +134,7 @@ where
format!("Unknown error")
};
let context: ContextRef<'_> = ContextRef::from_raw(ctx);
let context: ContextRef = ContextRef::from_raw(ctx);
throw_string(&context, message)
}
}
@ -155,17 +144,17 @@ where
/// 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
fn get_trampoline<F>(_closure: &F) -> sys::JSCFunctionData
where
F: Callback<'rt>,
F: Callback,
{
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
pub(crate) fn new_fn<F>(ctx: &ContextRef, func: F) -> sys::JSValue
where
F: Callback<'rt>,
F: Callback,
{
let closure = func;
let callback = get_trampoline(&closure);