|
const pointers = new WeakMap(); |
|
let localRef; |
|
|
|
function isObject(value) { |
|
return ( |
|
(value !== null && typeof value === 'object') |
|
|| typeof value === 'function' |
|
); |
|
} |
|
|
|
function getTargetType(value) { |
|
const pointer = pointers.get(value); |
|
if (pointer) { |
|
pointer(); // type is not needed because a pointer already exists for this value |
|
return 0; |
|
} |
|
if (Array.isArray(value)) { |
|
return 2; // Array |
|
} else if (typeof value === 'function') { |
|
return 'prototype' in value ? 3 : 4; // Arrow Function or Function |
|
} |
|
return 1; // Object |
|
} |
|
|
|
/** |
|
* We can’t transfer a (non-callable) object across realms. |
|
* But we can transfer a function that applies proxy-trapped operations |
|
* to the object. |
|
*/ |
|
export function wrapLocalValue(value) { |
|
if (isObject(value)) { |
|
let pointer = pointers.get(value); |
|
if (!pointer) { |
|
pointer = (trapName, ...args) => { |
|
switch (trapName) { |
|
case 1: // initialization |
|
return getTargetType(value); |
|
case 2: // linkage |
|
pointers.set(value, args[0]); |
|
return () => (localRef = value) && 0; // returning a pointer for when the value comes back. |
|
default: |
|
const wrappedArgs = args.map(arg => wrapForeignValue(arg)); |
|
const result = Reflect[trapName](value, ...wrappedArgs); |
|
return wrapLocalValue(result); |
|
} |
|
}; |
|
} |
|
return pointer; |
|
} else { |
|
return value; |
|
} |
|
} |
|
|
|
/** |
|
* To use a wrapped object from another realm in the current realm, |
|
* we create a Proxy that feeds the operations it traps to the function. |
|
*/ |
|
export function wrapForeignValue(value) { |
|
if (!isObject(value)) { |
|
return value; |
|
} |
|
localRef = undefined; |
|
const targetType = value(1); |
|
if (targetType === 0) { |
|
return localRef; |
|
} |
|
// All handler methods follow the same pattern. |
|
// To avoid repetitive code, we create it via a loop. |
|
const handler = {}; |
|
for (const trapName of Object.getOwnPropertyNames(Reflect)) { |
|
handler[trapName] = (_target, ...args) => { |
|
const wrappedArgs = args.map(arg => wrapLocalValue(arg)); |
|
const result = value(trapName, ...wrappedArgs); |
|
return wrapForeignValue(result); |
|
}; |
|
} |
|
const target = targetType === 2 ? [] : (targetType === 3 ? function () {} : (targetType === 4 ? () => {} : {})); |
|
const proxy = new Proxy(target, handler); |
|
pointers.set(proxy, value(2, () => (localRef = proxy) && 0)); |
|
return proxy; |
|
} |
|
|
|
export async function createShadowEval(shadowRealm) { |
|
const _getLocallyWrappedEval = await shadowRealm.importValue(import.meta.url, '_getLocallyWrappedEval'); |
|
return wrapForeignValue(_getLocallyWrappedEval()); |
|
} |
|
|
|
export async function createShadowImport(shadowRealm) { |
|
const _getLocallyWrappedImport = await shadowRealm.importValue(import.meta.url, '_getLocallyWrappedImport'); |
|
return wrapForeignValue(_getLocallyWrappedImport()); |
|
} |
|
|
|
export function _getLocallyWrappedEval() { |
|
return wrapLocalValue(globalThis.eval); |
|
} |
|
|
|
export function _getLocallyWrappedImport() { |
|
return wrapLocalValue((moduleSpecifier) => import(moduleSpecifier)); |
|
} |
@rauschma here is the diff: