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
.
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.
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.
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)
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.
The global object associated with the realm.
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)
A realm object can hook into the semantics of code evaluation using the three overrideable eval
methods.
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
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
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
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.
Is this proposal alive?