Remove unsafeRec from realmRec
Consistently use deconstruction
Remove safeEvaluators, define safeEval and safeFunction on realmRec
Collapse sanitize into context
MODIFY
shim/src/context.js
shim/src/context.js
-import { getScopedEvaluatorFactory } from './evaluators';
-import { sanitize } from './sanitize';
+import { repairAccessors } from './accessors';
+import { repairFunctions } from './functions';
+import { freeze } from './commons';
// Detection used in RollupJS.
const isNode = typeof exports === 'object' && typeof module !== 'undefined';
@@ -35,17 +36,18 @@ export function createUnsafeRec(context) {
context = createContext();
}
- const unsafeRec = {
+ const unsafeRec = freeze({
unsafeGlobal: context.global,
unsafeEval: context.eval,
unsafeFunction: context.Function
- };
+ });
- // Create the evaluator factory that will generate the evaluators
- // for each compartment realm.
- unsafeRec.scopedEvaluatorFactory = getScopedEvaluatorFactory(unsafeRec);
+ // Ensures that neither the legacy accessors nor the function constructors
+ // can be used to escape the confinement of the evaluators to execute in the
+ // context.
+ repairAccessors(unsafeRec);
+ repairFunctions(unsafeRec);
- sanitize(unsafeRec);
return unsafeRec;
}
@@ -55,7 +57,7 @@ function getCurrentContext() {
return (0, eval)(contextRecSrc);
}
-export function getCurrentUnsafeRec() {
+export function createCurrentUnsafeRec() {
const context = getCurrentContext();
return createUnsafeRec(context);
}

MODIFY
shim/src/evaluators.js
shim/src/evaluators.js
@@ -13,7 +13,7 @@ function buildOptimizer(constants) {
return `const {${constants.join(',')}} = arguments[0];`;
}
-export function getScopedEvaluatorFactory(unsafeRec, constants) {
+export function createScopedEvaluatorFactory(unsafeRec, constants) {
const { unsafeFunction } = unsafeRec;
const optimizer = buildOptimizer(constants);
@@ -31,8 +31,8 @@ export function getScopedEvaluatorFactory(unsafeRec, constants) {
`);
}
-export function getSafeEvaluator(realmRec) {
- const { unsafeRec, globalObject } = realmRec;
+export function createSafeEvaluator(unsafeRec, globalObject) {
+ const { unsafeGlobal, unsafeFunction } = unsafeRec;
// This proxy has several functions:
// 1. works with the sentinel to alternate between direct eval and confined eval.
@@ -41,13 +41,14 @@ export function getSafeEvaluator(realmRec) {
const handler = new Handler(unsafeRec);
const proxy = new Proxy(globalObject, handler);
- const scopedEvaluator = unsafeRec.scopedEvaluatorFactory(proxy);
+ const scopedEvaluatorFactory = createScopedEvaluatorFactory(unsafeRec);
+ const scopedEvaluator = scopedEvaluatorFactory(proxy);
// We use the the concise method syntax to create an eval without a
// [[Construct]] behavior (such that the invocation "new eval()" throws
// TypeError: eval is not a constructor"), but which still accepts a 'this'
// binding.
- const evaluator = {
+ const safeEval = {
eval(src) {
handler.useUnsafeEvaluator = true;
try {
@@ -63,24 +64,26 @@ export function getSafeEvaluator(realmRec) {
// Ensure that eval from any compartment in a root realm is an
// instance of Function in any compartment of the same root realm.
- const { unsafeGlobal, unsafeFunction } = unsafeRec;
- setPrototypeOf(evaluator, unsafeFunction.prototype);
+ setPrototypeOf(safeEval, unsafeFunction.prototype);
- defineProperty(evaluator, unsafeGlobal.Symbol.toStringTag, {
+ defineProperty(safeEval, unsafeGlobal.Symbol.toStringTag, {
value: 'function eval() { [shim code] }',
writable: false,
enumerable: false,
configurable: true
});
- return evaluator;
+
+ return safeEval;
}
/**
* A safe version of the native Function which relies on
* the safety of evalEvaluator for confinement.
*/
-export function getFunctionEvaluator(unsafeFunction, unsafeGlobal, safeEvaluator) {
- const SafeFunction = function Function(...params) {
+export function createFunctionEvaluator(unsafeRec, safeEval) {
+ const { unsafeFunction, unsafeGlobal } = unsafeRec;
+
+ const safeFunction = function Function(...params) {
const functionBody = `${params.pop()}` || '';
let functionParams = `${params.join(',')}`;
@@ -107,24 +110,25 @@ export function getFunctionEvaluator(unsafeFunction, unsafeGlobal, safeEvaluator
const src = `(function(${functionParams}){\n${functionBody}\n})`;
- return safeEvaluator(src);
+ return safeEval(src);
};
// Ensure that Function from any compartment in a root realm can be used
// with instance checks in any compartment of the same root realm.
- setPrototypeOf(SafeFunction, unsafeFunction.prototype);
+ setPrototypeOf(safeFunction, unsafeFunction.prototype);
// Ensure that any function created in any compartment in a root realm is an
// instance of Function in any compartment of the same root ralm.
- defineProperty(SafeFunction, 'prototype', { value: unsafeFunction.prototype });
+ defineProperty(safeFunction, 'prototype', { value: unsafeFunction.prototype });
// Provide a custom output without overwriting the Function.prototype.toString
// which is called by some libraries.
- defineProperty(SafeFunction, unsafeGlobal.Symbol.toStringTag, {
+ defineProperty(safeFunction, unsafeGlobal.Symbol.toStringTag, {
value: 'function Function() { [shim code] }',
writable: false,
enumerable: false,
configurable: true
});
- return SafeFunction;
+
+ return safeFunction;
}

MODIFY
shim/src/handler.js
shim/src/handler.js
@@ -4,7 +4,7 @@ export class Handler {
constructor(unsafeRec) {
this.unsafeGlobal = unsafeRec.unsafeGlobal;
- this.unsafeEval = unsafeRec.unsafeGlobal.eval;
+ this.unsafeEval = unsafeRec.unsafeEval;
// this flag allow us to determine if the eval() call is a controlled
// eval done by the realm's code or if it is user-land invocation, so

MODIFY
shim/src/intrinsics.js
shim/src/intrinsics.js
@@ -8,8 +8,8 @@ import { getPrototypeOf } from './commons';
* https://tc39.github.io/ecma262/#table-7
* https://tc39.github.io/ecma262/#table-73
*/
-export function getSharedIntrinsics(contextGlobal) {
- const g = contextGlobal;
+export function getSharedIntrinsics(unsafeRec) {
+ const { unsafeGlobal: g } = unsafeRec;
// the .constructor properties on evaluator intrinsics should already be
// fixed by this point, due to the sanitize() call inside createUnsafeRec()

MODIFY
shim/src/realm.js
shim/src/realm.js
-import { createUnsafeRec, getCurrentUnsafeRec } from './context';
-import { getSafeEvaluator, getFunctionEvaluator } from './evaluators';
+import { createUnsafeRec, createCurrentUnsafeRec } from './context';
+import { createSafeEvaluator, createFunctionEvaluator } from './evaluators';
import { getStdLib } from './stdlib';
import { getSharedIntrinsics } from './intrinsics';
-import { assign, create, defineProperty, defineProperties, getPrototypeOf } from './commons';
+import {
+ assign,
+ create,
+ defineProperty,
+ defineProperties,
+ freeze,
+ getPrototypeOf
+} from './commons';
const Realm2RealmRec = new WeakMap();
const RealmProto2UnsafeRec = new WeakMap();
@@ -45,10 +52,10 @@ function buildChildRealm(BaseRealm) {
eMessage = `${err.message}`;
// eName and eMessage are now child-realm primitive strings, and safe
// to expose
- } catch (_) {
+ } catch (ignored) {
// if err.name.toString() throws, keep the (parent realm) Error away
// from the child
- throw new Error('Something bad happened');
+ throw new Error('unknown error');
}
const ErrorConstructor = errorConstructors.get(eName) || Error;
throw new ErrorConstructor(eMessage);
@@ -116,35 +123,35 @@ function createRealmFacade(unsafeRec, BaseRealm) {
RealmProto2UnsafeRec.set(Realm.prototype, unsafeRec);
}
-function getGlobalObject(intrinsics) {
+function createGlobalObject(intrinsics) {
return create(intrinsics.ObjectPrototype);
}
-function createEvaluators(realmRec) {
- // Divergence from specifications: the evaluators are tied to
- // a global and they are tied to a realm and to the intrinsics
- // of that realm.
- const safeEvaluator = getSafeEvaluator(realmRec);
- const functionEvaluator = getFunctionEvaluator(
- realmRec.unsafeRec.unsafeFunction,
- realmRec.unsafeRec.unsafeGlobal,
- safeEvaluator
- );
-
- // Limitation: export a direct evaluator.
- realmRec.safeEvaluators = { eval: safeEvaluator, Function: functionEvaluator };
-}
-
function setDefaultBindings(realmRec) {
- const intrinsics = realmRec.sharedIntrinsics;
- const safeEvaluators = realmRec.safeEvaluators;
- const descs = getStdLib(intrinsics, safeEvaluators);
+ const descs = getStdLib(realmRec);
defineProperties(realmRec.globalObject, descs);
}
+function createRealmRec(unsafeRec) {
+ const sharedIntrinsics = getSharedIntrinsics(unsafeRec);
+ const globalObject = createGlobalObject(sharedIntrinsics);
+
+ const safeEval = createSafeEvaluator(unsafeRec, globalObject);
+ const safeFunction = createFunctionEvaluator(unsafeRec, safeEval);
+
+ const realmRec = freeze({
+ sharedIntrinsics,
+ globalObject,
+ safeEval,
+ safeFunction
+ });
+
+ setDefaultBindings(realmRec);
+ return realmRec;
+}
+
export default class Realm {
constructor(options) {
- const O = this;
options = Object(options); // Todo: sanitize
if (options.thisValue !== undefined) {
@@ -180,19 +187,8 @@ export default class Realm {
// facade catches all errors and translates them into local Error types
throw new TypeError('Realm only supports undefined or "inherited" intrinsics.');
}
- const sharedIntrinsics = getSharedIntrinsics(unsafeRec.unsafeGlobal);
- const globalObj = getGlobalObject(sharedIntrinsics);
-
- const realmRec = {
- unsafeRec,
- sharedIntrinsics,
- globalObject: globalObj,
- safeEvaluators: undefined
- };
- Realm2RealmRec.set(O, realmRec);
-
- createEvaluators(realmRec);
- setDefaultBindings(realmRec);
+ const realmRec = createRealmRec(unsafeRec);
+ Realm2RealmRec.set(this, realmRec);
}
get intrinsics() {
const O = this;
@@ -217,8 +213,8 @@ export default class Realm {
if (typeof O !== 'object') throw new TypeError();
if (!Realm2RealmRec.has(O)) throw new TypeError();
const realmRec = Realm2RealmRec.get(O);
- const evaluator = realmRec.safeEvaluators.eval;
- return evaluator(`${x}`);
+ const safeEval = realmRec.safeEval;
+ return safeEval(`${x}`);
}
static makeRootRealm() {
return new Realm();
@@ -232,7 +228,9 @@ export default class Realm {
}
}
-RealmProto2UnsafeRec.set(Realm.prototype, getCurrentUnsafeRec());
+// Create the unsafeRec from the current realm (the realm where the
+// Realm shim is loaded and executed).
+RealmProto2UnsafeRec.set(Realm.prototype, createCurrentUnsafeRec());
defineProperty(Realm.prototype, Symbol.toStringTag, {
value: 'function Realm() { [shim code] }',

DELETE
shim/src/sanitize.js
-import { repairAccessors } from './accessors';
-import { repairFunctions } from './functions';
-
-// Sanitizing ensures that neither the legacy
-// accessors nor the function constructors can be
-// used to escape the confinement of the evaluators
-// to execute in the context.
-
-export function sanitize(unsafeRec) {
- repairAccessors(unsafeRec);
- repairFunctions(unsafeRec);
-}

MODIFY
shim/src/stdlib.js
shim/src/stdlib.js
-export function getStdLib(intrinsics, safeEvaluators) {
+export function getStdLib(realmRec) {
+ const { sharedIntrinsics, safeEval, safeFunction } = realmRec;
+
const descriptors = {
// *** 18.1 Value Properties of the Global Object
@@ -13,7 +15,7 @@ export function getStdLib(intrinsics, safeEvaluators) {
const namedIntrinsics = [
// *** 18.2 Function Properties of the Global Object
- // 'eval', // comes from safeEvaluators instead
+ // 'eval', // comes from safeEval instead
'isFinite',
'isNaN',
'parseFloat',
@@ -35,7 +37,7 @@ export function getStdLib(intrinsics, safeEvaluators) {
'EvalError',
'Float32Array',
'Float64Array',
- // 'Function', // comes from safeEvaluators instead
+ // 'Function', // comes from safeFunction instead
'Int8Array',
'Int16Array',
'Int32Array',
@@ -84,7 +86,7 @@ export function getStdLib(intrinsics, safeEvaluators) {
for (const name of namedIntrinsics) {
descriptors[name] = {
- value: intrinsics[name],
+ value: sharedIntrinsics[name],
writable: true,
configurable: true
};
@@ -94,14 +96,14 @@ export function getStdLib(intrinsics, safeEvaluators) {
// *** 18.2 Function Properties of the Global Object
descriptors.eval = {
- value: safeEvaluators.eval,
+ value: safeEval,
writable: true,
configurable: true // todo: maybe make this non-configurable
};
// *** 18.3 Constructor Properties of the Global Object
descriptors.Function = {
- value: safeEvaluators.Function,
+ value: safeFunction,
writable: true,
configurable: true
};

Remove unsafeRec from realmRec (#122)

Consistently use deconstruction
Remove safeEvaluators, define safeEval and safeFunction on realmRec
Collapse sanitize into context