All self-hosted code is strict, so self-hosted functions invoked in a
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
...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
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
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
IsCallablespecified in the ECMAScript Language Specification.
ThrowRangeError, ThrowSyntaxError, which self-hosted code should use instead of
throwso that the error message is specified in js.msg and can be localized.
MakeConstructiblefunction described above.
The file Utilities.js provides some additional, JS-implemented helper functions. Of note, it provides implementations of
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
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
Testing self-hosted code without recompiling
Because recompiling can take a long time, the ability to test self-hosted code without it is very convenient. Therefore, the JS shell and all Gecko-based applications support loading self-hosted JS code from an external file at startup.
Pre-processing JS code for self-hosting
After setting up an environment for compiling Spidermonkey, simply run
make -C js/src selfhosting to pre-process the self-hosted JS code. This produces the file
selfhosted.js in your build directory's
Loading external JS code at startup
At startup, the Shell and Gecko-based applications check for the environment variable
MOZ_SELFHOSTEDJS. If that is set, it is used to load a file containing pre-processed JS code instead of using the embedded code for self-hosting.
# Start the shell with external self-hosted JS from within your build directory's 'js/src' subdir:
# Start Firefox with external self-hosted JS from within your build dir: MOZ_SELFHOSTEDJS='./js/src/js/src/selfhosted.js' dist/bin/run-mozilla.sh dist/bin/firefox-bin