From 283189fcce72f98f55d335292db0b3b77a252279 Mon Sep 17 00:00:00 2001 From: ExE Boss <3889017+ExE-Boss@users.noreply.github.com> Date: Tue, 22 Dec 2020 13:45:00 +0100 Subject: [PATCH 1/5] =?UTF-8?q?lib:=20add=C2=A0`SafeArray`=20to=C2=A0`prim?= =?UTF-8?q?ordials`=20and=C2=A0use=C2=A0it=20for=C2=A0`FreeList`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs: https://github.com/nodejs/node/pull/36304#issuecomment-748328076 Refs: https://github.com/nodejs/node/pull/36565 --- lib/internal/freelist.js | 3 +- lib/internal/per_context/primordials.js | 40 +++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 4 deletions(-) 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 {} From fddead672b8a6936bc44d5e30a9ed43a08e5e11a Mon Sep 17 00:00:00 2001 From: ExE Boss Date: Tue, 28 Jul 2026 08:45:00 +0200 Subject: [PATCH 2/5] =?UTF-8?q?fixup!=20lib:=20add=C2=A0`SafeArray`=20to?= =?UTF-8?q?=C2=A0`primordials`=20and=C2=A0use=C2=A0it=20for=C2=A0`FreeList?= =?UTF-8?q?`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/internal/per_context/primordials.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/internal/per_context/primordials.js b/lib/internal/per_context/primordials.js index 77fb1d9e2b3c36..4f3451f235866c 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, @@ -420,7 +421,7 @@ const makeSafe = (unsafe, safe, next, { ArrayPrototypeForEach(ReflectOwnKeys(unsafe.prototype), (key) => { if (!ReflectGetOwnPropertyDescriptor(safe.prototype, key)) { const desc = ReflectGetOwnPropertyDescriptor(unsafe.prototype, key); - if (iteratorMethods?.includes(key) ?? ( + if (iteratorMethods !== null ? ArrayPrototypeIncludes(iteratorMethods, key) : ( typeof desc.value === 'function' && desc.value.length === 0 && FunctionPrototypeCall(desc.value, dummy)?.next === next From 78fb7b4ebd7afe77077296259e72bd69c1e9e26f Mon Sep 17 00:00:00 2001 From: ExE Boss Date: Tue, 28 Jul 2026 09:00:00 +0200 Subject: [PATCH 3/5] =?UTF-8?q?fixup!=20lib:=20add=C2=A0`SafeArray`=20to?= =?UTF-8?q?=C2=A0`primordials`=20and=C2=A0use=C2=A0it=20for=C2=A0`FreeList?= =?UTF-8?q?`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/internal/per_context/primordials.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/internal/per_context/primordials.js b/lib/internal/per_context/primordials.js index 4f3451f235866c..3c551c4e8f8aee 100644 --- a/lib/internal/per_context/primordials.js +++ b/lib/internal/per_context/primordials.js @@ -351,6 +351,7 @@ const { SymbolSearch, SymbolSpecies, SymbolSplit, + SymbolToStringTag, WeakMap, WeakRef, WeakSet, @@ -439,12 +440,12 @@ const makeSafe = (unsafe, safe, next, { copyProps(unsafe.prototype, safe.prototype); } if (prototypeProperties !== null) { - Object.defineProperties(safe, prototypeProperties); + ObjectDefineProperties(safe, prototypeProperties); } copyProps(unsafe, safe); if (constructorProperties !== null) { - Object.defineProperties(safe, constructorProperties); + ObjectDefineProperties(safe, constructorProperties); } ObjectSetPrototypeOf(safe.prototype, null); @@ -457,8 +458,8 @@ primordials.makeSafe = makeSafe; // Subclass the constructors because we need to use their prototype // methods later. primordials.SafeArray = makeSafe( - Array, - class SafeArray extends Array { + 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 @@ -473,10 +474,10 @@ primordials.SafeArray = makeSafe( // 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], + iteratorMethods: ['entries', 'keys', 'values', SymbolIterator], // `Array` doesn't have a `Symbol.toStringTag` by default prototypeProperties: { - [Symbol.toStringTag]: { value: 'SafeArray' } + [SymbolToStringTag]: { value: 'SafeArray' } } } ); From fdbb1ce79b35e19436f6ef3d42a21de3b09902de Mon Sep 17 00:00:00 2001 From: ExE Boss Date: Tue, 28 Jul 2026 09:00:00 +0200 Subject: [PATCH 4/5] =?UTF-8?q?amend!=20lib:=20add=C2=A0`SafeArray`=20to?= =?UTF-8?q?=C2=A0`primordials`=20and=C2=A0use=C2=A0it=20for=C2=A0`FreeList?= =?UTF-8?q?`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit lib: add `SafeArray` to `primordials` and use it for `FreeList` Refs: https://github.com/nodejs/node/pull/36304#issuecomment-748328076 Refs: https://github.com/nodejs/node/pull/36565 Signed-off-by: ExE Boss From ff89dbebd3facc958c6c4763defcaa69415fb583 Mon Sep 17 00:00:00 2001 From: ExE Boss Date: Tue, 28 Jul 2026 09:10:00 +0200 Subject: [PATCH 5/5] =?UTF-8?q?fixup!=20lib:=20add=C2=A0`SafeArray`=20to?= =?UTF-8?q?=C2=A0`primordials`=20and=C2=A0use=C2=A0it=20for=C2=A0`FreeList?= =?UTF-8?q?`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/internal/per_context/primordials.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/internal/per_context/primordials.js b/lib/internal/per_context/primordials.js index 3c551c4e8f8aee..dc9b5f4d6d1ad9 100644 --- a/lib/internal/per_context/primordials.js +++ b/lib/internal/per_context/primordials.js @@ -471,15 +471,17 @@ primordials.SafeArray = makeSafe( }, 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: { - [SymbolToStringTag]: { value: 'SafeArray' } - } - } + __proto__: null, + [SymbolToStringTag]: { __proto__: null, value: 'SafeArray' }, + }, + }, ); primordials.SafeMap = makeSafe( Map,