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
, andIsCallable
specified in the ECMAScript Language Specification. ThrowTypeError
,ThrowRangeError, ThrowSyntaxError
, which self-hosted code should use instead ofthrow
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
.
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 js/src
subdirectory.
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.
Examples:
# Start the shell with external self-hosted JS from within your build directory's 'js/src' subdir:
MOZ_SELFHOSTEDJS='./selfhosted.js' ./js
# 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