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] =?UTF-8?q?lib:=20add=C2=A0`SafeArray`=20to=C2=A0`primordi?= =?UTF-8?q?als`=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 {}