Skip to content

Commit bf6fa74

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

File tree

2 files changed

+81
-1
lines changed

2 files changed

+81
-1
lines changed

src/node_api.cc

+43-1
Original file line numberDiff line numberDiff line change
@@ -2042,7 +2042,6 @@ napi_status napi_instanceof(napi_env e,
20422042
*result = false;
20432043

20442044
v8::Local<v8::Object> v8Cons;
2045-
v8::Local<v8::String> prototypeString;
20462045
v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(e);
20472046
v8::Local<v8::Context> context = isolate->GetCurrentContext();
20482047

@@ -2054,6 +2053,49 @@ napi_status napi_instanceof(napi_env e,
20542053
return napi_set_last_error(napi_function_expected);
20552054
}
20562055

2056+
napi_value value, key, jsRes;
2057+
napi_status status;
2058+
napi_valuetype valueType;
2059+
2060+
// Get "Symbol" from the global object
2061+
status = napi_get_global(e, &value);
2062+
if (status != napi_ok) return status;
2063+
status = napi_create_string_utf8(e, "Symbol", 6, &key);
2064+
if (status != napi_ok) return status;
2065+
status = napi_get_property(e, value, key, &value);
2066+
if (status != napi_ok) return status;
2067+
status = napi_get_type_of_value(e, value, &valueType);
2068+
if (status != napi_ok) return status;
2069+
2070+
// Get "hasInstance" from Symbol
2071+
if (valueType == napi_function) {
2072+
status = napi_create_string_utf8(e, "hasInstance", 11, &key);
2073+
if (status != napi_ok) return status;
2074+
status = napi_get_property(e, value, key, &value);
2075+
if (status != napi_ok) return status;
2076+
status = napi_get_type_of_value(e, value, &valueType);
2077+
if (status != napi_ok) return status;
2078+
2079+
// Retrieve the function at the Symbol(hasInstance) key of the constructor
2080+
if (valueType == napi_symbol) {
2081+
status = napi_get_property(e, constructor, value, &value);
2082+
if (status != napi_ok) return status;
2083+
status = napi_get_type_of_value(e, value, &valueType);
2084+
2085+
// Call the function to determine whether the object is an instance of the
2086+
// constructor
2087+
if (valueType == napi_function) {
2088+
status = napi_call_function(e, constructor, value, 1, &object, &jsRes);
2089+
if (status != napi_ok) return status;
2090+
return napi_get_value_bool(e, jsRes, result);
2091+
}
2092+
}
2093+
}
2094+
2095+
// If running constructor[Symbol.hasInstance](object) did not work, we perform
2096+
// a traditional instanceof (early Node.js 6.x).
2097+
2098+
v8::Local<v8::String> prototypeString;
20572099
CHECK_NEW_FROM_UTF8(isolate, prototypeString, "prototype");
20582100

20592101
auto maybe = v8Cons->Get(context, prototypeString);

test/addons-napi/test_instanceof/test.js

+38
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( object, constructor ) {
56+
return (addon.doInstanceOf( object, constructor) ===
57+
(object instanceof constructor ));
58+
}
59+
60+
var MyClass = function MyClass() {}
61+
Object.defineProperty(MyClass, Symbol.hasInstance, {
62+
value: function( candidate ) {
63+
return 'mark' in candidate;
64+
}
65+
} );
66+
67+
var MySubClass = function MySubClass() {}
68+
MySubClass.prototype = new MyClass();
69+
70+
var x = new MySubClass();
71+
var 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)