Skip to content

Commit

Permalink
first part of #61: construct instances from JsFunctions
Browse files Browse the repository at this point in the history
  • Loading branch information
Dave Herman committed May 10, 2016
1 parent 4c917d9 commit 7fc8d98
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 19 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 @@ -15,4 +15,7 @@ extern "system" {
#[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;

#[link_name = "NeonSys_Fun_Construct"]
pub fn construct(out: &mut Local, isolate: *mut c_void, fun: 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 @@ -368,6 +368,11 @@ extern "C" bool NeonSys_Fun_Call(v8::Local<v8::Value> *out, v8::Isolate *isolate
return maybe_result.ToLocal(out);
}

extern "C" bool NeonSys_Fun_Construct(v8::Local<v8::Object> *out, v8::Isolate *isolate, v8::Local<v8::Function> fun, int32_t argc, v8::Local<v8::Value> argv[]) {
v8::MaybeLocal<v8::Object> maybe_result = fun->NewInstance(isolate->GetCurrentContext(), 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 @@ -81,6 +81,7 @@ extern "C" {
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[]);
bool NeonSys_Fun_Construct(v8::Local<v8::Object> *out, v8::Isolate *isolate, v8::Local<v8::Function> fun, 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
43 changes: 31 additions & 12 deletions src/internal/js/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,21 @@ impl<T: Value> Kernel<()> for FunctionKernel<T> {
// Maximum number of function arguments in V8.
const V8_ARGC_LIMIT: usize = 65535;

unsafe fn prepare_call<'a, 'b, S: Scope<'a>, A, AS>(scope: &mut S, args: AS) -> VmResult<(*mut c_void, i32, *mut c_void)>
where A: Value + 'b,
AS: IntoIterator<Item=Handle<'b, A>>
{
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");
}
let isolate: *mut c_void = mem::transmute(scope.isolate().to_raw());
Ok((isolate, argc as i32, argv as *mut c_void))
}

impl JsFunction {
pub fn new<'a, T: Scope<'a>, U: Value>(scope: &mut T, f: fn(Call) -> JsResult<U>) -> JsResult<'a, JsFunction> {
build(|out| {
Expand All @@ -701,23 +716,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>
pub fn call<'a, 'b, S: Scope<'a>, T, A, AS>(self, scope: &mut S, this: Handle<'b, T>, args: AS) -> JsResult<'a, JsValue>
where T: Value,
A: Value + 'b,
AS: IntoIterator<Item=Handle<'b, A>>,
R: Value
AS: IntoIterator<Item=Handle<'b, A>>
{
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");
}
let (isolate, argc, argv) = try!(unsafe { prepare_call(scope, args) });
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)
neon_sys::fun::call(out, isolate, self.to_raw(), this.to_raw(), argc, argv)
}
})
}

pub fn construct<'a, 'b, S: Scope<'a>, A, AS>(self, scope: &mut S, args: AS) -> JsResult<'a, JsObject>
where A: Value + 'b,
AS: IntoIterator<Item=Handle<'b, A>>
{
let (isolate, argc, argv) = try!(unsafe { prepare_call(scope, args) });
build(|out| {
unsafe {
neon_sys::fun::construct(out, isolate, self.to_raw(), argc, argv)
}
})
}
Expand Down
10 changes: 7 additions & 3 deletions tests/lib/functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@ var addon = require('../native');
var assert = require('chai').assert;

describe('JsFunction', function() {
it('should return a JsFunction built in Rust', function () {
it('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 () {
it('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 () {
it('call a JsFunction built in JS that implements x => x + 1', function () {
assert.equal(addon.call_js_function(function(x) { return x + 1 }), 17);
});

it('new a JsFunction', function () {
assert.equal(addon.construct_js_function(Date), 1970);
});
});
14 changes: 12 additions & 2 deletions tests/native/src/js/functions.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use neon::vm::{Call, JsResult};
use neon::mem::Handle;
use neon::js::{JsNumber, JsNull, JsFunction};
use neon::js::{JsNumber, JsNull, JsFunction, Object, JsValue};

fn add1(call: Call) -> JsResult<JsNumber> {
let scope = call.scope;
Expand All @@ -16,5 +16,15 @@ 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)
try!(f.call(scope, JsNull::new(), args)).check::<JsNumber>()
}

pub fn construct_js_function(call: Call) -> JsResult<JsNumber> {
let scope = call.scope;
let f = try!(try!(call.arguments.require(scope, 0)).check::<JsFunction>());
let zero = JsNumber::new(scope, 0.0);
let o = try!(f.construct(scope, vec![zero]));
let get_utc_full_year_method = try!(try!(o.get(scope, "getUTCFullYear")).check::<JsFunction>());
let args: Vec<Handle<JsValue>> = vec![];
try!(get_utc_full_year_method.call(scope, o.upcast::<JsValue>(), args)).check::<JsNumber>()
}
3 changes: 1 addition & 2 deletions tests/native/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ register_module!(m, {

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


0 comments on commit 7fc8d98

Please sign in to comment.