Skip to content

Commit

Permalink
Merge pull request #989 from neon-bindings/monomorphize-jsfunction
Browse files Browse the repository at this point in the history
remove `JsFunction` type parameter
  • Loading branch information
dherman authored Jun 2, 2023
2 parents dd534ac + 9519abd commit 4c2e455
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 27 deletions.
8 changes: 2 additions & 6 deletions crates/neon/src/lifecycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ pub(crate) struct LocalTable {

pub(crate) type LocalCellValue = Box<dyn Any + Send + 'static>;

#[derive(Default)]
pub(crate) enum LocalCell {
#[default]
/// Uninitialized state.
Uninit,
/// Intermediate "dirty" state representing the middle of a `get_or_try_init` transaction.
Expand Down Expand Up @@ -143,12 +145,6 @@ impl LocalCell {
}
}

impl Default for LocalCell {
fn default() -> Self {
LocalCell::Uninit
}
}

impl LocalTable {
pub(crate) fn get(&mut self, index: usize) -> &mut LocalCell {
if index >= self.cells.len() {
Expand Down
35 changes: 14 additions & 21 deletions crates/neon/src/types_impl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ pub(crate) mod utf8;

use std::{
fmt::{self, Debug},
marker::PhantomData,
os::raw::c_void,
};

Expand Down Expand Up @@ -1037,12 +1036,11 @@ impl Object for JsArray {}
/// # Ok(f)
/// # }
/// ```
pub struct JsFunction<T: Object = JsObject> {
pub struct JsFunction {
raw: raw::Local,
marker: PhantomData<T>,
}

impl<T: Object> Object for JsFunction<T> {}
impl Object for JsFunction {}

// Maximum number of function arguments in V8.
const V8_ARGC_LIMIT: usize = 65535;
Expand Down Expand Up @@ -1118,18 +1116,15 @@ impl JsFunction {

unsafe {
if let Ok(raw) = sys::fun::new(cx.env().to_raw(), name, f) {
Ok(Handle::new_internal(JsFunction {
raw,
marker: PhantomData,
}))
Ok(Handle::new_internal(JsFunction { raw }))
} else {
Err(Throw::new())
}
}
}
}

impl<CL: Object> JsFunction<CL> {
impl JsFunction {
/// Calls this function.
///
/// **See also:** [`JsFunction::call_with`].
Expand Down Expand Up @@ -1170,7 +1165,11 @@ impl<CL: Object> JsFunction<CL> {
/// Calls this function as a constructor.
///
/// **See also:** [`JsFunction::construct_with`].
pub fn construct<'a, 'b, C: Context<'a>, AS>(&self, cx: &mut C, args: AS) -> JsResult<'a, CL>
pub fn construct<'a, 'b, C: Context<'a>, AS>(
&self,
cx: &mut C,
args: AS,
) -> JsResult<'a, JsObject>
where
AS: AsRef<[Handle<'b, JsValue>]>,
{
Expand Down Expand Up @@ -1212,24 +1211,21 @@ impl JsFunction {
/// # Safety
/// The caller must wrap in a `Handle` with an appropriate lifetime.
unsafe fn clone(&self) -> Self {
Self {
marker: PhantomData,
raw: self.raw,
}
Self { raw: self.raw }
}
}

impl<T: Object> Value for JsFunction<T> {}
impl Value for JsFunction {}

unsafe impl<T: Object> TransparentNoCopyWrapper for JsFunction<T> {
unsafe impl TransparentNoCopyWrapper for JsFunction {
type Inner = raw::Local;

fn into_inner(self) -> Self::Inner {
self.raw
}
}

impl<T: Object> ValueInternal for JsFunction<T> {
impl ValueInternal for JsFunction {
fn name() -> String {
"function".to_string()
}
Expand All @@ -1243,10 +1239,7 @@ impl<T: Object> ValueInternal for JsFunction<T> {
}

unsafe fn from_local(_env: Env, h: raw::Local) -> Self {
JsFunction {
raw: h,
marker: PhantomData,
}
JsFunction { raw: h }
}
}

Expand Down
22 changes: 22 additions & 0 deletions test/napi/lib/functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,28 @@ describe("JsFunction", function () {
assert.strictEqual(addon.get_number_or_default(), 0);
});

it("always provides an object for the this-binding", function () {
var meta1 = addon.assume_this_is_an_object.call(null);
assert.strictEqual(meta1.prototype, Object.getPrototypeOf(global));
assert.strictEqual(meta1.hasOwn, false);
assert.strictEqual(meta1.property, Object.getPrototypeOf(global).toString);

var meta2 = addon.assume_this_is_an_object.call(42);
assert.strictEqual(meta2.prototype, Number.prototype);
assert.strictEqual(meta2.hasOwn, false);
assert.strictEqual(meta2.property, Number.prototype.toString);

var meta3 = addon.assume_this_is_an_object.call(Object.create(null));
assert.strictEqual(meta3.prototype, null);
assert.strictEqual(meta3.hasOwn, false);
assert.strictEqual(meta3.property, undefined);

var meta4 = addon.assume_this_is_an_object.call({ toString: 17 });
assert.strictEqual(meta4.prototype, Object.prototype);
assert.strictEqual(meta4.hasOwn, true);
assert.strictEqual(meta4.property, 17);
});

it("distinguishes calls from constructs", function () {
assert.equal(addon.is_construct.call({}).wasConstructed, false);
assert.equal(new addon.is_construct().wasConstructed, true);
Expand Down
22 changes: 22 additions & 0 deletions test/napi/src/js/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,28 @@ pub fn get_number_or_default(mut cx: FunctionContext) -> JsResult<JsNumber> {
Ok(cx.number(n))
}

pub fn assume_this_is_an_object(mut cx: FunctionContext) -> JsResult<JsObject> {
let this: Handle<JsObject> = cx.this()?;
let result: Handle<JsObject> = cx.empty_object();
let object_class: Handle<JsFunction> = cx.global("Object")?;
let get_prototype_of: Handle<JsFunction> = object_class.get(&mut cx, "getPrototypeOf")?;
let object_prototype: Handle<JsObject> = object_class.get(&mut cx, "prototype")?;
let has_own_property: Handle<JsFunction> = object_prototype.get(&mut cx, "hasOwnProperty")?;
let proto: Result<Handle<JsValue>, Handle<JsValue>> =
cx.try_catch(|cx| get_prototype_of.call_with(cx).arg(this).apply(cx));
let proto: Handle<JsValue> = proto.unwrap_or_else(|_| cx.undefined().upcast());
let has_own: Handle<JsBoolean> = has_own_property
.call_with(&cx)
.this(this)
.arg(cx.string("toString"))
.apply(&mut cx)?;
let prop: Handle<JsValue> = this.get(&mut cx, "toString")?;
result.set(&mut cx, "prototype", proto)?;
result.set(&mut cx, "hasOwn", has_own)?;
result.set(&mut cx, "property", prop)?;
Ok(result)
}

pub fn is_construct(mut cx: FunctionContext) -> JsResult<JsObject> {
let this = cx.this::<JsObject>()?;
let construct = matches!(cx.kind(), CallKind::Construct);
Expand Down
1 change: 1 addition & 0 deletions test/napi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ fn main(mut cx: ModuleContext) -> NeonResult<()> {
cx.export_function("throw_and_catch", throw_and_catch)?;
cx.export_function("call_and_catch", call_and_catch)?;
cx.export_function("get_number_or_default", get_number_or_default)?;
cx.export_function("assume_this_is_an_object", assume_this_is_an_object)?;
cx.export_function("is_construct", is_construct)?;
cx.export_function("caller_with_drop_callback", caller_with_drop_callback)?;

Expand Down

0 comments on commit 4c2e455

Please sign in to comment.