From e75952faed7f5f3491ab509f2ce92d2ee5438f67 Mon Sep 17 00:00:00 2001 From: Kuitos Date: Wed, 15 Nov 2023 16:19:33 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20remove=20object=20spread?= =?UTF-8?q?=20operator=20for=20faster=20performance=20in=20big=20array=20i?= =?UTF-8?q?terator=20(#2812)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sandbox/common.ts | 13 +++++++++---- src/sandbox/proxySandbox.ts | 38 +++++++++++++++++++++++-------------- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/sandbox/common.ts b/src/sandbox/common.ts index ff60bff..221e7f3 100644 --- a/src/sandbox/common.ts +++ b/src/sandbox/common.ts @@ -70,10 +70,15 @@ export function rebindTarget2Fn(target: any, fn: any): any { 'toString', ); - Object.defineProperty(boundValue, 'toString', { - ...originToStringDescriptor, - ...(originToStringDescriptor?.get ? null : { value: () => fn.toString() }), - }); + Object.defineProperty( + boundValue, + 'toString', + Object.assign( + {}, + originToStringDescriptor, + originToStringDescriptor?.get ? null : { value: () => fn.toString() }, + ), + ); } } diff --git a/src/sandbox/proxySandbox.ts b/src/sandbox/proxySandbox.ts index 9b81cc2..f3847b2 100644 --- a/src/sandbox/proxySandbox.ts +++ b/src/sandbox/proxySandbox.ts @@ -24,9 +24,27 @@ function uniq(array: Array) { }, Object.create(null)); } -const cachedGlobalsInBrowser = globalsInBrowser - .concat(process.env.NODE_ENV === 'test' ? ['mockNativeWindowFunction'] : []) - .reduce>((acc, key) => ({ ...acc, [key]: true }), Object.create(null)); +/** + * transform array to object to enable faster element check with in operator + * @param array + */ +function array2TruthyObject(array: string[]): Record { + return array.reduce( + (acc, key) => { + acc[key] = true; + return acc; + }, + // Notes that babel will transpile spread operator to Object.assign({}, ...args), which will keep the prototype of Object in merged object, + // while this result used as Symbol.unscopables, it will make properties in Object.prototype always be escaped from proxy sandbox as unscopables check will look up prototype chain as well, + // such as hasOwnProperty, toString, valueOf, etc. + // so we should use Object.create(null) to create a pure object without prototype chain here. + Object.create(null), + ); +} + +const cachedGlobalsInBrowser = array2TruthyObject( + globalsInBrowser.concat(process.env.NODE_ENV === 'test' ? ['mockNativeWindowFunction'] : []), +); function isNativeGlobalProp(prop: string): boolean { return prop in cachedGlobalsInBrowser; } @@ -70,22 +88,14 @@ export const cachedGlobals = Array.from( ), ); -// transform cachedGlobals to object for faster element check -const cachedGlobalObjects = cachedGlobals.reduce((acc, globalProp) => ({ ...acc, [globalProp]: true }), {}); +const cachedGlobalObjects = array2TruthyObject(cachedGlobals); /* Variables who are impossible to be overwritten need to be escaped from proxy sandbox for performance reasons. But overwritten globals must not be escaped, otherwise they will be leaked to the global scope. see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/unscopables */ -const unscopables = without(cachedGlobals, ...accessingSpiedGlobals.concat(overwrittenGlobals)).reduce( - (acc, key) => ({ ...acc, [key]: true }), - // Notes that babel will transpile spread operator to Object.assign({}, ...args), which will keep the prototype of Object in merged object, - // while this result used as Symbol.unscopables, it will make properties in Object.prototype always be escaped from proxy sandbox as unscopables check will look up prototype chain as well, - // such as hasOwnProperty, toString, valueOf, etc. - // so we should use Object.create(null) to create a pure object without prototype chain here. - Object.create(null), -); +const unscopables = array2TruthyObject(without(cachedGlobals, ...accessingSpiedGlobals.concat(overwrittenGlobals))); const useNativeWindowForBindingsProps = new Map([ ['fetch', true], @@ -93,7 +103,7 @@ const useNativeWindowForBindingsProps = new Map([ ]); function createFakeWindow(globalContext: Window, speedy: boolean) { - // map always has the fastest performance in has check scenario + // map always has the fastest performance in has checked scenario // see https://jsperf.com/array-indexof-vs-set-has/23 const propertiesWithGetter = new Map(); const fakeWindow = {} as FakeWindow;