Skip to content

Commit f92db9d

Browse files
jasonginkfarnung
authored andcommitted
n-api: enable napi_wrap() to work with any object
Change to inserting an external object into the wrapper object's prototype chain, instead of injecting an alternate `this` object in the constructor callback adapter. The latter approach didn't work in certain scenarios because the JSRT runtime still returned the original `this` object. And with this change, the setting and checking of the extension flag, which distinguished wrapper objects from external objects, can be removed. Also removing the CallbackInfo.returnValue field which is not used anymore (since we switched to direct return values). PR-URL: nodejs#269 Reviewed-By: Taylor Woll <[email protected]> Reviewed-By: Kyle Farnung <[email protected]>
1 parent d8ef944 commit f92db9d

File tree

1 file changed

+35
-37
lines changed

1 file changed

+35
-37
lines changed

src/node_api_jsrt.cc

+35-37
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ struct CallbackInfo {
6969
uint16_t argc;
7070
napi_value* argv;
7171
void* data;
72-
napi_value returnValue;
7372
};
7473

7574
struct napi_env__ {
@@ -219,30 +218,9 @@ class ExternalCallback {
219218
CallbackInfo cbInfo;
220219
cbInfo.thisArg = reinterpret_cast<napi_value>(arguments[0]);
221220
cbInfo.isConstructCall = isConstructCall;
222-
223-
if (isConstructCall) {
224-
// For constructor callbacks, replace the 'this' arg with a new external
225-
// object, to support wrapping a native object in the external object.
226-
JsValueRef externalThis;
227-
if (JsNoError == JsCreateExternalObject(
228-
nullptr, jsrtimpl::ExternalData::Finalize, &externalThis)) {
229-
// Copy the prototype from the default 'this' arg to the new
230-
// 'external' this arg.
231-
if (arguments[0] != nullptr) {
232-
JsValueRef thisPrototype;
233-
if (JsNoError == JsGetPrototype(arguments[0], &thisPrototype)) {
234-
JsSetPrototype(externalThis, thisPrototype);
235-
}
236-
}
237-
238-
cbInfo.thisArg = reinterpret_cast<napi_value>(externalThis);
239-
}
240-
}
241-
242221
cbInfo.argc = argumentCount - 1;
243222
cbInfo.argv = reinterpret_cast<napi_value*>(arguments + 1);
244223
cbInfo.data = externalCallback->_data;
245-
cbInfo.returnValue = reinterpret_cast<napi_value>(undefinedValue);
246224

247225
napi_value result = externalCallback->_cb(
248226
externalCallback->_env, reinterpret_cast<napi_callback_info>(&cbInfo));
@@ -986,21 +964,12 @@ napi_status napi_typeof(napi_env env, napi_value vv, napi_valuetype* result) {
986964
case JsError: *result = napi_object; break;
987965

988966
default:
989-
// An "external" value is represented in JSRT as an Object that has
990-
// external data and DOES NOT allow extensions. (A wrapped object has
991-
// external data and DOES allow extensions.)
992967
bool hasExternalData;
993968
if (JsHasExternalData(value, &hasExternalData) != JsNoError) {
994969
hasExternalData = false;
995970
}
996971

997-
bool isExtensionAllowed;
998-
if (JsGetExtensionAllowed(value, &isExtensionAllowed) != JsNoError) {
999-
isExtensionAllowed = false;
1000-
}
1001-
1002-
*result =
1003-
(hasExternalData && !isExtensionAllowed) ? napi_external : napi_object;
972+
*result = hasExternalData ? napi_external : napi_object;
1004973
break;
1005974
}
1006975
return napi_ok;
@@ -1385,17 +1354,47 @@ napi_status napi_wrap(napi_env env,
13851354
env, native_object, finalize_cb, finalize_hint);
13861355
if (externalData == nullptr) return napi_set_last_error(napi_generic_failure);
13871356

1388-
CHECK_JSRT(JsSetExternalData(value, externalData));
1357+
// Create an external object that will hold the external data pointer.
1358+
JsValueRef external;
1359+
CHECK_JSRT(JsCreateExternalObject(
1360+
externalData, jsrtimpl::ExternalData::Finalize, &external));
1361+
1362+
// Insert the external object into the value's prototype chain.
1363+
JsValueRef valuePrototype;
1364+
CHECK_JSRT(JsGetPrototype(value, &valuePrototype));
1365+
CHECK_JSRT(JsSetPrototype(external, valuePrototype));
1366+
CHECK_JSRT(JsSetPrototype(value, external));
13891367

13901368
if (result != nullptr) {
1391-
napi_create_reference(env, js_object, 0, result);
1369+
CHECK_NAPI(napi_create_reference(env, js_object, 0, result));
13921370
}
13931371

13941372
return napi_ok;
13951373
}
13961374

1397-
napi_status napi_unwrap(napi_env env, napi_value jsObject, void** result) {
1398-
return napi_get_value_external(env, jsObject, result);
1375+
napi_status napi_unwrap(napi_env env, napi_value js_object, void** result) {
1376+
JsValueRef value = reinterpret_cast<JsValueRef>(js_object);
1377+
1378+
// Search the object's prototype chain for the wrapper with external data.
1379+
// Usually the wrapper would be the first in the chain, but it is OK for
1380+
// other objects to be inserted in the prototype chain.
1381+
JsValueRef wrapper = value;
1382+
bool hasExternalData = false;
1383+
do {
1384+
CHECK_JSRT(JsGetPrototype(wrapper, &wrapper));
1385+
if (wrapper == JS_INVALID_REFERENCE) {
1386+
return napi_invalid_arg;
1387+
}
1388+
CHECK_JSRT(JsHasExternalData(wrapper, &hasExternalData));
1389+
} while (!hasExternalData);
1390+
1391+
jsrtimpl::ExternalData* externalData;
1392+
CHECK_JSRT(JsGetExternalData(
1393+
wrapper, reinterpret_cast<void**>(&externalData)));
1394+
1395+
*result = (externalData != nullptr ? externalData->Data() : nullptr);
1396+
1397+
return napi_ok;
13991398
}
14001399

14011400
napi_status napi_create_external(napi_env env,
@@ -1413,7 +1412,6 @@ napi_status napi_create_external(napi_env env,
14131412
externalData,
14141413
jsrtimpl::ExternalData::Finalize,
14151414
reinterpret_cast<JsValueRef*>(result)));
1416-
CHECK_JSRT(JsPreventExtension(*reinterpret_cast<JsValueRef*>(result)));
14171415

14181416
return napi_ok;
14191417
}

0 commit comments

Comments
 (0)