Skip to content

Commit

Permalink
BindOptions::this() takes a TryIntoJs (and is therefore fallible)
Browse files Browse the repository at this point in the history
- tests for both strict and sloppy mode functions to show it works on primitives
  • Loading branch information
dherman committed Sep 9, 2024
1 parent b2a34c0 commit 94a0c08
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 4 deletions.
2 changes: 1 addition & 1 deletion crates/neon/src/object/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ where
pub fn bind(&'a mut self) -> NeonResult<BindOptions<'a, 'cx>> {
let callee: Handle<JsFunction> = self.this.get(self.cx, self.key)?;
let mut bind = callee.bind(self.cx);
bind.this(self.this);
bind.this(self.this)?;
Ok(bind)
}
}
Expand Down
7 changes: 4 additions & 3 deletions crates/neon/src/types_impl/function/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,10 @@ pub struct BindOptions<'a, 'cx: 'a> {

impl<'a, 'cx: 'a> BindOptions<'a, 'cx> {
/// Set the value of `this` for the function call.
pub fn this<V: Value>(&mut self, this: Handle<'cx, V>) -> &mut Self {
self.this = Some(this.upcast());
self
pub fn this<T: TryIntoJs<'cx>>(&mut self, this: T) -> NeonResult<&mut Self> {
let v = this.try_into_js(self.cx)?;
self.this = Some(v.upcast());
Ok(self)
}

/// Replaces the arguments list with the given arguments.
Expand Down
41 changes: 41 additions & 0 deletions test/napi/lib/functions.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
var addon = require("..");
var assert = require("chai").assert;

const STRICT = function() { "use strict"; return this; };
const SLOPPY = Function('return this;');

function isStrict(f) {
try {
f.caller;
return false;
} catch (e) {
return true;
}
}

describe("JsFunction", function () {
it("return a JsFunction built in Rust", function () {
assert.isFunction(addon.return_js_function());
Expand Down Expand Up @@ -41,6 +53,35 @@ describe("JsFunction", function () {
assert.equal(addon.call_parse_int_with_bind(), 42);
});

it("bind a JsFunction to an object", function () {
const result = addon.bind_js_function_to_object(function () {
return this.prop;
});

assert.equal(result, 42);
});

it("bind a strict JsFunction to a number", function() {
assert.isTrue(isStrict(STRICT));

// strict mode functions are allowed to have a primitive this binding
const result = addon.bind_js_function_to_number(STRICT);

assert.strictEqual(result, 42);
});

it("bind a sloppy JsFunction to a primitive", function() {
assert.isFalse(isStrict(SLOPPY));

// legacy JS functions (aka "sloppy mode") replace primitive this bindings
// with object wrappers, so 42 will get wrapped as new Number(42)
const result = addon.bind_js_function_to_number(SLOPPY);

assert.instanceOf(result, Number);
assert.strictEqual(typeof result, 'object');
assert.strictEqual(result.valueOf(), 42);
});

it("call a JsFunction with zero args", function () {
assert.equal(addon.call_js_function_with_zero_args(), -Infinity);
});
Expand Down
12 changes: 12 additions & 0 deletions test/napi/src/js/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,18 @@ pub fn call_parse_int_with_bind(mut cx: FunctionContext) -> JsResult<JsNumber> {
Ok(cx.number(x + 1.0))
}

pub fn bind_js_function_to_object(mut cx: FunctionContext) -> JsResult<JsValue> {
let f = cx.argument::<JsFunction>(0)?;
let obj = cx.empty_object();
obj.prop(&mut cx, "prop").set(42)?;
f.bind(&mut cx).this(obj)?.apply()
}

pub fn bind_js_function_to_number(mut cx: FunctionContext) -> JsResult<JsValue> {
let f = cx.argument::<JsFunction>(0)?;
f.bind(&mut cx).this(42)?.apply()
}

fn get_math_max<'a>(cx: &mut FunctionContext<'a>) -> JsResult<'a, JsFunction> {
let math: Handle<JsObject> = cx.global("Math")?;
let max: Handle<JsFunction> = math.get(cx, "max")?;
Expand Down
2 changes: 2 additions & 0 deletions test/napi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ fn main(mut cx: ModuleContext) -> NeonResult<()> {
)?;
cx.export_function("call_js_function_with_bind", call_js_function_with_bind)?;
cx.export_function("call_parse_int_with_bind", call_parse_int_with_bind)?;
cx.export_function("bind_js_function_to_object", bind_js_function_to_object)?;
cx.export_function("bind_js_function_to_number", bind_js_function_to_number)?;
cx.export_function(
"call_js_function_with_zero_args",
call_js_function_with_zero_args,
Expand Down

0 comments on commit 94a0c08

Please sign in to comment.