Skip to content

Commit

Permalink
optimize(sandbox): remove object spread operator for faster performan…
Browse files Browse the repository at this point in the history
…ce in big array iterator
  • Loading branch information
kuitos committed Nov 15, 2023
1 parent 0214bd2 commit 3f7e7f0
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 16 deletions.
8 changes: 4 additions & 4 deletions packages/sandbox/src/core/membrane/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
import { nativeGlobal } from '../../consts';
import { isPropertyFrozen } from '../../utils';
import { globalsInBrowser } from '../globals';
import { array2TruthyObject } from '../utils';
import { rebindTarget2Fn } from './utils';

declare global {
Expand Down Expand Up @@ -59,9 +60,9 @@ const isPropertyDescriptor = (v: unknown): boolean => {
);
};

const cachedGlobalsInBrowser = globalsInBrowser
.concat(process.env.NODE_ENV === 'test' ? ['mockNativeWindowFunction'] : [])
.reduce<Record<string, true>>((acc, key) => ({ ...acc, [key]: true }), Object.create(null) as Record<string, true>);
const cachedGlobalsInBrowser = array2TruthyObject(
globalsInBrowser.concat(process.env.NODE_ENV === 'test' ? ['mockNativeWindowFunction'] : []),
);
const isNativeGlobalProp = (prop: string): boolean => {
return prop in cachedGlobalsInBrowser;
};
Expand Down Expand Up @@ -174,7 +175,6 @@ export class Membrane {
// trap in operator
// see https://github.com/styled-components/styled-components/blob/master/packages/styled-components/src/constants.js#L12
has(membraneTarget: MembraneTarget, p: string | number | symbol): boolean {
// property in cachedGlobalObjects must return true to avoid escape from get trap
return p in membraneTarget || p in incubatorContext;
},

Expand Down
13 changes: 9 additions & 4 deletions packages/sandbox/src/core/membrane/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,15 @@ export function rebindTarget2Fn<T>(target: unknown, fn: T, receiver: unknown): T
'toString',
);

Object.defineProperty(boundValue, 'toString', {
...originToStringDescriptor,
...(originToStringDescriptor?.get ? null : { value: () => typedValue.toString() }),
});
Object.defineProperty(
boundValue,
'toString',
Object.assign(
{},
originToStringDescriptor,
originToStringDescriptor?.get ? null : { value: () => typedValue.toString() },
),
);
}
}

Expand Down
10 changes: 2 additions & 8 deletions packages/sandbox/src/core/sandbox/StandardSandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Compartment } from '../compartment';
import { globalsInES2015 } from '../globals';
import type { Endowments } from '../membrane';
import { Membrane } from '../membrane';
import { array2TruthyObject } from '../utils';
import type { Sandbox } from './types';
import { SandboxType } from './types';

Expand Down Expand Up @@ -77,14 +78,7 @@ export class StandardSandbox extends Compartment implements Sandbox {

const constantNames = Array.from(new Set(Object.keys(intrinsics).concat(globalsInES2015).concat(whitelistBOMAPIs)));
// intrinsics should not be escaped from sandbox
const unscopables = without(constantNames, ...Object.keys(intrinsics)).reduce(
(acc, key) => ({ ...acc, [key]: true }) as Record<string, 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) as Record<string, true>,
);
const unscopables = array2TruthyObject(without(constantNames, ...Object.keys(intrinsics)));
const membrane = new Membrane(incubatorContext, unscopables, {
whitelist: [],
endowments: { ...intrinsics, ...globals },
Expand Down
21 changes: 21 additions & 0 deletions packages/sandbox/src/core/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* @author Kuitos
* @since 2023-11-15
*/
/**
* transform the array to a truthy object for better performance with in operator check later
* @param array
*/
export function array2TruthyObject(array: string[]): Record<string, true> {
return array.reduce(
(obj, key) => {
obj[key] = true;
return obj;
},
// 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) as Record<string, true>,
);
}

0 comments on commit 3f7e7f0

Please sign in to comment.