Skip to content

Instantly share code, notes, and snippets.

@dherman
Last active March 8, 2024 07:04
Show Gist options
  • Star 56 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save dherman/7568885 to your computer and use it in GitHub Desktop.
Save dherman/7568885 to your computer and use it in GitHub Desktop.
ES6 Realms API

Notational Conventions

This section describes the conventions used here to describe type signatures.

A [T] is an array-like value (only ever used read-only in this API), i.e., one with an integer length and whose indexed properties from 0 to length - 1 are of type T.

A type T? should be read as T | undefined -- that is, an optional value that may be undefined.

Realms

A realm object abstracts the notion of a distinct global environment, with its own global object, copy of the standard library, and "intrinsics" (standard objects that are not bound to global variables, like the initial value of Object.prototype).

Extensible web: This is the dynamic equivalent of a same-origin <iframe> without DOM.

Realm Constructor

class Realm {
  constructor: (target: object?, handler: object?) -> void,

  // intended to be overridden by subclasses
  indirectEval(...any) -> stringable,             // default: no translation
  directEval(self: any, ...any) -> stringable,    // default: no translation
  nonEval(self: any, callee: any, ...any) -> any, // default: call callee on args
  init() -> void,                                 // default: make global instanceof global.Object and define standard globals

  // accessor methods
  get stdlib() -> PropertyDescriptorMap,          // property descriptor map for standard globals
  get intrinsics() -> { string: any, ... },       // original values of standard %foo% intrinsics

  // inherited accessor properties
  get global() -> object,                         // access this realm's global object

  // public methods
  eval(stringable) -> any                         // do an indirect eval in this realm
}

The constructor arguments are optional and have the same signature as the arguments to new Proxy. If at least one is provided, then the global object of the realm is created as if via new Proxy within the new realm. Otherwise, the global object of the realm is an ordinary object with the new realm's intrinsic %ObjectPrototype% as its [[Prototype]].

The indirectEval method allows custom evaluation behavior for indirect calls to the eval function. By default, it simply returns the source unmodified.

The directEval method allows custom evaluation behavior for direct calls to the eval function. By default, it simply returns the source unmodified.

The nonEval method allows custom evaluation behavior for direct function calls that appear to be direct calls to eval but whose callee turns out not to be the eval function. By default, it simply calls the callee on the arguments, using the self parameter as the this binding.

The init method allows custom initialization of the global state of the realm at construction time. By default, the init method defines the standard library on the global object with standard values and attributes.

Realm Properties

stdlib

A property descriptor map containing this realm's standard global bindings, with their standard attributes, of the ECMAScript standard library. These can be easily copied to a global object via

Object.defineProperties(global, realm.stdlib)

intrinsics

A dictionary object containing the initial values of this realm's intrinsics. For each intrinsic %Foo% there is an entry Foo in the dictionary. For example, the %ObjectPrototype% and %GeneratorFunction% intrinsics are mapped as ObjectPrototype and GeneratorFunction, respectively.

global

The global object associated with the realm.

Realm Methods

eval

Synchronously execute a top-level script. The source is interpreted as a Script and evaluated with this bound to the realm's global object.

Extensible web: This is the dynamic equivalent of a <script> in HTML.

// eval : (string) -> any
v = r.eval(src)

Meta-object protocol

A realm object can hook into the semantics of code evaluation using the three overrideable eval methods.

Indirect eval

The standard builtin eval function, when called indirectly as a first-class function, delegates to the indirectEval method to translate its source. For example:

class MyRealm extends Realm {
  indirectEval(src) {
    console.log("HELLO WORLD I AM INTERCEPTING YOUR TRANSMISSION");
    return src.replace(/foo/, "42");
  }
}
var r = new MyRealm();
var f = r.eval("eval"); // return the realm's global eval function
console.log(f("foo"));  // HELLO WORLD I AM INTERCEPTING YOUR TRANSMISSION
                        // 42

Direct eval

The special direct eval syntax delegates to the directEval method to translate its source. For example:

class MyRealm extends Realm {
  directEval() {
    console.log("INTERCEPTING DIRECT EVAL");
    return "1000";
  }
}
console.log(r.eval("eval('99')")); // INTERCEPTING DIRECT EVAL
                                   // 1000

Non-eval

The special direct eval syntax delegates to the nonEval method when the callee turns out not to be the builtin eval function. For example:

class MyRealm extends Realm {
  nonEval(self, callee, ...args) {
    console.log("INTERCEPTING NON-EVAL");
    return callee.apply(self, args);
  }
}
console.log(r.eval("(function(eval) { return eval(NaN) })(isNaN)")); // INTERCEPTING NON-EVAL
                                                                     // true

A note on Function

The Function constructor doesn't need any special meta-object protocol, since it can be configured simply by replacing the global binding of Function and the value of Function.prototype.constructor to point to a new function. Similarly, the constructor for generator functions can be replaced by modifying Object.getPrototypeOf(function*(){}).constructor.

As noted in the Nov 21, 2013 TC39 meeting notes, this technique has been used for quite some time in the SES project.

@trusktr
Copy link

trusktr commented Apr 18, 2016

This is the dynamic equivalent of a same-origin <iframe> without DOM.

Is there access to the same DOM as in the main global scope? Or just no DOM at all like a Web Worker?

@azu
Copy link

azu commented Apr 24, 2016

@caridy
Copy link

caridy commented Apr 30, 2016

yeah, we are actually moving to https://github.com/caridy/proposal-realms, where https://github.com/FUDCo/proposal-frozen-realms is a related (dependent of realms) effort, we will be working on both at the same time :)

@erights
Copy link

erights commented Apr 30, 2016

Does https://github.com/caridy/proposal-realms subsume this proposal? Can we just consider that one to be the latest?

@caridy
Copy link

caridy commented Aug 2, 2016

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment