Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/eslint.config_partial.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -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',
Expand Down
33 changes: 14 additions & 19 deletions lib/internal/main/worker_thread.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,9 +11,6 @@ const{
ObjectDefineProperty,
PromisePrototypeThen,
RegExpPrototypeExec,
globalThis: {
SharedArrayBuffer,
},
}=primordials;

const{
Expand DownExpand Up@@ -119,23 +116,21 @@ port.on('message', (message) =>{
require('internal/worker').assignEnvironmentData(environmentData);
setupMainThreadPort(mainThreadPort);

if(SharedArrayBuffer!==undefined){
// The counter is only passed to the workers created by the main thread,
// not to workers created by other workers.
letcachedCwd='';
letlastCounter=-1;
constoriginalCwd=process.cwd;

process.cwd=function(){
constcurrentCounter=AtomicsLoad(cwdCounter,0);
if(currentCounter===lastCounter)
returncachedCwd;
lastCounter=currentCounter;
cachedCwd=originalCwd();
// The counter is only passed to the workers created by the main thread,
// not to workers created by other workers.
letcachedCwd='';
letlastCounter=-1;
constoriginalCwd=process.cwd;

process.cwd=function(){
constcurrentCounter=AtomicsLoad(cwdCounter,0);
if(currentCounter===lastCounter)
returncachedCwd;
};
workerIo.sharedCwdCounter=cwdCounter;
}
lastCounter=currentCounter;
cachedCwd=originalCwd();
returncachedCwd;
};
workerIo.sharedCwdCounter=cwdCounter;

constisLoaderHookWorker=(filename==='internal/modules/esm/worker'&&doEval==='internal');
if(!isLoaderHookWorker){
Expand Down
8 changes: 2 additions & 6 deletions lib/internal/modules/esm/hooks.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,13 +15,8 @@ const{
SafeSet,
StringPrototypeSlice,
StringPrototypeToUpperCase,
globalThis,
}=primordials;

const{
SharedArrayBuffer,
}=globalThis;

const{
ERR_ASYNC_LOADER_REQUEST_NEVER_SETTLED,
ERR_INTERNAL_ASSERTION,
Expand All@@ -44,6 +39,7 @@ const{
validateString,
}=require('internal/validators');
const{
constructSharedArrayBuffer,
kEmptyObject,
}=require('internal/util');

Expand DownExpand Up@@ -535,7 +531,7 @@ class AsyncLoaderHookWorker{
const{ InternalWorker }=require('internal/worker');
MessageChannel??=require('internal/worker/io').MessageChannel;

constlock=newSharedArrayBuffer(SHARED_MEMORY_BYTE_LENGTH);
constlock=constructSharedArrayBuffer(SHARED_MEMORY_BYTE_LENGTH);
this.#lock =newInt32Array(lock);

this.#worker =newInternalWorker('internal/modules/esm/worker',{
Expand Down
11 changes: 6 additions & 5 deletions lib/internal/streams/fast-utf8-stream.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,13 +9,14 @@ const{
AtomicsWait,
Int32Array,
MathMax,
Number,
SymbolDispose,
globalThis: {
Number,
SharedArrayBuffer,
},
}=primordials;

const{
constructSharedArrayBuffer,
}=require('internal/util');

const{
Buffer,
}=require('buffer');
Expand DownExpand Up@@ -49,7 +50,7 @@ const{
constBUSY_WRITE_TIMEOUT=100;
constkEmptyBuffer=Buffer.allocUnsafe(0);

constkNil=newInt32Array(newSharedArrayBuffer(4));
constkNil=newInt32Array(constructSharedArrayBuffer(4));

functionsleep(ms){
// Also filters out NaN, non-number types, including empty strings, but allows bigints
Expand Down
2 changes: 2 additions & 0 deletions lib/internal/util.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -59,6 +59,7 @@ const{
}=require('internal/errors');
const{ signals }=internalBinding('constants').os;
const{
constructSharedArrayBuffer,
guessHandleType: _guessHandleType,
defineLazyProperties,
privateSymbols: {
Expand DownExpand Up@@ -954,6 +955,7 @@ module.exports ={
assertTypeScript,
assignFunctionName,
cachedResult,
constructSharedArrayBuffer,
convertToValidSignal,
createClassWrapper,
decorateErrorStack,
Expand Down
11 changes: 6 additions & 5 deletions lib/internal/worker.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,7 +24,6 @@ const{
SymbolFor,
TypedArrayPrototypeFill,
Uint32Array,
globalThis:{SharedArrayBuffer },
} = primordials;

const EventEmitter = require('events');
Expand DownExpand Up@@ -62,7 +61,10 @@ const{
const{createMainThreadPort, destroyMainThreadPort } = require('internal/worker/messaging');
const{deserializeError } = require('internal/error_serdes');
const{fileURLToPath, isURL, pathToFileURL } = require('internal/url');
const{kEmptyObject } = require('internal/util');
const{
constructSharedArrayBuffer,
kEmptyObject,
} = require('internal/util');
const{validateArray, validateString, validateObject, validateNumber } = require('internal/validators');
const{
throwIfBuildingSnapshot,
Expand DownExpand Up@@ -106,9 +108,8 @@ let cwdCounter;

const environmentData = new SafeMap();

// SharedArrayBuffers can be disabled with --enable-sharedarraybuffer-per-context.
if (isMainThread && SharedArrayBuffer !== undefined){
cwdCounter = new Uint32Array(new SharedArrayBuffer(4));
if (isMainThread){
cwdCounter = new Uint32Array(constructSharedArrayBuffer(4));
const originalChdir = process.chdir;
process.chdir = function(path){
AtomicsAdd(cwdCounter, 0, 1);
Expand Down
11 changes: 5 additions & 6 deletions lib/internal/worker/messaging.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,18 +6,17 @@ const{
AtomicsWaitAsync,
Int32Array,
SafeMap,
globalThis,
}=primordials;

const{
SharedArrayBuffer,
}=globalThis;

const{
isMainThread,
threadId: currentThreadId,
}=internalBinding('worker');

const{
constructSharedArrayBuffer,
}=require('internal/util');

const{
codes: {
ERR_WORKER_MESSAGING_ERRORED,
Expand DownExpand Up@@ -203,7 +202,7 @@ async function postMessageToThread(threadId, value, transferList, timeout){
thrownewERR_WORKER_MESSAGING_SAME_THREAD();
}

constmemory=newSharedArrayBuffer(WORKER_MESSAGING_SHARED_DATA);
constmemory=constructSharedArrayBuffer(WORKER_MESSAGING_SHARED_DATA);
conststatus=newInt32Array(memory);
constpromise=AtomicsWaitAsync(status,WORKER_MESSAGING_STATUS_INDEX,0,timeout).value;

Expand Down
32 changes: 31 additions & 1 deletion src/node_util.cc
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand All@@ -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;
Expand DownExpand Up@@ -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);
Expand All@@ -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,
Expand DownExpand Up@@ -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");
Expand Down
18 changes: 18 additions & 0 deletions test/parallel/test-internal-util-construct-sab.js
Original file line numberDiff line numberDiff line change
@@ -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);
}
1 change: 1 addition & 0 deletions typings/internalBinding/util.d.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;
Expand Down
Loading