JSAPI User Guide
From MDC
This document explains how to embed SpiderMonkey, the Mozilla JavaScript engine, in your C/C++ program.
JavaScript is widely used for client-side scripts that run in the browser. But Mozilla's JavaScript engine is a library that can be linked into any C/C++ program, not just a browser. Many applications can benefit from scripting. These programs can execute JavaScript code from C using the SpiderMonkey API.
[edit] What SpiderMonkey does
The JavaScript engine compiles and executes scripts containing JavaScript statements and functions. The engine handles memory allocation for the objects needed to execute scripts, and it cleans up—garbage collects—objects it no longer needs.
SpiderMonkey supports versions 1.0 through 1.8 of the JavaScript language. JS 1.3 and later conform to the ECMAScript specification, ECMA 262-3. Later versions also contain Mozilla extensions such as array comprehensions and generators. SpiderMonkey also supports E4X (optionally).
The word JavaScript may bring to mind features such as event handlers (like onclick), DOM objects, window.open, and XMLHttpRequest. But in Mozilla, all of these features are actually provided by other components, not the SpiderMonkey engine itself. SpiderMonkey provides a few core JavaScript data types—numbers, strings, Arrays, Objects, and so on—and a few methods, such as Array.push. It also makes it easy for each application to expose some of its own objects and functions to JavaScript code. Browsers expose DOM objects. Your application will expose objects that are relevant for the kind of scripts you want to write. It is up to the application developer to decide what objects and methods are exposed to scripts.
[edit] Using the SpiderMonkey library
Your application can use SpiderMonkey like any other C/C++ library. To build SpiderMonkey from source, see SpiderMonkey Build Documentation. You can build SpiderMonkey as a static library or as a shared library.
Some platforms (such as Debian Linux) provide SpiderMonkey as a prebuilt package; this can be easier to install but harder to debug.
C/C++ code accesses SpiderMonkey via the JSAPI, by including the header "jsapi.h". The JSAPI provides functions for setting up the JavaScript runtime, compiling and executing scripts, creating and examining JavaScript data structures, handling errors, enabling security checks, and debugging scripts.
An overview of JSAPI functionality follows. For more details, see the JSAPI Reference.
[edit] Key Spidermonkey concepts
In order to run any JavaScript code in SpiderMonkey, an application must have three key elements: a JSRuntime, a JSContext, and a global object. This section describes what these things are. The next section explains how to set them up, using JSAPI functions.
Runtimes. A JSRuntime, or runtime, is the space in which the JavaScript variables, objects, scripts, and contexts used by your application are allocated. Every JSContext and every object in an application lives within a JSRuntime. They cannot travel to other runtimes or be shared across runtimes. Most applications only need one runtime.
Contexts. A JSContext, or context, is like a little machine that can do many things involving JavaScript code and objects. It can compile and execute scripts, get and set object properties, call JavaScript functions, convert JavaScript data from one type to another, create objects, and so on. Almost all JSAPI functions require a JSContext * as the first argument, just like most <stdio.h> functions require a FILE *.
There is a close association between contexts and threads. Simple, single-threaded applications can use a single context for everything. But each context can only do one thing at a time, so in a multi-threaded application, only one thread at a time should use any given context. Such an application typically has one context per thread. JavaScript objects, on the other hand, are not permanently associated with the script, thread, or context that created them. They can be shared among many scripts or even many threads, as shown in the figure below.
Figure 1.1 illustrates the relationship of scripts to the runtime, contexts, and objects.
Global objects. Lastly, the global object contains all the classes, functions, and variables that are available for JavaScript code to use. Whenever JavaScript code does something like window.open("http://www.mozilla.org/"), it is accessing a global property, in this case window. JSAPI applications have full control over what global properties scripts can see. The application starts out by creating an object and populating it with the standard JavaScript classes, like Array and Object. Then it adds whatever custom classes, functions, and variables (like window) the application wants to provide; see Custom objects below. Each time the application runs a JS script (using, for example, JS_EvaluateScript), it provides the global object for that script to use. As the script runs, it can create global functions and variables of its own. All of these functions, classes, and variables are stored as properties of the global object.
[edit] JSAPI basics
Each of the three key elements described in the previous section requires a few JSAPI calls:
- The runtime: Use
JS_NewRuntimeto create it andJS_DestroyRuntimeto clean it up when you're done. When your application is done with SpiderMonkey altogether, useJS_ShutDownto free any remaining cached resources. (This is a mere nicety if the process is about to exit anyway. But as that is not always the case, callingJS_Shutdownis a good habit to get into.)
- The context: Use
JS_NewContextandJS_DestroyContext. For maximum ECMAScript standard compliance, applications should also useJS_SetOptionsto enableJSOPTION_VAROBJFIX. To get the latest JavaScript language features, applications may useJS_SetVersion. Error reporting is also per-context and is enabled usingJS_SetErrorReporter.
- The global object: To create this object, you first need a
JSClasswith theJSCLASS_GLOBAL_FLAGSoption. The example below defines a very basicJSClass(namedglobal_class) with no methods or properties of its own. UseJS_NewObjectto create a global object. UseJS_InitStandardClassesto populate it with the standard JavaScript globals.
This may seem like a lot of pieces for a simple application. It amounts to about 50 lines of code, as shown below. But the JSAPI is designed to scale to applications that need many threads, many contexts, and many global objects. It is a fine-grained API, supporting many different combinations of the parts, and giving applications precise control over how SpiderMonkey behaves.
Here is the boilerplate code necessary for a minimal JSAPI application. It contains everything described above.
#include "jsapi.h" /* The class of the global object. */ static JSClass global_class = { "global", JSCLASS_GLOBAL_FLAGS, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, JSCLASS_NO_OPTIONAL_MEMBERS }; /* The error reporter callback. */ void reportError(JSContext *cx, const char *message, JSErrorReport *report) { fprintf(stderr, "%s:%u:%s\n", report->filename ? report->filename : "<no filename>", (unsigned int) report->lineno, message); } int main(int argc, const char *argv[]) { /* JS variables. */ JSRuntime *rt; JSContext *cx; JSObject *global; . . . /* Create a JS runtime. */ rt = JS_NewRuntime(8L * 1024L * 1024L); if (rt == NULL) return 1; /* Create a context. */ cx = JS_NewContext(rt, 8192); if (cx == NULL) return 1; JS_SetOptions(cx, JSOPTION_VAROBJFIX); JS_SetVersion(cx, JSVERSION_LATEST); JS_SetErrorReporter(cx, reportError); /* Create the global object. */ global = JS_NewObject(cx, &global_class, NULL, NULL); if (global == NULL) return 1; /* Populate the global object with the standard globals, like Object and Array. */ if (!JS_InitStandardClasses(cx, global)) return 1; . . . /* Your application code here. This may include JS API calls to create your own custom JS objects and run scripts. */ . . . /* Cleanup. */ JS_DestroyContext(cx); JS_DestroyRuntime(rt); JS_ShutDown(); return 0; }
A typical JSAPI application should also run garbage collection periodically. An application can do this by installing a branch callback, using JS_SetBranchCallback, and periodically calling JS_MaybeGC from that.
Multithreaded applications have additional basic requirements, including build requirements. See JS_THREADSAFE.
[edit] JSContext
Because there is a certain amount of overhead associated with allocating and maintaining contexts, a JSAPI application should:
- Create only as many contexts as it needs at one time.
- Keep contexts for as long as they may be needed, rather than destroying and recreating them as needed.
If your application creates multiple runtimes, the application may need to know which runtime a context is associated with. In this case, use JS_GetRuntime.
Use JS_SetContextPrivate and JS_GetContextPrivate to associate application-specific data with a context.
[edit] More sample code
The following examples illustrate how to achieve a few different effects using the JSAPI. If any of these examples is unclear, read on for further explanation.
Note that the most important example is in the JSAPI basics section above. More JSAPI code samples appear in the JSAPI Phrasebook.
[edit] Defining objects and properties
/* Statically initialize a class to make "one-off" objects. */ JSClass my_class = { "MyClass", /* All of these can be replaced with the corresponding JS_*Stub function pointers. */ my_addProperty, my_delProperty, my_getProperty, my_setProperty, my_enumerate, my_resolve, my_convert, my_finalize }; JSObject *obj; /* * Define an object named in the global scope that can be enumerated by * for/in loops. The parent object is passed as the second argument, as * with all other API calls that take an object/name pair. The prototype * passed in is null, so the default object prototype will be used. */ obj = JS_DefineObject(cx, globalObj, "myObject", &my_class, NULL, JSPROP_ENUMERATE); /* * Define a bunch of properties with a JSPropertySpec array statically * initialized and terminated with a null-name entry. Besides its name, * each property has a "tiny" identifier (MY_COLOR, e.g.) that can be used * in switch statements (in a common my_getProperty function, for example). */ enum my_tinyid { MY_COLOR, MY_HEIGHT, MY_WIDTH, MY_FUNNY, MY_ARRAY, MY_RDONLY }; static JSPropertySpec my_props[] = { {"color", MY_COLOR, JSPROP_ENUMERATE}, {"height", MY_HEIGHT, JSPROP_ENUMERATE}, {"width", MY_WIDTH, JSPROP_ENUMERATE}, {"funny", MY_FUNNY, JSPROP_ENUMERATE}, {"array", MY_ARRAY, JSPROP_ENUMERATE}, {"rdonly", MY_RDONLY, JSPROP_READONLY}, {0} }; JS_DefineProperties(cx, obj, my_props); /* * Given the above definitions and call to JS_DefineProperties, obj will * need this sort of "getter" method in its class (my_class, above). See * the example for the "It" class in js.c. */ static JSBool my_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { if (JSVAL_IS_INT(id)) { switch (JSVAL_TO_INT(id)) { case MY_COLOR: *vp = . . .; break; case MY_HEIGHT: *vp = . . .; break; case MY_WIDTH: *vp = . . .; break; case MY_FUNNY: *vp = . . .; break; case MY_ARRAY: *vp = . . .; break; case MY_RDONLY: *vp = . . .; break; } } return JS_TRUE; }
[edit] Defining functions
/* Define a bunch of native functions first: */
static JSBool
my_abs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
jsdouble x, z;
if (!JS_ValueToNumber(cx, argv[0], &x))
return JS_FALSE;
z = (x < 0) ? -x : x;
return JS_NewDoubleValue(cx, z, rval);
}
. . .
/*
* Use a JSFunctionSpec array terminated with a null name to define a
* bunch of native functions.
*/
static JSFunctionSpec my_functions[] = {
/* name native nargs */
{"abs", my_abs, 1},
{"acos", my_acos, 1},
{"asin", my_asin, 1},
. . .
{0}
};
/*
* Pass a particular object to define methods for it alone. If you pass
* a prototype object, the methods will apply to all instances past and
* future of the prototype's class (see below for classes).
*/
JS_DefineFunctions(cx, globalObj, my_functions);
[edit] Defining classes
This pulls together the above API elements by defining a constructor function, a prototype object, and properties of the prototype and of the constructor, all with one API call.
Initialize a class by defining its constructor function, prototype, and per-instance and per-class properties. The latter are called "static" below by analogy to Java. They are defined in the constructor object's scope, so that MyClass.myStaticProp works along with new MyClass().
JS_InitClass takes a lot of arguments, but you can pass NULL for any of the last four if there are no such properties or methods.
Note that you do not need to call JS_InitClass to make a new instance of that class—otherwise there would be a chicken-and-egg problem making the global object—but you should call JS_InitClass if you require a constructor function for script authors to call via new, and/or a class prototype object (MyClass.prototype) for authors to extend with new properties at run time. In general, if you want to support multiple instances that share behavior, use JS_InitClass.
protoObj = JS_InitClass(cx, globalObj, NULL, &my_class, /* native constructor function and min arg count */ MyClass, 0, /* prototype object properties and methods -- these will be "inherited" by all instances through delegation up the instance's prototype link. */ my_props, my_methods, /* class constructor properties and methods */ my_static_props, my_static_methods);
[edit] Running scripts
/* These should indicate source location for diagnostics. */ char *filename; uintN lineno; /* * The return value comes back here -- if it could be a GC thing, you must * add it to the GC's "root set" with JS_AddRoot(cx, &thing) where thing * is a JSString *, JSObject *, or jsdouble *, and remove the root before * rval goes out of scope, or when rval is no longer needed. */ jsval rval; JSBool ok; /* * Some example source in a C string. Larger, non-null-terminated buffers * can be used, if you pass the buffer length to JS_EvaluateScript. */ char *source = "x * f(y)"; ok = JS_EvaluateScript(cx, globalObj, source, strlen(source), filename, lineno, &rval); if (ok) { /* Should get a number back from the example source. */ jsdouble d; ok = JS_ValueToNumber(cx, rval, &d); . . . }
[edit] Calling functions
/* Call a global function named "foo" that takes no arguments. */ ok = JS_CallFunctionName(cx, globalObj, "foo", 0, 0, &rval); jsval argv[2]; /* Call a function in obj's scope named "method", passing two arguments. */ argv[0] = . . .; argv[1] = . . .; ok = JS_CallFunctionName(cx, obj, "method", 2, argv, &rval);
[edit] Garbage collection
If your JSAPI application crashes, it is likely due to a GC-related error. The application must ensure that the garbage collector can reach all the objects, numbers, and strings that are still being used. Otherwise, the GC will free the memory occupied by those values, leading to a probable crash the next time your program tries to use them.
There are many ways to ensure that a value is GC-reachable.
- If you just need the value to remain reachable for the duration of a
JSNativecall, store it in*rvalor an element of theargvarray. The values stored in those locations are always reachable. To get extraargvslots, useJSFunctionSpec.extra.
- If a custom object needs certain values to remain in memory, just store the values in properties of the object. As long as the object is reachable, its properties will remain reachable. If these values must not be accessible from JavaScript, use reserved slots instead. Or store the values in private data and implement
JSClass.mark.
- If a function creates new objects, strings, or numbers, it can use
JS_EnterLocalRootScopeandJS_LeaveLocalRootScopeto keep those values alive for the duration of the function.
- To keep a value alive permanently, store it in a GC root.
Still, GC bugs do occur. These two functions, both available only in DEBUG builds, are especially useful for debugging GC-related crashes:
- Use
JS_SetGCZealto enable extra garbage collection. GC zeal usually causes a GC-related crash to occur much sooner (closer to its cause) and more reliably. It's for development and debugging only, because the extra garbage collection makes JS very slow.
- Use
JS_DumpHeapto dump the JS heap or specific interesting parts of it.
See SpiderMonkey Garbage Collection Tips for more details.
[edit] Initializing built-in and global JS objects
The JavaScript engine provides several built-in objects that simplify some of your development tasks. For example, the built-in Array object makes it easy for you to create and manipulate array structures in the JS engine. Similarly, the Date object provides a uniform mechanism for working with and handling dates. For a complete list of built-in objects supported in the engine, see the reference entry for JS_InitStandardClasses.
The JS engine always uses function and global objects. In general, the global object resides behind the scenes, providing a default scope for all other JS objects and global variables you create and use in your applications. Before you can create your own objects, you will want to initialize the global object. The function object enables objects to have and call constructors.
A single API call, JS_InitStandardClasses, initializes the global and function objects and the built-in engine objects so that your application can use them:
if (!JS_InitStandardClasses(cx, global)) return JS_FALSE; /* Initialization failed. */
JS_InitStandardClasses returns JS_FALSE if initialization fails.
You can specify a different global object for your application. For example, the Firefox browser uses its own global object, window. To change the global object for you application, call JS_SetGlobalObject.
[edit] Creating and initializing custom objects
In addition to using the engine's built-in objects, you will create, initialize, and use your own JS objects. This is especially true if you are using the JS engine with scripts to automate your application. Custom JS objects can provide direct program services, or they can serve as interfaces to your program's services. For example, a custom JS object that provides direct service might be one that handles all of an application's network access, or might serve as an intermediary broker of database services. Or a JS object that mirrors data and functions that already exist in the application may provide an object-oriented interface to C code that is not otherwise, strictly-speaking, object-oriented itself. Such a custom object acts as an interface to the application itself, passing values from the application to the user, and receiving and processing user input before returning it to the application. Such an object might also be used to provide access control to the underlying functions of the application.
There are two ways to create custom objects that the JS engine can use:
- Write a JS script that creates an object, its properties, methods, and constructor, and then pass the script to the JS engine at run time.
- Embed code in your application that defines the object's properties and methods, call the engine to initialize a new object, and then set the object's properties through additional engine calls. An advantage of this method is that your application can contain native methods that directly manipulate the object embedding.
In either case, if you create an object and then want it to persist in the run time where it can be used by other scripts, you must root the object by calling JS_AddRoot or JS_AddNamedRoot. Using these functions ensures that the JS engine will keep track of the objects and clean them up during garbage collection, if appropriate.
[edit] Creating an object from a script
One reason to create a custom JS object from a script is when you only need an object to exist as long as the script that uses it is executing. To create objects that persist across script calls, you can embed the object code in your application instead of using a script.
Note: You can also use scripts to create persistent objects, too.
To create a custom object using a script:
- Define and spec the object. What is it intended to do? What are its data members (properties)? What are its methods (functions)? Does it require a run time constructor function?
- Code the JS script that defines and creates the object. For example:
function myfun(){
var x = newObject();
.
.
.
}
NOTE: Object scripting using JavaScript occurs outside the context of embedding the JS engine in your applications. For more information about object scripting, see the Client-Side JavaScript Guide and the Server-Side JavaScript Guide.
Embed the appropriate JS engine call(s) in your application to compile and execute the script. You have two choices: 1.) compile and execute a script with a single call to
JS_EvaluateScript,JS_EvaluateUCScriptor 2.) compile the script once with a call toJS_CompileScriptorJS_CompileUCScript, and then execute it repeatedly with individual calls toJS_ExecuteScript. The "UC" versions of these calls provide support for Unicode-encoded scripts.
An object you create using a script only can be made available only during the lifetime of the script, or can be created to persist after the script completes execution. Ordinarily, once script execution is complete, its objects are destroyed. In many cases, this behavior is just what your application needs. In other cases, however, you will want object persistence across scripts, or for the lifetime of your application. In these cases you need to embed object creation code directly in your application, or you need to tie the object directly to the global object so that it persists as long as the global object itself persists.
[edit] Custom objects
Embedding a custom JS object in an application is useful when object persistence is required or when you know that you want an object to be available to several scripts. For example, a custom object that represents a user's ID and access rights may be needed during the entire lifetime of the application. It saves overhead and time to create and populate this object once, instead of recreating it over and over again with a script each time the user's ID or permissions need to be checked.
One way to embed a custom object in an application is to:
- Create a
JSPropertySpecdata type, and populate it with the property information for your object, including the name of the property's get and set methods. - Create a
JSFunctionSpecdata type, and populate it with information about the methods used by your object. - Create the actual C functions that are executed in response to your object's method calls.
- Call to
JS_NewObjectorJS_ConstructObjectto instantiate the object. - Call
JS_DefineFunctionsto create the object's methods. - Call
JS_DefinePropertiesto create the object's properties.
The code that describes persistent, custom JS objects should be placed near the start of application execution, before any code that relies upon the prior existence of the object. Embedded engine calls that instantiate and populate the custom object should also appear before any code that relies on the prior existence of the object.
Note: An alternate, and in many cases, easier way to create a custom object in application code is to call JS_DefineObject to create the object, and then make repeated calls to JS_SetProperty to set the object's properties. For more information about defining an object, see JS_DefineObject . For more information about setting an object's properties, see JS_SetProperty.
[edit] Providing private data for objects
Like contexts, you can associate large quantities of data with an object without having to store the data in the object itself. Call JS_SetPrivate to establish a pointer to private data for the object, and call JS_GetPrivate to retrieve the pointer so that you can access the data. Your application is responsible for creating and managing this optional private data.
To create private data and associate it with an object:
- Establish the private data as you would a normal C void pointer variable.
- Call
JS_SetPrivate, specify the object for which to establish private data, and specify the pointer to the data.
For example:
JS_SetPrivate(cx, obj, pdata);
To retrieve the data at a later time, call JS_GetPrivate, and pass the object as an argument. This function returns the pointer to an object's private data:
pdata = JS_GetPrivate(cx, obj);
[edit] Handling Unicode
The JS engine now provides Unicode-enabled versions of many API functions that handle scripts, including JS functions. These functions permit you to pass Unicode-encoded scripts directly to the engine for compilation and execution. The following table lists standard engine functions and their Unicode equivalents:
Unicode-enabled functions work exactly like their traditional namesakes, except that where traditional functions take a char * argument, the Unicode versions take a jschar * argument.
[edit] Working with JS data types
JavaScript defines its own data types. Some of these data types correspond directly to their C counterparts. Others, such as JSObject, jsdouble, and JSString, are specific to JavaScript.
Generally, you declare and use JS data types in your application just as you do standard C data types. The JS engine, however, keeps separate track of JS data type variables that require more than a word of storage: JSObject, jsdouble, and JSString. Periodically, the engine examines these variables to see if they are still in use, and if they are not, it garbage collects them, freeing the storage space for reuse.
Garbage collection makes effective reuse of the heap, but overly frequent garbage collection may be a performance issue. You can control the approximate frequency of garbage collection based on the size of the JS run time you allocate for your application in relation to the number of JS variables and objects your application uses. If your application creates and uses many JS objects and variables, you may want to allocate a sufficiently large run time to reduce the likelihood of frequent garbage collection.
NOTE: Your application can also call JS_GC or JS_MaybeGC to force garbage collection at any time. JS_GC forces garbage collection. JS_MaybeGC performs conditional garbage collection only if a certain percentage of space initially allocated to the run time is in use at the time you invoke the function.
[edit] Working with JS Values
Main article: jsval
JavaScript is a dynamically typed language: variables and properties do not have a type that is fixed at compile time. How can a statically typed language, like C or C++, in which all variables have types, interact with JavaScript? The JSAPI provides a data type, jsval, which can contain JavaScript values of any type. A jsval can be a number, a string, a boolean value, a reference to an object (like an Object, Array, Date, or Function), or one of the special values null or undefined.
For integers and boolean values, a jsval contains the value itself. In other cases, the jsval is a pointer to the object, string, or number. A jsval variable fits in the same amount of memory as any other pointer. Using jsvals improves engine efficiency, and permits many API functions to handle a variety of underlying data types.
The JSAPI includes macros that test the JavaScript data type of a jsval. These are:
You can also test the value pointed to by a jsval to see if it is null (JSVAL_IS_NULL) or undefined (JSVAL_IS_VOID).
If a jsval points to a JS data type of JSObject, jsdouble, or JSString, you can cast the jsval to its underlying data type using JSVAL_TO_OBJECT, JSVAL_TO_DOUBLE, and JSVAL_TO_STRING, respectively. This is useful in some cases where your application or a JS engine call requires a variable or argument of a specific data type, rather than a jsval. Similarly, you can convert a JSObject, jsdouble, or JSString * to a jsval using OBJECT_TO_JSVAL, DOUBLE_TO_JSVAL, or STRING_TO_JSVAL.
[edit] Compiled scripts
The easiest way to run a script is to use JS_EvaluateScript, which compiles and executes the script in one go.
But sometimes an application needs to run a script many times. In this case, it may be faster to compile the script once and execute it multiple times.
The JSAPI provides a type, JSScript, that represents a compiled script. The life cycle of a JSScript looks like this:
- The application compiles some JavaScript code using
JS_CompileScript,JS_CompileFile, orJS_CompileFileHandle. These functions return a pointer to a newJSScript.
- The application calls
JS_ExecuteScript(orJS_ExecuteScriptPart) any number of times. It is safe to use aJSScriptin multiple different contexts (and different global objects?), but only within theJSRuntimeand thread in which it was created.
- The application cleans up the compiled script by calling
JS_DestroyScript.
Here is some example code using a compiled script:
/* * Compile a script and execute it repeatedly until an * error occurs. (If this ever returns, it returns false. * If there's no error it just keeps going.) */ JSBool compileAndRepeat(JSContext *cx, const char *filename) { JSScript *script; script = JS_CompileFile(cx, JS_GetGlobalObject(cx), filename); if (script == NULL) return JS_FALSE; /* compilation error */ for (;;) { jsval result; if (!JS_ExecuteScript(cx, JS_GetGlobalObject(cx), script, &result)) break; JS_MaybeGC(cx); } JS_DestroyScript(cx, script); return JS_FALSE; }
Sometimes it is inconvenient for an application to manage the lifetime of each script. Failing to call JS_DestroyScript can lead to memory leaks, and calling it too soon can cause crashes.
An alternative is to tie the lifetime of the compiled script to the lifetime of a JavaScript object, and let the garbage collector destroy the script when it is no longer reachable. The JSAPI provides this feature via the JS_NewScriptObject function. The life cycle of a script using this feature is like this:
- The application compiles some JavaScript code.
- To protect the compiled script from garbage collection, the application creates a compiled script object by calling
JS_NewScriptObjectand makes that object GC-reachable usingJS_SetProperty,JS_SetReservedSlot,JS_AddRoot, or some other function.
- The application executes the compiled script any number of times.
- As the application progresses, eventually it doesn't need the compiled script anymore, and the compiled script object becomes unreachable.
- The garbage collector then eventually collects the unreachable script and its components.
Here is example code demonstrating the technique—but note that this is case is not really complex enough to warrant the use of JS_NewScriptObject. The above example does the same thing much more directly.
/* * Compile a script and execute it repeatedly until an * error occurs. (If this ever returns, it returns false. * If there's no error it just keeps going.) */ JSBool compileAndRepeat(JSContext *cx, const char *filename) { JSScript *script; JSObject *scriptObj; script = JS_CompileFile(cx, JS_GetGlobalObject(cx), filename); if (script == NULL) return JS_FALSE; /* compilation error */ scriptObj = JS_NewScriptObject(cx, script); if (scriptObj == NULL) { JS_DestroyScript(cx, script); return JS_FALSE; } if (!JS_AddNamedRoot(cx, &scriptObj, "compileAndRepeat script object")) return JS_FALSE; for (;;) { jsval result; if (!JS_ExecuteScript(cx, JS_GetGlobalObject(cx), script, &result)) break; JS_MaybeGC(cx); } JS_RemoveRoot(cx, &scrobj); /* scrobj becomes unreachable and will eventually be collected. */ return JS_FALSE; }
[edit] Unicode string support
As with other API calls, the names of Unicode-enabled API string functions correspond one-for-one with the standard engine API string function names as follows: if a standard function name is JS_NewStringCopyN, the corresponding Unicode version of the function is JS_NewUCStringCopyN. Unicode-enabled API string functions are also available for interned string.
[edit] Interned strings
The JavaScript engine maintains a pool of interned strings. The JSAPI offers three functions for interning strings. They are documented under JS_InternString.
String jsids are also interned.
[edit] Security
Many applications use SpiderMonkey to run untrusted code. Since the application decides what custom objects are exposed to scripts, some applications can provide a secure sandbox for scripts simply by exposing no dangerous methods or sensitive information. (Such an application might still want to abort very-long-running scripts, to block simple denial-of-service attacks. See JS_SetBranchCallback.) This simple approach is easy to implement and easy to reason about, but it is not sufficient for all applications.
SpiderMonkey is designed to support custom, application-defined security models. For example, the Firefox browser has a complex and powerful security model. Some JavaScript code ("chrome") has full access to the system. Scripts from web pages ("content") have very limited access. The same origin policy governs a script's access to data and functions from another web page.
The SpiderMonkey security model is based on the Java principals security model. This model provides a common security interface, but the actual security implementation is up to you.
To use SpiderMonkey's security features:
- Decide what security policy you want to enforce.
- Insert a call to
JS_CheckAccessat each point in your application where a security check is necessary. (Some security checks are also built into the JavaScript engine; you must decide what security policy to enforce for each of these checks.)
- Implement one or more
JSPrincipalsobjects in your application. You need oneJSPrincipalsobject for each different set of privileges that a script might have.
- When compiling or executing code, use the JSAPI functions that attach principals to the compiled code. These functions have
ForPrincipalsin the name. They are listed below. The purpose of using these functions is to ensure that your access check callbacks have accurate information about who is trying to access an object.
- Implement access check callback functions (see
JSClass.checkAccessandJS_SetCheckObjectAccessCallback). These will be called fromJS_CheckAccessand sometimes from within the JavaScript engine. An access check callback function can use jsdbgapi.h functions such asJS_FrameIteratorandJS_StackFramePrincipalsto obtain the principals of the code that is trying to perform the checked operation. Then it determines whether to allow the operation to proceed.
| Function | Purpose |
|---|---|
JS_CompileScriptForPrincipals,JS_CompileUCScriptForPrincipals,JS_CompileFileHandleForPrincipals |
Compile a script with security information. (To execute a compiled script, use JS_ExecuteScript.) |
JS_CompileFunctionForPrincipals,JS_CompileUCFunctionForPrincipals |
Create a JavaScript function with security information. |
JS_EvaluateScriptForPrincipals,JS_EvaluateUCScriptForPrincipals |
Compile and execute a security-enabled script. |

