From 05760b7068054dc187109538e7efd6e11278e42d Mon Sep 17 00:00:00 2001 From: dejang Date: Mon, 29 Jan 2024 14:26:43 -0500 Subject: [PATCH] feat: optional TypeArrays remapping in realm --- .../src/__tests__/intrinsics.spec.ts | 69 ++++--- packages/near-membrane-base/src/intrinsics.ts | 168 ++++++++++-------- .../near-membrane-dom/src/browser-realm.ts | 11 +- packages/near-membrane-dom/src/types.ts | 1 + packages/near-membrane-node/src/node-realm.ts | 9 +- packages/near-membrane-node/src/types.ts | 1 + 6 files changed, 152 insertions(+), 107 deletions(-) diff --git a/packages/near-membrane-base/src/__tests__/intrinsics.spec.ts b/packages/near-membrane-base/src/__tests__/intrinsics.spec.ts index 0bde739a..53f1777a 100644 --- a/packages/near-membrane-base/src/__tests__/intrinsics.spec.ts +++ b/packages/near-membrane-base/src/__tests__/intrinsics.spec.ts @@ -29,22 +29,13 @@ const ESGlobalKeys = [ // *** 19.3 Constructor Properties of the Global Object // 'AggregateError', // Reflective // 'Array', // Reflective - // 'ArrayBuffer', // Remapped 'BigInt', - // 'BigInt64Array', // Remapped - // 'BigUint64Array', // Remapped 'Boolean', - // 'DataView', // Remapped // 'Date', // Remapped // 'Error', // Reflective // 'EvalError', // Reflective 'FinalizationRegistry', - // 'Float32Array', // Remapped - // 'Float64Array', // Remapped // 'Function', // dangerous & Reflective - // 'Int8Array', // Remapped - // 'Int16Array', // Remapped - // 'Int32Array', // Remapped // 'Map', // Remapped 'Number', // 'Object', // Reflective @@ -56,15 +47,10 @@ const ESGlobalKeys = [ // 'ReferenceError', // Reflective 'RegExp', // 'Set', // Remapped - // 'SharedArrayBuffer', // Remapped 'String', 'Symbol', // 'SyntaxError', // Reflective // 'TypeError', // Reflective - // 'Uint8Array', // Remapped - // 'Uint8ClampedArray', // Remapped - // 'Uint16Array', // Remapped - // 'Uint32Array', // Remapped // 'URIError', // Reflective // 'WeakMap', // Remapped // 'WeakSet', // Remapped @@ -102,28 +88,31 @@ const ReflectiveIntrinsicObjectNames = [ ]; const RemappedIntrinsicObjectNames = [ - 'ArrayBuffer', 'Atomics', + 'Date', + 'Intl', + 'Map', + 'Promise', + 'Set', + 'WeakMap', + 'WeakSet', +]; + +const TypedAraysInstrinsics = [ + 'ArrayBuffer', 'BigInt64Array', 'BigUint64Array', 'DataView', - 'Date', 'Float32Array', 'Float64Array', 'Int16Array', 'Int32Array', 'Int8Array', - 'Intl', - 'Map', - 'Promise', - 'Set', 'SharedArrayBuffer', 'Uint16Array', 'Uint32Array', 'Uint8Array', 'Uint8ClampedArray', - 'WeakMap', - 'WeakSet', ]; describe('assignFilteredGlobalDescriptorsFromPropertyDescriptorMap', () => { @@ -158,9 +147,10 @@ describe('assignFilteredGlobalDescriptorsFromPropertyDescriptorMap', () => { } }); it('includes Remapped ES intrinsics', () => { - expect.assertions(RemappedIntrinsicObjectNames.length); + const remappedObjectNames = [...RemappedIntrinsicObjectNames, ...TypedAraysInstrinsics]; + expect.assertions(remappedObjectNames.length); - const shape = RemappedIntrinsicObjectNames.reduce((accum, key) => { + const shape = remappedObjectNames.reduce((accum, key) => { (accum as any)[key] = (globalThis as any)[key]; return accum; }, {}); @@ -168,7 +158,7 @@ describe('assignFilteredGlobalDescriptorsFromPropertyDescriptorMap', () => { {}, Object.getOwnPropertyDescriptors(shape) ); - for (const key of RemappedIntrinsicObjectNames) { + for (const key of remappedObjectNames) { expect(descs[key]).toBeDefined(); } }); @@ -186,6 +176,22 @@ describe('assignFilteredGlobalDescriptorsFromPropertyDescriptorMap', () => { writable: true, }); }); + it('should not remap TypedArrays when flag is false', () => { + expect.assertions(RemappedIntrinsicObjectNames.length); + + const shape = RemappedIntrinsicObjectNames.reduce((accum, key) => { + (accum as any)[key] = (globalThis as any)[key]; + return accum; + }, {}); + const descs = assignFilteredGlobalDescriptorsFromPropertyDescriptorMap( + {}, + Object.getOwnPropertyDescriptors(shape), + false + ); + for (const key of RemappedIntrinsicObjectNames) { + expect(descs[key]).toBeDefined(); + } + }); }); describe('getFilteredGlobalOwnKeys', () => { @@ -214,12 +220,13 @@ describe('getFilteredGlobalOwnKeys', () => { } }); it('includes Remapped ES intrinsics', () => { - const shape = RemappedIntrinsicObjectNames.reduce((accum, key) => { + const remappedObjectNames = [...RemappedIntrinsicObjectNames, ...TypedAraysInstrinsics]; + const shape = remappedObjectNames.reduce((accum, key) => { (accum as any)[key] = (globalThis as any)[key]; return accum; }, {}); const filteredOwnKeys = getFilteredGlobalOwnKeys(shape); - expect(filteredOwnKeys).toEqual(RemappedIntrinsicObjectNames); + expect(filteredOwnKeys).toEqual(remappedObjectNames); }); it('should include non-ES built-ins', () => { const filteredOwnKeys = getFilteredGlobalOwnKeys({ @@ -227,6 +234,14 @@ describe('getFilteredGlobalOwnKeys', () => { }); expect(filteredOwnKeys).toEqual(['Foo']); }); + it('should not remap TypedArrays when flag is false', () => { + const shape = RemappedIntrinsicObjectNames.reduce((accum, key) => { + (accum as any)[key] = (globalThis as any)[key]; + return accum; + }, {}); + const filteredOwnKeys = getFilteredGlobalOwnKeys(shape, false); + expect(filteredOwnKeys).toEqual(RemappedIntrinsicObjectNames); + }); }); describe('linkIntrinsics()', () => { diff --git a/packages/near-membrane-base/src/intrinsics.ts b/packages/near-membrane-base/src/intrinsics.ts index 1bedccc2..79648916 100644 --- a/packages/near-membrane-base/src/intrinsics.ts +++ b/packages/near-membrane-base/src/intrinsics.ts @@ -27,81 +27,90 @@ import { VirtualEnvironment } from './environment'; * problematic, and requires a lot more work to guarantee that objects from both sides * can be considered equivalents (without identity discontinuity). */ -const ESGlobalKeys = [ - // *** 19.1 Value Properties of the Global Object - 'globalThis', - 'Infinity', - 'NaN', - 'undefined', +function getESGlobalKeys(remapTypedArrays = true) { + const ESGlobalKeys = [ + // *** 19.1 Value Properties of the Global Object + 'globalThis', + 'Infinity', + 'NaN', + 'undefined', - // *** 19.2 Function Properties of the Global Object - // 'eval', // dangerous & Reflective - 'isFinite', - 'isNaN', - 'parseFloat', - 'parseInt', - 'decodeURI', - 'decodeURIComponent', - 'encodeURI', - 'encodeURIComponent', + // *** 19.2 Function Properties of the Global Object + // 'eval', // dangerous & Reflective + 'isFinite', + 'isNaN', + 'parseFloat', + 'parseInt', + 'decodeURI', + 'decodeURIComponent', + 'encodeURI', + 'encodeURIComponent', - // *** 19.3 Constructor Properties of the Global Object - // 'AggregateError', // Reflective - // 'Array', // Reflective - // 'ArrayBuffer', // Remapped - 'BigInt', - // 'BigInt64Array', // Remapped - // 'BigUint64Array', // Remapped - 'Boolean', - // 'DataView', // Remapped - // 'Date', // Remapped - // 'Error', // Reflective - // 'EvalError', // Reflective - 'FinalizationRegistry', - // 'Float32Array', // Remapped - // 'Float64Array', // Remapped - // 'Function', // dangerous & Reflective - // 'Int8Array', // Remapped - // 'Int16Array', // Remapped - // 'Int32Array', // Remapped - // 'Map', // Remapped - 'Number', - // 'Object', // Reflective - // Allow blue `Promise` constructor to overwrite the Red one so that promises - // created by the `Promise` constructor or APIs like `fetch` will work. - // 'Promise', // Remapped - // 'Proxy', // Reflective - // 'RangeError', // Reflective - // 'ReferenceError', // Reflective - 'RegExp', - // 'Set', // Remapped - // 'SharedArrayBuffer', // Remapped - 'String', - 'Symbol', - // 'SyntaxError', // Reflective - // 'TypeError', // Reflective - // 'Uint8Array', // Remapped - // 'Uint8ClampedArray', // Remapped - // 'Uint16Array', // Remapped - // 'Uint32Array', // Remapped - // 'URIError', // Reflective - // 'WeakMap', // Remapped - // 'WeakSet', // Remapped - 'WeakRef', + // *** 19.3 Constructor Properties of the Global Object + // 'AggregateError', // Reflective + // 'Array', // Reflective + 'BigInt', + 'Boolean', + // 'Date', // Remapped + // 'Error', // Reflective + // 'EvalError', // Reflective + 'FinalizationRegistry', + // 'Function', // dangerous & Reflective + // 'Map', // Remapped + 'Number', + // 'Object', // Reflective + // Allow blue `Promise` constructor to overwrite the Red one so that promises + // created by the `Promise` constructor or APIs like `fetch` will work. + // 'Promise', // Remapped + // 'Proxy', // Reflective + // 'RangeError', // Reflective + // 'ReferenceError', // Reflective + 'RegExp', + // 'Set', // Remapped - // *** 18.4 Other Properties of the Global Object - // 'Atomics', // Remapped - 'JSON', - 'Math', - 'Reflect', + 'String', + 'Symbol', + // 'SyntaxError', // Reflective + // 'TypeError', // Reflective + // 'URIError', // Reflective + // 'WeakMap', // Remapped + // 'WeakSet', // Remapped + 'WeakRef', - // *** Annex B - 'escape', - 'unescape', + // *** 18.4 Other Properties of the Global Object + // 'Atomics', // Remapped + 'JSON', + 'Math', + 'Reflect', - // *** ECMA-402 - // 'Intl', // Remapped -]; + // *** Annex B + 'escape', + 'unescape', + + // *** ECMA-402 + // 'Intl', // Remapped + ]; + + if (remapTypedArrays === false) { + ESGlobalKeys.push( + 'ArrayBuffer', + 'BigInt64Array', + 'BigUint64Array', + 'DataView', + 'Float32Array', + 'Float64Array', + 'Int8Array', + 'Int16Array', + 'Int32Array', + 'SharedArrayBuffer', + 'Uint8Array', + 'Uint8ClampedArray', + 'Uint16Array', + 'Uint32Array' + ); + } + return ESGlobalKeys; +} // These are foundational things that should never be wrapped but are equivalent // @TODO: Revisit this list. @@ -122,10 +131,10 @@ const ReflectiveIntrinsicObjectNames = [ 'globalThis', ]; -const ESGlobalsAndReflectiveIntrinsicObjectNames = toSafeArray([ - ...ESGlobalKeys, - ...ReflectiveIntrinsicObjectNames, -]); +function getESGlobalsAndReflectiveIntrinsicObjectNames(remapTypedArrays = true) { + const ESGlobalKeys = getESGlobalKeys(remapTypedArrays); + return toSafeArray([...ESGlobalKeys, ...ReflectiveIntrinsicObjectNames]); +} function getGlobalObjectOwnKeys(source: object): PropertyKey[] { const ownKeys = ReflectOwnKeys(source); @@ -140,8 +149,10 @@ function getGlobalObjectOwnKeys(source: object): PropertyKey[] { export function assignFilteredGlobalDescriptorsFromPropertyDescriptorMap< T extends PropertyDescriptorMap ->(descs: T, source: PropertyDescriptorMap): T { +>(descs: T, source: PropertyDescriptorMap, includeTypedArrays?: boolean): T { const ownKeys = getGlobalObjectOwnKeys(source); + const ESGlobalsAndReflectiveIntrinsicObjectNames = + getESGlobalsAndReflectiveIntrinsicObjectNames(includeTypedArrays); for (let i = 0, { length } = ownKeys; i < length; i += 1) { const ownKey = ownKeys[i]; // Avoid overriding ECMAScript global names that correspond to @@ -161,10 +172,15 @@ export function assignFilteredGlobalDescriptorsFromPropertyDescriptorMap< return descs; } -export function getFilteredGlobalOwnKeys(source: object): PropertyKey[] { +export function getFilteredGlobalOwnKeys( + source: object, + includeTypedArrays?: boolean +): PropertyKey[] { const result: PropertyKey[] = []; let resultOffset = 0; const ownKeys = getGlobalObjectOwnKeys(source); + const ESGlobalsAndReflectiveIntrinsicObjectNames = + getESGlobalsAndReflectiveIntrinsicObjectNames(includeTypedArrays); for (let i = 0, { length } = ownKeys; i < length; i += 1) { const ownKey = ownKeys[i]; // Avoid overriding ECMAScript global names that correspond to global diff --git a/packages/near-membrane-dom/src/browser-realm.ts b/packages/near-membrane-dom/src/browser-realm.ts index f69c419f..ec9169dd 100644 --- a/packages/near-membrane-dom/src/browser-realm.ts +++ b/packages/near-membrane-dom/src/browser-realm.ts @@ -80,6 +80,7 @@ function createIframeVirtualEnvironment( instrumentation, keepAlive = true, liveTargetCallback, + remapTypedArrays, signSourceCallback, // eslint-disable-next-line prefer-object-spread } = ObjectAssign({ __proto__: null }, providedOptions) as BrowserEnvironmentOptions; @@ -92,7 +93,9 @@ function createIframeVirtualEnvironment( const shouldUseDefaultGlobalOwnKeys = typeof globalObjectShape !== 'object' || globalObjectShape === null; if (shouldUseDefaultGlobalOwnKeys && defaultGlobalOwnKeys === null) { - defaultGlobalOwnKeys = filterWindowKeys(getFilteredGlobalOwnKeys(redWindow)); + defaultGlobalOwnKeys = filterWindowKeys( + getFilteredGlobalOwnKeys(redWindow, remapTypedArrays) + ); } let blueConnector = blueCreateHooksCallbackCache.get(blueRefs.document) as | Connector @@ -151,7 +154,11 @@ function createIframeVirtualEnvironment( ); if (endowments) { const filteredEndowments: PropertyDescriptorMap = {}; - assignFilteredGlobalDescriptorsFromPropertyDescriptorMap(filteredEndowments, endowments); + assignFilteredGlobalDescriptorsFromPropertyDescriptorMap( + filteredEndowments, + endowments, + remapTypedArrays + ); removeWindowDescriptors(filteredEndowments); env.remapProperties(blueRefs.window, filteredEndowments); } diff --git a/packages/near-membrane-dom/src/types.ts b/packages/near-membrane-dom/src/types.ts index 3ea73d70..0b655e6f 100644 --- a/packages/near-membrane-dom/src/types.ts +++ b/packages/near-membrane-dom/src/types.ts @@ -13,5 +13,6 @@ export interface BrowserEnvironmentOptions { instrumentation?: Instrumentation; keepAlive?: boolean; liveTargetCallback?: LiveTargetCallback; + remapTypedArrays?: boolean; signSourceCallback?: SignSourceCallback; } diff --git a/packages/near-membrane-node/src/node-realm.ts b/packages/near-membrane-node/src/node-realm.ts index aa21f8d7..885c4503 100644 --- a/packages/near-membrane-node/src/node-realm.ts +++ b/packages/near-membrane-node/src/node-realm.ts @@ -33,6 +33,7 @@ export default function createVirtualEnvironment( globalObjectShape, instrumentation, liveTargetCallback, + remapTypedArrays, signSourceCallback, } = ObjectAssign({ __proto__: null }, providedOptions) as NodeEnvironmentOptions; let blueConnector = blueCreateHooksCallbackCache.get(globalObject) as Connector | undefined; @@ -59,7 +60,7 @@ export default function createVirtualEnvironment( const shouldUseDefaultGlobalOwnKeys = typeof globalObjectShape !== 'object' || globalObjectShape === null; if (shouldUseDefaultGlobalOwnKeys && defaultGlobalOwnKeys === null) { - defaultGlobalOwnKeys = getFilteredGlobalOwnKeys(redGlobalObject); + defaultGlobalOwnKeys = getFilteredGlobalOwnKeys(redGlobalObject, remapTypedArrays); } env.lazyRemapProperties( @@ -71,7 +72,11 @@ export default function createVirtualEnvironment( if (endowments) { const filteredEndowments = {}; - assignFilteredGlobalDescriptorsFromPropertyDescriptorMap(filteredEndowments, endowments); + assignFilteredGlobalDescriptorsFromPropertyDescriptorMap( + filteredEndowments, + endowments, + remapTypedArrays + ); env.remapProperties(globalObject, filteredEndowments); } return env; diff --git a/packages/near-membrane-node/src/types.ts b/packages/near-membrane-node/src/types.ts index 911fb51c..36420557 100644 --- a/packages/near-membrane-node/src/types.ts +++ b/packages/near-membrane-node/src/types.ts @@ -11,5 +11,6 @@ export interface NodeEnvironmentOptions { globalObjectShape?: object; instrumentation?: Instrumentation; liveTargetCallback?: LiveTargetCallback; + remapTypedArrays?: boolean; signSourceCallback?: SignSourceCallback; }