[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
|
|
@ -1,65 +1,58 @@
|
||||||
use crate::{ContextRef, Runtime, ValueResult};
|
use crate::{ContextRef, Runtime, ValueResult};
|
||||||
use oden_js_sys as sys;
|
use oden_js_sys as sys;
|
||||||
use std::marker;
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
pub struct AtomRef<'r> {
|
pub struct AtomRef {
|
||||||
pub(crate) atom: sys::JSAtom,
|
pub(crate) atom: sys::JSAtom,
|
||||||
_marker: marker::PhantomData<&'r Runtime>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> AtomRef<'r> {
|
impl AtomRef {
|
||||||
pub(crate) fn from_raw(atom: sys::JSAtom, _ctx: &ContextRef<'r>) -> AtomRef<'r> {
|
pub(crate) fn from_raw(atom: sys::JSAtom, _ctx: &ContextRef) -> AtomRef {
|
||||||
AtomRef {
|
AtomRef { atom }
|
||||||
atom,
|
|
||||||
_marker: marker::PhantomData,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dup(&self, context: &ContextRef<'r>) -> Atom<'r> {
|
pub fn dup(&self, context: &ContextRef) -> Atom {
|
||||||
unsafe {
|
unsafe {
|
||||||
sys::JS_DupAtom(context.ctx, self.atom);
|
sys::JS_DupAtom(context.ctx, self.atom);
|
||||||
}
|
}
|
||||||
Atom::from_raw(self.atom, context)
|
Atom::from_raw(self.atom, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_value(&self, context: &ContextRef<'r>) -> ValueResult<'r> {
|
pub fn to_value(&self, context: &ContextRef) -> ValueResult {
|
||||||
context.check_exception(unsafe { sys::JS_AtomToValue(context.ctx, self.atom) })
|
context.check_exception(unsafe { sys::JS_AtomToValue(context.ctx, self.atom) })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_string_value(&self, context: &ContextRef<'r>) -> ValueResult<'r> {
|
pub fn to_string_value(&self, context: &ContextRef) -> ValueResult {
|
||||||
context.check_exception(unsafe { sys::JS_AtomToString(context.ctx, self.atom) })
|
context.check_exception(unsafe { sys::JS_AtomToString(context.ctx, self.atom) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Atom<'r> {
|
pub struct Atom {
|
||||||
atom: AtomRef<'r>,
|
atom: AtomRef,
|
||||||
rt: *mut sys::JSRuntime,
|
rt: Runtime,
|
||||||
_phantom: marker::PhantomData<&'r Runtime>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> Atom<'r> {
|
impl Atom {
|
||||||
pub(crate) fn from_raw(atom: sys::JSAtom, ctx: &ContextRef<'r>) -> Self {
|
pub(crate) fn from_raw(atom: sys::JSAtom, ctx: &ContextRef) -> Self {
|
||||||
Atom {
|
Atom {
|
||||||
atom: AtomRef::from_raw(atom, ctx),
|
atom: AtomRef::from_raw(atom, ctx),
|
||||||
rt: unsafe { sys::JS_GetRuntime(ctx.ctx) },
|
rt: Runtime::from_raw(unsafe { sys::JS_GetRuntime(ctx.ctx) }),
|
||||||
_phantom: marker::PhantomData,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> Deref for Atom<'r> {
|
impl Deref for Atom {
|
||||||
type Target = AtomRef<'r>;
|
type Target = AtomRef;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.atom
|
&self.atom
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> Drop for Atom<'r> {
|
impl Drop for Atom {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
sys::JS_FreeAtomRT(self.rt, self.atom.atom);
|
sys::JS_FreeAtomRT(self.rt.rt, self.atom.atom);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 oden_js_sys as sys;
|
||||||
use std::ffi::{c_int, CString};
|
use std::ffi::{c_int, CString};
|
||||||
use std::marker;
|
|
||||||
use std::panic::catch_unwind;
|
use std::panic::catch_unwind;
|
||||||
|
|
||||||
pub trait Callback<'rt>:
|
pub trait Callback: Fn(&ContextRef, &ValueRef, &[&ValueRef]) -> ValueResult {}
|
||||||
Fn(&ContextRef<'rt>, &ValueRef<'rt>, &[&ValueRef<'rt>]) -> ValueResult<'rt>
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'rt, T> Callback<'rt> for T where
|
impl<T> Callback for T where T: Fn(&ContextRef, &ValueRef, &[&ValueRef]) -> ValueResult {}
|
||||||
T: Fn(&ContextRef<'rt>, &ValueRef<'rt>, &[&ValueRef<'rt>]) -> ValueResult<'rt>
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CallbackObject<'rt, T: Callback<'rt>> {
|
struct CallbackObject<T: Callback> {
|
||||||
callback: T,
|
callback: T,
|
||||||
_phantom: marker::PhantomData<&'rt Runtime>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'rt, T: Callback<'rt>> CallbackObject<'rt, T> {
|
impl<T: Callback> CallbackObject<T> {
|
||||||
fn new(callback: T, context: &ContextRef<'rt>) -> ValueResult<'rt> {
|
fn new(callback: T, context: &ContextRef) -> ValueResult {
|
||||||
let obj = CallbackObject {
|
let obj = CallbackObject { callback };
|
||||||
callback,
|
|
||||||
_phantom: marker::PhantomData,
|
|
||||||
};
|
|
||||||
obj.into_value(context)
|
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 {
|
fn class_id() -> &'static ClassID {
|
||||||
static ID: ClassID = ClassID::new("CallbackObject");
|
static ID: ClassID = ClassID::new("CallbackObject");
|
||||||
&ID
|
&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;
|
let ctx = context.ctx;
|
||||||
match context.new_string(&message) {
|
match context.new_string(&message) {
|
||||||
Ok(e) => unsafe {
|
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,
|
ctx: *mut sys::JSContext,
|
||||||
_this: sys::JSValue,
|
_this: sys::JSValue,
|
||||||
argc: c_int,
|
argc: c_int,
|
||||||
|
|
@ -79,15 +68,15 @@ fn callback_impl<'rt, F>(
|
||||||
data: *mut sys::JSValue,
|
data: *mut sys::JSValue,
|
||||||
) -> sys::JSValue
|
) -> sys::JSValue
|
||||||
where
|
where
|
||||||
F: Callback<'rt>,
|
F: Callback,
|
||||||
{
|
{
|
||||||
let context: ContextRef<'_> = ContextRef::from_raw(ctx);
|
let context: ContextRef = ContextRef::from_raw(ctx);
|
||||||
let this: ValueRef = ValueRef::from_raw(_this, &context);
|
let this: ValueRef = ValueRef::from_raw(_this);
|
||||||
|
|
||||||
let mut actual_args = Vec::new();
|
let mut actual_args = Vec::new();
|
||||||
unsafe {
|
unsafe {
|
||||||
for arg in std::slice::from_raw_parts(argv, argc.try_into().unwrap()) {
|
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.
|
// 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) {
|
let result = match CallbackObject::<F>::try_from_value(&data_ref) {
|
||||||
Ok(closure) => (closure.callback)(&context, &this, &args),
|
Ok(closure) => (closure.callback)(&context, &this, &args),
|
||||||
Err(e) => Err(e),
|
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,
|
ctx: *mut sys::JSContext,
|
||||||
_this: sys::JSValue,
|
_this: sys::JSValue,
|
||||||
argc: c_int,
|
argc: c_int,
|
||||||
|
|
@ -134,9 +123,9 @@ unsafe extern "C" fn trampoline<'rt, F>(
|
||||||
data: *mut sys::JSValue,
|
data: *mut sys::JSValue,
|
||||||
) -> sys::JSValue
|
) -> sys::JSValue
|
||||||
where
|
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,
|
Ok(r) => r,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let message = if let Some(e) = e.downcast_ref::<&'static str>() {
|
let message = if let Some(e) = e.downcast_ref::<&'static str>() {
|
||||||
|
|
@ -145,7 +134,7 @@ where
|
||||||
format!("Unknown error")
|
format!("Unknown error")
|
||||||
};
|
};
|
||||||
|
|
||||||
let context: ContextRef<'_> = ContextRef::from_raw(ctx);
|
let context: ContextRef = ContextRef::from_raw(ctx);
|
||||||
throw_string(&context, message)
|
throw_string(&context, message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -155,17 +144,17 @@ where
|
||||||
/// function here to intuit the type of the closure, otherwise otherwise
|
/// function here to intuit the type of the closure, otherwise otherwise
|
||||||
/// there's nothing we can put in the `<>` for `trampoline`. This also
|
/// there's nothing we can put in the `<>` for `trampoline`. This also
|
||||||
/// ensures that the rust compiler actually generates the trampoline.
|
/// 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
|
where
|
||||||
F: Callback<'rt>,
|
F: Callback,
|
||||||
{
|
{
|
||||||
Some(trampoline::<F>)
|
Some(trampoline::<F>)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a new value that wraps a closure.
|
/// 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
|
where
|
||||||
F: Callback<'rt>,
|
F: Callback,
|
||||||
{
|
{
|
||||||
let closure = func;
|
let closure = func;
|
||||||
let callback = get_trampoline(&closure);
|
let callback = get_trampoline(&closure);
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ impl fmt::Debug for ClassID {
|
||||||
pub trait Class: Sized {
|
pub trait Class: Sized {
|
||||||
fn class_id() -> &'static ClassID;
|
fn class_id() -> &'static ClassID;
|
||||||
|
|
||||||
fn into_value<'r>(self, context: &ContextRef<'r>) -> ValueResult<'r> {
|
fn into_value(self, context: &ContextRef) -> ValueResult {
|
||||||
let class_id = Self::class_id();
|
let class_id = Self::class_id();
|
||||||
|
|
||||||
// Check to see if the class is registered with the runtime. If not,
|
// Check to see if the class is registered with the runtime. If not,
|
||||||
|
|
@ -122,7 +122,7 @@ pub trait Class: Sized {
|
||||||
Ok(val)
|
Ok(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_from_value_mut<'r>(value: &ValueRef<'r>) -> Result<'r, RefMut<'r, Self>> {
|
fn try_from_value_mut<'a>(value: &'a ValueRef) -> Result<RefMut<'a, Self>> {
|
||||||
let class = Self::class_id();
|
let class = Self::class_id();
|
||||||
|
|
||||||
// SAFETY: value.val is known to be valid, from the ValueRef.
|
// SAFETY: value.val is known to be valid, from the ValueRef.
|
||||||
|
|
@ -139,11 +139,11 @@ pub trait Class: Sized {
|
||||||
.borrow_mut())
|
.borrow_mut())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_value_mut<'r>(value: &ValueRef<'r>) -> RefMut<'r, Self> {
|
fn from_value_mut<'a>(value: &'a ValueRef) -> RefMut<'a, Self> {
|
||||||
Self::try_from_value_mut(value).expect("Wrong type for value")
|
Self::try_from_value_mut(value).expect("Wrong type for value")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_from_value<'r>(value: &ValueRef<'r>) -> Result<'r, Ref<'r, Self>> {
|
fn try_from_value<'a>(value: &'a ValueRef) -> Result<Ref<'a, Self>> {
|
||||||
let class = Self::class_id();
|
let class = Self::class_id();
|
||||||
|
|
||||||
// SAFETY: value.val is known to be valid, from the ValueRef.
|
// SAFETY: value.val is known to be valid, from the ValueRef.
|
||||||
|
|
@ -160,7 +160,7 @@ pub trait Class: Sized {
|
||||||
.borrow())
|
.borrow())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_value<'r>(value: &ValueRef<'r>) -> Ref<'r, Self> {
|
fn from_value<'a>(value: &'a ValueRef) -> Ref<'a, Self> {
|
||||||
match Self::try_from_value(value) {
|
match Self::try_from_value(value) {
|
||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
Err(_) => panic!(
|
Err(_) => panic!(
|
||||||
|
|
@ -171,7 +171,7 @@ pub trait Class: Sized {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prototype<'r>(context: &ContextRef<'r>) -> ValueResult<'r> {
|
fn prototype(context: &ContextRef) -> ValueResult {
|
||||||
context.new_object()
|
context.new_object()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -192,7 +192,7 @@ pub trait Class: Sized {
|
||||||
/// collector won't collect it.
|
/// collector won't collect it.
|
||||||
fn mark<F>(&self, _mark: F)
|
fn mark<F>(&self, _mark: F)
|
||||||
where
|
where
|
||||||
F: for<'r> Fn(&ValueRef<'r>) -> (),
|
F: Fn(&ValueRef) -> (),
|
||||||
{
|
{
|
||||||
// By default, nothing.
|
// By default, nothing.
|
||||||
}
|
}
|
||||||
|
|
@ -268,7 +268,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn round_trips() {
|
fn round_trips() {
|
||||||
let rt = Runtime::new();
|
let rt = Runtime::new();
|
||||||
let ctx = Context::new(&rt);
|
let ctx = Context::new(rt);
|
||||||
|
|
||||||
let x = X { x: 76 };
|
let x = X { x: 76 };
|
||||||
let val = x.into_value(&ctx).expect("Unable to create value!");
|
let val = x.into_value(&ctx).expect("Unable to create value!");
|
||||||
|
|
@ -280,7 +280,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn double_create() {
|
fn double_create() {
|
||||||
let rt = Runtime::new();
|
let rt = Runtime::new();
|
||||||
let ctx = Context::new(&rt);
|
let ctx = Context::new(rt);
|
||||||
|
|
||||||
let x = X { x: 76 };
|
let x = X { x: 76 };
|
||||||
let _v1 = x.into_value(&ctx).expect("Unable to create value!");
|
let _v1 = x.into_value(&ctx).expect("Unable to create value!");
|
||||||
|
|
@ -292,7 +292,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn bad_class_id() {
|
fn bad_class_id() {
|
||||||
let rt = Runtime::new();
|
let rt = Runtime::new();
|
||||||
let ctx = Context::new(&rt);
|
let ctx = Context::new(rt);
|
||||||
|
|
||||||
let y = Y { y: 110 };
|
let y = Y { y: 110 };
|
||||||
let val = y.into_value(&ctx).expect("Unable to create value!");
|
let val = y.into_value(&ctx).expect("Unable to create value!");
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ use crate::{
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use oden_js_sys as sys;
|
use oden_js_sys as sys;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::marker;
|
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
/// Different ways to evaluate JavaScript. See the various `eval` methods on
|
/// Different ways to evaluate JavaScript. See the various `eval` methods on
|
||||||
|
|
@ -43,17 +42,13 @@ bitflags! {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ContextRef<'rt> {
|
pub struct ContextRef {
|
||||||
pub(crate) ctx: *mut sys::JSContext,
|
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 {
|
pub(crate) fn from_raw(ctx: *mut sys::JSContext) -> Self {
|
||||||
ContextRef {
|
ContextRef { ctx }
|
||||||
ctx,
|
|
||||||
_marker: marker::PhantomData,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_registered_class(&self, id: &ClassID) -> bool {
|
pub fn is_registered_class(&self, id: &ClassID) -> bool {
|
||||||
|
|
@ -118,7 +113,7 @@ impl<'rt> ContextRef<'rt> {
|
||||||
filename: &str,
|
filename: &str,
|
||||||
eval_type: EvalType,
|
eval_type: EvalType,
|
||||||
flags: EvalFlags,
|
flags: EvalFlags,
|
||||||
) -> ValueResult<'rt> {
|
) -> ValueResult {
|
||||||
let c_input = match CString::new(input) {
|
let c_input = match CString::new(input) {
|
||||||
Ok(cs) => Ok(cs),
|
Ok(cs) => Ok(cs),
|
||||||
Err(_) => Err(Error::UnexpectedNul),
|
Err(_) => Err(Error::UnexpectedNul),
|
||||||
|
|
@ -146,7 +141,7 @@ impl<'rt> ContextRef<'rt> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a new string atom.
|
/// 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) {
|
let c_value = match CString::new(value) {
|
||||||
Ok(cs) => Ok(cs),
|
Ok(cs) => Ok(cs),
|
||||||
Err(_) => Err(Error::UnexpectedNul),
|
Err(_) => Err(Error::UnexpectedNul),
|
||||||
|
|
@ -161,12 +156,12 @@ impl<'rt> ContextRef<'rt> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a new value of type object.
|
/// 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) })
|
self.check_exception(unsafe { sys::JS_NewObject(self.ctx) })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a new value from a boolean.
|
/// 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
|
where
|
||||||
T: Into<bool>,
|
T: Into<bool>,
|
||||||
{
|
{
|
||||||
|
|
@ -174,14 +169,14 @@ impl<'rt> ContextRef<'rt> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a new value that wraps a strongly-typed closure.
|
/// 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))
|
self.new_dynamic_fn(|c, _, a| func.call(c, a))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a new value that wraps a dynamically-typed closure.
|
/// 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
|
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
|
// Constructing a new function is complicated enough that it needs to
|
||||||
// be out of line.
|
// be out of line.
|
||||||
|
|
@ -189,7 +184,7 @@ impl<'rt> ContextRef<'rt> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a new value from an int32.
|
/// 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
|
where
|
||||||
T: Into<i32>,
|
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,
|
/// This returns a value of type Int32 if the value fits into an int32,
|
||||||
/// otherwise it returns a value of type Float64.
|
/// 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
|
where
|
||||||
T: Into<u32>,
|
T: Into<u32>,
|
||||||
{
|
{
|
||||||
|
|
@ -208,7 +203,7 @@ impl<'rt> ContextRef<'rt> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a new value from a float64.
|
/// 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
|
where
|
||||||
T: Into<f64>,
|
T: Into<f64>,
|
||||||
{
|
{
|
||||||
|
|
@ -216,7 +211,7 @@ impl<'rt> ContextRef<'rt> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a new BigInt from an i64
|
/// 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
|
where
|
||||||
T: Into<i64>,
|
T: Into<i64>,
|
||||||
{
|
{
|
||||||
|
|
@ -224,7 +219,7 @@ impl<'rt> ContextRef<'rt> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a new BigInt from an u64
|
/// 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
|
where
|
||||||
T: Into<u64>,
|
T: Into<u64>,
|
||||||
{
|
{
|
||||||
|
|
@ -232,12 +227,12 @@ impl<'rt> ContextRef<'rt> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a new array value.
|
/// 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) })
|
self.check_exception(unsafe { sys::JS_NewArray(self.ctx) })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a new value from a string.
|
/// 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) {
|
let c_value = match CString::new(value) {
|
||||||
Ok(cs) => Ok(cs),
|
Ok(cs) => Ok(cs),
|
||||||
Err(_) => Err(Error::UnexpectedNul),
|
Err(_) => Err(Error::UnexpectedNul),
|
||||||
|
|
@ -249,7 +244,7 @@ impl<'rt> ContextRef<'rt> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the null value.
|
/// Get the null value.
|
||||||
pub fn null(&self) -> Value<'rt> {
|
pub fn null(&self) -> Value {
|
||||||
let v = sys::JSValue {
|
let v = sys::JSValue {
|
||||||
u: sys::JSValueUnion {
|
u: sys::JSValueUnion {
|
||||||
ptr: std::ptr::null_mut(),
|
ptr: std::ptr::null_mut(),
|
||||||
|
|
@ -260,7 +255,7 @@ impl<'rt> ContextRef<'rt> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the undefined value.
|
/// Get the undefined value.
|
||||||
pub fn undefined(&self) -> Value<'rt> {
|
pub fn undefined(&self) -> Value {
|
||||||
let v = sys::JSValue {
|
let v = sys::JSValue {
|
||||||
u: sys::JSValueUnion {
|
u: sys::JSValueUnion {
|
||||||
ptr: std::ptr::null_mut(),
|
ptr: std::ptr::null_mut(),
|
||||||
|
|
@ -271,14 +266,14 @@ impl<'rt> ContextRef<'rt> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch the global object for the context.
|
/// 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) })
|
self.check_exception(unsafe { sys::JS_GetGlobalObject(self.ctx) })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check the value to see if it is the special marker for an exception,
|
/// 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
|
/// and if so grab the exception from the context and return it in an
|
||||||
/// 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<'rt> {
|
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(Error::Exception(self.exception()))
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -290,17 +285,18 @@ impl<'rt> ContextRef<'rt> {
|
||||||
/// public because anything that might raise an exception should be
|
/// public because anything that might raise an exception should be
|
||||||
/// returning a Result<> instead, to separate the exception flow from the
|
/// returning a Result<> instead, to separate the exception flow from the
|
||||||
/// value flow.
|
/// 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)
|
Value::from_raw(unsafe { sys::JS_GetException(self.ctx) }, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Context<'rt> {
|
pub struct Context {
|
||||||
value: ContextRef<'rt>,
|
value: ContextRef,
|
||||||
|
runtime: Runtime,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'rt> Context<'rt> {
|
impl Context {
|
||||||
/// Construct a new JavaScript context with many of the useful instrinisc
|
/// Construct a new JavaScript context with many of the useful instrinisc
|
||||||
/// functions defined. Objects created by this context can be shared
|
/// functions defined. Objects created by this context can be shared
|
||||||
/// across contexts belonging to the same runtime.
|
/// 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
|
/// If you don't want those objects, call `new_raw`, and then add the
|
||||||
/// intrinsics that you want.
|
/// 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)) };
|
let value = unsafe { ContextRef::from_raw(sys::JS_NewContext(runtime.rt)) };
|
||||||
Context { value }
|
Context { value, runtime }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a new JavaScript context without any intrinsic
|
/// 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_`
|
/// You will probably want to call one or more of the `add_intrinsic_`
|
||||||
/// functions to add useful types to the runtime.
|
/// 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)) };
|
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> {
|
impl Deref for Context {
|
||||||
type Target = ContextRef<'rt>;
|
type Target = ContextRef;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.value
|
&self.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'rt> DerefMut for Context<'rt> {
|
impl DerefMut for Context {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
&mut self.value
|
&mut self.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'rt> Drop for Context<'rt> {
|
impl Drop for Context {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
sys::JS_FreeContext(self.value.ctx);
|
sys::JS_FreeContext(self.value.ctx);
|
||||||
|
|
@ -368,7 +369,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn basic_evaluation() {
|
fn basic_evaluation() {
|
||||||
let rt = Runtime::new();
|
let rt = Runtime::new();
|
||||||
let ctx = Context::new(&rt);
|
let ctx = Context::new(rt);
|
||||||
|
|
||||||
let val = ctx
|
let val = ctx
|
||||||
.eval("1+1", "script", EvalType::Global, EvalFlags::NONE)
|
.eval("1+1", "script", EvalType::Global, EvalFlags::NONE)
|
||||||
|
|
@ -380,7 +381,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_compiled() {
|
fn eval_compiled() {
|
||||||
let rt = Runtime::new();
|
let rt = Runtime::new();
|
||||||
let ctx = Context::new(&rt);
|
let ctx = Context::new(rt);
|
||||||
|
|
||||||
let compiled = ctx
|
let compiled = ctx
|
||||||
.eval("1 + 1", "script", EvalType::Global, EvalFlags::COMPILE_ONLY)
|
.eval("1 + 1", "script", EvalType::Global, EvalFlags::COMPILE_ONLY)
|
||||||
|
|
@ -395,7 +396,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn global_object() {
|
fn global_object() {
|
||||||
let rt = Runtime::new();
|
let rt = Runtime::new();
|
||||||
let ctx = Context::new(&rt);
|
let ctx = Context::new(rt);
|
||||||
|
|
||||||
let mut go = ctx.global_object().unwrap();
|
let mut go = ctx.global_object().unwrap();
|
||||||
assert_eq!(ValueType::Object, go.value_type());
|
assert_eq!(ValueType::Object, go.value_type());
|
||||||
|
|
@ -409,18 +410,14 @@ mod tests {
|
||||||
assert_eq!(String::from("15"), result.to_string(&ctx).unwrap());
|
assert_eq!(String::from("15"), result.to_string(&ctx).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cb<'c>(
|
fn cb(ctx: &ContextRef, _this: &ValueRef, args: &[&ValueRef]) -> ValueResult {
|
||||||
ctx: &ContextRef<'c>,
|
|
||||||
_this: &ValueRef<'c>,
|
|
||||||
args: &[&ValueRef<'c>],
|
|
||||||
) -> ValueResult<'c> {
|
|
||||||
Ok(args[1].dup(ctx))
|
Ok(args[1].dup(ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn new_objects() {
|
fn new_objects() {
|
||||||
let rt = Runtime::new();
|
let rt = Runtime::new();
|
||||||
let ctx = Context::new(&rt);
|
let ctx = Context::new(rt);
|
||||||
|
|
||||||
let mut go = ctx.global_object().unwrap();
|
let mut go = ctx.global_object().unwrap();
|
||||||
|
|
||||||
|
|
@ -461,7 +458,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn real_closures() {
|
fn real_closures() {
|
||||||
let rt = Runtime::new();
|
let rt = Runtime::new();
|
||||||
let ctx = Context::new(&rt);
|
let ctx = Context::new(rt);
|
||||||
|
|
||||||
let return_value = ctx.new_string("unsafe").unwrap();
|
let return_value = ctx.new_string("unsafe").unwrap();
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,12 @@ use crate::{ContextRef, Error, Result, ValueRef};
|
||||||
use std::num::TryFromIntError;
|
use std::num::TryFromIntError;
|
||||||
|
|
||||||
pub trait TryFromValue: Sized {
|
pub trait TryFromValue: Sized {
|
||||||
fn try_from_value<'r>(value: &ValueRef<'r>, ctx: &ContextRef<'r>) -> Result<'r, Self>;
|
fn try_from_value(value: &ValueRef, ctx: &ContextRef) -> Result<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFromValue for u8 {
|
impl TryFromValue for u8 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_from_value<'r>(value: &ValueRef<'r>, ctx: &ContextRef<'r>) -> Result<'r, Self> {
|
fn try_from_value(value: &ValueRef, ctx: &ContextRef) -> Result<Self> {
|
||||||
let v = value.to_u32(&ctx)?;
|
let v = value.to_u32(&ctx)?;
|
||||||
v.try_into()
|
v.try_into()
|
||||||
.map_err(|e: TryFromIntError| Error::ConversionError(e.to_string()))
|
.map_err(|e: TryFromIntError| Error::ConversionError(e.to_string()))
|
||||||
|
|
@ -16,7 +16,7 @@ impl TryFromValue for u8 {
|
||||||
|
|
||||||
impl TryFromValue for u16 {
|
impl TryFromValue for u16 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_from_value<'r>(value: &ValueRef<'r>, ctx: &ContextRef<'r>) -> Result<'r, Self> {
|
fn try_from_value(value: &ValueRef, ctx: &ContextRef) -> Result<Self> {
|
||||||
let v = value.to_u32(&ctx)?;
|
let v = value.to_u32(&ctx)?;
|
||||||
v.try_into()
|
v.try_into()
|
||||||
.map_err(|e: TryFromIntError| Error::ConversionError(e.to_string()))
|
.map_err(|e: TryFromIntError| Error::ConversionError(e.to_string()))
|
||||||
|
|
@ -25,21 +25,21 @@ impl TryFromValue for u16 {
|
||||||
|
|
||||||
impl TryFromValue for u32 {
|
impl TryFromValue for u32 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_from_value<'r>(value: &ValueRef<'r>, ctx: &ContextRef<'r>) -> Result<'r, Self> {
|
fn try_from_value(value: &ValueRef, ctx: &ContextRef) -> Result<Self> {
|
||||||
value.to_u32(&ctx)
|
value.to_u32(&ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl<'c,'d> TryFrom<&'c ValueRef<'d>> for u64 {
|
// impl<'c,'d> TryFrom<&'c ValueRef<'d>> for u64 {
|
||||||
// #[inline]
|
// #[inline]
|
||||||
// fn try_from_value<'r>(value: &ValueRef<'r>, ctx:&ContextRef<'r>) -> Result<'r, Self> {
|
// fn try_from_value(value: &ValueRef, ctx:&ContextRef) -> Result< Self> {
|
||||||
// value.to_u64()
|
// value.to_u64()
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
impl TryFromValue for i8 {
|
impl TryFromValue for i8 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_from_value<'r>(value: &ValueRef<'r>, ctx: &ContextRef<'r>) -> Result<'r, Self> {
|
fn try_from_value(value: &ValueRef, ctx: &ContextRef) -> Result<Self> {
|
||||||
let v = value.to_i32(&ctx)?;
|
let v = value.to_i32(&ctx)?;
|
||||||
v.try_into()
|
v.try_into()
|
||||||
.map_err(|e: TryFromIntError| Error::ConversionError(e.to_string()))
|
.map_err(|e: TryFromIntError| Error::ConversionError(e.to_string()))
|
||||||
|
|
@ -48,7 +48,7 @@ impl TryFromValue for i8 {
|
||||||
|
|
||||||
impl TryFromValue for i16 {
|
impl TryFromValue for i16 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_from_value<'r>(value: &ValueRef<'r>, ctx: &ContextRef<'r>) -> Result<'r, Self> {
|
fn try_from_value(value: &ValueRef, ctx: &ContextRef) -> Result<Self> {
|
||||||
let v = value.to_i32(&ctx)?;
|
let v = value.to_i32(&ctx)?;
|
||||||
v.try_into()
|
v.try_into()
|
||||||
.map_err(|e: TryFromIntError| Error::ConversionError(e.to_string()))
|
.map_err(|e: TryFromIntError| Error::ConversionError(e.to_string()))
|
||||||
|
|
@ -57,21 +57,21 @@ impl TryFromValue for i16 {
|
||||||
|
|
||||||
impl TryFromValue for i32 {
|
impl TryFromValue for i32 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_from_value<'r>(value: &ValueRef<'r>, ctx: &ContextRef<'r>) -> Result<'r, Self> {
|
fn try_from_value(value: &ValueRef, ctx: &ContextRef) -> Result<Self> {
|
||||||
value.to_i32(&ctx)
|
value.to_i32(&ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFromValue for i64 {
|
impl TryFromValue for i64 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_from_value<'r>(value: &ValueRef<'r>, ctx: &ContextRef<'r>) -> Result<'r, Self> {
|
fn try_from_value(value: &ValueRef, ctx: &ContextRef) -> Result<Self> {
|
||||||
value.to_i64(&ctx)
|
value.to_i64(&ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFromValue for f32 {
|
impl TryFromValue for f32 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_from_value<'r>(value: &ValueRef<'r>, ctx: &ContextRef<'r>) -> Result<'r, Self> {
|
fn try_from_value(value: &ValueRef, ctx: &ContextRef) -> Result<Self> {
|
||||||
let v = value.to_float64(&ctx)?;
|
let v = value.to_float64(&ctx)?;
|
||||||
Ok(v as f32)
|
Ok(v as f32)
|
||||||
}
|
}
|
||||||
|
|
@ -79,21 +79,21 @@ impl TryFromValue for f32 {
|
||||||
|
|
||||||
impl TryFromValue for f64 {
|
impl TryFromValue for f64 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_from_value<'r>(value: &ValueRef<'r>, ctx: &ContextRef<'r>) -> Result<'r, Self> {
|
fn try_from_value(value: &ValueRef, ctx: &ContextRef) -> Result<Self> {
|
||||||
value.to_float64(&ctx)
|
value.to_float64(&ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFromValue for bool {
|
impl TryFromValue for bool {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_from_value<'r>(value: &ValueRef<'r>, _ctx: &ContextRef<'r>) -> Result<'r, Self> {
|
fn try_from_value(value: &ValueRef, _ctx: &ContextRef) -> Result<Self> {
|
||||||
value.to_bool()
|
value.to_bool()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFromValue for String {
|
impl TryFromValue for String {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_from_value<'r>(value: &ValueRef<'r>, ctx: &ContextRef<'r>) -> Result<'r, Self> {
|
fn try_from_value(value: &ValueRef, ctx: &ContextRef) -> Result<Self> {
|
||||||
value.to_string(&ctx)
|
value.to_string(&ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -101,7 +101,7 @@ impl TryFromValue for String {
|
||||||
// impl<'c, T: Class> TryFrom<&'c ValueRef<'_>> for T {
|
// impl<'c, T: Class> TryFrom<&'c ValueRef<'_>> for T {
|
||||||
//
|
//
|
||||||
// #[inline]
|
// #[inline]
|
||||||
// fn try_from_value<'r>(value: &ValueRef<'r>, ctx:&ContextRef<'r>) -> Result<'r, Self> {
|
// fn try_from_value(value: &ValueRef, ctx:&ContextRef) -> Result< Self> {
|
||||||
// T::from_value(value)
|
// T::from_value(value)
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
|
||||||
|
|
@ -2,20 +2,18 @@ use super::{TryFromValue, TryIntoValue};
|
||||||
use crate::{ContextRef, Error, ValueRef, ValueResult};
|
use crate::{ContextRef, Error, ValueRef, ValueResult};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
pub trait IntoRustFunctionResult<'r> {
|
pub trait IntoRustFunctionResult {
|
||||||
fn into_res(self, ctx: &ContextRef<'r>) -> ValueResult<'r>;
|
fn into_res(self, ctx: &ContextRef) -> ValueResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r, T: TryIntoValue<'r>> IntoRustFunctionResult<'r> for T {
|
impl<T: TryIntoValue> IntoRustFunctionResult for T {
|
||||||
fn into_res(self, ctx: &ContextRef<'r>) -> ValueResult<'r> {
|
fn into_res(self, ctx: &ContextRef) -> ValueResult {
|
||||||
self.try_into_value(ctx)
|
self.try_into_value(ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r, T: TryIntoValue<'r>, E: std::fmt::Display> IntoRustFunctionResult<'r>
|
impl<T: TryIntoValue, E: std::fmt::Display> IntoRustFunctionResult for core::result::Result<T, E> {
|
||||||
for core::result::Result<T, E>
|
fn into_res(self, ctx: &ContextRef) -> ValueResult {
|
||||||
{
|
|
||||||
fn into_res(self, ctx: &ContextRef<'r>) -> ValueResult<'r> {
|
|
||||||
match self {
|
match self {
|
||||||
Ok(v) => v.try_into_value(ctx),
|
Ok(v) => v.try_into_value(ctx),
|
||||||
Err(e) => Err(Error::RustFunctionError(e.to_string())),
|
Err(e) => Err(Error::RustFunctionError(e.to_string())),
|
||||||
|
|
@ -23,21 +21,21 @@ impl<'r, T: TryIntoValue<'r>, E: std::fmt::Display> IntoRustFunctionResult<'r>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait RustFunction<'r, F> {
|
pub trait RustFunction<F> {
|
||||||
fn argument_count() -> usize;
|
fn argument_count() -> usize;
|
||||||
fn call(&self, context: &ContextRef<'r>, args: &[&ValueRef<'r>]) -> ValueResult<'r>;
|
fn call(&self, context: &ContextRef, args: &[&ValueRef]) -> ValueResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r, R, F> RustFunction<'r, PhantomData<(&R, &F)>> for F
|
impl<R, F> RustFunction<PhantomData<(&R, &F)>> for F
|
||||||
where
|
where
|
||||||
R: IntoRustFunctionResult<'r>,
|
R: IntoRustFunctionResult,
|
||||||
F: Fn(&ContextRef<'r>) -> R + Sized,
|
F: Fn(&ContextRef) -> R + Sized,
|
||||||
{
|
{
|
||||||
fn argument_count() -> usize {
|
fn argument_count() -> usize {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&self, context: &ContextRef<'r>, args: &[&ValueRef<'r>]) -> ValueResult<'r> {
|
fn call(&self, context: &ContextRef, args: &[&ValueRef]) -> ValueResult {
|
||||||
if args.len() != 0 {
|
if args.len() != 0 {
|
||||||
return Err(Error::ArgumentCountMismatch {
|
return Err(Error::ArgumentCountMismatch {
|
||||||
expected: Self::argument_count(),
|
expected: Self::argument_count(),
|
||||||
|
|
@ -50,17 +48,17 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r, R, A, F> RustFunction<'r, PhantomData<(&R, &A, &F)>> for F
|
impl<R, A, F> RustFunction<PhantomData<(&R, &A, &F)>> for F
|
||||||
where
|
where
|
||||||
R: IntoRustFunctionResult<'r>,
|
R: IntoRustFunctionResult,
|
||||||
A: TryFromValue,
|
A: TryFromValue,
|
||||||
F: Fn(&ContextRef<'r>, A) -> R + Sized,
|
F: Fn(&ContextRef, A) -> R + Sized,
|
||||||
{
|
{
|
||||||
fn argument_count() -> usize {
|
fn argument_count() -> usize {
|
||||||
1
|
1
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&self, context: &ContextRef<'r>, args: &[&ValueRef<'r>]) -> ValueResult<'r> {
|
fn call(&self, context: &ContextRef, args: &[&ValueRef]) -> ValueResult {
|
||||||
if args.len() != Self::argument_count() {
|
if args.len() != Self::argument_count() {
|
||||||
return Err(Error::ArgumentCountMismatch {
|
return Err(Error::ArgumentCountMismatch {
|
||||||
expected: Self::argument_count(),
|
expected: Self::argument_count(),
|
||||||
|
|
@ -74,18 +72,18 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r, R, A, B, F> RustFunction<'r, PhantomData<(&R, &A, &B, &F)>> for F
|
impl<R, A, B, F> RustFunction<PhantomData<(&R, &A, &B, &F)>> for F
|
||||||
where
|
where
|
||||||
R: IntoRustFunctionResult<'r>,
|
R: IntoRustFunctionResult,
|
||||||
A: TryFromValue,
|
A: TryFromValue,
|
||||||
B: TryFromValue,
|
B: TryFromValue,
|
||||||
F: Fn(&ContextRef<'r>, A, B) -> R + Sized,
|
F: Fn(&ContextRef, A, B) -> R + Sized,
|
||||||
{
|
{
|
||||||
fn argument_count() -> usize {
|
fn argument_count() -> usize {
|
||||||
2
|
2
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&self, context: &ContextRef<'r>, args: &[&ValueRef<'r>]) -> ValueResult<'r> {
|
fn call(&self, context: &ContextRef, args: &[&ValueRef]) -> ValueResult {
|
||||||
if args.len() != Self::argument_count() {
|
if args.len() != Self::argument_count() {
|
||||||
return Err(Error::ArgumentCountMismatch {
|
return Err(Error::ArgumentCountMismatch {
|
||||||
expected: Self::argument_count(),
|
expected: Self::argument_count(),
|
||||||
|
|
@ -100,19 +98,19 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r, R, A, B, C, F> RustFunction<'r, PhantomData<(&R, &A, &B, &C, &F)>> for F
|
impl<R, A, B, C, F> RustFunction<PhantomData<(&R, &A, &B, &C, &F)>> for F
|
||||||
where
|
where
|
||||||
R: IntoRustFunctionResult<'r>,
|
R: IntoRustFunctionResult,
|
||||||
A: TryFromValue,
|
A: TryFromValue,
|
||||||
B: TryFromValue,
|
B: TryFromValue,
|
||||||
C: TryFromValue,
|
C: TryFromValue,
|
||||||
F: Fn(&ContextRef<'r>, A, B, C) -> R + Sized,
|
F: Fn(&ContextRef, A, B, C) -> R + Sized,
|
||||||
{
|
{
|
||||||
fn argument_count() -> usize {
|
fn argument_count() -> usize {
|
||||||
3
|
3
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&self, context: &ContextRef<'r>, args: &[&ValueRef<'r>]) -> ValueResult<'r> {
|
fn call(&self, context: &ContextRef, args: &[&ValueRef]) -> ValueResult {
|
||||||
if args.len() != Self::argument_count() {
|
if args.len() != Self::argument_count() {
|
||||||
return Err(Error::ArgumentCountMismatch {
|
return Err(Error::ArgumentCountMismatch {
|
||||||
expected: Self::argument_count(),
|
expected: Self::argument_count(),
|
||||||
|
|
@ -128,20 +126,20 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r, R, A, B, C, D, F> RustFunction<'r, PhantomData<(&R, &A, &B, &C, &D, &F)>> for F
|
impl<R, A, B, C, D, F> RustFunction<PhantomData<(&R, &A, &B, &C, &D, &F)>> for F
|
||||||
where
|
where
|
||||||
R: IntoRustFunctionResult<'r>,
|
R: IntoRustFunctionResult,
|
||||||
A: TryFromValue,
|
A: TryFromValue,
|
||||||
B: TryFromValue,
|
B: TryFromValue,
|
||||||
C: TryFromValue,
|
C: TryFromValue,
|
||||||
D: TryFromValue,
|
D: TryFromValue,
|
||||||
F: Fn(&ContextRef<'r>, A, B, C, D) -> R + Sized,
|
F: Fn(&ContextRef, A, B, C, D) -> R + Sized,
|
||||||
{
|
{
|
||||||
fn argument_count() -> usize {
|
fn argument_count() -> usize {
|
||||||
4
|
4
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&self, context: &ContextRef<'r>, args: &[&ValueRef<'r>]) -> ValueResult<'r> {
|
fn call(&self, context: &ContextRef, args: &[&ValueRef]) -> ValueResult {
|
||||||
if args.len() != Self::argument_count() {
|
if args.len() != Self::argument_count() {
|
||||||
return Err(Error::ArgumentCountMismatch {
|
return Err(Error::ArgumentCountMismatch {
|
||||||
expected: Self::argument_count(),
|
expected: Self::argument_count(),
|
||||||
|
|
@ -158,21 +156,21 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r, R, A, B, C, D, E, F> RustFunction<'r, PhantomData<(&R, &A, &B, &C, &D, &E, &F)>> for F
|
impl<R, A, B, C, D, E, F> RustFunction<PhantomData<(&R, &A, &B, &C, &D, &E, &F)>> for F
|
||||||
where
|
where
|
||||||
R: IntoRustFunctionResult<'r>,
|
R: IntoRustFunctionResult,
|
||||||
A: TryFromValue,
|
A: TryFromValue,
|
||||||
B: TryFromValue,
|
B: TryFromValue,
|
||||||
C: TryFromValue,
|
C: TryFromValue,
|
||||||
D: TryFromValue,
|
D: TryFromValue,
|
||||||
E: TryFromValue,
|
E: TryFromValue,
|
||||||
F: Fn(&ContextRef<'r>, A, B, C, D, E) -> R + Sized,
|
F: Fn(&ContextRef, A, B, C, D, E) -> R + Sized,
|
||||||
{
|
{
|
||||||
fn argument_count() -> usize {
|
fn argument_count() -> usize {
|
||||||
5
|
5
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&self, context: &ContextRef<'r>, args: &[&ValueRef<'r>]) -> ValueResult<'r> {
|
fn call(&self, context: &ContextRef, args: &[&ValueRef]) -> ValueResult {
|
||||||
if args.len() != Self::argument_count() {
|
if args.len() != Self::argument_count() {
|
||||||
return Err(Error::ArgumentCountMismatch {
|
return Err(Error::ArgumentCountMismatch {
|
||||||
expected: Self::argument_count(),
|
expected: Self::argument_count(),
|
||||||
|
|
|
||||||
|
|
@ -1,110 +1,110 @@
|
||||||
use crate::{Class, ContextRef, Error, Value, ValueRef, ValueResult};
|
use crate::{Class, ContextRef, Error, Value, ValueRef, ValueResult};
|
||||||
|
|
||||||
pub trait TryIntoValue<'r> {
|
pub trait TryIntoValue {
|
||||||
fn try_into_value(self, ctx: &ContextRef<'r>) -> ValueResult<'r>;
|
fn try_into_value(self, ctx: &ContextRef) -> ValueResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> TryIntoValue<'r> for u8 {
|
impl TryIntoValue for u8 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_into_value(self, ctx: &ContextRef<'r>) -> ValueResult<'r> {
|
fn try_into_value(self, ctx: &ContextRef) -> ValueResult {
|
||||||
ctx.new_u64(self)
|
ctx.new_u64(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> TryIntoValue<'r> for u16 {
|
impl TryIntoValue for u16 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_into_value(self, ctx: &ContextRef<'r>) -> ValueResult<'r> {
|
fn try_into_value(self, ctx: &ContextRef) -> ValueResult {
|
||||||
ctx.new_u64(self)
|
ctx.new_u64(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> TryIntoValue<'r> for u32 {
|
impl TryIntoValue for u32 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_into_value(self, ctx: &ContextRef<'r>) -> ValueResult<'r> {
|
fn try_into_value(self, ctx: &ContextRef) -> ValueResult {
|
||||||
ctx.new_u64(self)
|
ctx.new_u64(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> TryIntoValue<'r> for u64 {
|
impl TryIntoValue for u64 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_into_value(self, ctx: &ContextRef<'r>) -> ValueResult<'r> {
|
fn try_into_value(self, ctx: &ContextRef) -> ValueResult {
|
||||||
ctx.new_u64(self)
|
ctx.new_u64(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> TryIntoValue<'r> for i8 {
|
impl TryIntoValue for i8 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_into_value(self, ctx: &ContextRef<'r>) -> ValueResult<'r> {
|
fn try_into_value(self, ctx: &ContextRef) -> ValueResult {
|
||||||
ctx.new_i32(self)
|
ctx.new_i32(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> TryIntoValue<'r> for i16 {
|
impl TryIntoValue for i16 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_into_value(self, ctx: &ContextRef<'r>) -> ValueResult<'r> {
|
fn try_into_value(self, ctx: &ContextRef) -> ValueResult {
|
||||||
ctx.new_i32(self)
|
ctx.new_i32(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> TryIntoValue<'r> for i32 {
|
impl TryIntoValue for i32 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_into_value(self, ctx: &ContextRef<'r>) -> ValueResult<'r> {
|
fn try_into_value(self, ctx: &ContextRef) -> ValueResult {
|
||||||
ctx.new_i32(self)
|
ctx.new_i32(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> TryIntoValue<'r> for i64 {
|
impl TryIntoValue for i64 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_into_value(self, ctx: &ContextRef<'r>) -> ValueResult<'r> {
|
fn try_into_value(self, ctx: &ContextRef) -> ValueResult {
|
||||||
ctx.new_i64(self)
|
ctx.new_i64(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> TryIntoValue<'r> for f32 {
|
impl TryIntoValue for f32 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_into_value(self, ctx: &ContextRef<'r>) -> ValueResult<'r> {
|
fn try_into_value(self, ctx: &ContextRef) -> ValueResult {
|
||||||
ctx.new_f64(self)
|
ctx.new_f64(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> TryIntoValue<'r> for f64 {
|
impl TryIntoValue for f64 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_into_value(self, ctx: &ContextRef<'r>) -> ValueResult<'r> {
|
fn try_into_value(self, ctx: &ContextRef) -> ValueResult {
|
||||||
ctx.new_f64(self)
|
ctx.new_f64(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> TryIntoValue<'r> for bool {
|
impl TryIntoValue for bool {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_into_value(self, ctx: &ContextRef<'r>) -> ValueResult<'r> {
|
fn try_into_value(self, ctx: &ContextRef) -> ValueResult {
|
||||||
ctx.new_bool(self)
|
ctx.new_bool(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> TryIntoValue<'r> for &str {
|
impl TryIntoValue for &str {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_into_value(self, ctx: &ContextRef<'r>) -> ValueResult<'r> {
|
fn try_into_value(self, ctx: &ContextRef) -> ValueResult {
|
||||||
ctx.new_string(self)
|
ctx.new_string(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> TryIntoValue<'r> for &ValueRef<'r> {
|
impl TryIntoValue for &ValueRef {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_into_value(self, ctx: &ContextRef<'r>) -> ValueResult<'r> {
|
fn try_into_value(self, ctx: &ContextRef) -> ValueResult {
|
||||||
Ok(self.dup(ctx))
|
Ok(self.dup(ctx))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> TryIntoValue<'r> for Value<'r> {
|
impl TryIntoValue for Value {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_into_value(self, ctx: &ContextRef<'r>) -> ValueResult<'r> {
|
fn try_into_value(self, ctx: &ContextRef) -> ValueResult {
|
||||||
Ok(self.dup(ctx))
|
Ok(self.dup(ctx))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> TryIntoValue<'r> for Error<'r> {
|
impl TryIntoValue for Error {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_into_value(self, ctx: &ContextRef<'r>) -> ValueResult<'r> {
|
fn try_into_value(self, ctx: &ContextRef) -> ValueResult {
|
||||||
match self {
|
match self {
|
||||||
Error::TooManyClasses => Err(Error::TooManyClasses),
|
Error::TooManyClasses => Err(Error::TooManyClasses),
|
||||||
Error::WrongClass(c) => Err(Error::WrongClass(c)),
|
Error::WrongClass(c) => Err(Error::WrongClass(c)),
|
||||||
|
|
@ -121,9 +121,9 @@ impl<'r> TryIntoValue<'r> for Error<'r> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r, T: Class> TryIntoValue<'r> for T {
|
impl<T: Class> TryIntoValue for T {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_into_value(self, ctx: &ContextRef<'r>) -> ValueResult<'r> {
|
fn try_into_value(self, ctx: &ContextRef) -> ValueResult {
|
||||||
self.into_value(ctx)
|
self.into_value(ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ pub use runtime::Runtime;
|
||||||
pub use value::{Value, ValueRef, ValueType};
|
pub use value::{Value, ValueRef, ValueType};
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum Error<'ctx> {
|
pub enum Error {
|
||||||
#[error("too many classes have been registered")]
|
#[error("too many classes have been registered")]
|
||||||
TooManyClasses,
|
TooManyClasses,
|
||||||
#[error("the specified value is not an instance of the class {0}")]
|
#[error("the specified value is not an instance of the class {0}")]
|
||||||
|
|
@ -37,7 +37,7 @@ pub enum Error<'ctx> {
|
||||||
#[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")]
|
||||||
Exception(Value<'ctx>),
|
Exception(Value),
|
||||||
}
|
}
|
||||||
pub type Result<'ctx, T> = core::result::Result<T, Error<'ctx>>;
|
pub type Result<T> = core::result::Result<T, Error>;
|
||||||
pub type ValueResult<'ctx> = core::result::Result<Value<'ctx>, Error<'ctx>>;
|
pub type ValueResult = core::result::Result<Value, Error>;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,9 @@
|
||||||
use oden_js_sys as sys;
|
use oden_js_sys as sys;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
|
struct PrivateState {
|
||||||
|
refs: u64,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Runtime {
|
pub struct Runtime {
|
||||||
|
|
@ -7,7 +12,23 @@ pub struct Runtime {
|
||||||
|
|
||||||
impl Runtime {
|
impl Runtime {
|
||||||
pub fn new() -> Runtime {
|
pub fn new() -> Runtime {
|
||||||
let rt = unsafe { sys::JS_NewRuntime() };
|
let state = Box::new(RefCell::new(PrivateState { refs: 1 }));
|
||||||
|
let rt = unsafe {
|
||||||
|
let rt = sys::JS_NewRuntime();
|
||||||
|
sys::JS_SetRuntimeOpaque(rt, Box::into_raw(state) as *mut _);
|
||||||
|
rt
|
||||||
|
};
|
||||||
|
Runtime { rt }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn from_raw(rt: *mut sys::JSRuntime) -> Self {
|
||||||
|
let mut state = unsafe {
|
||||||
|
let ptr = sys::JS_GetRuntimeOpaque(rt) as *const RefCell<PrivateState>;
|
||||||
|
ptr.as_ref()
|
||||||
|
.expect("We already know this runtime is one of ours!")
|
||||||
|
.borrow_mut()
|
||||||
|
};
|
||||||
|
state.refs += 1;
|
||||||
Runtime { rt }
|
Runtime { rt }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -37,11 +58,36 @@ impl Runtime {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Clone for Runtime {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Runtime::from_raw(self.rt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Drop for Runtime {
|
impl Drop for Runtime {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
let should_free = {
|
||||||
|
let mut state = unsafe {
|
||||||
|
let ptr = sys::JS_GetRuntimeOpaque(self.rt) as *const RefCell<PrivateState>;
|
||||||
|
ptr.as_ref()
|
||||||
|
.expect("We already know this runtime is one of ours!")
|
||||||
|
.borrow_mut()
|
||||||
|
};
|
||||||
|
state.refs -= 1;
|
||||||
|
state.refs == 0
|
||||||
|
};
|
||||||
|
|
||||||
|
if should_free {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
let opaque = sys::JS_GetRuntimeOpaque(self.rt);
|
||||||
sys::JS_RunGC(self.rt);
|
sys::JS_RunGC(self.rt);
|
||||||
sys::JS_FreeRuntime(self.rt);
|
sys::JS_FreeRuntime(self.rt);
|
||||||
|
|
||||||
|
if !opaque.is_null() {
|
||||||
|
// Just let the system drop it here.
|
||||||
|
let _ = Box::from_raw(opaque as *mut RefCell<PrivateState>);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ use crate::{AtomRef, ContextRef, Error, Result, Runtime, RustFunction};
|
||||||
use oden_js_sys as sys;
|
use oden_js_sys as sys;
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::marker;
|
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
|
@ -52,29 +51,13 @@ impl From<i32> for ValueType {
|
||||||
/// you will want to create and manipulate `Value`, but you will occasionally
|
/// you will want to create and manipulate `Value`, but you will occasionally
|
||||||
/// receive `ValueRef` in calls that originate from JavaScript
|
/// receive `ValueRef` in calls that originate from JavaScript
|
||||||
/// code. (`ValueRef` is to `Value` as `str` is to `String`, if that helps.)
|
/// code. (`ValueRef` is to `Value` as `str` is to `String`, if that helps.)
|
||||||
pub struct ValueRef<'r> {
|
pub struct ValueRef {
|
||||||
pub(crate) val: sys::JSValue,
|
pub(crate) val: sys::JSValue,
|
||||||
_phantom: marker::PhantomData<&'r Runtime>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub type Callback<'r, 'ctx> = fn(
|
impl ValueRef {
|
||||||
// ctx: &'ctx Context<'r>,
|
pub fn from_raw(val: sys::JSValue) -> Self {
|
||||||
// this_val: &ValueRef<'r, 'ctx>,
|
ValueRef { val }
|
||||||
// argc: usize,
|
|
||||||
// argvc: &[&ValueRef<'r, 'ctx>],
|
|
||||||
// ) -> Value<'r, 'ctx>;
|
|
||||||
|
|
||||||
impl<'r> ValueRef<'r> {
|
|
||||||
/// Wrap a value in a specific context.
|
|
||||||
///
|
|
||||||
/// **WARNING**: The specified value *must* be associated with at least
|
|
||||||
/// the runtime of the specified context, if not the context itself. This
|
|
||||||
/// function makes no attempt to validate this.
|
|
||||||
pub fn from_raw(val: sys::JSValue, _ctx: &ContextRef<'r>) -> Self {
|
|
||||||
ValueRef {
|
|
||||||
val,
|
|
||||||
_phantom: marker::PhantomData,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the type of JavaScript value that this is.
|
/// Get the type of JavaScript value that this is.
|
||||||
|
|
@ -137,7 +120,7 @@ impl<'r> ValueRef<'r> {
|
||||||
self.value_type() == ValueType::Int
|
self.value_type() == ValueType::Int
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_i32(&self, ctx: &ContextRef<'r>) -> Result<'r, i32> {
|
pub fn to_i32(&self, ctx: &ContextRef) -> Result<i32> {
|
||||||
unsafe {
|
unsafe {
|
||||||
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);
|
||||||
|
|
@ -149,7 +132,7 @@ impl<'r> ValueRef<'r> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_u32(&self, ctx: &ContextRef<'r>) -> Result<'r, u32> {
|
pub fn to_u32(&self, ctx: &ContextRef) -> Result<u32> {
|
||||||
unsafe {
|
unsafe {
|
||||||
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);
|
||||||
|
|
@ -161,7 +144,7 @@ impl<'r> ValueRef<'r> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_i64(&self, ctx: &ContextRef<'r>) -> Result<'r, i64> {
|
pub fn to_i64(&self, ctx: &ContextRef) -> Result<i64> {
|
||||||
unsafe {
|
unsafe {
|
||||||
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);
|
||||||
|
|
@ -180,7 +163,7 @@ impl<'r> ValueRef<'r> {
|
||||||
self.value_type() == ValueType::Bool
|
self.value_type() == ValueType::Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_bool(&self) -> Result<'r, bool> {
|
pub fn to_bool(&self) -> Result<bool> {
|
||||||
if self.value_type() == ValueType::Bool {
|
if self.value_type() == ValueType::Bool {
|
||||||
Ok(unsafe { self.val.u.int32 } > 0)
|
Ok(unsafe { self.val.u.int32 } > 0)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -201,7 +184,7 @@ impl<'r> ValueRef<'r> {
|
||||||
self.value_type() == ValueType::Float64
|
self.value_type() == ValueType::Float64
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_float64(&self, ctx: &ContextRef<'r>) -> Result<'r, f64> {
|
pub fn to_float64(&self, ctx: &ContextRef) -> Result<f64> {
|
||||||
unsafe {
|
unsafe {
|
||||||
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);
|
||||||
|
|
@ -216,7 +199,7 @@ impl<'r> ValueRef<'r> {
|
||||||
/// Take a reference to this value so that it outlives this current reference.
|
/// Take a reference to this value so that it outlives this current reference.
|
||||||
///
|
///
|
||||||
/// (This is a very cheap operation: at most it increments a reference count.)
|
/// (This is a very cheap operation: at most it increments a reference count.)
|
||||||
pub fn dup(&self, ctx: &ContextRef<'r>) -> Value<'r> {
|
pub fn dup(&self, ctx: &ContextRef) -> Value {
|
||||||
unsafe {
|
unsafe {
|
||||||
sys::JS_DupValue(ctx.ctx, self.val);
|
sys::JS_DupValue(ctx.ctx, self.val);
|
||||||
}
|
}
|
||||||
|
|
@ -229,7 +212,7 @@ impl<'r> ValueRef<'r> {
|
||||||
///
|
///
|
||||||
/// JavsScript exceptions are are returned as values where `value_type`
|
/// JavsScript exceptions are are returned as values where `value_type`
|
||||||
/// is `ValueType::Exception`.
|
/// is `ValueType::Exception`.
|
||||||
pub fn eval_function(&self, ctx: &ContextRef<'r>) -> Result<'r, Value<'r>> {
|
pub fn eval_function(&self, ctx: &ContextRef) -> Result<Value> {
|
||||||
match self.value_type() {
|
match self.value_type() {
|
||||||
ValueType::Module | ValueType::FunctionBytecode => (),
|
ValueType::Module | ValueType::FunctionBytecode => (),
|
||||||
found => {
|
found => {
|
||||||
|
|
@ -249,25 +232,16 @@ impl<'r> ValueRef<'r> {
|
||||||
Ok(Value::from_raw(result, ctx))
|
Ok(Value::from_raw(result, ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_property(&self, ctx: &ContextRef<'r>, prop: &str) -> Result<'r, Value<'r>> {
|
pub fn get_property(&self, ctx: &ContextRef, prop: &str) -> Result<Value> {
|
||||||
let atom = ctx.new_atom(prop)?;
|
let atom = ctx.new_atom(prop)?;
|
||||||
self.get_property_atom(ctx, &atom)
|
self.get_property_atom(ctx, &atom)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_property_atom(
|
pub fn get_property_atom(&self, ctx: &ContextRef, prop: &AtomRef) -> Result<Value> {
|
||||||
&self,
|
|
||||||
ctx: &ContextRef<'r>,
|
|
||||||
prop: &AtomRef<'r>,
|
|
||||||
) -> Result<'r, Value<'r>> {
|
|
||||||
ctx.check_exception(unsafe { sys::JS_GetProperty(ctx.ctx, self.val, prop.atom) })
|
ctx.check_exception(unsafe { sys::JS_GetProperty(ctx.ctx, self.val, prop.atom) })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_property(
|
pub fn set_property(&mut self, ctx: &ContextRef, prop: &str, val: &ValueRef) -> Result<()> {
|
||||||
&mut self,
|
|
||||||
ctx: &ContextRef<'r>,
|
|
||||||
prop: &str,
|
|
||||||
val: &ValueRef,
|
|
||||||
) -> Result<'r, ()> {
|
|
||||||
// TODO: Consume API
|
// TODO: Consume API
|
||||||
let atom = ctx.new_atom(prop)?;
|
let atom = ctx.new_atom(prop)?;
|
||||||
self.set_property_atom(ctx, &atom, val)
|
self.set_property_atom(ctx, &atom, val)
|
||||||
|
|
@ -275,10 +249,10 @@ impl<'r> ValueRef<'r> {
|
||||||
|
|
||||||
pub fn set_property_atom(
|
pub fn set_property_atom(
|
||||||
&mut self,
|
&mut self,
|
||||||
ctx: &ContextRef<'r>,
|
ctx: &ContextRef,
|
||||||
prop: &AtomRef<'r>,
|
prop: &AtomRef,
|
||||||
val: &ValueRef,
|
val: &ValueRef,
|
||||||
) -> Result<'r, ()> {
|
) -> Result<()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
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);
|
||||||
|
|
@ -292,29 +266,24 @@ impl<'r> ValueRef<'r> {
|
||||||
|
|
||||||
pub fn set_fn<F>(
|
pub fn set_fn<F>(
|
||||||
&mut self,
|
&mut self,
|
||||||
ctx: &ContextRef<'r>,
|
ctx: &ContextRef,
|
||||||
prop: &str,
|
prop: &str,
|
||||||
func: impl RustFunction<'r, F>,
|
func: impl RustFunction<F>,
|
||||||
) -> Result<'r, ()> {
|
) -> Result<()> {
|
||||||
let vr: Value<'r> = ctx.new_fn(func)?;
|
let vr: Value = ctx.new_fn(func)?;
|
||||||
self.set_property(ctx, prop, &vr)
|
self.set_property(ctx, prop, &vr)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_dynamic_method<F>(
|
pub fn set_dynamic_method<F>(&mut self, ctx: &ContextRef, prop: &str, func: F) -> Result<()>
|
||||||
&mut self,
|
|
||||||
ctx: &ContextRef<'r>,
|
|
||||||
prop: &str,
|
|
||||||
func: F,
|
|
||||||
) -> Result<'r, ()>
|
|
||||||
where
|
where
|
||||||
F: Fn(&ContextRef<'r>, &ValueRef<'r>, &[&ValueRef<'r>]) -> Result<'r, Value<'r>>,
|
F: Fn(&ContextRef, &ValueRef, &[&ValueRef]) -> Result<Value>,
|
||||||
{
|
{
|
||||||
let vr: Value<'r> = ctx.new_dynamic_fn(func)?;
|
let vr: Value = ctx.new_dynamic_fn(func)?;
|
||||||
self.set_property(ctx, prop, &vr)
|
self.set_property(ctx, prop, &vr)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert this value into a string representation of the same value.
|
/// Convert this value into a string representation of the same value.
|
||||||
pub fn to_string(&self, ctx: &ContextRef<'r>) -> Result<'r, String> {
|
pub fn to_string(&self, ctx: &ContextRef) -> Result<String> {
|
||||||
// SAFETY: ctx's life is bound by the lifetime of our Context, and
|
// SAFETY: ctx's life is bound by the lifetime of our Context, and
|
||||||
// sys::JS_ToCStringLen2 will return non-null, unless we're out of
|
// sys::JS_ToCStringLen2 will return non-null, unless we're out of
|
||||||
// memory.
|
// memory.
|
||||||
|
|
@ -342,7 +311,7 @@ impl<'r> ValueRef<'r> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ctx> fmt::Debug for ValueRef<'ctx> {
|
impl<'ctx> 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)
|
||||||
|
|
@ -352,50 +321,48 @@ impl<'ctx> fmt::Debug for ValueRef<'ctx> {
|
||||||
|
|
||||||
/// An owned Value from JavaScript. Unlike ValueRef, this type stands in for
|
/// An owned Value from JavaScript. Unlike ValueRef, this type stands in for
|
||||||
/// values that you construct in Rust code; otherwise they are identical.
|
/// values that you construct in Rust code; otherwise they are identical.
|
||||||
pub struct Value<'r> {
|
pub struct Value {
|
||||||
value: ValueRef<'r>,
|
value: ValueRef,
|
||||||
rt: *mut sys::JSRuntime,
|
rt: Runtime,
|
||||||
_phantom: marker::PhantomData<&'r Runtime>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> Value<'r> {
|
impl Value {
|
||||||
/// Take ownership of a value in a specific context.
|
/// Take ownership of a value in a specific context.
|
||||||
///
|
///
|
||||||
/// **WARNING**: The specified value *must* be associated with at least
|
/// **WARNING**: The specified value *must* be associated with at least
|
||||||
/// the runtime of the specified context, if not the context itself. This
|
/// the runtime of the specified context, if not the context itself. This
|
||||||
/// function makes no attempt to validate this.
|
/// function makes no attempt to validate this.
|
||||||
pub(crate) fn from_raw(val: sys::JSValue, ctx: &ContextRef<'r>) -> Self {
|
pub(crate) fn from_raw(val: sys::JSValue, ctx: &ContextRef) -> Self {
|
||||||
Value {
|
Value {
|
||||||
value: ValueRef::from_raw(val, ctx),
|
value: ValueRef::from_raw(val),
|
||||||
rt: unsafe { sys::JS_GetRuntime(ctx.ctx) },
|
rt: Runtime::from_raw(unsafe { sys::JS_GetRuntime(ctx.ctx) }),
|
||||||
_phantom: marker::PhantomData,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> Deref for Value<'r> {
|
impl Deref for Value {
|
||||||
type Target = ValueRef<'r>;
|
type Target = ValueRef;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.value
|
&self.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> DerefMut for Value<'r> {
|
impl DerefMut for Value {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
&mut self.value
|
&mut self.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> Drop for Value<'r> {
|
impl Drop for Value {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
sys::JS_FreeValueRT(self.rt, self.val);
|
sys::JS_FreeValueRT(self.rt.rt, self.val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> fmt::Debug for Value<'r> {
|
impl fmt::Debug for Value {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
self.value.fmt(f)
|
self.value.fmt(f)
|
||||||
}
|
}
|
||||||
|
|
@ -409,7 +376,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn value_type() {
|
fn value_type() {
|
||||||
let rt = Runtime::new();
|
let rt = Runtime::new();
|
||||||
let mut ctx = Context::new(&rt);
|
let mut ctx = Context::new(rt);
|
||||||
ctx.add_intrinsic_bigfloat();
|
ctx.add_intrinsic_bigfloat();
|
||||||
ctx.add_intrinsic_bigdecimal();
|
ctx.add_intrinsic_bigdecimal();
|
||||||
|
|
||||||
|
|
@ -436,36 +403,3 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// NOTE: We can happily create and return values, like this:
|
|
||||||
/// ```
|
|
||||||
/// use oden_js::*;
|
|
||||||
/// fn my_test<'a>(ctx: &'a Context<'a>) -> Value<'a> {
|
|
||||||
/// ctx.new_i32(123).unwrap()
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// But our lifetime bounds on values keep them from outliving their runtimes:
|
|
||||||
///
|
|
||||||
/// ```compile_fail
|
|
||||||
/// use oden_js::*;
|
|
||||||
/// fn my_test<'r>() -> oden_js::Value<'r> {
|
|
||||||
/// let rt = Runtime::new();
|
|
||||||
/// let ctx = Context::new(&rt);
|
|
||||||
///
|
|
||||||
/// ctx.new_i32(123).unwrap()
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// Contexts are fundamentally transient, though:
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use oden_js::*;
|
|
||||||
/// fn my_test<'r>(runtime: &'r Runtime) -> sheetland_js::Value<'r> {
|
|
||||||
/// let ctx = Context::new(runtime);
|
|
||||||
///
|
|
||||||
/// ctx.new_i32(123).unwrap()
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
#[cfg(doctest)]
|
|
||||||
pub struct ValuesObeyLifetimes {}
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue