-
Notifications
You must be signed in to change notification settings - Fork 283
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(neon): JsFunction::bind()
and Object::prop()
#1056
base: main
Are you sure you want to change the base?
Changes from all commits
cc1b061
f7c05b8
8c03fab
33b0c2a
acf0ec5
3d32a52
db27665
a3f084d
589fabc
59fa3f4
c4fcf56
7ef24b5
d2c770d
8d18e19
b2a34c0
94a0c08
45be1f3
bb4f231
dd1ffdf
3257f01
6148d8e
50a8832
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,19 +32,28 @@ | |
//! [hierarchy]: crate::types#the-javascript-type-hierarchy | ||
//! [symbol]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol | ||
|
||
use smallvec::smallvec; | ||
|
||
use crate::{ | ||
context::Context, | ||
context::{internal::ContextInternal, Context, Cx}, | ||
handle::{Handle, Root}, | ||
result::{NeonResult, Throw}, | ||
sys::{self, raw}, | ||
types::{build, function::CallOptions, utf8::Utf8, JsFunction, JsUndefined, JsValue, Value}, | ||
types::{ | ||
build, | ||
extract::{TryFromJs, TryIntoJs}, | ||
function::{BindOptions, CallOptions}, | ||
private::ValueInternal, | ||
utf8::Utf8, | ||
JsFunction, JsObject, JsUndefined, JsValue, Value, | ||
}, | ||
}; | ||
|
||
#[cfg(feature = "napi-6")] | ||
use crate::{result::JsResult, types::JsArray}; | ||
|
||
/// A property key in a JavaScript object. | ||
pub trait PropertyKey { | ||
pub trait PropertyKey: Copy { | ||
unsafe fn get_from<'c, C: Context<'c>>( | ||
self, | ||
cx: &mut C, | ||
|
@@ -134,8 +143,90 @@ impl<'a> PropertyKey for &'a str { | |
} | ||
} | ||
|
||
/// A builder for accessing an object property. | ||
/// | ||
/// The builder methods make it convenient to get and set properties | ||
/// as well as to bind and call methods. | ||
/// ``` | ||
/// # use neon::prelude::*; | ||
/// # fn foo(mut cx: FunctionContext) -> JsResult<JsString> { | ||
/// # let obj: Handle<JsObject> = cx.argument(0)?; | ||
/// let x: f64 = obj | ||
/// .prop(&mut cx, "x") | ||
/// .get()?; | ||
/// | ||
/// obj.prop(&mut cx, "y") | ||
/// .set(x)?; | ||
/// | ||
/// let s: String = obj.prop(&mut cx, "toString") | ||
/// .bind()? | ||
/// .apply()?; | ||
/// # Ok(cx.string(s)) | ||
/// # } | ||
/// ``` | ||
pub struct PropOptions<'a, 'cx, K> | ||
where | ||
'cx: 'a, | ||
K: PropertyKey, | ||
{ | ||
pub(crate) cx: &'a mut Cx<'cx>, | ||
pub(crate) this: Handle<'cx, JsObject>, | ||
pub(crate) key: K, | ||
} | ||
|
||
impl<'a, 'cx, K> PropOptions<'a, 'cx, K> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you think it would be useful to add a Additionally, can we add a |
||
where | ||
'cx: 'a, | ||
K: PropertyKey, | ||
{ | ||
/// Gets the property from the object and attempts to convert it to a Rust value. | ||
/// Equivalent to calling `R::from_js(cx, obj.get(cx)?)`. | ||
/// | ||
/// May throw an exception either during accessing the property or converting the | ||
/// result type. | ||
pub fn get<R: TryFromJs<'cx>>(&mut self) -> NeonResult<R> { | ||
let v = self.this.get_value(self.cx, self.key)?; | ||
R::from_js(self.cx, v) | ||
} | ||
|
||
/// Sets the property on the object to a value converted from Rust. | ||
/// Equivalent to calling `obj.set(cx, v.try_into_js(cx)?)`. | ||
/// | ||
/// May throw an exception either during converting the value or setting the property. | ||
pub fn set<V: TryIntoJs<'cx>>(&mut self, v: V) -> NeonResult<bool> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we eliminate the We could also add let o = cx.empty_object()
.prop("a")
.set("this is a")?
.prop("b")
.set("this is b")?
.to_value(); The chaining is up to you, but at the least we should change the return to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Without chaining: let o = cx.empty_object();
o.prop("a").set("this is a")?;
o.prop("b").set("this is b")?;
o.prop("c").set("this is c")?; |
||
let v = v.try_into_js(self.cx)?; | ||
self.this.set(self.cx, self.key, v) | ||
} | ||
|
||
/// Gets the property from the object as a method and binds `this` to the object. | ||
/// | ||
/// May throw an exception either during accessing the property or downcasting it | ||
/// to a function. | ||
pub fn bind(&'a mut self) -> NeonResult<BindOptions<'a, 'cx>> { | ||
let callee: Handle<JsValue> = self.this.get(self.cx, self.key)?; | ||
let this = Some(self.this.upcast()); | ||
Ok(BindOptions { | ||
cx: self.cx, | ||
callee, | ||
this, | ||
args: smallvec![], | ||
}) | ||
} | ||
} | ||
|
||
/// The trait of all object types. | ||
pub trait Object: Value { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we want to deprecate any of the other getter/setter methods? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's make them |
||
/// Create a [`PropOptions`] for accessing a property. | ||
fn prop<'a, 'cx: 'a, K: PropertyKey>( | ||
&self, | ||
cx: &'a mut Cx<'cx>, | ||
key: K, | ||
) -> PropOptions<'a, 'cx, K> { | ||
let this: Handle<'_, JsObject> = | ||
Handle::new_internal(unsafe { ValueInternal::from_local(cx.env(), self.to_local()) }); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We are extending the lifetime In other words, this is safe because Neon invariants enforce There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I should add this to a |
||
PropOptions { cx, this, key } | ||
} | ||
|
||
/// Gets a property from a JavaScript object that may be `undefined` and | ||
/// attempts to downcast the value if it existed. | ||
fn get_opt<'a, V: Value, C: Context<'a>, K: PropertyKey>( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,14 @@ | ||
use crate::{context::Cx, result::JsResult, types::extract::TryIntoJs}; | ||
use crate::{ | ||
context::Cx, | ||
result::{JsResult, NeonResult}, | ||
types::{ | ||
extract::TryIntoJs, | ||
function::{ | ||
private::{ArgsVec, TryIntoArgumentsInternal}, | ||
TryIntoArguments, | ||
}, | ||
}, | ||
}; | ||
|
||
/// Wraps a closure that will be lazily evaluated when [`TryIntoJs::try_into_js`] is | ||
/// called. | ||
|
@@ -48,4 +58,21 @@ where | |
} | ||
} | ||
|
||
impl<'cx, F, O> TryIntoArgumentsInternal<'cx> for With<F, O> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit, it makes a bit more sense to me for this implementation to be with the |
||
where | ||
F: FnOnce(&mut Cx) -> O, | ||
O: TryIntoArgumentsInternal<'cx>, | ||
{ | ||
fn try_into_args_vec(self, cx: &mut Cx<'cx>) -> NeonResult<ArgsVec<'cx>> { | ||
(self.0)(cx).try_into_args_vec(cx) | ||
} | ||
} | ||
|
||
impl<'cx, F, O> TryIntoArguments<'cx> for With<F, O> | ||
where | ||
F: FnOnce(&mut Cx) -> O, | ||
O: TryIntoArguments<'cx>, | ||
{ | ||
} | ||
|
||
impl<F, O> super::private::Sealed for With<F, O> where for<'cx> F: FnOnce(&mut Cx<'cx>) -> O {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we make this
Clone
?