diff --git a/lib/internal/freelist.js b/lib/internal/freelist.js index ac2b12c4d45a54..01b423648148e2 100644 --- a/lib/internal/freelist.js +++ b/lib/internal/freelist.js @@ -2,6 +2,7 @@ const { ReflectApply, + SafeArray, } = primordials; class FreeList { @@ -9,7 +10,7 @@ class FreeList { this.name = name; this.ctor = ctor; this.max = max; - this.list = []; + this.list = new SafeArray(); } alloc() { diff --git a/lib/internal/per_context/primordials.js b/lib/internal/per_context/primordials.js index 49576c2d949764..dc9b5f4d6d1ad9 100644 --- a/lib/internal/per_context/primordials.js +++ b/lib/internal/per_context/primordials.js @@ -313,6 +313,7 @@ function copyPrototype(src, dest, prefix) { const { Array: ArrayConstructor, ArrayPrototypeForEach, + ArrayPrototypeIncludes, ArrayPrototypeMap, ArrayPrototypePushApply, ArrayPrototypeSlice, @@ -350,6 +351,7 @@ const { SymbolSearch, SymbolSpecies, SymbolSplit, + SymbolToStringTag, WeakMap, WeakRef, WeakSet, @@ -410,17 +412,21 @@ const copyProps = (src, dest) => { /** * @type {typeof primordials.makeSafe} */ -const makeSafe = (unsafe, safe, next) => { +const makeSafe = (unsafe, safe, next, { + iteratorMethods = null, + constructorProperties = null, + prototypeProperties = null, +} = { __proto__: null }) => { if (next) { const dummy = new unsafe(); ArrayPrototypeForEach(ReflectOwnKeys(unsafe.prototype), (key) => { if (!ReflectGetOwnPropertyDescriptor(safe.prototype, key)) { const desc = ReflectGetOwnPropertyDescriptor(unsafe.prototype, key); - if ( + if (iteratorMethods !== null ? ArrayPrototypeIncludes(iteratorMethods, key) : ( typeof desc.value === 'function' && desc.value.length === 0 && FunctionPrototypeCall(desc.value, dummy)?.next === next - ) { + )) { const createIterator = uncurryThis(desc.value); const SafeIterator = createSafeIterator(createIterator, next); desc.value = function() { @@ -433,7 +439,14 @@ const makeSafe = (unsafe, safe, next) => { } else { copyProps(unsafe.prototype, safe.prototype); } + if (prototypeProperties !== null) { + ObjectDefineProperties(safe, prototypeProperties); + } + copyProps(unsafe, safe); + if (constructorProperties !== null) { + ObjectDefineProperties(safe, constructorProperties); + } ObjectSetPrototypeOf(safe.prototype, null); ObjectFreeze(safe.prototype); @@ -444,6 +457,32 @@ primordials.makeSafe = makeSafe; // Subclass the constructors because we need to use their prototype // methods later. +primordials.SafeArray = makeSafe( + ArrayConstructor, + class SafeArray extends ArrayConstructor { + // This ensures that only the length taking overload is supported, + // all other uses should use `SafeArray.from` or `SafeArray.of`. + // This is necessary to support `ArraySpeciesCreate`, which invokes + // the constructor with argument `length`: + // https://tc39.es/ecma262/#sec-arrayspeciescreate + constructor(length = 0) { + super(+length); + } + }, + primordials.ArrayIteratorPrototypeNext, + { + __proto__: null, + // Many of the array methods have a length of 0 and return + // a primitive or an iterable that isn't an iterator, which breaks + // the assumptions about which methods return iterators in `makeSafe` + iteratorMethods: ['entries', 'keys', 'values', SymbolIterator], + // `Array` doesn't have a `Symbol.toStringTag` by default + prototypeProperties: { + __proto__: null, + [SymbolToStringTag]: { __proto__: null, value: 'SafeArray' }, + }, + }, +); primordials.SafeMap = makeSafe( Map, class SafeMap extends Map {}, diff --git a/typings/primordials.d.ts b/typings/primordials.d.ts index 7a424c7295028d..8a01c8526d75e2 100644 --- a/typings/primordials.d.ts +++ b/typings/primordials.d.ts @@ -40,7 +40,11 @@ type TypedArrayContentType = InstanceType[nu */ declare namespace primordials { export function uncurryThis unknown>(fn: T): UncurryThis; - export function makeSafe(unsafe: NewableFunction, safe: T, next?: Function): T; + export function makeSafe(unsafe: NewableFunction, safe: T, next?: Function, options?: { + readonly iteratorMethods?: ReadonlyArray | null; + readonly constructorProperties?: (PropertyDescriptorMap & ThisType) | null; + readonly prototypeProperties?: (PropertyDescriptorMap & ThisType) | null; + }): T; export import decodeURI = globalThis.decodeURI; export import decodeURIComponent = globalThis.decodeURIComponent;