Skip to content
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

JsFunction::construct #86

Merged
merged 2 commits into from
May 11, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
6 changes: 4 additions & 2 deletions src/internal/js/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use neon_sys::raw;
use internal::mem::{Handle, HandleInternal, Managed};
use internal::scope::{Scope, ScopeInternal};
use internal::vm::{Isolate, IsolateInternal, JsResult, VmResult, FunctionCall, CallbackInfo, Lock, LockState, Throw, This, Kernel, exec_function_kernel};
use internal::js::{Value, ValueInternal, JsFunction, JsObject, JsValue, JsUndefined, build};
use internal::js::{Value, ValueInternal, JsFunction, JsObject, Object, JsValue, JsUndefined, build};
use internal::js::error::{JsError, Kind};

#[repr(C)]
Expand Down Expand Up @@ -218,6 +218,8 @@ impl<T: Class> This for T {
}
}

impl<T: Class> Object for T { }

pub trait ClassInternal: Class {
fn metadata_opt<'a, T: Scope<'a>>(scope: &mut T) -> Option<ClassMetadata> {
scope.isolate()
Expand Down Expand Up @@ -346,7 +348,7 @@ impl<T: Class> JsClass<T> {
}
}

pub fn constructor<'a, U: Scope<'a>>(&self, _: &mut U) -> JsResult<'a, JsFunction> {
pub fn constructor<'a, U: Scope<'a>>(&self, _: &mut U) -> JsResult<'a, JsFunction<T>> {
build(|out| {
unsafe {
neon_sys::class::constructor(out, self.to_raw())
Expand Down
68 changes: 49 additions & 19 deletions src/internal/js/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod class;

use std::mem;
use std::os::raw::c_void;
use std::marker::PhantomData;
use neon_sys;
use neon_sys::raw;
use neon_sys::tag::Tag;
Expand Down Expand Up @@ -118,7 +119,7 @@ impl<'a> Handle<'a, JsValue> {
Tag::String => Variant::String(Handle::new(JsString(self.to_raw()))),
Tag::Object => Variant::Object(Handle::new(JsObject(self.to_raw()))),
Tag::Array => Variant::Array(Handle::new(JsArray(self.to_raw()))),
Tag::Function => Variant::Function(Handle::new(JsFunction(self.to_raw()))),
Tag::Function => Variant::Function(Handle::new(JsFunction { raw: self.to_raw(), marker: PhantomData })),
Tag::Other => Variant::Other(self.clone())
}
}
Expand Down Expand Up @@ -661,7 +662,10 @@ impl Object for JsArray { }
/// A JavaScript function object.
#[repr(C)]
#[derive(Clone, Copy)]
pub struct JsFunction(raw::Local);
pub struct JsFunction<T: Object=JsObject> {
raw: raw::Local,
marker: PhantomData<T>
}

#[repr(C)]
pub struct FunctionKernel<T: Value>(fn(Call) -> JsResult<T>);
Expand Down Expand Up @@ -690,6 +694,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 @@ -700,38 +719,49 @@ 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>
impl<C: Object> JsFunction<C> {
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, C>
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)
}
})
}
}

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

impl Managed for JsFunction {
fn to_raw(self) -> raw::Local { self.0 }
impl<T: Object> Managed for JsFunction<T> {
fn to_raw(self) -> raw::Local { self.raw }

fn from_raw(h: raw::Local) -> Self { JsFunction(h) }
fn from_raw(h: raw::Local) -> Self {
JsFunction {
raw: h,
marker: PhantomData
}
}
}

impl ValueInternal for JsFunction {
impl<T: Object> ValueInternal for JsFunction<T> {
fn is_typeof<Other: Value>(other: Other) -> bool {
unsafe { neon_sys::tag::is_function(other.to_raw()) }
}
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(())
});