From 9cbf4c9e43de434014f723f2d6ace63db4fa7e6b Mon Sep 17 00:00:00 2001 From: Kevin Eady <8634912+KevinEady@users.noreply.github.com> Date: Thu, 13 Nov 2025 22:45:17 +0100 Subject: [PATCH] node-api: support SharedArrayBuffer in napi_create_dataview PR-URL: https://github.com/nodejs/node/pull/60473 Reviewed-By: Colin Ihrig Reviewed-By: Chengzhong Wu Reviewed-By: Anna Henningsen --- doc/api/n-api.md | 14 +++++--- src/js_native_api_v8.cc | 35 ++++++++++++------- test/js-native-api/test_dataview/binding.gyp | 5 ++- test/js-native-api/test_dataview/test.js | 24 +++++++++++-- .../test_dataview/test_dataview.c | 15 ++++++-- 5 files changed, 70 insertions(+), 23 deletions(-) diff --git a/doc/api/n-api.md b/doc/api/n-api.md index 039b97e162..b2032f2c08 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -2806,6 +2806,10 @@ exceeds the size of the `ArrayBuffer`, a `RangeError` exception is raised. ```c @@ -2818,16 +2822,18 @@ napi_status napi_create_dataview(napi_env env, * `[in] env`: The environment that the API is invoked under. * `[in] length`: Number of elements in the `DataView`. -* `[in] arraybuffer`: `ArrayBuffer` underlying the `DataView`. +* `[in] arraybuffer`: `ArrayBuffer` or `SharedArrayBuffer` underlying the + `DataView`. * `[in] byte_offset`: The byte offset within the `ArrayBuffer` from which to start projecting the `DataView`. * `[out] result`: A `napi_value` representing a JavaScript `DataView`. Returns `napi_ok` if the API succeeded. -This API creates a JavaScript `DataView` object over an existing `ArrayBuffer`. -`DataView` objects provide an array-like view over an underlying data buffer, -but one which allows items of different size and type in the `ArrayBuffer`. +This API creates a JavaScript `DataView` object over an existing `ArrayBuffer` +or `SharedArrayBuffer`. `DataView` objects provide an array-like view over an +underlying data buffer, but one which allows items of different size and type in +the `ArrayBuffer` or `SharedArrayBuffer`. It is required that `byte_length + byte_offset` is less than or equal to the size in bytes of the array passed in. If not, a `RangeError` exception is diff --git a/src/js_native_api_v8.cc b/src/js_native_api_v8.cc index f79cd1f141..a5e30895da 100644 --- a/src/js_native_api_v8.cc +++ b/src/js_native_api_v8.cc @@ -3344,21 +3344,30 @@ napi_status NAPI_CDECL napi_create_dataview(napi_env env, CHECK_ARG(env, result); v8::Local value = v8impl::V8LocalValueFromJsValue(arraybuffer); - RETURN_STATUS_IF_FALSE(env, value->IsArrayBuffer(), napi_invalid_arg); - v8::Local buffer = value.As(); - if (byte_length + byte_offset > buffer->ByteLength()) { - napi_throw_range_error(env, - "ERR_NAPI_INVALID_DATAVIEW_ARGS", - "byte_offset + byte_length should be less than or " - "equal to the size in bytes of the array passed in"); - return napi_set_last_error(env, napi_pending_exception); + auto create_dataview = [&](auto buffer) -> napi_status { + if (byte_length + byte_offset > buffer->ByteLength()) { + napi_throw_range_error( + env, + "ERR_NAPI_INVALID_DATAVIEW_ARGS", + "byte_offset + byte_length should be less than or " + "equal to the size in bytes of the array passed in"); + return napi_set_last_error(env, napi_pending_exception); + } + + v8::Local data_view = + v8::DataView::New(buffer, byte_offset, byte_length); + *result = v8impl::JsValueFromV8LocalValue(data_view); + return GET_RETURN_STATUS(env); + }; + + if (value->IsArrayBuffer()) { + return create_dataview(value.As()); + } else if (value->IsSharedArrayBuffer()) { + return create_dataview(value.As()); + } else { + return napi_set_last_error(env, napi_invalid_arg); } - v8::Local DataView = - v8::DataView::New(buffer, byte_offset, byte_length); - - *result = v8impl::JsValueFromV8LocalValue(DataView); - return GET_RETURN_STATUS(env); } napi_status NAPI_CDECL napi_is_dataview(napi_env env, diff --git a/test/js-native-api/test_dataview/binding.gyp b/test/js-native-api/test_dataview/binding.gyp index 6423539081..c6d8a22c03 100644 --- a/test/js-native-api/test_dataview/binding.gyp +++ b/test/js-native-api/test_dataview/binding.gyp @@ -4,7 +4,10 @@ "target_name": "test_dataview", "sources": [ "test_dataview.c" - ] + ], + + # For node_api_is_sharedarraybuffer + 'defines': [ 'NAPI_EXPERIMENTAL', 'NODE_API_EXPERIMENTAL_NO_WARNING' ] } ] } diff --git a/test/js-native-api/test_dataview/test.js b/test/js-native-api/test_dataview/test.js index a6be584940..218d9446a6 100644 --- a/test/js-native-api/test_dataview/test.js +++ b/test/js-native-api/test_dataview/test.js @@ -5,7 +5,7 @@ const assert = require('assert'); // Testing api calls for arrays const test_dataview = require(`./build/${common.buildType}/test_dataview`); -// Test for creating dataview +// Test for creating dataview with ArrayBuffer { const buffer = new ArrayBuffer(128); const template = Reflect.construct(DataView, [buffer]); @@ -15,10 +15,30 @@ const test_dataview = require(`./build/${common.buildType}/test_dataview`); `Expect ${theDataview} to be a DataView`); } -// Test for creating dataview with invalid range +// Test for creating dataview with SharedArrayBuffer +{ + const buffer = new SharedArrayBuffer(128); + const template = new DataView(buffer); + + const theDataview = test_dataview.CreateDataViewFromJSDataView(template); + assert.ok(theDataview instanceof DataView, + `Expect ${theDataview} to be a DataView`); + + assert.strictEqual(template.buffer, theDataview.buffer); +} + +// Test for creating dataview with ArrayBuffer and invalid range { const buffer = new ArrayBuffer(128); assert.throws(() => { test_dataview.CreateDataView(buffer, 10, 200); }, RangeError); } + +// Test for creating dataview with SharedArrayBuffer and invalid range +{ + const buffer = new SharedArrayBuffer(128); + assert.throws(() => { + test_dataview.CreateDataView(buffer, 10, 200); + }, RangeError); +} diff --git a/test/js-native-api/test_dataview/test_dataview.c b/test/js-native-api/test_dataview/test_dataview.c index 9f62b734c6..ec564e6e28 100644 --- a/test/js-native-api/test_dataview/test_dataview.c +++ b/test/js-native-api/test_dataview/test_dataview.c @@ -20,9 +20,18 @@ static napi_value CreateDataView(napi_env env, napi_callback_info info) { bool is_arraybuffer; NODE_API_CALL(env, napi_is_arraybuffer(env, arraybuffer, &is_arraybuffer)); - NODE_API_ASSERT(env, is_arraybuffer, - "Wrong type of arguments. Expects a ArrayBuffer as the first " - "argument."); + + if (!is_arraybuffer) { + bool is_sharedarraybuffer; + NODE_API_CALL( + env, + node_api_is_sharedarraybuffer(env, arraybuffer, &is_sharedarraybuffer)); + NODE_API_ASSERT(env, + is_sharedarraybuffer, + "Wrong type of arguments. Expects a SharedArrayBuffer or " + "ArrayBuffer as the first " + "argument."); + } napi_valuetype valuetype1; NODE_API_CALL(env, napi_typeof(env, args[1], &valuetype1));