From ccee0af193ee77d2902628eb5cbbf0bdd4a5ad41 Mon Sep 17 00:00:00 2001 From: Gabriel Schulhof Date: Wed, 22 Mar 2017 19:30:45 +0200 Subject: [PATCH] napi: change napi_instanceof() to use Symbol.hasInstance Re https://github.com/nodejs/node/pull/11975#discussion_r107331066 Fixes https://github.com/nodejs/abi-stable-node/issues/182 Closes https://github.com/nodejs/abi-stable-node/pull/185 --- src/node_api.cc | 44 +++++++++++++++++++++++- test/addons-napi/test_instanceof/test.js | 38 ++++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/src/node_api.cc b/src/node_api.cc index 3b10f32e80..e645341d8f 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -2042,7 +2042,6 @@ napi_status napi_instanceof(napi_env e, *result = false; v8::Local v8Cons; - v8::Local prototypeString; v8::Isolate* isolate = v8impl::V8IsolateFromJsEnv(e); v8::Local context = isolate->GetCurrentContext(); @@ -2054,6 +2053,49 @@ napi_status napi_instanceof(napi_env e, return napi_set_last_error(napi_function_expected); } + napi_value value, key, jsRes; + napi_status status; + napi_valuetype valueType; + + // Get "Symbol" from the global object + status = napi_get_global(e, &value); + if (status != napi_ok) return status; + status = napi_create_string_utf8(e, "Symbol", 6, &key); + if (status != napi_ok) return status; + status = napi_get_property(e, value, key, &value); + if (status != napi_ok) return status; + status = napi_get_type_of_value(e, value, &valueType); + if (status != napi_ok) return status; + + // Get "hasInstance" from Symbol + if (valueType == napi_function) { + status = napi_create_string_utf8(e, "hasInstance", 11, &key); + if (status != napi_ok) return status; + status = napi_get_property(e, value, key, &value); + if (status != napi_ok) return status; + status = napi_get_type_of_value(e, value, &valueType); + if (status != napi_ok) return status; + + // Retrieve the function at the Symbol(hasInstance) key of the constructor + if (valueType == napi_symbol) { + status = napi_get_property(e, constructor, value, &value); + if (status != napi_ok) return status; + status = napi_get_type_of_value(e, value, &valueType); + + // Call the function to determine whether the object is an instance of the + // constructor + if (valueType == napi_function) { + status = napi_call_function(e, constructor, value, 1, &object, &jsRes); + if (status != napi_ok) return status; + return napi_get_value_bool(e, jsRes, result); + } + } + } + + // If running constructor[Symbol.hasInstance](object) did not work, we perform + // a traditional instanceof (early Node.js 6.x). + + v8::Local prototypeString; CHECK_NEW_FROM_UTF8(isolate, prototypeString, "prototype"); auto maybe = v8Cons->Get(context, prototypeString); diff --git a/test/addons-napi/test_instanceof/test.js b/test/addons-napi/test_instanceof/test.js index 9500d864dd..478303196e 100644 --- a/test/addons-napi/test_instanceof/test.js +++ b/test/addons-napi/test_instanceof/test.js @@ -47,3 +47,41 @@ testFile( path.join(path.resolve(__dirname, '..', '..', '..', 'deps', 'v8', 'test', 'mjsunit'), 'instanceof-2.js')); + +// We can only perform this test if we have a working Symbol.hasInstance +if (typeof Symbol !== 'undefined' && 'hasInstance' in Symbol && + typeof Symbol.hasInstance === 'symbol') { + + function compareToNative( object, constructor ) { + return (addon.doInstanceOf( object, constructor) === + (object instanceof constructor )); + } + + var MyClass = function MyClass() {} + Object.defineProperty(MyClass, Symbol.hasInstance, { + value: function( candidate ) { + return 'mark' in candidate; + } + } ); + + var MySubClass = function MySubClass() {} + MySubClass.prototype = new MyClass(); + + var x = new MySubClass(); + var y = new MySubClass(); + x.mark = true; + + compareToNative(x, MySubClass); + compareToNative(y, MySubClass); + compareToNative(x, MyClass); + compareToNative(y, MyClass); + + x = new MyClass(); + y = new MyClass(); + x.mark = true; + + compareToNative(x, MySubClass); + compareToNative(y, MySubClass); + compareToNative(x, MyClass); + compareToNative(y, MyClass); +}