BOOLEAN_TO_JSVAL
DOUBLE_TO_JSVAL
INT_FITS_IN_JSVAL
INT_TO_JSVAL
JS::Add*Root
JS::AutoIdArray
JS::AutoSaveExceptionState
JS::AutoValueArray
JS::AutoVectorRooter
JS::BooleanValue
JS::Call
JS::CallArgs
JS::CloneFunctionObject
JS::Compile
JS::CompileFunction
JS::CompileOffThread
JS::CompileOptions
JS::Construct
JS::CreateError
JS::CurrentGlobalOrNull
JS::DeflateStringToUTF8Buffer
JS::DoubleNaNValue
JS::DoubleValue
JS::Evaluate
JS::FalseValue
JS::Float32Value
JS::GetDeflatedUTF8StringLength
JS::GetFirstArgumentAsTypeHint
JS::GetSelfHostedFunction
JS::Handle
JS::HandleValueArray
JS::IdentifyStandardInstance
JS::Int32Value
JS::IsCallable
JS::MutableHandle
JS::NewFunctionFromSpec
JS::NullHandleValue
JS::NullValue
JS::NumberValue
JS::ObjectOrNullValue
JS::ObjectValue
JS::OrdinaryToPrimitive
JS::PersistentRooted
JS::PropertySpecNameEqualsId
JS::PropertySpecNameIsSymbol
JS::PropertySpecNameToPermanentId
JS::ProtoKeyToId
JS::Remove*Root
JS::Rooted
JS::SetLargeAllocationFailureCallback
JS::SetOutOfMemoryCallback
JS::SourceBufferHolder
JS::StringValue
JS::SymbolValue
JS::ToBoolean
JS::ToInt32
JS::ToInt64
JS::ToNumber
JS::ToPrimitive
JS::ToString
JS::ToUint16
JS::ToUint32
JS::ToUint64
JS::TrueHandleValue
JS::TrueValue
JS::UndefinedHandleValue
JS::UndefinedValue
JS::Value
JSAutoByteString
JSAutoCompartment
JSBool
JSCheckAccessOp
JSClass
JSClass.call
JSClass.flags
JSConstDoubleSpec
JSConvertOp
JSDeletePropertyOp
JSEnumerateOp
JSErrorFormatString
JSErrorReport
JSExceptionState
JSExnType
JSExtendedClass
JSExtendedClass.outerObject
JSExtendedClass.wrappedObject
JSFUN_BOUND_METHOD
JSFUN_GLOBAL_PARENT
JSFastNative
JSFinalizeOp
JSFreeOp
JSFunction
JSFunctionSpec
JSGetObjectOps
JSHasInstanceOp
JSID_EMPTY
JSID_IS_EMPTY
JSID_IS_GCTHING
JSID_IS_INT
JSID_IS_STRING
JSID_IS_SYMBOL
JSID_IS_VOID
JSID_IS_ZERO
JSID_VOID
JSIdArray
JSIteratorOp
JSMarkOp
JSNative
JSNewEnumerateOp
JSNewResolveOp
JSObject
JSObjectOp
JSObjectOps.defaultValue
JSObjectOps.defineProperty
JSObjectOps.destroyObjectMap
JSObjectOps.dropProperty
JSObjectOps.enumerate
JSObjectOps.getAttributes
JSObjectOps.getProperty
JSObjectOps.getRequiredSlot
JSObjectOps.lookupProperty
JSObjectOps.newObjectMap
JSObjectOps.setProto
JSObjectPrincipalsFinder
JSPRINCIPALS_HOLD
JSPrincipals
JSPrincipalsTranscoder
JSProperty
JSPropertyDescriptor
JSPropertyOp
JSPropertySpec
JSProtoKey
JSReserveSlotsOp
JSResolveOp
JSRuntime
JSSecurityCallbacks.contentSecurityPolicyAllows
JSString
JSStringFinalizer
JSTraceOp
JSType
JSVAL_IS_BOOLEAN
JSVAL_IS_DOUBLE
JSVAL_IS_GCTHING
JSVAL_IS_INT
JSVAL_IS_NULL
JSVAL_IS_NUMBER
JSVAL_IS_OBJECT
JSVAL_IS_PRIMITIVE
JSVAL_IS_STRING
JSVAL_IS_VOID
JSVAL_LOCK
JSVAL_NULL
JSVAL_ONE
JSVAL_TO_BOOLEAN
JSVAL_TO_DOUBLE
JSVAL_TO_GCTHING
JSVAL_TO_INT
JSVAL_TO_OBJECT
JSVAL_TO_STRING
JSVAL_TRUE
JSVAL_UNLOCK
JSVAL_VOID
JSVAL_ZERO
JSVersion
JSXDRObjectOp
JS_ASSERT_STRING_IS_FLAT
JS_Add*Root
JS_AddArgumentFormatter
JS_AddExternalStringFinalizer
JS_AddFinalizeCallback
JS_AliasElement
JS_AliasProperty
JS_AlreadyHasOwnProperty
JS_BeginRequest
JS_BindCallable
JS_BufferIsCompilableUnit
JS_CStringsAreUTF8
JS_CallFunction
JS_CheckAccess
JS_CheckForInterrupt
JS_ClearContextThread
JS_ClearDateCaches
JS_ClearNewbornRoots
JS_ClearNonGlobalObject
JS_ClearPendingException
JS_ClearRegExpStatics
JS_ClearScope
JS_CloneFunctionObject
JS_CompareStrings
JS_CompileFileHandleForPrincipals
JS_CompileFileHandleForPrincipalsVersion
JS_CompileFunction
JS_CompileFunctionForPrincipals
JS_CompileScript
JS_CompileScriptForPrincipals
JS_CompileUCFunctionForPrincipalsVersion
JS_CompileUTF8File
JS_CompileUTF8FileHandle
JS_ConcatStrings
JS_ConstructObject
JS_ContextIterator
JS_ConvertArguments
JS_ConvertArgumentsVA
JS_ConvertValue
JS_DecompileFunction
JS_DecompileFunctionBody
JS_DecompileScript
JS_DecompileScriptObject
JS_DeepFreezeObject
JS_DefaultValue
JS_DefineConstDoubles
JS_DefineElement
JS_DefineFunction
JS_DefineFunctions
JS_DefineObject
JS_DefineOwnProperty
JS_DefineProperties
JS_DefineProperty
JS_DefinePropertyWithTinyId
JS_DeleteElement
JS_DeleteElement2
JS_DeleteProperty
JS_DeleteProperty2
JS_DestroyContext
JS_DestroyIdArray
JS_DestroyRuntime
JS_DestroyScript
JS_DoubleIsInt32
JS_DoubleToInt32
JS_DropExceptionState
JS_DumpHeap
JS_DumpNamedRoots
JS_EncodeCharacters
JS_EncodeString
JS_EncodeStringToBuffer
JS_EnterCompartment
JS_EnterCrossCompartmentCall
JS_EnterLocalRootScope
JS_Enumerate
JS_EnumerateDiagnosticMemoryRegions
JS_EnumerateResolvedStandardClasses
JS_EnumerateStandardClasses
JS_ErrorFromException
JS_EvaluateScript
JS_EvaluateScriptForPrincipals
JS_ExecuteRegExp
JS_ExecuteScript
JS_ExecuteScriptPart
JS_ExecuteScriptVersion
JS_FORGET_STRING_FLATNESS
JS_FS
JS_FileEscapedString
JS_Finish
JS_FlattenString
JS_FlushCaches
JS_ForgetLocalRoot
JS_ForwardGetPropertyTo
JS_FreezeObject
JS_GC
JS_GET_CLASS
JS_GetArrayLength
JS_GetArrayPrototype
JS_GetClass
JS_GetClassObject
JS_GetClassPrototype
JS_GetCompartmentPrivate
JS_GetConstructor
JS_GetContextPrivate
JS_GetContextThread
JS_GetDefaultFreeOp
JS_GetElement
JS_GetEmptyString
JS_GetEmptyStringValue
JS_GetErrorPrototype
JS_GetExternalStringClosure
JS_GetExternalStringFinalizer
JS_GetFlatStringChars
JS_GetFunctionArity
JS_GetFunctionCallback
JS_GetFunctionFlags
JS_GetFunctionId
JS_GetFunctionName
JS_GetFunctionObject
JS_GetFunctionPrototype
JS_GetFunctionScript
JS_GetGCParameter
JS_GetGlobalForCompartmentOrNull
JS_GetGlobalForObject
JS_GetGlobalForObject3
JS_GetGlobalForScopeChain
JS_GetGlobalObject
JS_GetImplementationVersion
JS_GetInstancePrivate
JS_GetInternedStringChars
JS_GetLatin1FlatStringChars
JS_GetLatin1InternedStringChars
JS_GetLatin1StringCharsAndLength
JS_GetLocaleCallbacks
JS_GetNaNValue
JS_GetObjectPrototype
JS_GetObjectRuntime
JS_GetOptions
JS_GetOwnPropertyDescriptor
JS_GetParent
JS_GetParentRuntime
JS_GetPendingException
JS_GetPositiveInfinityValue
JS_GetPrivate
JS_GetProperty
JS_GetPropertyAttributes
JS_GetPropertyAttrsGetterAndSetter
JS_GetPropertyDefault
JS_GetPropertyDescriptor
JS_GetPrototype
JS_GetRegExpFlags
JS_GetRegExpSource
JS_GetReservedSlot
JS_GetRuntime
JS_GetRuntimePrivate
JS_GetScopeChain
JS_GetSecurityCallbacks
JS_GetStringBytes
JS_GetStringCharAt
JS_GetStringChars
JS_GetStringCharsAndLength
JS_GetStringEncodingLength
JS_GetStringLength
JS_GetTwoByteExternalStringChars
JS_GetTypeName
JS_GetVersion
JS_HasArrayLength
JS_HasElement
JS_HasInstance
JS_HasOwnProperty
JS_HasProperty
JS_IdArrayGet
JS_IdArrayLength
JS_IdToProtoKey
JS_IdToValue
JS_Init
JS_InitCTypesClass
JS_InitClass
JS_InitStandardClasses
JS_InstanceOf
JS_InternJSString
JS_InternString
JS_IsArrayObject
JS_IsAssigning
JS_IsBuiltinEvalFunction
JS_IsBuiltinFunctionConstructor
JS_IsConstructing
JS_IsConstructing_PossiblyWithGivenThisObject
JS_IsConstructor
JS_IsExceptionPending
JS_IsExtensible
JS_IsExternalString
JS_IsGlobalObject
JS_IsIdentifier
JS_IsNative
JS_IsNativeFunction
JS_IsRunning
JS_IsStopIteration
JS_IterateCompartments
JS_LeaveCompartment
JS_LeaveCrossCompartmentCall
JS_LeaveLocalRootScope
JS_LeaveLocalRootScopeWithResult
JS_LinkConstructorAndPrototype
JS_Lock
JS_LockGCThing
JS_LookupElement
JS_LookupProperty
JS_LooselyEqual
JS_MakeStringImmutable
JS_MapGCRoots
JS_MaybeGC
JS_New
JS_NewArrayObject
JS_NewCompartmentAndGlobalObject
JS_NewContext
JS_NewDateObject
JS_NewDateObjectMsec
JS_NewDependentString
JS_NewDouble
JS_NewDoubleValue
JS_NewExternalString
JS_NewFunction
JS_NewGlobalObject
JS_NewNumberValue
JS_NewObject
JS_NewObjectForConstructor
JS_NewPlainObject
JS_NewPropertyIterator
JS_NewRegExpObject
JS_NewRuntime
JS_NewScriptObject
JS_NewStringCopyN
JS_NewStringCopyZ
JS_NewUCString
JS_NextProperty
JS_Now
JS_NumberValue
JS_ObjectIsDate
JS_ObjectIsFunction
JS_ObjectIsRegExp
JS_PSGS
JS_ParseJSON
JS_PopArguments
JS_PreventExtensions
JS_PropertyStub
JS_PushArguments
JS_PutEscapedString
JS_Remove*Root
JS_RemoveExternalStringFinalizer
JS_RemoveRootRT
JS_ReportError
JS_ReportErrorNumber
JS_ReportOutOfMemory
JS_ReportPendingException
JS_ResolveStandardClass
JS_RestoreExceptionState
JS_SET_TRACING_DETAILS
JS_SameValue
JS_SaveExceptionState
JS_SaveFrameChain
JS_ScheduleGC
JS_SealObject
JS_SetAllNonReservedSlotsToUndefined
JS_SetArrayLength
JS_SetBranchCallback
JS_SetCallReturnValue2
JS_SetCheckObjectAccessCallback
JS_SetCompartmentNameCallback
JS_SetContextCallback
JS_SetDefaultLocale
JS_SetDestroyCompartmentCallback
JS_SetElement
JS_SetErrorReporter
JS_SetExtraGCRoots
JS_SetFunctionCallback
JS_SetGCCallback
JS_SetGCParametersBasedOnAvailableMemory
JS_SetGCZeal
JS_SetGlobalObject
JS_SetICUMemoryFunctions
JS_SetInterruptCallback
JS_SetNativeStackQuota
JS_SetObjectPrincipalsFinder
JS_SetOperationCallback
JS_SetOptions
JS_SetParent
JS_SetPendingException
JS_SetPrincipalsTranscoder
JS_SetPrivate
JS_SetProperty
JS_SetPropertyAttributes
JS_SetPrototype
JS_SetRegExpInput
JS_SetScriptStackQuota
JS_SetThreadStackLimit
JS_SetVersion
JS_SetVersionForCompartment
JS_ShutDown
JS_StrictlyEqual
JS_StringEqualsAscii
JS_StringHasBeenInterned
JS_StringHasLatin1Chars
JS_StringIsFlat
JS_StringToVersion
JS_SuspendRequest
JS_THREADSAFE
JS_ThrowStopIteration
JS_ToggleOptions
JS_TracerInit
JS_TypeOfValue
JS_Unlock
JS_ValueToBoolean
JS_ValueToECMAInt32
JS_ValueToFunction
JS_ValueToId
JS_ValueToInt32
JS_ValueToNumber
JS_ValueToObject
JS_ValueToSource
JS_ValueToString
JS_VersionToString
JS_YieldRequest
JS_freeop
JS_malloc
JS_updateMallocCounter
OBJECT_TO_JSVAL
PRIVATE_TO_JSVAL
Property attributes
STRING_TO_JSVAL
Stored value
jschar
jsdouble
jsid
jsint
This document explains how to embed SpiderMonkey, the Mozilla JavaScript engine, in your 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++ program, not just a browser. Many applications can benefit from scripting. These programs can execute JavaScript code from C++ using the SpiderMonkey API.
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.
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, Array
s, Object
s, 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.
To build SpiderMonkey from source, see SpiderMonkey Build Documentation.
Some systems (such as Debian) provide SpiderMonkey as a prebuilt package. Build it from source instead. Your program will be easier to debug.
C++ code accesses SpiderMonkey via the JSAPI, by including the header "jsapi.h"
. An overview of JSAPI functionality follows. For more details, see the JSAPI Reference.
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. You will need at least one JSRuntime
per thread that uses the JSAPI.
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 *
.
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.
Each of the three key elements described in the previous section requires a few JSAPI calls:
The runtime: Use JS_NewRuntime
to create it and JS_DestroyRuntime
to clean it up when you're done. When your application is done with SpiderMonkey altogether, use JS_ShutDown
to 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, calling JS_Shutdown
is a good habit to get into.)
The context: Use JS_NewContext
and JS_DestroyContext
. For maximum ECMAScript standard compliance, applications should also use JS_SetOptions
to enable JSOPTION_VAROBJFIX
. To get the latest JavaScript language features, applications may use JS_SetVersion
. Error reporting is also per-context and is enabled using JS_SetErrorReporter
.
The global object: To create this object, you first need a JSClass
with the JSCLASS_GLOBAL_FLAGS
option. The example below defines a very basic JSClass
(named global_class
) with no methods or properties of its own. Use JS_NewGlobalObject
to create a global object. Use JS_InitStandardClasses
to populate it with the standard JavaScript globals.
This may seem like a lot of pieces for a simple application. It amounts to about 80 lines of code, as shown below. But the JSAPI is designed to scale to applications that need 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" using namespace JS; // The class of the global object. static JSClass globalClass = { "global", JSCLASS_GLOBAL_FLAGS, JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, nullptr, nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook }; // 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 run(JSContext *cx) { // Enter a request before running anything in the context. JSAutoRequest ar(cx); // Create the global object and a new compartment. RootedObject global(cx); global = JS_NewGlobalObject(cx, &globalClass, nullptr, JS::DontFireOnNewGlobalHook); if (!global) return 1; // Enter the new global object's compartment. JSAutoCompartment ac(cx, global); // 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 JSAPI calls to create your // own custom JS objects and run scripts. return 0; } int main(int argc, const char *argv[]) { // Initialize the JS engine. if (!JS_Init()) return 1; // Create a JS runtime. JSRuntime *rt = JS_NewRuntime(8L * 1024L * 1024L); if (!rt) return 1; // Create a context. JSContext *cx = JS_NewContext(rt, 8192); if (!cx) return 1; JS_SetErrorReporter(rt, reportError); int status = run(cx); // Shut everything down. JS_DestroyContext(cx); JS_DestroyRuntime(rt); JS_ShutDown(); return status; }
Each JSNative
has the same signature, regardless of what arguments it expects to receive from JavaScript.
The JavaScript arguments to the function are given in argc
and vp
. argc
tells how many actual arguments the caller passed, and JS_ARGV(cx, vp)
returns an array of those arguments. The arguments do not have native C++ types like int
and float
; rather, they are jsval
s, JavaScript values. The native function uses JS_ConvertArguments
to convert the arguments to C++ types and store them in local variables. The native function uses JS_SET_RVAL(cx, vp, val)
to store its JavaScript return value.
On success, a JSNative
must call JS_SET_RVAL
and return true
. The value passed to JS_SET_RVAL
is returned to the JavaScript caller.
On failure, a JSNative
calls an error-reporting function, in this case JS_ReportError
, and returns false
. This causes a JavaScript exception to be thrown. The caller can catch the exception using a JavaScript try/catch
statement.
To make native functions callable from JavaScript, declare a table of JSFunctionSpec
s describing the functions. Then call JS_DefineFunctions
.
static JSFunctionSpec myjs_global_functions[] = { JS_FS("rand", myjs_rand, 0, 0), JS_FS("srand", myjs_srand, 0, 0), JS_FS("system", myjs_system, 1, 0), JS_FS_END }; ... if (!JS_DefineFunctions(cx, global, myjs_global_functions)) return false; ...
Once the functions are defined in global
, any script that uses global
as the global object can call them, just as any web page can call alert
. In the environment we have created, the "hello world" script looks like this:
system("echo hello world");
This section aims to fill in the major gaps in the picture of the JSAPI presented so far. To do anything useful with SpiderMonkey, you must read all three sections.
Main article: JS::Value
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, JS::Value
(also with a deprecated jsval
typedef), which can contain JavaScript values of any type. A JS::Value
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 an object, string, or number.
Warning: Like C++ pointers, and unlike JavaScript var
s, a JS::Value
is not automatically initialized to a safe value, and can become a dangling pointer!
A dangling pointer is a pointer that used to point to a valid object, but no longer does because the object no longer exists. Using a dangling pointer can crash a C++ program (or worse). In the case of JS::Value
, the JavaScript garbage collector recycles objects, strings, and numbers that don't appear to be in use, and a JS::Value
by itself does not protect its referent from the garbage collector. See Garbage collection below for crucial information on how to use JS::Value
s safely.
JS::Value
includes member functions to test the JavaScript data type. These are isObject()
, isNumber()
, isInt32()
, isDouble()
, isString()
, isBoolean()
, isNull()
, and isUndefined()
.
If a JS::Value
contains a JSObject
, double
, or JSString
, you can cast it to its underlying data type using the toObject()
, toDouble()
, and toString()
member functions, respectively. This is useful in some cases where your application or a JSAPI function requires a variable or argument of a specific data type, rather than a JS::Value
. Similarly, you can create a JS::Value
wrapping a JSObject
, double
, or JSString
pointer to a JS::Value
using JS::ObjectValue(JSObject&)
, JS::DoubleValue(double)
, or JS::StringValue(JSString*)
.
As it runs, JavaScript code implicitly allocates memory for objects, strings, variables, and so on. Garbage collection is the process by which the JavaScript engine detects when those pieces of memory are no longer reachable—that is, they could not possibly ever be used again—and reclaims the memory.
Garbage collection has two important consequences for JSAPI applications. First, the application must be very careful to ensure that any values it needs are GC-reachable. The garbage collector is rather eager about its job. Any object you leave lying around will be destroyed if you don't tell the JSAPI you're still using it. Second, the application should take steps to reduce the performance impact of 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.
JSNative
call, store it in *rval
or an element of the argv
array. The values stored in those locations are always reachable. To get extra argv
slots, use JSFunctionSpec.extra
.JSClass.mark
.JS_EnterLocalRootScope
and JS_LeaveLocalRootScope
to keep those values alive for the duration of the function.Still, GC bugs do occur. These two functions, both available only in DEBUG
builds, are especially useful for debugging GC-related crashes:
JS_SetGCZeal
to 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.JS_DumpHeap
to dump the SpiderMonkey heap or specific interesting parts of it.See SpiderMonkey Garbage Collection Internals for more details.
Overly frequent garbage collection can be a performance issue. Some applications can reduce the frequency of garbage collection simply by increasing the initial size of the JSRuntime
.
Perhaps the best technique is to perform garbage collection during idle time, when it is least likely to have any impact on the user. By default, the JavaScript engine performs garbage collection when it has no other choice except to grow the process. This means that garbage collection typically happens when memory-intensive code is running, perhaps the worst possible time. An application can trigger garbage collection at a more convenient time by calling JS_GC
or JS_MaybeGC
. JS_GC
forces garbage collection. JS_MaybeGC
performs garbage collection only if it is likely to reclaim a worthwhile amount of memory.
The importance of checking the return value of JSAPI functions, of course, goes without saying. Almost every JSAPI function that takes a JSContext *
argument can fail. The system might run out of memory. There might be a syntax error in a script. Or a script might explicitly throw
an exception.
The JavaScript language has exceptions, and C++ has exceptions, but they are not the same thing. SpiderMonkey does not use C++ exceptions for anything. JSAPI functions never throw C++ exceptions, and when SpiderMonkey calls an application callback, the callback must not throw a C++ exception.
We have already seen one example of how to throw an exception from a JSNative
function. Simply call JS_ReportError
, with printf
-style arguments, and return false
.
rc = system(cmd); if (rc != 0) { /* Throw a JavaScript exception. */ JS_ReportError(cx, "Command failed with exit code %d", rc); return false; }
This is very much like the JavaScript statement throw new Error("Command failed with exit code " + rc);
. Again, note that calling JS_ReportError
does not cause a C++ exception to be thrown. It only creates a new JavaScript Error object and stores it in the context as the current pending exception. The application must also return false
.
Once the C++ function returns false
, the JavaScript engine starts unwinding the JavaScript stack, looking for a catch
or finally
block to execute. But SpiderMonkey's stack unwinding never removes application's C++ functions from the stack. Instead, SpiderMonkey simply returns false
or nullptr
to the application, which can then handle the error as it chooses—or just return false
to let it propagate further up the stack.
Several more examples of throwing and catching exceptions can be found in the JSAPI Phrasebook.
TODO your custom errorreporter
TODO when errors are reported
The JS_Compile
*, JS_Call
*, JS_Execute
*, and JS_Evaluate
* functions automatically pass exceptions to the error reporter in certain cases. Each of these functions checks, just before it returns, to see if an exception is pending in the current JSContext
. If so, it then checks to see if there is any other JavaScript script or function on the stack in that JSContext
. If so, then the exception might yet be caught, so SpiderMonkey does nothing and returns false
, allowing the exception to propagate. But if nothing is on the JavaScript stack, then the uncaught exception is passed to the error reporter and the pending exception is cleared.
The basic consequence is that top-level application code can just set an error reporter and start calling JSAPI functions. It never has to explicitly handle uncaught exceptions; the error reporter is automatically called. An application can disable automatic uncaught-exception handling using the JSOPTION_DONT_REPORT_UNCAUGHT
option, but it must then deal with uncaught exceptions explicitly by calling JS_IsExceptionPending
, JS_GetPendingException
, JS_ReportPendingException
, and/or JS_ClearPendingException
whenever a JSAPI function returns false
or nullptr
.
Another way for a JSNative
callback to report an error is like this:
if (!p) { JS_ReportOutOfMemory(cx); return false; }
This does something subtly different from what JS_ReportError
does.
Most errors, including those raised by JS_ReportError
, are represented as JavaScript exceptions and thus interact with the JavaScript exception-handling language features, try
, catch
, and finally
. However, in some cases we do not want scripts to be able to catch
an error; we want script execution to terminate right away. If the system runs out of memory in the middle of a script, we do not want finally
blocks to execute, because almost anything a script does requires at least a little memory, and we have none. If a script has been running too long and we want to kill it, it's no good to throw an exception—the script could just catch
it and keep going.
Therefore JS_ReportOutOfMemory(cx)
does not set the pending exception. It is an uncatchable error.
If SpiderMonkey runs out of memory, or a JSAPI callback returns false
without an exception pending, this is treated as an uncatchable error. The JavaScript stack is unwound in the normal way except that catch
and finally
blocks are ignored. The most recent JSAPI call returns false
or nullptr
to the application.
An uncatchable error leaves the JSContext
in a good state. It can be used again right away. The application does not have to do anything to “recover” from the error, as far as the JSAPI is concerned. (Of course, if the error is that the system is out of memory, that problem remains to be dealt with.)
Here is some example code that throws an uncatchable error.
/* Call the error reporter, if any. This part is optional. */ JS_ReportError(cx, "The server room is on fire!"); JS_ReportPendingException(cx); /* Make sure the error is uncatchable. */ JS_ClearPendingException(cx); return false;
The following examples illustrate how to achieve a few different effects using the JSAPI.
Note that the most important example is in the "A minimal example" section above. More JSAPI code samples appear in the JSAPI Phrasebook.
/* 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, nullptr, 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 true; }
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 nullptr
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, nullptr, &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);
/* These should indicate source location for diagnostics. */ char *filename; unsigned int 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); . . . }
/* 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);
You should never create more than one JSContext per JSRuntime. Support for multiple JSContexts per JSRuntime will be removed in the future.
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.
For a complete list of built-in objects provided by SpiderMonkey, see JS_InitStandardClasses
.
The global object that an application provides to scripts largely determines what those scripts can do. For example, the Firefox browser uses its own global object, window
. To change the global object for your application, call JS_SetGlobalObject
.
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:
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.
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:
JS_EvaluateScript
, JS_EvaluateUCScript
or 2.) compile the script once with a call to JS_CompileScript
or JS_CompileUCScript
, and then execute it repeatedly with individual calls to JS_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.
An application can create a custom object without bothering with a JSClass
:
JSPropertyOp
for each getter or setter. Write a JSNative
or JSFastNative
for each method.JSPropertySpec
array containing information about your custom object's properties, including getters and setters.JSFunctionSpec
array containing information about your custom object's methods.JS_NewObject
, JS_ConstructObject
, or JS_DefineObject
to create the object.JS_DefineProperties
to define the object's properties.JS_DefineFunctions
to define the object's methods.JS_SetProperty
can also be used to create properties on an object. The properties it creates do not have getters or setters; they are ordinary JavaScript properties.
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:
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);
To pass Unicode data between JavaScript and native code, represent the data in UTF-16 in memory. JavaScript strings, property names, and programs are all made up of jschar
s, which are 16-bit unsigned integers.
Many JSAPI functions operate on null-terminated, 8-bit char
strings. These functions convert their char *
arguments to 16-bit strings by zero-extending each 8-bit char
to 16 bits—unless JS_C_STRINGS_ARE_UTF8
is defined or JS_SetCStringsAreUTF8
has been called, in which case each char *
string is interpreted as UTF-8 Unicode text.
The JSAPI provides jschar
-based versions of many API functions that operate on strings, object properties, and JavaScript code.
jschar
-based functions work exactly like their char
-based namesakes, except that where traditional functions take a char *
argument, the Unicode versions take a jschar *
argument, usually with a separate argument specifying the length of the string in jschar
s.
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:
JS_CompileScript
, JS_CompileUTF8File
, or JS_CompileFileHandle
. These functions return a pointer to a new JSScript
.JS_ExecuteScript
(or JS_ExecuteScriptPart
) any number of times. It is safe to use a JSScript
in multiple different contexts, but only within the JSRuntime
and global in which it was created.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_CompileUTF8File(cx, JS_GetGlobalObject(cx), filename); if (!script) return false; /* compilation error */ for (;;) { jsval result; if (!JS_ExecuteScript(cx, JS_GetGlobalObject(cx), script, &result)) break; JS_MaybeGC(cx); } return false; }
The lifetime of the compiled script is tied to the lifetime of a JavaScript object, the garbage collector destroys 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:
JS_NewScriptObject
and makes that object GC-reachable using JS_SetProperty
, JS_SetReservedSlot
, JS_AddRoot
, or some other function.Here is example code demonstrating the technique—but note that this case is not really complex enough to warrant the use of JS_NewScriptObject
. The above example does the same thing 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_CompileUTF8File(cx, JS_GetGlobalObject(cx), filename); if (!script) return false; /* compilation error */ scriptObj = JS_NewScriptObject(cx, script); if (!scriptObj) { JS_DestroyScript(cx, script); return false; } if (!JS_AddNamedObjectRoot(cx, &scriptObj, "compileAndRepeat script object")) return false; for (;;) { jsval result; if (!JS_ExecuteScript(cx, JS_GetGlobalObject(cx), script, &result)) break; JS_MaybeGC(cx); } JS_RemoveObjectRoot(cx, &scriptObj); /* scriptObj becomes unreachable and will eventually be collected. */ return false; }
Many applications use SpiderMonkey to run untrusted code. In designing this kind of application, it's important to think through the security concerns ahead of time. Your application will need to do several things.
while(true){}
should not hang your application. To stop execution of scripts that run too long, use JS_SetOperationCallback
. Likewise, a function like function f(){f();}
should not crash your application with a stack overflow. To block that, use JS_SetNativeStackQuota
.The first two problems are important but fairly straightforward and will not be discussed further here. The rest of this section tells how you can control scripts' access to data and functionality.
Do you ever worry about your snake eating your mouse? No? If you don't have both a snake and a mouse, you don't have this problem.
Likewise, if your application doesn't have both untrusted users (snakes) and sensitive data or dangerous functionality that's exposed to JavaScript (mice), then you don't need to read any further. The functions and objects created by JS_InitStandardClasses
are safe. They do not provide access to files, the network, or anything browser-related. The most sensitive information they expose to scripts is the current date and time.
One way to keep a snake from eating a mouse is to keep the mouse and the snake in separate cages.
One way to keep user A from accessing user B's sensitive data or dangerous functions is to keep each user's code in a separate sandbox. That is, create a separate JSContext and global object for each user, and always run each script in the appropriate context. When setting up a new global object, simply don't define any functions the user shouldn't have access to. This approach is called object-capabilities security. To learn more about it, watch the movie or read the book.
The metaphor is misleading in one regard: the snake can't reach the mouse because there's a physical barrier in the way. With SpiderMonkey the situation is more subtle. There's no barrier; there's just no way to get there from here. How can a malicious script get a reference to an object from another sandbox? It might as well be in a parallel universe. Even global variables are per-sandbox. There is no getObjectFromOtherSandbox()
function. Your application just needs to take care not to expose any such function to scripts.
In short, never pass one user's data and objects to another user's code, and you'll have no access control issues. SpiderMonkey won't do it if you don't.
Trade-offs. Object-capabilities security is security without run-time security checks. It is easy to implement, easy to reason about, and fast. But in fairness there are some drawbacks. First, the failure mode is pretty severe. If you do accidentally leak an object from one sandbox into another, the genie is out of the bottle. Once a malicious script gets a reference to a single object in a sandbox, it can use that object to get a reference to the sandbox's global object, and from there, almost any other object or function in that sandbox. There is no way to fix it except to destroy both sandboxes and start over. A second drawback is that the system doesn't automatically respond to changes in user privileges. Suppose user A is not an administrator, so you set up sandbox A with no administrator functionality. If you promote user A to be an admin, SpiderMonkey won't magically update sandbox A to have the administrator classes and functions you didn't define before. Your application will have to do that explicitly. Conversely, if you want to strip user A's administrator privileges, but you have already given administrator functions to user A's scripts, that's even worse. You have no choice but to destroy user A's sandbox and start over.
Another way to keep a snake from eating a mouse is to watch the snake constantly, and if it tries to eat the mouse, intervene.
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 other web pages.
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 fine-grained security features:
Decide what security policy you want to enforce.
Insert a call to JS_CheckAccess
at 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 JSPrincipals
objects in your application. You need one JSPrincipals
object 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 ForPrincipals
in 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.
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 script with security information. |
Implement access check callback functions (see JSClass.checkAccess
and JS_SetCheckObjectAccessCallback
). These will be called from JS_CheckAccess
and sometimes from within the JavaScript engine. An access check callback function can use jsdbgapi.h functions such as JS_FrameIterator
and JS_StackFramePrincipals
to obtain the principals of the code that is trying to perform the checked operation. Then it determines whether to allow the operation to proceed.
There are features provided by the JSAPI that make it easier to implement JavaScript tracers and profilers.
If you configure with --enable-trace-jscalls, you can use JS_SetFunctionCallback()
to set up a C function to be called whenever a JavaScript function is about to be called, or has finished executing:
void funcTransition(const JSFunction *func, const JSScript *scr, const JSContext *const_cx, JSBool entering) { JSContext *cx = const_cast<JSContext*>(const_cx); JSString *name = JS_GetFunctionId((JSFunction*)func); const char *entExit; const char *nameStr; /* build a C string for the function's name */ if (!name) { nameStr = "Unnamed function"; } else { nameStr = JS_EncodeString(cx, name); } /* build a string for whether we're entering or exiting */ if (entering) { entExit = "Entering"; } else { entExit = "Exiting"; } /* output information about the trace */ printf("%s JavaScript function: %s at time: %ld", entExit, nameStr, clock()); } void enableTracing(JSContext *cx) { JS_SetFunctionCallback(cx, funcTransition); } void disableTracing(JSContext *cx) { JS_SetFunctionCallback(cx, nullptr); }