Skip to content

Instantly share code, notes, and snippets.

@rauschma
Last active May 25, 2022 17:23
Show Gist options
  • Save rauschma/388fbf155941bd20ef161c6a17d7f0ab to your computer and use it in GitHub Desktop.
Save rauschma/388fbf155941bd20ef161c6a17d7f0ab to your computer and use it in GitHub Desktop.
Transferring objects between a ShadowRealm and an incubator realm

Transferring objects between a ShadowRealm and an incubator realm

Material:

<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Membrane</title>
</head>
<body>
<script type="module">
import {createShadowEval} from './membrane-lib.js';
const sr = new ShadowRealm();
const shadowEval = await createShadowEval(sr);
const obj = shadowEval(`
({
brand: 'Ford',
owner: {
first: 'Jane',
last: 'Doe',
},
})
`);
console.log(obj.brand); // 'Ford'
console.log(obj.owner.first); // 'Jane'
</script>
</body>
</html>
function isObject(value) {
return (
(value !== null && typeof value === 'object')
|| typeof value === 'function'
);
}
/**
* 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)) {
return (trapName, ...args) => {
const wrappedArgs = args.map(arg => wrapForeignValue(arg));
const result = Reflect[trapName](value, ...wrappedArgs);
return wrapLocalValue(result);
};
} 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;
}
// 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 = function () {};
return new Proxy(target, handler);
}
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));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment