src: add internal binding for constructing SharedArrayBuffers

PR-URL: https://github.com/nodejs/node/pull/60497
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Shelley Vohr <shelley.vohr@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
This commit is contained in:
Renegade334 2025-10-28 19:33:31 +00:00 committed by Antoine du Hamel
parent 519c537875
commit fdff838ce3
No known key found for this signature in database
GPG Key ID: 20B1A390B168D356
5 changed files with 54 additions and 3 deletions

View File

@ -225,10 +225,10 @@ export default [
message: 'Use `const { ShadowRealm } = globalThis;` instead of the global.',
},
// SharedArrayBuffer is not available in primordials because it can be
// disabled with --no-harmony-sharedarraybuffer CLI flag.
// disabled with --enable-sharedarraybuffer-per-context CLI flag.
{
name: 'SharedArrayBuffer',
message: 'Use `const { SharedArrayBuffer } = globalThis;` instead of the global.',
message: "Use `const { constructSharedArrayBuffer } = require('internal/util');` instead of the global.",
},
{
name: 'TextDecoder',

View File

@ -59,6 +59,7 @@ const {
} = require('internal/errors');
const { signals } = internalBinding('constants').os;
const {
constructSharedArrayBuffer,
guessHandleType: _guessHandleType,
defineLazyProperties,
privateSymbols: {
@ -954,6 +955,7 @@ module.exports = {
assertTypeScript,
assignFunctionName,
cachedResult,
constructSharedArrayBuffer,
convertToValidSignal,
createClassWrapper,
decorateErrorStack,

View File

@ -10,6 +10,7 @@ namespace util {
using v8::ALL_PROPERTIES;
using v8::Array;
using v8::ArrayBuffer;
using v8::ArrayBufferView;
using v8::BigInt;
using v8::Boolean;
@ -34,6 +35,7 @@ using v8::ONLY_WRITABLE;
using v8::Promise;
using v8::PropertyFilter;
using v8::Proxy;
using v8::SharedArrayBuffer;
using v8::SKIP_STRINGS;
using v8::SKIP_SYMBOLS;
using v8::StackFrame;
@ -438,6 +440,30 @@ static void DefineLazyProperties(const FunctionCallbackInfo<Value>& args) {
}
}
void ConstructSharedArrayBuffer(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
int64_t length;
// Note: IntegerValue() clamps its output, so excessively large input values
// will not overflow
if (!args[0]->IntegerValue(env->context()).To(&length)) {
return;
}
if (length < 0 ||
static_cast<uint64_t>(length) > ArrayBuffer::kMaxByteLength) {
env->ThrowRangeError("Invalid array buffer length");
return;
}
Local<SharedArrayBuffer> sab;
if (!SharedArrayBuffer::MaybeNew(env->isolate(), static_cast<size_t>(length))
.ToLocal(&sab)) {
// Note: SharedArrayBuffer::MaybeNew doesn't schedule an exception if it
// fails
env->ThrowRangeError("Array buffer allocation failed");
return;
}
args.GetReturnValue().Set(sab);
}
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(GetPromiseDetails);
registry->Register(GetProxyDetails);
@ -455,6 +481,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(IsInsideNodeModules);
registry->Register(DefineLazyProperties);
registry->Register(DefineLazyPropertiesGetter);
registry->Register(ConstructSharedArrayBuffer);
}
void Initialize(Local<Object> target,
@ -554,9 +581,12 @@ void Initialize(Local<Object> target,
SetMethodNoSideEffect(context, target, "getCallSites", GetCallSites);
SetMethod(context, target, "sleep", Sleep);
SetMethod(context, target, "parseEnv", ParseEnv);
SetMethod(
context, target, "arrayBufferViewHasBuffer", ArrayBufferViewHasBuffer);
SetMethod(context,
target,
"constructSharedArrayBuffer",
ConstructSharedArrayBuffer);
Local<String> should_abort_on_uncaught_toggle =
FIXED_ONE_BYTE_STRING(env->isolate(), "shouldAbortOnUncaughtToggle");

View File

@ -0,0 +1,18 @@
// Flags: --enable-sharedarraybuffer-per-context --expose-internals
'use strict';
require('../common');
const assert = require('assert');
const { isSharedArrayBuffer } = require('util/types');
const { constructSharedArrayBuffer } = require('internal/util');
// We're testing that we can construct a SAB even when the global is not exposed.
assert.strictEqual(typeof SharedArrayBuffer, 'undefined');
for (const length of [undefined, 0, 1, 2 ** 32]) {
assert(isSharedArrayBuffer(constructSharedArrayBuffer(length)));
}
for (const length of [-1, Number.MAX_SAFE_INTEGER + 1, 2 ** 64]) {
assert.throws(() => constructSharedArrayBuffer(length), RangeError);
}

View File

@ -46,6 +46,7 @@ export interface UtilBinding {
parseEnv(content: string): Record<string, string>;
styleText(format: Array<string> | string, text: string): string;
isInsideNodeModules(frameLimit: number, defaultValue: unknown): boolean;
constructSharedArrayBuffer(length?: number): SharedArrayBuffer;
constants: {
kPending: 0;