From 7f1644254a4f11e687e5f2c51082fb10febd6775 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Fri, 4 Dec 2020 12:35:49 +0100 Subject: [PATCH] lib: make safe primordials safe to iterate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/36391 Reviewed-By: Michaƫl Zasso Reviewed-By: James M Snell --- lib/internal/per_context/primordials.js | 45 ++++++++++++++++++- lib/internal/source_map/source_map_cache.js | 11 +---- lib/internal/worker/io.js | 4 +- .../test-worker-terminate-source-map.js | 13 +++--- 4 files changed, 55 insertions(+), 18 deletions(-) diff --git a/lib/internal/per_context/primordials.js b/lib/internal/per_context/primordials.js index 9f61b9e849dc2b..c9482c39cf3a5e 100644 --- a/lib/internal/per_context/primordials.js +++ b/lib/internal/per_context/primordials.js @@ -91,9 +91,52 @@ function copyPrototype(src, dest, prefix) { } } +const createSafeIterator = (factory, next) => { + class SafeIterator { + constructor(iterable) { + this._iterator = factory(iterable); + } + next() { + return next(this._iterator); + } + [Symbol.iterator]() { + return this; + } + } + Object.setPrototypeOf(SafeIterator.prototype, null); + Object.freeze(SafeIterator.prototype); + Object.freeze(SafeIterator); + return SafeIterator; +}; + function makeSafe(unsafe, safe) { - copyProps(unsafe.prototype, safe.prototype); + if (Symbol.iterator in unsafe.prototype) { + const dummy = new unsafe(); + let next; // We can reuse the same `next` method. + + for (const key of Reflect.ownKeys(unsafe.prototype)) { + if (!Reflect.getOwnPropertyDescriptor(safe.prototype, key)) { + const desc = Reflect.getOwnPropertyDescriptor(unsafe.prototype, key); + if ( + typeof desc.value === 'function' && + desc.value.length === 0 && + Symbol.iterator in (desc.value.call(dummy) ?? {}) + ) { + const createIterator = uncurryThis(desc.value); + if (next == null) next = uncurryThis(createIterator(dummy).next); + const SafeIterator = createSafeIterator(createIterator, next); + desc.value = function() { + return new SafeIterator(this); + }; + } + Reflect.defineProperty(safe.prototype, key, desc); + } + } + } else { + copyProps(unsafe.prototype, safe.prototype); + } copyProps(unsafe, safe); + Object.setPrototypeOf(safe.prototype, null); Object.freeze(safe.prototype); Object.freeze(safe); diff --git a/lib/internal/source_map/source_map_cache.js b/lib/internal/source_map/source_map_cache.js index ff259ee4325d1d..b95653ebba84e9 100644 --- a/lib/internal/source_map/source_map_cache.js +++ b/lib/internal/source_map/source_map_cache.js @@ -7,17 +7,12 @@ const { ObjectKeys, ObjectGetOwnPropertyDescriptor, ObjectPrototypeHasOwnProperty, - Map, - MapPrototypeEntries, RegExpPrototypeTest, SafeMap, StringPrototypeMatch, StringPrototypeSplit, - uncurryThis, } = primordials; -const MapIteratorNext = uncurryThis(MapPrototypeEntries(new Map()).next); - function ObjectGetValueSafe(obj, key) { const desc = ObjectGetOwnPropertyDescriptor(obj, key); return ObjectPrototypeHasOwnProperty(desc, 'value') ? desc.value : undefined; @@ -195,11 +190,7 @@ function rekeySourceMap(cjsModuleInstance, newInstance) { function sourceMapCacheToObject() { const obj = ObjectCreate(null); - const it = MapPrototypeEntries(esmSourceMapCache); - let entry; - while (!(entry = MapIteratorNext(it)).done) { - const k = entry.value[0]; - const v = entry.value[1]; + for (const { 0: k, 1: v } of esmSourceMapCache) { obj[k] = v; } diff --git a/lib/internal/worker/io.js b/lib/internal/worker/io.js index 2066cad1c84c05..492a6cc575e27f 100644 --- a/lib/internal/worker/io.js +++ b/lib/internal/worker/io.js @@ -1,6 +1,7 @@ 'use strict'; const { + ArrayPrototypeForEach, ArrayPrototypeMap, ArrayPrototypePush, FunctionPrototypeCall, @@ -259,8 +260,7 @@ class WritableWorkerStdio extends Writable { [kStdioWantsMoreDataCallback]() { const cbs = this[kWritableCallbacks]; this[kWritableCallbacks] = []; - for (const cb of cbs) - cb(); + ArrayPrototypeForEach(cbs, (cb) => cb()); if ((this[kPort][kWaitingStreams] -= cbs.length) === 0) this[kPort].unref(); } diff --git a/test/parallel/test-worker-terminate-source-map.js b/test/parallel/test-worker-terminate-source-map.js index 8cd40a6607422c..c855dab975be01 100644 --- a/test/parallel/test-worker-terminate-source-map.js +++ b/test/parallel/test-worker-terminate-source-map.js @@ -27,16 +27,19 @@ const { callCount } = workerData; function increaseCallCount() { callCount[0]++; } // Increase the call count when a forbidden method is called. -Object.getPrototypeOf((new Map()).entries()).next = increaseCallCount; -Map.prototype.entries = increaseCallCount; -Object.keys = increaseCallCount; -Object.create = increaseCallCount; -Object.hasOwnProperty = increaseCallCount; for (const property of ['_cache', 'lineLengths', 'url']) { Object.defineProperty(Object.prototype, property, { get: increaseCallCount, set: increaseCallCount }); } +Object.getPrototypeOf([][Symbol.iterator]()).next = increaseCallCount; +Object.getPrototypeOf((new Map()).entries()).next = increaseCallCount; +Array.prototype[Symbol.iterator] = increaseCallCount; +Map.prototype[Symbol.iterator] = increaseCallCount; +Map.prototype.entries = increaseCallCount; +Object.keys = increaseCallCount; +Object.create = increaseCallCount; +Object.hasOwnProperty = increaseCallCount; parentPort.postMessage('done');