Self-hosted builtins in SpiderMonkey

Since Firefox 17, SpiderMonkey has the ability to self-host built-in functions in JavaScript. These docs describe the current state as of Nightly 45.

Differences from normal JavaScript

All self-hosted code is strict, so self-hosted functions invoked in a null or undefined scope won't be run in the scope of the global object.

Self-hosted code has access to some functionality that's not available to normal JS code. Most importantly, it's possible to invoke any function within the scope of any object using the syntax callFunction(fun, receiver, ...args) (or callContentFunction, see below), which causes fun to be called within the scope of receiver with ...args as its arguments. In contrast to Function.prototype.call, this syntax makes it impossible for other code to interfere and gets compiled to bytecode that doesn't have any overhead compared to a normal function invocation.

OTOH, normal method calls are forbidden in self-hosted code. So instead of receiver.fun(arg1, ..., argN) you have to use callFunction(fun, receiver, arg1, ... argN). This restriction was added because otherwise it's extremely easy to accidentally call methods that have been changed by content, changing the behavior from what was expected.

If the fun can potentially be a content-provided function, callContentFunction has to be used. This is to prevent accidentally calling content functions when assuming that content can't interfere with behavior. E.g., if receiver is an object that content has access to, then callFunction(receiver.fun, receiver) wouldn't be guaranteed to work, because content might have changed the value of receiver.fun, so callContentFunction(receiver.fun, receiver) has to be used.

Self-hosted functions by default are not constructors and do not have a prototype property, so that they meet the requirements for standard built-in functions as described in the ECMAScript Language Specification 5.1, clause 15. To make a self-hosted function a constructor, call MakeConstructible(FunctionName) after the function declaration. A prototype property can be added from the self-hosted code itself.

All self-hosted functions have direct access to each other and can rely on references being stable, i.e. not changeable by client JS code.

OTOH, self-hosted code doesn't have access to most of the C++-implemented builtins. You can not, for example, use Function.prototype.apply directly. A select set of C++-implemented builtins is installed via the intrinsic_functions array in SelfHosting.cpp, as described below. Using the same mechanism, C++-implemented helper functions are made available to self-hosted code. Some general-purpose functions provided in this way are:

  • The abstract operations ToObject, ToInteger, and IsCallable specified in the ECMAScript Language Specification.
  • ThrowTypeError, ThrowRangeError, ThrowSyntaxError, which self-hosted code should use instead of throw so that the error message is specified in js.msg and can be localized.
  • The MakeConstructible function described above.

The file Utilities.js provides some additional, JS-implemented helper functions. Of note, it provides implementations of List and Record, types defined in the ECMAScript specifications that are similar to Array and Object, but can't be modified by application code.

The SelfHostingDefines.h header contains

Adding self-hosted functions to host objects

First, the code has to be embedded into SpiderMonkey. The easiest way to accomplish that is to add the code to a pre-existing .js file in builtin/. If it doesn't fit into any of those, create a new .js file in that directory and add it to the selfhosted.inputs list in moz.build.

To add a self-hosted function as a method to a host object, add it to that host object's JSFunctionSpec array using the JS_SELF_HOSTED_FN macro. Example:

JS_SELF_HOSTED_FN("forEach",     "ArrayForEach",     1,0)

This causes the self-hosted function ArrayForEach to be installed as the host object's method forEach.

Making JSNatives available to self-hosted code

For a JSNative to be available to self-hosted code, add it to the intrinsic_functions JSFunctionSpec array in SelfHosting.cpp.

Debugging self-hosted code

Self-hosted code by default is hidden from client JavaScript code; in particular, self-hosted frames will be filtered out of the stack traces of exceptions. To include self-hosted frames in stack traces (in debug builds only), set the environment variable MOZ_SHOW_ALL_JS_FRAMES.