node-api: extend type-tagging to externals
Since externals behave as JavaScript objects on the JavaScript side, allow them to be type-tagged. Signed-off-by: Gabriel Schulhof <gabrielschulhof@gmail.com> PR-URL: https://github.com/nodejs/node/pull/47141 Reviewed-By: Michael Dawson <midawson@redhat.com> Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
This commit is contained in:
parent
22537f34a4
commit
b4365c161c
@ -733,13 +733,13 @@ napiVersion: 8
|
||||
-->
|
||||
|
||||
A 128-bit value stored as two unsigned 64-bit integers. It serves as a UUID
|
||||
with which JavaScript objects can be "tagged" in order to ensure that they are
|
||||
of a certain type. This is a stronger check than [`napi_instanceof`][], because
|
||||
the latter can report a false positive if the object's prototype has been
|
||||
manipulated. Type-tagging is most useful in conjunction with [`napi_wrap`][]
|
||||
because it ensures that the pointer retrieved from a wrapped object can be
|
||||
safely cast to the native type corresponding to the type tag that had been
|
||||
previously applied to the JavaScript object.
|
||||
with which JavaScript objects or [externals][] can be "tagged" in order to
|
||||
ensure that they are of a certain type. This is a stronger check than
|
||||
[`napi_instanceof`][], because the latter can report a false positive if the
|
||||
object's prototype has been manipulated. Type-tagging is most useful in
|
||||
conjunction with [`napi_wrap`][] because it ensures that the pointer retrieved
|
||||
from a wrapped object can be safely cast to the native type corresponding to the
|
||||
type tag that had been previously applied to the JavaScript object.
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
@ -4969,7 +4969,7 @@ To this end, Node-API provides type-tagging capabilities.
|
||||
|
||||
A type tag is a 128-bit integer unique to the addon. Node-API provides the
|
||||
`napi_type_tag` structure for storing a type tag. When such a value is passed
|
||||
along with a JavaScript object stored in a `napi_value` to
|
||||
along with a JavaScript object or [external][] stored in a `napi_value` to
|
||||
`napi_type_tag_object()`, the JavaScript object will be "marked" with the
|
||||
type tag. The "mark" is invisible on the JavaScript side. When a JavaScript
|
||||
object arrives into a native binding, `napi_check_object_type_tag()` can be used
|
||||
@ -5255,15 +5255,15 @@ napi_status napi_type_tag_object(napi_env env,
|
||||
```
|
||||
|
||||
* `[in] env`: The environment that the API is invoked under.
|
||||
* `[in] js_object`: The JavaScript object to be marked.
|
||||
* `[in] js_object`: The JavaScript object or [external][] to be marked.
|
||||
* `[in] type_tag`: The tag with which the object is to be marked.
|
||||
|
||||
Returns `napi_ok` if the API succeeded.
|
||||
|
||||
Associates the value of the `type_tag` pointer with the JavaScript object.
|
||||
`napi_check_object_type_tag()` can then be used to compare the tag that was
|
||||
attached to the object with one owned by the addon to ensure that the object
|
||||
has the right type.
|
||||
Associates the value of the `type_tag` pointer with the JavaScript object or
|
||||
[external][]. `napi_check_object_type_tag()` can then be used to compare the tag
|
||||
that was attached to the object with one owned by the addon to ensure that the
|
||||
object has the right type.
|
||||
|
||||
If the object already has an associated type tag, this API will return
|
||||
`napi_invalid_arg`.
|
||||
@ -5285,7 +5285,8 @@ napi_status napi_check_object_type_tag(napi_env env,
|
||||
```
|
||||
|
||||
* `[in] env`: The environment that the API is invoked under.
|
||||
* `[in] js_object`: The JavaScript object whose type tag to examine.
|
||||
* `[in] js_object`: The JavaScript object or [external][] whose type tag to
|
||||
examine.
|
||||
* `[in] type_tag`: The tag with which to compare any tag found on the object.
|
||||
* `[out] result`: Whether the type tag given matched the type tag on the
|
||||
object. `false` is also returned if no type tag was found on the object.
|
||||
@ -6455,6 +6456,8 @@ the add-on's file name during loading.
|
||||
[async_hooks `type`]: async_hooks.md#type
|
||||
[context-aware addons]: addons.md#context-aware-addons
|
||||
[docs]: https://github.com/nodejs/node-addon-api#api-documentation
|
||||
[external]: #napi_create_external
|
||||
[externals]: #napi_create_external
|
||||
[global scope]: globals.md
|
||||
[gyp-next]: https://github.com/nodejs/gyp-next
|
||||
[module scope]: modules.md#the-module-scope
|
||||
|
@ -165,12 +165,26 @@ assert.strictEqual(newObject.test_string, 'test string');
|
||||
const obj2 = test_object.TypeTaggedInstance(1);
|
||||
const obj3 = test_object.TypeTaggedInstance(2);
|
||||
const obj4 = test_object.TypeTaggedInstance(3);
|
||||
const external = test_object.TypeTaggedExternal(2);
|
||||
const plainExternal = test_object.PlainExternal();
|
||||
|
||||
// Verify that we do not allow type tag indices greater than the largest
|
||||
// available index.
|
||||
assert.throws(() => test_object.TypeTaggedInstance(39), {
|
||||
name: 'RangeError',
|
||||
message: 'Invalid type index',
|
||||
});
|
||||
assert.throws(() => test_object.TypeTaggedExternal(39), {
|
||||
name: 'RangeError',
|
||||
message: 'Invalid type index',
|
||||
});
|
||||
|
||||
// Verify that type tags are correctly accepted.
|
||||
assert.strictEqual(test_object.CheckTypeTag(0, obj1), true);
|
||||
assert.strictEqual(test_object.CheckTypeTag(1, obj2), true);
|
||||
assert.strictEqual(test_object.CheckTypeTag(2, obj3), true);
|
||||
assert.strictEqual(test_object.CheckTypeTag(3, obj4), true);
|
||||
assert.strictEqual(test_object.CheckTypeTag(2, external), true);
|
||||
|
||||
// Verify that wrongly tagged objects are rejected.
|
||||
assert.strictEqual(test_object.CheckTypeTag(0, obj2), false);
|
||||
@ -180,10 +194,19 @@ assert.strictEqual(newObject.test_string, 'test string');
|
||||
assert.strictEqual(test_object.CheckTypeTag(2, obj4), false);
|
||||
assert.strictEqual(test_object.CheckTypeTag(3, obj3), false);
|
||||
assert.strictEqual(test_object.CheckTypeTag(4, obj3), false);
|
||||
assert.strictEqual(test_object.CheckTypeTag(0, external), false);
|
||||
assert.strictEqual(test_object.CheckTypeTag(1, external), false);
|
||||
assert.strictEqual(test_object.CheckTypeTag(3, external), false);
|
||||
assert.strictEqual(test_object.CheckTypeTag(4, external), false);
|
||||
|
||||
// Verify that untagged objects are rejected.
|
||||
assert.strictEqual(test_object.CheckTypeTag(0, {}), false);
|
||||
assert.strictEqual(test_object.CheckTypeTag(1, {}), false);
|
||||
assert.strictEqual(test_object.CheckTypeTag(0, plainExternal), false);
|
||||
assert.strictEqual(test_object.CheckTypeTag(1, plainExternal), false);
|
||||
assert.strictEqual(test_object.CheckTypeTag(2, plainExternal), false);
|
||||
assert.strictEqual(test_object.CheckTypeTag(3, plainExternal), false);
|
||||
assert.strictEqual(test_object.CheckTypeTag(4, plainExternal), false);
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -605,13 +605,23 @@ static napi_value TestSeal(napi_env env,
|
||||
}
|
||||
|
||||
// We create two type tags. They are basically 128-bit UUIDs.
|
||||
static const napi_type_tag type_tags[5] = {
|
||||
{ 0xdaf987b3cc62481a, 0xb745b0497f299531 },
|
||||
{ 0xbb7936c374084d9b, 0xa9548d0762eeedb9 },
|
||||
{ 0xa5ed9ce2e4c00c38, 0 },
|
||||
{ 0, 0 },
|
||||
{ 0xa5ed9ce2e4c00c38, 0xdaf987b3cc62481a },
|
||||
#define TYPE_TAG_COUNT 5
|
||||
static const napi_type_tag type_tags[TYPE_TAG_COUNT] = {
|
||||
{0xdaf987b3cc62481a, 0xb745b0497f299531},
|
||||
{0xbb7936c374084d9b, 0xa9548d0762eeedb9},
|
||||
{0xa5ed9ce2e4c00c38, 0},
|
||||
{0, 0},
|
||||
{0xa5ed9ce2e4c00c38, 0xdaf987b3cc62481a},
|
||||
};
|
||||
#define VALIDATE_TYPE_INDEX(env, type_index) \
|
||||
do { \
|
||||
if ((type_index) >= TYPE_TAG_COUNT) { \
|
||||
NODE_API_CALL((env), \
|
||||
napi_throw_range_error((env), \
|
||||
"NODE_API_TEST_INVALID_TYPE_INDEX", \
|
||||
"Invalid type index")); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static napi_value
|
||||
TypeTaggedInstance(napi_env env, napi_callback_info info) {
|
||||
@ -621,12 +631,42 @@ TypeTaggedInstance(napi_env env, napi_callback_info info) {
|
||||
|
||||
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, &which_type, NULL, NULL));
|
||||
NODE_API_CALL(env, napi_get_value_uint32(env, which_type, &type_index));
|
||||
VALIDATE_TYPE_INDEX(env, type_index);
|
||||
NODE_API_CALL(env, napi_create_object(env, &instance));
|
||||
NODE_API_CALL(env, napi_type_tag_object(env, instance, &type_tags[type_index]));
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
// V8 will not allowe us to construct an external with a NULL data value.
|
||||
#define IN_LIEU_OF_NULL ((void*)0x1)
|
||||
|
||||
static napi_value PlainExternal(napi_env env, napi_callback_info info) {
|
||||
napi_value instance;
|
||||
|
||||
NODE_API_CALL(
|
||||
env, napi_create_external(env, IN_LIEU_OF_NULL, NULL, NULL, &instance));
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
static napi_value TypeTaggedExternal(napi_env env, napi_callback_info info) {
|
||||
size_t argc = 1;
|
||||
uint32_t type_index;
|
||||
napi_value instance, which_type;
|
||||
|
||||
NODE_API_CALL(env,
|
||||
napi_get_cb_info(env, info, &argc, &which_type, NULL, NULL));
|
||||
NODE_API_CALL(env, napi_get_value_uint32(env, which_type, &type_index));
|
||||
VALIDATE_TYPE_INDEX(env, type_index);
|
||||
NODE_API_CALL(
|
||||
env, napi_create_external(env, IN_LIEU_OF_NULL, NULL, NULL, &instance));
|
||||
NODE_API_CALL(env,
|
||||
napi_type_tag_object(env, instance, &type_tags[type_index]));
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
static napi_value
|
||||
CheckTypeTag(napi_env env, napi_callback_info info) {
|
||||
size_t argc = 2;
|
||||
@ -636,6 +676,7 @@ CheckTypeTag(napi_env env, napi_callback_info info) {
|
||||
|
||||
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
|
||||
NODE_API_CALL(env, napi_get_value_uint32(env, argv[0], &type_index));
|
||||
VALIDATE_TYPE_INDEX(env, type_index);
|
||||
NODE_API_CALL(env, napi_check_object_type_tag(env,
|
||||
argv[1],
|
||||
&type_tags[type_index],
|
||||
@ -648,31 +689,36 @@ CheckTypeTag(napi_env env, napi_callback_info info) {
|
||||
EXTERN_C_START
|
||||
napi_value Init(napi_env env, napi_value exports) {
|
||||
napi_property_descriptor descriptors[] = {
|
||||
DECLARE_NODE_API_PROPERTY("Get", Get),
|
||||
DECLARE_NODE_API_PROPERTY("GetNamed", GetNamed),
|
||||
DECLARE_NODE_API_PROPERTY("GetPropertyNames", GetPropertyNames),
|
||||
DECLARE_NODE_API_PROPERTY("GetSymbolNames", GetSymbolNames),
|
||||
DECLARE_NODE_API_PROPERTY("GetEnumerableWritableNames", GetEnumerableWritableNames),
|
||||
DECLARE_NODE_API_PROPERTY("GetOwnWritableNames", GetOwnWritableNames),
|
||||
DECLARE_NODE_API_PROPERTY("GetEnumerableConfigurableNames", GetEnumerableConfigurableNames),
|
||||
DECLARE_NODE_API_PROPERTY("GetOwnConfigurableNames", GetOwnConfigurableNames),
|
||||
DECLARE_NODE_API_PROPERTY("Set", Set),
|
||||
DECLARE_NODE_API_PROPERTY("SetNamed", SetNamed),
|
||||
DECLARE_NODE_API_PROPERTY("Has", Has),
|
||||
DECLARE_NODE_API_PROPERTY("HasNamed", HasNamed),
|
||||
DECLARE_NODE_API_PROPERTY("HasOwn", HasOwn),
|
||||
DECLARE_NODE_API_PROPERTY("Delete", Delete),
|
||||
DECLARE_NODE_API_PROPERTY("New", New),
|
||||
DECLARE_NODE_API_PROPERTY("Inflate", Inflate),
|
||||
DECLARE_NODE_API_PROPERTY("Wrap", Wrap),
|
||||
DECLARE_NODE_API_PROPERTY("Unwrap", Unwrap),
|
||||
DECLARE_NODE_API_PROPERTY("TestSetProperty", TestSetProperty),
|
||||
DECLARE_NODE_API_PROPERTY("TestHasProperty", TestHasProperty),
|
||||
DECLARE_NODE_API_PROPERTY("TypeTaggedInstance", TypeTaggedInstance),
|
||||
DECLARE_NODE_API_PROPERTY("CheckTypeTag", CheckTypeTag),
|
||||
DECLARE_NODE_API_PROPERTY("TestGetProperty", TestGetProperty),
|
||||
DECLARE_NODE_API_PROPERTY("TestFreeze", TestFreeze),
|
||||
DECLARE_NODE_API_PROPERTY("TestSeal", TestSeal),
|
||||
DECLARE_NODE_API_PROPERTY("Get", Get),
|
||||
DECLARE_NODE_API_PROPERTY("GetNamed", GetNamed),
|
||||
DECLARE_NODE_API_PROPERTY("GetPropertyNames", GetPropertyNames),
|
||||
DECLARE_NODE_API_PROPERTY("GetSymbolNames", GetSymbolNames),
|
||||
DECLARE_NODE_API_PROPERTY("GetEnumerableWritableNames",
|
||||
GetEnumerableWritableNames),
|
||||
DECLARE_NODE_API_PROPERTY("GetOwnWritableNames", GetOwnWritableNames),
|
||||
DECLARE_NODE_API_PROPERTY("GetEnumerableConfigurableNames",
|
||||
GetEnumerableConfigurableNames),
|
||||
DECLARE_NODE_API_PROPERTY("GetOwnConfigurableNames",
|
||||
GetOwnConfigurableNames),
|
||||
DECLARE_NODE_API_PROPERTY("Set", Set),
|
||||
DECLARE_NODE_API_PROPERTY("SetNamed", SetNamed),
|
||||
DECLARE_NODE_API_PROPERTY("Has", Has),
|
||||
DECLARE_NODE_API_PROPERTY("HasNamed", HasNamed),
|
||||
DECLARE_NODE_API_PROPERTY("HasOwn", HasOwn),
|
||||
DECLARE_NODE_API_PROPERTY("Delete", Delete),
|
||||
DECLARE_NODE_API_PROPERTY("New", New),
|
||||
DECLARE_NODE_API_PROPERTY("Inflate", Inflate),
|
||||
DECLARE_NODE_API_PROPERTY("Wrap", Wrap),
|
||||
DECLARE_NODE_API_PROPERTY("Unwrap", Unwrap),
|
||||
DECLARE_NODE_API_PROPERTY("TestSetProperty", TestSetProperty),
|
||||
DECLARE_NODE_API_PROPERTY("TestHasProperty", TestHasProperty),
|
||||
DECLARE_NODE_API_PROPERTY("TypeTaggedInstance", TypeTaggedInstance),
|
||||
DECLARE_NODE_API_PROPERTY("TypeTaggedExternal", TypeTaggedExternal),
|
||||
DECLARE_NODE_API_PROPERTY("PlainExternal", PlainExternal),
|
||||
DECLARE_NODE_API_PROPERTY("CheckTypeTag", CheckTypeTag),
|
||||
DECLARE_NODE_API_PROPERTY("TestGetProperty", TestGetProperty),
|
||||
DECLARE_NODE_API_PROPERTY("TestFreeze", TestFreeze),
|
||||
DECLARE_NODE_API_PROPERTY("TestSeal", TestSeal),
|
||||
};
|
||||
|
||||
init_test_null(env, exports);
|
||||
|
Loading…
x
Reference in New Issue
Block a user