Skip to content

Commit

Permalink
napi: Implement napi_instanceof()
Browse files Browse the repository at this point in the history
Since FunctionTemplate::HasInstance() is inaccessible, we must implement
the Javascript instanceof operator using the public V8 API. This does
that, and uses the V8 tests for instanceof to ensure that the semantics
of the API are identical to the semantics of the Javascript operator.

Closes nodejs#48
  • Loading branch information
Gabriel Schulhof authored and jasongin committed Jan 26, 2017
1 parent f74e46f commit f94bbd3
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 0 deletions.
41 changes: 41 additions & 0 deletions src/node_jsvmapi.cc
Original file line number Diff line number Diff line change
Expand Up @@ -815,6 +815,47 @@ napi_value napi_new_instance(napi_env e, napi_value cons,
return v8impl::JsValueFromV8LocalValue(result);
}

bool napi_instanceof(napi_env e, napi_value obj, napi_value cons) {
bool returnValue = false;

v8::Local<v8::Value> v8Obj = v8impl::V8LocalValueFromJsValue(obj);
v8::Local<v8::Value> v8Cons = v8impl::V8LocalValueFromJsValue(cons);
v8::Isolate *isolate = v8impl::V8IsolateFromJsEnv(e);

if (!v8Cons->IsFunction()) {
napi_throw_type_error(e, "constructor must be a function");

// Error handling needs to be done here
return false;
}

v8Cons =
v8Cons->ToObject()->Get(v8::String::NewFromUtf8(isolate, "prototype"));

if (!v8Cons->IsObject()) {
napi_throw_type_error(e, "constructor prototype must be an object");

// Error handling needs to be done here
return false;
}

if (!v8Obj->StrictEquals(v8Cons)) {
for (v8::Local<v8::Value> originalObj = v8Obj;
!(v8Obj->IsNull() || v8Obj->IsUndefined());
v8Obj = v8Obj->ToObject()->GetPrototype()) {
if (v8Obj->StrictEquals(v8Cons)) {
returnValue =
!(originalObj->IsNumber() ||
originalObj->IsBoolean() ||
originalObj->IsString());
break;
}
}
}

return returnValue;
}

napi_value napi_make_external(napi_env e, napi_value v) {
return v;
}
Expand Down
1 change: 1 addition & 0 deletions src/node_jsvmapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ NODE_EXTERN napi_value napi_call_function(napi_env e, napi_value scope,
int argc, napi_value* argv);
NODE_EXTERN napi_value napi_new_instance(napi_env e, napi_value cons,
int argc, napi_value* argv);
NODE_EXTERN bool napi_instanceof(napi_env e, napi_value obj, napi_value cons);

// Temporary method needed to support wrapping JavascriptObject in an external
// object wrapper capable of storing external data. This workaround is only
Expand Down
8 changes: 8 additions & 0 deletions test/addons-abi/test_instanceof/binding.gyp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"targets": [
{
"target_name": "test_instanceof",
"sources": [ "test_instanceof.cc" ]
}
]
}
37 changes: 37 additions & 0 deletions test/addons-abi/test_instanceof/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
var fs = require( 'fs' );

require('../../common');
var assert = require('assert');
var addon = require('./build/Release/test_instanceof');
var path = require( 'path' );

function assertTrue(assertion) {
return assert.strictEqual(true, assertion);
}

function assertFalse(assertion) {
assert.strictEqual(false, assertion);
}

function assertEquals(leftHandSide, rightHandSide) {
assert.equal(leftHandSide, rightHandSide);
}

function assertThrows(statement) {
assert.throws(function() {
eval(statement);
}, Error);
}

function testFile( fileName ) {
eval( fs.readFileSync( fileName, { encoding: 'utf8' } )
.replace( /[(]([^\s(]+)\s+instanceof\s+([^)]+)[)]/g,
'( addon.doInstanceOf( $1, $2 ) )' ) );
}

testFile(
path.join( path.resolve( __dirname, '..', '..', '..', 'deps', 'v8', 'test', 'mjsunit' ),
'instanceof.js' ) );
testFile(
path.join( path.resolve( __dirname, '..', '..', '..', 'deps', 'v8', 'test', 'mjsunit' ),
'instanceof-2.js' ) );
19 changes: 19 additions & 0 deletions test/addons-abi/test_instanceof/test_instanceof.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#include <node_jsvmapi.h>
#include <stdio.h>

void doInstanceOf(napi_env env, napi_func_cb_info info) {
napi_value arguments[2];

napi_get_cb_args(env, info, arguments, 2);

napi_set_return_value(env, info,
napi_create_boolean(env, napi_instanceof(env, arguments[0], arguments[1])));
}

void Init(napi_env env, napi_value exports, napi_value module) {
napi_set_property(env, exports,
napi_property_name(env, "doInstanceOf"),
napi_create_function(env, doInstanceOf));
}

NODE_MODULE_ABI(addon, Init)

0 comments on commit f94bbd3

Please sign in to comment.