Added support for nodeJS
MODIFY
README.md
README.md
@@ -157,7 +157,23 @@ const ns = r.evaluate('import("foo")'); // where referrerNamespace is null, and
A shim implementation of the Realm API can be found [here](shim/README.md).
-And you can play around with the Shim [here](https://rawgit.com/caridy/proposal-realms/master/shim/examples/simple.html).
+And you can play around with the current version of the shim [here](https://rawgit.com/tc39/proposal-realms/master/shim/examples/frozen.html).
+
+To use the shim in a webpage:
+```html
+ <script src="../dist/realm-shim.min.js"></script>
+ <script>
+ const r = new Realm();
+ [...]
+ </script>
+```
+
+To use the shim in node:
+```js
+ const Realm = require('./realm-shim.min.js');
+ const r = new Realm();
+ [...]
+```
## Contributing

MODIFY
shim/src/accessors.js
shim/src/accessors.js
@@ -14,8 +14,8 @@ import {
* and ES2016 semantics, we do this by redefining them while in 'use strict'
* https://tc39.github.io/ecma262/#sec-object.prototype.__defineGetter__
*/
-export function repairAccessors(sandbox) {
- const { unsafeGlobal: g } = sandbox;
+export function repairAccessors(contextRec) {
+ const { contextGlobal: g } = contextRec;
defineProperties(g.Object.prototype, {
__defineGetter__: {

ADD
shim/src/context.js
+import { getDirectEvalEvaluatorFactory } from './evaluators';
+import { sanitize } from './sanitize';
+
+// Detection used in RollupJS.
+const isNode = typeof exports === 'object' && typeof module !== 'undefined';
+const vm = isNode ? require('vm') : undefined;
+
+const contextRecSrc = '({ global: this, eval, Function })';
+
+// The contextRec is shim-specific. It acts as the mechanism
+// to obtain a fresh set of intrinsics together with their
+// associated eval and Function evaluators. This association
+// must be respected since the evaluators are imposing a
+// set of intrinsics, aka the "undeniables".
+
+function createNodeContext() {
+ const context = vm.runInNewContext(contextRecSrc);
+ return context;
+}
+
+function createBrowserContext() {
+ const iframe = document.createElement('iframe');
+ iframe.style.display = 'none';
+
+ document.body.appendChild(iframe);
+ const context = iframe.contentWindow.eval(contextRecSrc);
+
+ return context;
+}
+
+const createContext = isNode ? createNodeContext : createBrowserContext;
+
+export function createContextRec(context) {
+ if (context === undefined) {
+ context = createContext();
+ }
+
+ const contextRec = {
+ contextGlobal: context.global,
+ contextEval: context.eval,
+ contextFunction: context.Function
+ };
+
+ // Create the evaluator factory that will generate the evaluators
+ // for each compartment realm.
+ contextRec.evalEvaluatorFactory = getDirectEvalEvaluatorFactory(contextRec);
+
+ sanitize(contextRec);
+ return contextRec;
+}
+
+// The current context is the context where the
+// Realm shim is being parsed and executed.
+function getCurrentContext() {
+ return (0, eval)(contextRecSrc);
+}
+
+export function getCurrentContextRec() {
+ const context = getCurrentContext();
+ return createContextRec(context);
+}

MODIFY
shim/src/evaluators.js
shim/src/evaluators.js
// Portions adapted from V8 - Copyright 2016 the V8 project authors.
// https://github.com/v8/v8/blob/master/src/builtins/builtins-function.cc
-import { GlobalObject, Intrinsics, ShimSandbox } from './symbols';
+import { GlobalObject, Intrinsics, ContextRec } from './symbols';
import { defineProperty, getOwnPropertyDescriptor, setPrototypeOf } from './commons';
import { Handler } from './handler';
@@ -14,14 +14,14 @@ function buildOptimizer(constants) {
return `const {${constants.join(',')}} = arguments[0];`;
}
-export function getDirectEvalEvaluatorFactory(sandbox, constants) {
- const { unsafeFunction } = sandbox;
+export function getDirectEvalEvaluatorFactory(contextRec, constants) {
+ const { contextFunction } = contextRec;
const optimizer = buildOptimizer(constants);
// Create a function in sloppy mode that returns
// a function in strict mode.
- return unsafeFunction(`
+ return contextFunction(`
with (arguments[0]) {
${optimizer}
return function() {
@@ -33,16 +33,16 @@ export function getDirectEvalEvaluatorFactory(sandbox, constants) {
}
export function getDirectEvalEvaluator(realmRec) {
- const { [ShimSandbox]: sandbox, [GlobalObject]: globalObject } = realmRec;
+ const { [ContextRec]: contextRec, [GlobalObject]: globalObject } = realmRec;
// This proxy has several functions:
// 1. works with the sentinel to alternate between direct eval and confined eval.
// 2. shadows all properties of the hidden global by declaring them as undefined.
// 3. resolves all existing properties of the sandboxed global.
- const handler = new Handler(sandbox);
+ const handler = new Handler(contextRec);
const proxy = new Proxy(globalObject, handler);
- const scopedEvaluator = sandbox.evalEvaluatorFactory(proxy);
+ const scopedEvaluator = contextRec.evalEvaluatorFactory(proxy);
// Create an eval without a [[Construct]] behavior such that the
// invocation "new eval()" throws TypeError: eval is not a constructor".
@@ -58,8 +58,8 @@ export function getDirectEvalEvaluator(realmRec) {
// Ensure that eval from any compartment in a root realm is an
// instance of Function in any compartment of the same root ralm.
- const { unsafeFunction } = sandbox;
- setPrototypeOf(evaluator, unsafeFunction.prototype.constructor);
+ const { contextFunction } = contextRec;
+ setPrototypeOf(evaluator, contextFunction.prototype.constructor);
evaluator.toString = () => 'function eval() { [shim code] }';
return evaluator;
@@ -70,7 +70,7 @@ export function getDirectEvalEvaluator(realmRec) {
* the safety of evalEvaluator for confinement.
*/
export function getFunctionEvaluator(realmRec) {
- const { [ShimSandbox]: sandbox, [Intrinsics]: intrinsics } = realmRec;
+ const { [ContextRec]: contextRec, [Intrinsics]: intrinsics } = realmRec;
const evaluator = function Function(...params) {
const functionBody = params.pop() || '';
@@ -97,13 +97,13 @@ export function getFunctionEvaluator(realmRec) {
// Ensure that Function from any compartment in a root realm can be used
// with instance checks in any compartment of the same root realm.
- const { unsafeFunction } = sandbox;
- setPrototypeOf(evaluator, unsafeFunction.prototype.constructor);
+ const { contextFunction } = contextRec;
+ setPrototypeOf(evaluator, contextFunction.prototype.constructor);
// 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.
const desc = getOwnPropertyDescriptor(evaluator, 'prototype');
- desc.value = unsafeFunction.prototype;
+ desc.value = contextFunction.prototype;
defineProperty(evaluator, 'prototype', desc);
evaluator.toString = () => 'function Function() { [shim code] }';

MODIFY
shim/src/functions.js
shim/src/functions.js
@@ -13,14 +13,14 @@ import { defineProperty, defineProperties, getPrototypeOf, setPrototypeOf } from
* 4. Replace its prototype property's constructor with itself
* 5. Replace its [[Prototype]] slot with the noop constructor of Function
*/
-function repairFunction(sandbox, functionName, functionDecl) {
- const { unsafeEval, unsafeFunction } = sandbox;
+function repairFunction(contextRec, functionName, functionDecl) {
+ const { contextEval, contextFunction } = contextRec;
- const FunctionInstance = unsafeEval(`(${functionDecl}(){})`);
+ const FunctionInstance = contextEval(`(${functionDecl}(){})`);
const FunctionPrototype = getPrototypeOf(FunctionInstance);
// Block evaluation of source when calling constructor on the prototype of functions.
- const TamedFunction = unsafeFunction('throw new Error("Not available");');
+ const TamedFunction = contextFunction('throw new Error("Not available");');
defineProperties(TamedFunction, {
name: {
@@ -33,7 +33,7 @@ function repairFunction(sandbox, functionName, functionDecl) {
defineProperty(FunctionPrototype, 'constructor', { value: TamedFunction });
// Ensures that all functions meet "instanceof Function" in a realm.
- setPrototypeOf(TamedFunction, unsafeFunction.prototype.constructor);
+ setPrototypeOf(TamedFunction, contextFunction.prototype.constructor);
}
/**
@@ -42,17 +42,17 @@ function repairFunction(sandbox, functionName, functionDecl) {
* safe replacements that preserve SES confinement. After this block is done,
* the originals should no longer be reachable.
*/
-export function repairFunctions(sandbox) {
- const { unsafeGlobal: g } = sandbox;
+export function repairFunctions(contextRec) {
+ const { contextGlobal: g } = contextRec;
// Here, the order of operation is important: Function needs to be
// repaired first since the other constructors need it.
- repairFunction(sandbox, 'Function', 'function');
- repairFunction(sandbox, 'GeneratorFunction', 'function*');
- repairFunction(sandbox, 'AsyncFunction', 'async function');
+ repairFunction(contextRec, 'Function', 'function');
+ repairFunction(contextRec, 'GeneratorFunction', 'function*');
+ repairFunction(contextRec, 'AsyncFunction', 'async function');
const hasAsyncIteration = typeof g.Symbol.asyncIterator !== 'undefined';
if (hasAsyncIteration) {
- repairFunction(sandbox, 'AsyncGeneratorFunction', 'async function*');
+ repairFunction(contextRec, 'AsyncGeneratorFunction', 'async function*');
}
}

MODIFY
shim/src/handler.js
shim/src/handler.js
@@ -2,9 +2,9 @@ export class Handler {
// Properties stored on the handler
// are not available from the proxy.
- constructor(sandbox) {
- const { unsafeGlobal } = sandbox;
- this.unsafeGlobal = unsafeGlobal;
+ constructor(contextRec) {
+ const { contextGlobal } = contextRec;
+ this.contextGlobal = contextGlobal;
// 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
@@ -17,7 +17,7 @@ export class Handler {
if (prop === 'eval') {
if (this.isInternalEvaluation) {
this.isInternalEvaluation = false;
- return this.unsafeGlobal.eval;
+ return this.contextGlobal.eval;
}
return target.eval;
}
@@ -39,7 +39,7 @@ export class Handler {
if (prop in target) {
return true;
}
- if (prop in this.unsafeGlobal) {
+ if (prop in this.contextGlobal) {
return true;
}
return false;

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 getIntrinsics(sandbox) {
- const { unsafeGlobal: g } = sandbox;
+export function getIntrinsics(contextRec) {
+ const { contextGlobal: g } = contextRec;
// Anonymous intrinsics.

MODIFY
shim/src/realm.js
shim/src/realm.js
-import { createSandbox } from './sandbox';
+import { createContextRec, getCurrentContextRec } from './context';
import { getDirectEvalEvaluator, getFunctionEvaluator } from './evaluators';
import { getStdLib } from './stdlib';
import { getIntrinsics } from './intrinsics';
@@ -6,45 +6,28 @@ import { tamperProofDataProperties } from './tamper-proof';
import { deepFreeze } from './deep-freeze';
import { IsCallable } from './utils';
import { assign, create, defineProperties, getPrototypeOf } from './commons';
-import {
- Intrinsics,
- GlobalObject,
- DirectEvalEvaluator,
- FunctionEvaluator,
- ShimSandbox
-} from './symbols';
+import { Intrinsics, GlobalObject, DirectEvalEvaluator, ContextRec } from './symbols';
const Realm2RealmRec = new WeakMap();
-const RealmProto2Sandbox = new WeakMap();
+const RealmProto2ContextRec = new WeakMap();
-function getCurrentContext() {
- // eslint-disable-next-line no-new-func
- return new Function('return this')();
-}
-
-function getCurrentSandbox() {
- const context = getCurrentContext();
- const sandbox = createSandbox(context);
- return sandbox;
-}
-
-function createRealmFacade(sandbox, BaseRealm) {
- const { unsafeFunction, unsafeGlobal } = sandbox;
+function createRealmFacade(contextRec, BaseRealm) {
+ const { contextFunction, contextGlobal } = contextRec;
// The BaseRealm is the Realm class created by
// the shim. It's only valid for the context where
// it was parsed.
// The Realm facade is a lightwwight class built in the
- // context a different sandbox, that provide a fully
+ // context a different context, that provide a fully
// functional Realm class using the intrisics
- // of that sandbox.
+ // of that context.
// This process is simplified becuase all methods
// and properties on a realm instance already return
- // values using the intrinsics of the realm's sandbox.
+ // values using the intrinsics of the realm's context.
- const Realm = unsafeFunction(
+ const Realm = contextFunction(
'BaseRealm',
`
@@ -76,8 +59,8 @@ return Realm;
`
)(BaseRealm);
- unsafeGlobal.Realm = Realm;
- RealmProto2Sandbox.set(Realm.prototype, sandbox);
+ contextGlobal.Realm = Realm;
+ RealmProto2ContextRec.set(Realm.prototype, contextRec);
}
function getGlobaObject(intrinsics) {
@@ -91,13 +74,12 @@ function createEvaluators(realmRec) {
const directEvalEvaluator = getDirectEvalEvaluator(realmRec);
const functionEvaluator = getFunctionEvaluator(realmRec);
- realmRec[DirectEvalEvaluator] = directEvalEvaluator;
- realmRec[FunctionEvaluator] = functionEvaluator;
-
// Limitation: export a direct evaluator.
const intrinsics = realmRec[Intrinsics];
intrinsics.eval = directEvalEvaluator;
intrinsics.Function = functionEvaluator;
+
+ realmRec[DirectEvalEvaluator] = directEvalEvaluator;
}
function setDefaultBindings(realmRec) {
@@ -111,25 +93,25 @@ export default class Realm {
const O = this;
options = Object(options); // Todo: sanitize
- let sandbox;
+ let contextRec;
if (options.intrinsics === 'inherit') {
// In "inherit" mode, we create a compartment realm and inherit
- // the sandbox since we share the intrinsics. We create a new
+ // the context since we share the intrinsics. We create a new
// set to allow us to define eval() anf Function() for the realm.
- sandbox = RealmProto2Sandbox.get(getPrototypeOf(this));
+ contextRec = RealmProto2ContextRec.get(getPrototypeOf(this));
} else if (options.intrinsics === undefined) {
// When intrinics are not provided, we create a root realm
- // using the fresh set of new intrinics from a new sandbox.
- sandbox = createSandbox();
- createRealmFacade(sandbox, Realm);
+ // using the fresh set of new intrinics from a new context.
+ contextRec = createContextRec();
+ createRealmFacade(contextRec, Realm);
} else {
throw new TypeError('Realm only supports undefined or "inherited" intrinsics.');
}
- const intrinsics = getIntrinsics(sandbox);
+ const intrinsics = getIntrinsics(contextRec);
const globalObj = getGlobaObject(intrinsics);
const realmRec = {
- [ShimSandbox]: sandbox,
+ [ContextRec]: contextRec,
[Intrinsics]: intrinsics,
[GlobalObject]: globalObj,
[DirectEvalEvaluator]: undefined
@@ -191,8 +173,6 @@ export default class Realm {
}
}
-// The current sandbox is the sandbox where the
-// Realm shim is being parsed and executed.
-RealmProto2Sandbox.set(Realm.prototype, getCurrentSandbox());
+RealmProto2ContextRec.set(Realm.prototype, getCurrentContextRec());
Realm.toString = () => 'function Realm() { [shim code] }';

DELETE
shim/src/sandbox.js
-import { getDirectEvalEvaluatorFactory } from './evaluators';
-import { sanitize } from './sanitize';
-
-// The sandbox is shim-specific. It acts as the mechanism
-// to obtain a fresh set of intrinsics together with their
-// associated eval and Function evaluators. This association
-// must be respected since the evaluators are imposing a
-// set of intrinsics, aka the "undeniables".
-
-function createBrowserContext() {
- const iframe = document.createElement('iframe');
-
- iframe.title = 'script';
- iframe.style.display = 'none';
- iframe.setAttribute('aria-hidden', true);
-
- document.body.appendChild(iframe);
-
- return iframe.contentWindow;
-}
-
-export function createSandbox(context) {
- if (context === undefined) {
- context = createBrowserContext();
- }
-
- // The sandbox is entirely defined by these three objects.
- // Reusing the terminology from SES/Caja.
- const sandbox = {
- unsafeGlobal: context,
- unsafeEval: context.eval,
- unsafeFunction: context.Function
- };
-
- // Create the evaluator factory that will generate the evaluators
- // for each compartment realm.
- sandbox.evalEvaluatorFactory = getDirectEvalEvaluatorFactory(sandbox);
-
- sanitize(sandbox);
- return sandbox;
-}

MODIFY
shim/src/sanitize.js
shim/src/sanitize.js
@@ -4,9 +4,9 @@ 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 sandbox.
+// to execute in the context.
-export function sanitize(sandbox) {
- repairAccessors(sandbox);
- repairFunctions(sandbox);
+export function sanitize(contextRec) {
+ repairAccessors(contextRec);
+ repairFunctions(contextRec);
}

MODIFY
shim/src/symbols.js
shim/src/symbols.js
-export const RealmRecord = Symbol('Realm Slot');
export const Intrinsics = Symbol('Intrinsics Slot');
export const GlobalObject = Symbol('GlobalObject Slot');
export const DirectEvalEvaluator = Symbol('DirectEvalEvaluator Slot');
-export const FunctionEvaluator = Symbol('FunctionEvaluator Slot');
-export const ShimSandbox = Symbol('Shim Sandbox');
+export const ContextRec = Symbol('Shim Context');

Added support for nodeJS