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 ad5707954738ae..392e31395c0926 100644 --- a/lib/internal/per_context/primordials.js +++ b/lib/internal/per_context/primordials.js @@ -109,7 +109,11 @@ const createSafeIterator = (factory, next) => { return SafeIterator; }; -function makeSafe(unsafe, safe) { +function makeSafe(unsafe, safe, { + iteratorMethods = null, + constructorProperties = null, + prototypeProperties = null, +} = {}) { if (Symbol.iterator in unsafe.prototype) { const dummy = new unsafe(); let next; // We can reuse the same `next` method. @@ -117,11 +121,11 @@ function makeSafe(unsafe, safe) { for (const key of Reflect.ownKeys(unsafe.prototype)) { if (!Reflect.getOwnPropertyDescriptor(safe.prototype, key)) { const desc = Reflect.getOwnPropertyDescriptor(unsafe.prototype, key); - if ( + if (iteratorMethods?.includes(key) ?? ( typeof desc.value === 'function' && desc.value.length === 0 && Symbol.iterator in (desc.value.call(dummy) ?? {}) - ) { + )) { const createIterator = uncurryThis(desc.value); next ??= uncurryThis(createIterator(dummy).next); const SafeIterator = createSafeIterator(createIterator, next); @@ -135,7 +139,14 @@ function makeSafe(unsafe, safe) { } else { copyProps(unsafe.prototype, safe.prototype); } + if (prototypeProperties !== null) { + Object.defineProperties(safe, prototypeProperties); + } + copyProps(unsafe, safe); + if (constructorProperties !== null) { + Object.defineProperties(safe, constructorProperties); + } Object.setPrototypeOf(safe.prototype, null); Object.freeze(safe.prototype); @@ -146,6 +157,29 @@ primordials.makeSafe = makeSafe; // Subclass the constructors because we need to use their prototype // methods later. +primordials.SafeArray = makeSafe( + Array, + class SafeArray extends Array { + // 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); + } + }, + { + // 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', Symbol.iterator], + // `Array` doesn't have a `Symbol.toStringTag` by default + prototypeProperties: { + [Symbol.toStringTag]: { value: 'SafeArray' } + } + } +); primordials.SafeMap = makeSafe( Map, class SafeMap extends Map {}