Skip to content

Commit 7d2546e

Browse files
committed
src: add internal binding for constructing SharedArrayBuffers
1 parent bdf03bf commit 7d2546e

File tree

5 files changed

+54
-3
lines changed

5 files changed

+54
-3
lines changed

lib/eslint.config_partial.mjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,10 +225,10 @@ export default [
225225
message: 'Use `const { ShadowRealm } = globalThis;` instead of the global.',
226226
},
227227
// SharedArrayBuffer is not available in primordials because it can be
228-
// disabled with --no-harmony-sharedarraybuffer CLI flag.
228+
// disabled with --enable-sharedarraybuffer-per-context CLI flag.
229229
{
230230
name: 'SharedArrayBuffer',
231-
message: 'Use `const { SharedArrayBuffer } = globalThis;` instead of the global.',
231+
message: "Use `const { constructSharedArrayBuffer } = require('internal/util');` instead of the global.",
232232
},
233233
{
234234
name: 'TextDecoder',

lib/internal/util.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ const {
5959
} = require('internal/errors');
6060
const { signals } = internalBinding('constants').os;
6161
const {
62+
constructSharedArrayBuffer,
6263
guessHandleType: _guessHandleType,
6364
defineLazyProperties,
6465
privateSymbols: {
@@ -954,6 +955,7 @@ module.exports = {
954955
assertTypeScript,
955956
assignFunctionName,
956957
cachedResult,
958+
constructSharedArrayBuffer,
957959
convertToValidSignal,
958960
createClassWrapper,
959961
decorateErrorStack,

src/node_util.cc

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ namespace util {
1010

1111
using v8::ALL_PROPERTIES;
1212
using v8::Array;
13+
using v8::ArrayBuffer;
1314
using v8::ArrayBufferView;
1415
using v8::BigInt;
1516
using v8::Boolean;
@@ -34,6 +35,7 @@ using v8::ONLY_WRITABLE;
3435
using v8::Promise;
3536
using v8::PropertyFilter;
3637
using v8::Proxy;
38+
using v8::SharedArrayBuffer;
3739
using v8::SKIP_STRINGS;
3840
using v8::SKIP_SYMBOLS;
3941
using v8::StackFrame;
@@ -438,6 +440,30 @@ static void DefineLazyProperties(const FunctionCallbackInfo<Value>& args) {
438440
}
439441
}
440442

443+
void ConstructSharedArrayBuffer(const FunctionCallbackInfo<Value>& args) {
444+
Environment* env = Environment::GetCurrent(args);
445+
int64_t length;
446+
// Note: IntegerValue() clamps its output, so excessively large input values
447+
// will not overflow
448+
if (!args[0]->IntegerValue(env->context()).To(&length)) {
449+
return;
450+
}
451+
if (length < 0 ||
452+
static_cast<uint64_t>(length) > ArrayBuffer::kMaxByteLength) {
453+
env->ThrowRangeError("Invalid array buffer length");
454+
return;
455+
}
456+
Local<SharedArrayBuffer> sab;
457+
if (!SharedArrayBuffer::MaybeNew(env->isolate(), static_cast<size_t>(length))
458+
.ToLocal(&sab)) {
459+
// Note: SharedArrayBuffer::MaybeNew doesn't schedule an exception if it
460+
// fails
461+
env->ThrowRangeError("Array buffer allocation failed");
462+
return;
463+
}
464+
args.GetReturnValue().Set(sab);
465+
}
466+
441467
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
442468
registry->Register(GetPromiseDetails);
443469
registry->Register(GetProxyDetails);
@@ -455,6 +481,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
455481
registry->Register(IsInsideNodeModules);
456482
registry->Register(DefineLazyProperties);
457483
registry->Register(DefineLazyPropertiesGetter);
484+
registry->Register(ConstructSharedArrayBuffer);
458485
}
459486

460487
void Initialize(Local<Object> target,
@@ -554,9 +581,12 @@ void Initialize(Local<Object> target,
554581
SetMethodNoSideEffect(context, target, "getCallSites", GetCallSites);
555582
SetMethod(context, target, "sleep", Sleep);
556583
SetMethod(context, target, "parseEnv", ParseEnv);
557-
558584
SetMethod(
559585
context, target, "arrayBufferViewHasBuffer", ArrayBufferViewHasBuffer);
586+
SetMethod(context,
587+
target,
588+
"constructSharedArrayBuffer",
589+
ConstructSharedArrayBuffer);
560590

561591
Local<String> should_abort_on_uncaught_toggle =
562592
FIXED_ONE_BYTE_STRING(env->isolate(), "shouldAbortOnUncaughtToggle");
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Flags: --enable-sharedarraybuffer-per-context --expose-internals
2+
'use strict';
3+
4+
require('../common');
5+
const assert = require('assert');
6+
const { isSharedArrayBuffer } = require('util/types');
7+
const { constructSharedArrayBuffer } = require('internal/util');
8+
9+
// We're testing that we can construct a SAB even when the global is not exposed.
10+
assert.strictEqual(typeof SharedArrayBuffer, 'undefined');
11+
12+
for (const length of [undefined, 0, 1, 2 ** 32]) {
13+
assert(isSharedArrayBuffer(constructSharedArrayBuffer(length)));
14+
}
15+
16+
for (const length of [-1, Number.MAX_SAFE_INTEGER + 1, 2 ** 64]) {
17+
assert.throws(() => constructSharedArrayBuffer(length), RangeError);
18+
}

typings/internalBinding/util.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export interface UtilBinding {
4646
parseEnv(content: string): Record<string, string>;
4747
styleText(format: Array<string> | string, text: string): string;
4848
isInsideNodeModules(frameLimit: number, defaultValue: unknown): boolean;
49+
constructSharedArrayBuffer(length?: number): SharedArrayBuffer;
4950

5051
constants: {
5152
kPending: 0;

0 commit comments

Comments
 (0)