From cc7ec889e863433c248bc4b5c8e33f61ccc40f29 Mon Sep 17 00:00:00 2001 From: Gabriel Schulhof Date: Fri, 14 Jun 2019 16:44:18 -0700 Subject: n-api: support type-tagging objects `napi_instanceof()` is insufficient for reliably establishing the data type to which a pointer stored with `napi_wrap()` or `napi_create_external()` inside a JavaScript object points. Thus, we need a way to "mark" an object with a value that, when later retrieved, can unambiguously tell us whether it is safe to cast the pointer stored inside it to a certain structure. Such a check must survive loading/unloading/multiple instances of an addon, so we use UUIDs chosen *a priori*. Fixes: https://github.com/nodejs/node/issues/28164 Co-authored-by: Anna Henningsen PR-URL: https://github.com/nodejs/node/pull/28237 Reviewed-By: Anna Henningsen Reviewed-By: Michael Dawson Reviewed-By: Colin Ihrig Signed-off-by: Gabriel Schulhof --- benchmark/napi/type-tag-check/binding.gyp | 8 +++ benchmark/napi/type-tag-check/index.js | 18 +++++++ benchmark/napi/type-tag/binding.c | 84 +++++++++++++++++++++++++++++ benchmark/napi/type-tag/binding.gyp | 8 +++ benchmark/napi/type-tag/check-object-tag.js | 18 +++++++ benchmark/napi/type-tag/index.js | 18 +++++++ 6 files changed, 154 insertions(+) create mode 100644 benchmark/napi/type-tag-check/binding.gyp create mode 100644 benchmark/napi/type-tag-check/index.js create mode 100644 benchmark/napi/type-tag/binding.c create mode 100644 benchmark/napi/type-tag/binding.gyp create mode 100644 benchmark/napi/type-tag/check-object-tag.js create mode 100644 benchmark/napi/type-tag/index.js (limited to 'benchmark') diff --git a/benchmark/napi/type-tag-check/binding.gyp b/benchmark/napi/type-tag-check/binding.gyp new file mode 100644 index 00000000000..595ab325233 --- /dev/null +++ b/benchmark/napi/type-tag-check/binding.gyp @@ -0,0 +1,8 @@ +{ + 'targets': [ + { + 'target_name': 'binding', + 'sources': [ '../type-tag/binding.c' ] + } + ] +} diff --git a/benchmark/napi/type-tag-check/index.js b/benchmark/napi/type-tag-check/index.js new file mode 100644 index 00000000000..346dfb7812d --- /dev/null +++ b/benchmark/napi/type-tag-check/index.js @@ -0,0 +1,18 @@ +'use strict'; +const common = require('../../common.js'); + +let binding; +try { + binding = require(`./build/${common.buildType}/binding`); +} catch { + console.error(`${__filename}: Binding failed to load`); + process.exit(0); +} + +const bench = common.createBenchmark(main, { + n: [1e5, 1e6, 1e7], +}); + +function main({ n }) { + binding.checkObjectTag(n, bench, bench.start, bench.end); +} diff --git a/benchmark/napi/type-tag/binding.c b/benchmark/napi/type-tag/binding.c new file mode 100644 index 00000000000..7bc8b5d7502 --- /dev/null +++ b/benchmark/napi/type-tag/binding.c @@ -0,0 +1,84 @@ +#include +#define NAPI_EXPERIMENTAL +#include + +#define NAPI_CALL(call) \ + do { \ + napi_status status = call; \ + assert(status == napi_ok && #call " failed"); \ + } while (0); + +#define EXPORT_FUNC(env, exports, name, func) \ + do { \ + napi_value js_func; \ + NAPI_CALL(napi_create_function((env), \ + (name), \ + NAPI_AUTO_LENGTH, \ + (func), \ + NULL, \ + &js_func)); \ + NAPI_CALL(napi_set_named_property((env), \ + (exports), \ + (name), \ + js_func)); \ + } while (0); + +static const napi_type_tag tag = { + 0xe7ecbcd5954842f6, 0x9e75161c9bf27282 +}; + +static napi_value TagObject(napi_env env, napi_callback_info info) { + size_t argc = 4; + napi_value argv[4]; + uint32_t n; + uint32_t index; + napi_handle_scope scope; + + NAPI_CALL(napi_get_cb_info(env, info, &argc, argv, NULL, NULL)); + NAPI_CALL(napi_get_value_uint32(env, argv[0], &n)); + NAPI_CALL(napi_open_handle_scope(env, &scope)); + napi_value objects[n]; + for (index = 0; index < n; index++) { + NAPI_CALL(napi_create_object(env, &objects[index])); + } + + // Time the object tag creation. + NAPI_CALL(napi_call_function(env, argv[1], argv[2], 0, NULL, NULL)); + for (index = 0; index < n; index++) { + NAPI_CALL(napi_type_tag_object(env, objects[index], &tag)); + } + NAPI_CALL(napi_call_function(env, argv[1], argv[3], 1, &argv[0], NULL)); + + NAPI_CALL(napi_close_handle_scope(env, scope)); + return NULL; +} + +static napi_value CheckObjectTag(napi_env env, napi_callback_info info) { + size_t argc = 4; + napi_value argv[4]; + uint32_t n; + uint32_t index; + bool is_of_type; + + NAPI_CALL(napi_get_cb_info(env, info, &argc, argv, NULL, NULL)); + NAPI_CALL(napi_get_value_uint32(env, argv[0], &n)); + napi_value object; + NAPI_CALL(napi_create_object(env, &object)); + NAPI_CALL(napi_type_tag_object(env, object, &tag)); + + // Time the object tag checking. + NAPI_CALL(napi_call_function(env, argv[1], argv[2], 0, NULL, NULL)); + for (index = 0; index < n; index++) { + NAPI_CALL(napi_check_object_type_tag(env, object, &tag, &is_of_type)); + assert(is_of_type && " type mismatch"); + } + NAPI_CALL(napi_call_function(env, argv[1], argv[3], 1, &argv[0], NULL)); + + return NULL; +} + +NAPI_MODULE_INIT() { + EXPORT_FUNC(env, exports, "tagObject", TagObject); + EXPORT_FUNC(env, exports, "checkObjectTag", CheckObjectTag); + return exports; +} diff --git a/benchmark/napi/type-tag/binding.gyp b/benchmark/napi/type-tag/binding.gyp new file mode 100644 index 00000000000..413621ade33 --- /dev/null +++ b/benchmark/napi/type-tag/binding.gyp @@ -0,0 +1,8 @@ +{ + 'targets': [ + { + 'target_name': 'binding', + 'sources': [ 'binding.c' ] + } + ] +} diff --git a/benchmark/napi/type-tag/check-object-tag.js b/benchmark/napi/type-tag/check-object-tag.js new file mode 100644 index 00000000000..346dfb7812d --- /dev/null +++ b/benchmark/napi/type-tag/check-object-tag.js @@ -0,0 +1,18 @@ +'use strict'; +const common = require('../../common.js'); + +let binding; +try { + binding = require(`./build/${common.buildType}/binding`); +} catch { + console.error(`${__filename}: Binding failed to load`); + process.exit(0); +} + +const bench = common.createBenchmark(main, { + n: [1e5, 1e6, 1e7], +}); + +function main({ n }) { + binding.checkObjectTag(n, bench, bench.start, bench.end); +} diff --git a/benchmark/napi/type-tag/index.js b/benchmark/napi/type-tag/index.js new file mode 100644 index 00000000000..3f85b9af8e7 --- /dev/null +++ b/benchmark/napi/type-tag/index.js @@ -0,0 +1,18 @@ +'use strict'; +const common = require('../../common.js'); + +let binding; +try { + binding = require(`./build/${common.buildType}/binding`); +} catch { + console.error(`${__filename}: Binding failed to load`); + process.exit(0); +} + +const bench = common.createBenchmark(main, { + n: [1e3, 1e4, 1e5], +}); + +function main({ n }) { + binding.tagObject(n, bench, bench.start, bench.end); +} -- cgit v1.2.3