Repository: nodejs/node
Pull request: node-api: support SharedArrayBuffer in napi_create_dataview

The previous implementation does not support calling `napi_create_dataview` with a `SharedArrayBuffer`, as the check for `value->IsArrayBuffer()` returns `false`, causing the Node-API call to return an `napi_invalid_arg`.

Changed files:
filename: doc/api/n-api.md
patch: @@ -2806,6 +2806,10 @@ exceeds the size of the `ArrayBuffer`, a `RangeError` exception is raised.
 <!-- YAML
 added: v8.3.0
 napiVersion: 1
+changes:
+  - version: REPLACEME
+    pr-url: https://github.com/nodejs/node/pull/60473
+    description: Added support for `SharedArrayBuffer`.
 -->
 
 ```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

filename: src/js_native_api_v8.cc
patch: @@ -3344,21 +3344,30 @@ napi_status NAPI_CDECL napi_create_dataview(napi_env env,
   CHECK_ARG(env, result);
 
   v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(arraybuffer);
-  RETURN_STATUS_IF_FALSE(env, value->IsArrayBuffer(), napi_invalid_arg);
 
-  v8::Local<v8::ArrayBuffer> buffer = value.As<v8::ArrayBuffer>();
-  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<v8::DataView> DataView =
-      v8::DataView::New(buffer, byte_offset, byte_length);
+  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);
+    }
 
-  *result = v8impl::JsValueFromV8LocalValue(DataView);
-  return GET_RETURN_STATUS(env);
+    v8::Local<v8::DataView> 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<v8::ArrayBuffer>());
+  } else if (value->IsSharedArrayBuffer()) {
+    return create_dataview(value.As<v8::SharedArrayBuffer>());
+  } else {
+    return napi_set_last_error(env, napi_invalid_arg);
+  }
 }
 
 napi_status NAPI_CDECL napi_is_dataview(napi_env env,

filename: test/js-native-api/test_dataview/binding.gyp
patch: @@ -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' ]
     }
   ]
 }

filename: test/js-native-api/test_dataview/test.js
patch: @@ -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);
+}

filename: test/js-native-api/test_dataview/test_dataview.c
patch: @@ -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));