[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:
parent
898b1fe129
commit
9f808cea31
10 changed files with 269 additions and 312 deletions
|
|
@ -5,7 +5,6 @@ use crate::{
|
|||
use bitflags::bitflags;
|
||||
use oden_js_sys as sys;
|
||||
use std::ffi::CString;
|
||||
use std::marker;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
/// Different ways to evaluate JavaScript. See the various `eval` methods on
|
||||
|
|
@ -43,17 +42,13 @@ bitflags! {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ContextRef<'rt> {
|
||||
pub struct ContextRef {
|
||||
pub(crate) ctx: *mut sys::JSContext,
|
||||
_marker: marker::PhantomData<&'rt Runtime>,
|
||||
}
|
||||
|
||||
impl<'rt> ContextRef<'rt> {
|
||||
impl ContextRef {
|
||||
pub(crate) fn from_raw(ctx: *mut sys::JSContext) -> Self {
|
||||
ContextRef {
|
||||
ctx,
|
||||
_marker: marker::PhantomData,
|
||||
}
|
||||
ContextRef { ctx }
|
||||
}
|
||||
|
||||
pub fn is_registered_class(&self, id: &ClassID) -> bool {
|
||||
|
|
@ -118,7 +113,7 @@ impl<'rt> ContextRef<'rt> {
|
|||
filename: &str,
|
||||
eval_type: EvalType,
|
||||
flags: EvalFlags,
|
||||
) -> ValueResult<'rt> {
|
||||
) -> ValueResult {
|
||||
let c_input = match CString::new(input) {
|
||||
Ok(cs) => Ok(cs),
|
||||
Err(_) => Err(Error::UnexpectedNul),
|
||||
|
|
@ -146,7 +141,7 @@ impl<'rt> ContextRef<'rt> {
|
|||
}
|
||||
|
||||
/// Construct a new string atom.
|
||||
pub fn new_atom(&self, value: &str) -> Result<'rt, Atom<'rt>> {
|
||||
pub fn new_atom(&self, value: &str) -> Result<Atom> {
|
||||
let c_value = match CString::new(value) {
|
||||
Ok(cs) => Ok(cs),
|
||||
Err(_) => Err(Error::UnexpectedNul),
|
||||
|
|
@ -161,12 +156,12 @@ impl<'rt> ContextRef<'rt> {
|
|||
}
|
||||
|
||||
/// Construct a new value of type object.
|
||||
pub fn new_object(&self) -> ValueResult<'rt> {
|
||||
pub fn new_object(&self) -> ValueResult {
|
||||
self.check_exception(unsafe { sys::JS_NewObject(self.ctx) })
|
||||
}
|
||||
|
||||
/// Construct a new value from a boolean.
|
||||
pub fn new_bool<T>(&self, value: T) -> ValueResult<'rt>
|
||||
pub fn new_bool<T>(&self, value: T) -> ValueResult
|
||||
where
|
||||
T: Into<bool>,
|
||||
{
|
||||
|
|
@ -174,14 +169,14 @@ impl<'rt> ContextRef<'rt> {
|
|||
}
|
||||
|
||||
/// Construct a new value that wraps a strongly-typed closure.
|
||||
pub fn new_fn<F>(&self, func: impl RustFunction<'rt, F>) -> ValueResult<'rt> {
|
||||
pub fn new_fn<F>(&self, func: impl RustFunction<F>) -> ValueResult {
|
||||
self.new_dynamic_fn(|c, _, a| func.call(c, a))
|
||||
}
|
||||
|
||||
/// Construct a new value that wraps a dynamically-typed closure.
|
||||
pub fn new_dynamic_fn<F>(&self, func: F) -> ValueResult<'rt>
|
||||
pub fn new_dynamic_fn<F>(&self, func: F) -> ValueResult
|
||||
where
|
||||
F: Fn(&ContextRef<'rt>, &ValueRef<'rt>, &[&ValueRef<'rt>]) -> ValueResult<'rt>,
|
||||
F: Fn(&ContextRef, &ValueRef, &[&ValueRef]) -> ValueResult,
|
||||
{
|
||||
// Constructing a new function is complicated enough that it needs to
|
||||
// be out of line.
|
||||
|
|
@ -189,7 +184,7 @@ impl<'rt> ContextRef<'rt> {
|
|||
}
|
||||
|
||||
/// Construct a new value from an int32.
|
||||
pub fn new_i32<T>(&self, value: T) -> ValueResult<'rt>
|
||||
pub fn new_i32<T>(&self, value: T) -> ValueResult
|
||||
where
|
||||
T: Into<i32>,
|
||||
{
|
||||
|
|
@ -200,7 +195,7 @@ impl<'rt> ContextRef<'rt> {
|
|||
///
|
||||
/// This returns a value of type Int32 if the value fits into an int32,
|
||||
/// otherwise it returns a value of type Float64.
|
||||
pub fn new_u32<T>(&self, value: T) -> ValueResult<'rt>
|
||||
pub fn new_u32<T>(&self, value: T) -> ValueResult
|
||||
where
|
||||
T: Into<u32>,
|
||||
{
|
||||
|
|
@ -208,7 +203,7 @@ impl<'rt> ContextRef<'rt> {
|
|||
}
|
||||
|
||||
/// Construct a new value from a float64.
|
||||
pub fn new_f64<T>(&self, value: T) -> ValueResult<'rt>
|
||||
pub fn new_f64<T>(&self, value: T) -> ValueResult
|
||||
where
|
||||
T: Into<f64>,
|
||||
{
|
||||
|
|
@ -216,7 +211,7 @@ impl<'rt> ContextRef<'rt> {
|
|||
}
|
||||
|
||||
/// Construct a new BigInt from an i64
|
||||
pub fn new_i64<T>(&self, value: T) -> ValueResult<'rt>
|
||||
pub fn new_i64<T>(&self, value: T) -> ValueResult
|
||||
where
|
||||
T: Into<i64>,
|
||||
{
|
||||
|
|
@ -224,7 +219,7 @@ impl<'rt> ContextRef<'rt> {
|
|||
}
|
||||
|
||||
/// Construct a new BigInt from an u64
|
||||
pub fn new_u64<T>(&self, value: T) -> ValueResult<'rt>
|
||||
pub fn new_u64<T>(&self, value: T) -> ValueResult
|
||||
where
|
||||
T: Into<u64>,
|
||||
{
|
||||
|
|
@ -232,12 +227,12 @@ impl<'rt> ContextRef<'rt> {
|
|||
}
|
||||
|
||||
/// Construct a new array value.
|
||||
pub fn new_array(&self) -> ValueResult<'rt> {
|
||||
pub fn new_array(&self) -> ValueResult {
|
||||
self.check_exception(unsafe { sys::JS_NewArray(self.ctx) })
|
||||
}
|
||||
|
||||
/// Construct a new value from a string.
|
||||
pub fn new_string(&self, value: &str) -> ValueResult<'rt> {
|
||||
pub fn new_string(&self, value: &str) -> ValueResult {
|
||||
let c_value = match CString::new(value) {
|
||||
Ok(cs) => Ok(cs),
|
||||
Err(_) => Err(Error::UnexpectedNul),
|
||||
|
|
@ -249,7 +244,7 @@ impl<'rt> ContextRef<'rt> {
|
|||
}
|
||||
|
||||
/// Get the null value.
|
||||
pub fn null(&self) -> Value<'rt> {
|
||||
pub fn null(&self) -> Value {
|
||||
let v = sys::JSValue {
|
||||
u: sys::JSValueUnion {
|
||||
ptr: std::ptr::null_mut(),
|
||||
|
|
@ -260,7 +255,7 @@ impl<'rt> ContextRef<'rt> {
|
|||
}
|
||||
|
||||
/// Get the undefined value.
|
||||
pub fn undefined(&self) -> Value<'rt> {
|
||||
pub fn undefined(&self) -> Value {
|
||||
let v = sys::JSValue {
|
||||
u: sys::JSValueUnion {
|
||||
ptr: std::ptr::null_mut(),
|
||||
|
|
@ -271,14 +266,14 @@ impl<'rt> ContextRef<'rt> {
|
|||
}
|
||||
|
||||
/// Fetch the global object for the context.
|
||||
pub fn global_object(&self) -> ValueResult<'rt> {
|
||||
pub fn global_object(&self) -> ValueResult {
|
||||
self.check_exception(unsafe { sys::JS_GetGlobalObject(self.ctx) })
|
||||
}
|
||||
|
||||
/// Check the value to see if it is the special marker for an exception,
|
||||
/// and if so grab the exception from the context and return it in an
|
||||
/// error value. Otherwise, just return success with the value, wrapped.
|
||||
pub(crate) fn check_exception(&self, val: sys::JSValue) -> ValueResult<'rt> {
|
||||
pub(crate) fn check_exception(&self, val: sys::JSValue) -> ValueResult {
|
||||
if unsafe { sys::JS_ValueGetTag(val) } == sys::JS_TAG_EXCEPTION {
|
||||
Err(Error::Exception(self.exception()))
|
||||
} else {
|
||||
|
|
@ -290,17 +285,18 @@ impl<'rt> ContextRef<'rt> {
|
|||
/// 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(&self) -> Value<'rt> {
|
||||
pub(crate) fn exception(&self) -> Value {
|
||||
Value::from_raw(unsafe { sys::JS_GetException(self.ctx) }, self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Context<'rt> {
|
||||
value: ContextRef<'rt>,
|
||||
pub struct Context {
|
||||
value: ContextRef,
|
||||
runtime: Runtime,
|
||||
}
|
||||
|
||||
impl<'rt> Context<'rt> {
|
||||
impl Context {
|
||||
/// Construct a new JavaScript context with many of the useful instrinisc
|
||||
/// functions defined. Objects created by this context can be shared
|
||||
/// across contexts belonging to the same runtime.
|
||||
|
|
@ -321,9 +317,9 @@ impl<'rt> Context<'rt> {
|
|||
///
|
||||
/// If you don't want those objects, call `new_raw`, and then add the
|
||||
/// intrinsics that you want.
|
||||
pub fn new(runtime: &Runtime) -> Context {
|
||||
pub fn new(runtime: Runtime) -> Context {
|
||||
let value = unsafe { ContextRef::from_raw(sys::JS_NewContext(runtime.rt)) };
|
||||
Context { value }
|
||||
Context { value, runtime }
|
||||
}
|
||||
|
||||
/// Construct a new JavaScript context without any intrinsic
|
||||
|
|
@ -332,27 +328,32 @@ impl<'rt> Context<'rt> {
|
|||
///
|
||||
/// You will probably want to call one or more of the `add_intrinsic_`
|
||||
/// functions to add useful types to the runtime.
|
||||
pub fn new_raw(runtime: &Runtime) -> Context {
|
||||
pub fn new_raw(runtime: Runtime) -> Context {
|
||||
let value = unsafe { ContextRef::from_raw(sys::JS_NewContextRaw(runtime.rt)) };
|
||||
Context { value }
|
||||
Context { value, runtime }
|
||||
}
|
||||
|
||||
/// Get the runtime underlying this context.
|
||||
pub fn runtime(&self) -> &Runtime {
|
||||
&self.runtime
|
||||
}
|
||||
}
|
||||
|
||||
impl<'rt> Deref for Context<'rt> {
|
||||
type Target = ContextRef<'rt>;
|
||||
impl Deref for Context {
|
||||
type Target = ContextRef;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<'rt> DerefMut for Context<'rt> {
|
||||
impl DerefMut for Context {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<'rt> Drop for Context<'rt> {
|
||||
impl Drop for Context {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
sys::JS_FreeContext(self.value.ctx);
|
||||
|
|
@ -368,7 +369,7 @@ mod tests {
|
|||
#[test]
|
||||
fn basic_evaluation() {
|
||||
let rt = Runtime::new();
|
||||
let ctx = Context::new(&rt);
|
||||
let ctx = Context::new(rt);
|
||||
|
||||
let val = ctx
|
||||
.eval("1+1", "script", EvalType::Global, EvalFlags::NONE)
|
||||
|
|
@ -380,7 +381,7 @@ mod tests {
|
|||
#[test]
|
||||
fn eval_compiled() {
|
||||
let rt = Runtime::new();
|
||||
let ctx = Context::new(&rt);
|
||||
let ctx = Context::new(rt);
|
||||
|
||||
let compiled = ctx
|
||||
.eval("1 + 1", "script", EvalType::Global, EvalFlags::COMPILE_ONLY)
|
||||
|
|
@ -395,7 +396,7 @@ mod tests {
|
|||
#[test]
|
||||
fn global_object() {
|
||||
let rt = Runtime::new();
|
||||
let ctx = Context::new(&rt);
|
||||
let ctx = Context::new(rt);
|
||||
|
||||
let mut go = ctx.global_object().unwrap();
|
||||
assert_eq!(ValueType::Object, go.value_type());
|
||||
|
|
@ -409,18 +410,14 @@ mod tests {
|
|||
assert_eq!(String::from("15"), result.to_string(&ctx).unwrap());
|
||||
}
|
||||
|
||||
fn cb<'c>(
|
||||
ctx: &ContextRef<'c>,
|
||||
_this: &ValueRef<'c>,
|
||||
args: &[&ValueRef<'c>],
|
||||
) -> ValueResult<'c> {
|
||||
fn cb(ctx: &ContextRef, _this: &ValueRef, args: &[&ValueRef]) -> ValueResult {
|
||||
Ok(args[1].dup(ctx))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_objects() {
|
||||
let rt = Runtime::new();
|
||||
let ctx = Context::new(&rt);
|
||||
let ctx = Context::new(rt);
|
||||
|
||||
let mut go = ctx.global_object().unwrap();
|
||||
|
||||
|
|
@ -461,7 +458,7 @@ mod tests {
|
|||
#[test]
|
||||
fn real_closures() {
|
||||
let rt = Runtime::new();
|
||||
let ctx = Context::new(&rt);
|
||||
let ctx = Context::new(rt);
|
||||
|
||||
let return_value = ctx.new_string("unsafe").unwrap();
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue