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.
Extensible web: This is the dynamic equivalent of a same-origin <iframe>
without DOM.
r = new Realm(options); // {
// indirectEval: ((...any) -> string)?,
// directEval: {
// translate: ((...any) -> string)?,
// fallback: ((any, any, ...any) -> any)?
//. }?,
// init: ((object) -> void)?
// }?
Every component of the constructor argument is optional (meaning that it can be undefined
and will be provided with a default behavior).
The indirectEval
hook allows custom evaluation behavior for indirect calls to the eval
function.
The directEval
hooks allow custom evaluation behavior for direct calls to the eval
function. The translate
hook is used when a true direct eval
is performed, that is, when a call to an apparent direct eval
turns out to be calling the true eval
function. The fallback
hook is used when an apparent direct eval
turns out to be calling something other than the true eval
function. The first argument passed to the fallback
hook is the this
binding, the second argument is the callee, and the remaining arguments are the arguments that were passed to the call.
The init
callback is passed an object containing the standard ECMAScript builtins for the realm. A host environment may also provide additional builtins specific to the host environment. (For example, the browser would provide atob
and btoa
and other standard browser globals that are not tied to the DOM.) The this
binding of the initializer function is provided as the new realm object. This can be used to initialize the realm, for example:
var realm = new Realm({
init: function(realm, builtins) {
extend(realm.global, builtins);
}
});
If the init
callback is provided, the global object is initialized to an empty object with a null
[[Prototype]]. If it is not provided, the global object defaults to containing the complete set of normal builtins, with their standard attributes as specified in the standard library, and with the realm's Object.prototype
intrinsic as the [[Prototype]].
The init
callback is called with the newly created realm object as its first parameter and with an object containing the normal builtins. The builtins object has the new realm's Object.prototype
as its [[Prototype]].
The global object associated with the realm.
Synchronously execute a top-level script.
Extensible web: This is the dynamic equivalent of a <script>
in HTML.
// eval : (string) -> any
v = r.eval(src)
The configuration of a realm via the Realm
constructor affects two aspects of the semantics of code run within that realm.
The builtin eval
function, when called indirectly as a first-class function, delegates to the indirectEval
hook to translate its source. For example:
var r = new Realm({
indirectEval: function(src) {
console.log("HELLO WORLD I AM INTERCEPTING YOUR TRANSMISSION");
return src.replace(/foo/, "42");
}
});
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 direct eval
syhtax goes through one of two hooks depending on whether the dynamic value of the callee turns out to be the realm's intrinsic eval
function.
Question: Are the terms "valid" and "invalid" good here? If so, should we change directEval.translate
to directEval.valid
and directEval.fallback
to directEval.invalid
?
When the direct eval is valid, that is, its callee evaluates to the realm's intrinsic eval
function, the realm's directEval.translate
hook is invoked. For example:
var r = new Realm({
directEval: {
translate: function(src) {
console.log("INTERCEPTING DIRECT EVAL");
return "1000";
}
}
});
console.log(r.eval("eval('99')")); // INTERCEPTING DIRECT EVAL
// 1000
When a direct eval
is invalid, that is, its callee evaluates to anything other than the realm's intrinsic eval
function, then the directEval.fallback
hook is triggered. It is passed the current this
value, the callee's actual value, and then the actual arguments. For example:
var r = new Realm({
directEval: {
fallback: function(self, callee, ...args) {
console.log("INTERCEPTING INVALID DIRECT EVAL");
return callee.apply(self, args);
}
}
});
console.log(r.eval("(function(eval) { return eval(NaN) })(isNaN)")); // INTERCEPTING INVALID DIRECT 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?