Skip to content

Commit c379d22

Browse files
author
Gabriel Schulhof
committed
napi: change napi_instanceof() to use Symbol.hasInstance
Re nodejs/node#11975 (comment) Fixes #182 Closes #185
1 parent 6028af3 commit c379d22

File tree

4 files changed

+81
-3
lines changed

4 files changed

+81
-3
lines changed

src/node_api.cc

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2018,7 +2018,6 @@ napi_status napi_instanceof(napi_env env,
20182018
*result = false;
20192019

20202020
v8::Local<v8::Object> ctor;
2021-
v8::Local<v8::String> prototype_string;
20222021
v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(env);
20232022
v8::Local<v8::Context> context = isolate->GetCurrentContext();
20242023

@@ -2030,6 +2029,47 @@ napi_status napi_instanceof(napi_env env,
20302029
return napi_set_last_error(napi_function_expected);
20312030
}
20322031

2032+
napi_value value, js_result;
2033+
napi_status status;
2034+
napi_valuetype value_type;
2035+
2036+
// Get "Symbol" from the global object
2037+
status = napi_get_global(env, &value);
2038+
if (status != napi_ok) return status;
2039+
status = napi_get_named_property(env, value, "Symbol", &value);
2040+
if (status != napi_ok) return status;
2041+
status = napi_typeof(env, value, &value_type);
2042+
if (status != napi_ok) return status;
2043+
2044+
// Get "hasInstance" from Symbol
2045+
if (value_type == napi_function) {
2046+
status = napi_get_named_property(env, value, "hasInstance", &value);
2047+
if (status != napi_ok) return status;
2048+
status = napi_typeof(env, value, &value_type);
2049+
if (status != napi_ok) return status;
2050+
2051+
// Retrieve the function at the Symbol(hasInstance) key of the constructor
2052+
if (value_type == napi_symbol) {
2053+
status = napi_get_property(env, constructor, value, &value);
2054+
if (status != napi_ok) return status;
2055+
status = napi_typeof(env, value, &value_type);
2056+
if (status != napi_ok) return status;
2057+
2058+
// Call the function to determine whether the object is an instance of the
2059+
// constructor
2060+
if (value_type == napi_function) {
2061+
status = napi_call_function(env, constructor, value, 1, &object,
2062+
&js_result);
2063+
if (status != napi_ok) return status;
2064+
return napi_get_value_bool(env, js_result, result);
2065+
}
2066+
}
2067+
}
2068+
2069+
// If running constructor[Symbol.hasInstance](object) did not work, we perform
2070+
// a traditional instanceof (early Node.js 6.x).
2071+
2072+
v8::Local<v8::String> prototype_string;
20332073
CHECK_NEW_FROM_UTF8(isolate, prototype_string, "prototype");
20342074

20352075
auto maybe = ctor->Get(context, prototype_string);

test/addons-napi/test_buffer/test_buffer.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ void staticBuffer(napi_env env, napi_callback_info info) {
122122
env,
123123
napi_create_external_buffer(env,
124124
sizeof(theText),
125-
theText,
125+
(void *)theText,
126126
noopDeleter,
127127
NULL, // finalize_hint
128128
&theBuffer));

test/addons-napi/test_conversions/test_conversions.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#include <node_api.h>
22

33
void ThrowLastError(napi_env env) {
4-
napi_extended_error_info* error_info = napi_get_last_error_info();
4+
const napi_extended_error_info* error_info = napi_get_last_error_info();
55
if (error_info->error_code != napi_ok) {
66
napi_throw_error(env, error_info->error_message);
77
}

test/addons-napi/test_instanceof/test.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,41 @@ testFile(
4747
path.join(path.resolve(__dirname, '..', '..', '..',
4848
'deps', 'v8', 'test', 'mjsunit'),
4949
'instanceof-2.js'));
50+
51+
// We can only perform this test if we have a working Symbol.hasInstance
52+
if (typeof Symbol !== 'undefined' && 'hasInstance' in Symbol &&
53+
typeof Symbol.hasInstance === 'symbol') {
54+
55+
function compareToNative(theObject, theConstructor) {
56+
assert.strictEqual(addon.doInstanceOf(theObject, theConstructor),
57+
(theObject instanceof theConstructor));
58+
}
59+
60+
const MyClass = function MyClass() {};
61+
Object.defineProperty(MyClass, Symbol.hasInstance, {
62+
value: function(candidate) {
63+
return 'mark' in candidate;
64+
}
65+
});
66+
67+
const MySubClass = function MySubClass() {};
68+
MySubClass.prototype = new MyClass();
69+
70+
let x = new MySubClass();
71+
let y = new MySubClass();
72+
x.mark = true;
73+
74+
compareToNative(x, MySubClass);
75+
compareToNative(y, MySubClass);
76+
compareToNative(x, MyClass);
77+
compareToNative(y, MyClass);
78+
79+
x = new MyClass();
80+
y = new MyClass();
81+
x.mark = true;
82+
83+
compareToNative(x, MySubClass);
84+
compareToNative(y, MySubClass);
85+
compareToNative(x, MyClass);
86+
compareToNative(y, MyClass);
87+
}

0 commit comments

Comments
 (0)