Skip to content

Commit

Permalink
Merge pull request #85 from rustbridge/dherman-function-call
Browse files Browse the repository at this point in the history
closes #60: call JS functions from Rust
  • Loading branch information
Dave Herman committed May 10, 2016
2 parents e65068e + 9409ff0 commit e6115f3
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 4 deletions.
3 changes: 3 additions & 0 deletions crates/neon-sys/src/fun.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,7 @@ extern "system" {
#[link_name = "NeonSys_Fun_GetKernel"]
pub fn get_kernel(obj: Local) -> *mut c_void;

#[link_name = "NeonSys_Fun_Call"]
pub fn call(out: &mut Local, isolate: *mut c_void, fun: Local, this: Local, argc: i32, argv: *mut c_void) -> bool;

}
5 changes: 5 additions & 0 deletions crates/neon-sys/src/neon.cc
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,11 @@ extern "C" void *NeonSys_Fun_GetKernel(v8::Local<v8::External> data) {
return data->Value();
}

extern "C" bool NeonSys_Fun_Call(v8::Local<v8::Value> *out, v8::Isolate *isolate, v8::Local<v8::Function> fun, v8::Local<v8::Value> self, int32_t argc, v8::Local<v8::Value> argv[]) {
v8::MaybeLocal<v8::Value> maybe_result = fun->Call(isolate->GetCurrentContext(), self, argc, argv);
return maybe_result.ToLocal(out);
}

extern "C" tag_t NeonSys_Tag_Of(v8::Local<v8::Value> val) {
return val->IsNull() ? tag_null
: val->IsUndefined() ? tag_undefined
Expand Down
1 change: 1 addition & 0 deletions crates/neon-sys/src/neon.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ extern "C" {
bool NeonSys_Fun_New(v8::Local<v8::Function> *out, v8::Isolate *isolate, v8::FunctionCallback callback, void *kernel);
void NeonSys_Fun_ExecKernel(void *kernel, NeonSys_RootScopeCallback callback, v8::FunctionCallbackInfo<v8::Value> *info, void *scope);
void *NeonSys_Fun_GetKernel(v8::Local<v8::External> obj);
bool NeonSys_Fun_Call(v8::Local<v8::Value> *out, v8::Isolate *isolate, v8::Local<v8::Function> fun, v8::Local<v8::Value> self, int32_t argc, v8::Local<v8::Value> argv[]);

typedef void *(*NeonSys_AllocateCallback)(const v8::FunctionCallbackInfo<v8::Value> *info);
typedef bool (*NeonSys_ConstructCallback)(const v8::FunctionCallbackInfo<v8::Value> *info);
Expand Down
23 changes: 23 additions & 0 deletions src/internal/js/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,8 @@ impl<T: Value> Kernel<()> for FunctionKernel<T> {
}
}

// Maximum number of function arguments in V8.
const V8_ARGC_LIMIT: usize = 65535;

impl JsFunction {
pub fn new<'a, T: Scope<'a>, U: Value>(scope: &mut T, f: fn(Call) -> JsResult<U>) -> JsResult<'a, JsFunction> {
Expand All @@ -698,6 +700,27 @@ impl JsFunction {
}
})
}

pub fn call<'a, 'b, S: Scope<'a>, T, A, AS, R>(self, scope: &mut S, this: Handle<'b, T>, args: AS) -> JsResult<'a, R>
where T: Value,
A: Value + 'b,
AS: IntoIterator<Item=Handle<'b, A>>,
R: Value
{
let mut v: Vec<_> = args.into_iter().collect();
let mut slice = &mut v[..];
let argv = slice.as_mut_ptr();
let argc = slice.len();
if argc > V8_ARGC_LIMIT {
return JsError::throw(Kind::RangeError, "too many arguments");
}
build(|out| {
unsafe {
let isolate: *mut c_void = mem::transmute(scope.isolate().to_raw());
neon_sys::fun::call(out, isolate, self.to_raw(), this.to_raw(), argc as i32, argv as *mut c_void)
}
})
}
}

impl Value for JsFunction { }
Expand Down
16 changes: 16 additions & 0 deletions tests/lib/functions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
var addon = require('../native');
var assert = require('chai').assert;

describe('JsFunction', function() {
it('should return a JsFunction built in Rust', function () {
assert.isFunction(addon.return_js_function());
});

it('should return a JsFunction built in Rust that implements x => x + 1', function () {
assert.equal(addon.return_js_function()(41), 42);
});

it('should call a JsFunction built in JS that implements x => x + 1', function () {
assert.equal(addon.call_js_function(function(x) { return x + 1 }), 17);
});
});
20 changes: 20 additions & 0 deletions tests/native/src/js/functions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use neon::vm::{Call, JsResult};
use neon::mem::Handle;
use neon::js::{JsNumber, JsNull, JsFunction};

fn add1(call: Call) -> JsResult<JsNumber> {
let scope = call.scope;
let x = try!(try!(call.arguments.require(scope, 0)).check::<JsNumber>()).value();
Ok(JsNumber::new(scope, x + 1.0))
}

pub fn return_js_function(call: Call) -> JsResult<JsFunction> {
JsFunction::new(call.scope, add1)
}

pub fn call_js_function(call: Call) -> JsResult<JsNumber> {
let scope = call.scope;
let f = try!(try!(call.arguments.require(scope, 0)).check::<JsFunction>());
let args: Vec<Handle<JsNumber>> = vec![JsNumber::new(scope, 16.0)];
f.call(scope, JsNull::new(), args)
}
13 changes: 9 additions & 4 deletions tests/native/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@
extern crate neon;

mod js {
pub mod strings;
pub mod integers;
pub mod arrays;
pub mod objects;
pub mod strings;
pub mod integers;
pub mod arrays;
pub mod objects;
pub mod functions;
}

use js::strings::return_js_string;
use js::integers::return_js_integer;
use js::arrays::*;
use js::objects::*;
use js::functions::*;

register_module!(m, {
try!(m.export("return_js_string", return_js_string));
Expand All @@ -25,6 +27,9 @@ register_module!(m, {
try!(m.export("return_js_object", return_js_object));
try!(m.export("return_js_object_with_integer", return_js_object_with_integer));
try!(m.export("return_js_object_with_string", return_js_object_with_string));

try!(m.export("return_js_function", return_js_function));
try!(m.export("call_js_function", call_js_function));
Ok(())
});

Expand Down

0 comments on commit e6115f3

Please sign in to comment.