JSAPI Cookbook

  • Revision slug: SpiderMonkey/JSAPI_Cookbook
  • Revision title: JSAPI Cookbook
  • Revision id: 233949
  • Created:
  • Creator: Jorend
  • Is current revision? No
  • Comment 20 words added, 10 words removed

Revision Content

This article shows the JSAPI equivalent for a tiny handful of common JavaScript idioms.

Note: The FOSS wiki page contains a few links to other libraries and programs that can make life easier when using Spidermonkey and JSAPI.

Basics

Finding the global object

Many of these recipes require finding the current global object first.

// JavaScript
var global = this;

There is a function, JS_GetGlobalForScopeChain(cx), that makes a best guess, and sometimes that is the best that can be done. But in a JSNative the correct way to do this is:

/* JSAPI */
JSBool myNative(JSContext *cx, uintN argc, jsval *vp)
{
    JSObject *global = JS_GetGlobalForObject(cx, JS_CALLEE(cx, vp));
    ...
}

Defining a function

// JavaScript
function justForFun() {
    return null;
}
/* JSAPI */
JSBool justForFun(JSContext *cx, uintN argc, jsval *vp)
{
    JS_SET_RVAL(cx, vp, JSVAL_NULL);
    return JS_TRUE;
}

...

/*
 * Add this to your JSContext setup code.
 * This makes your C function visible as a global function in JavaScript.
 */
if (!JS_DefineFunction(cx, global, "justForFun", &justForFun, 0, 0))
    return JS_FALSE;

To define many JSAPI functions at once, use JS_DefineFunctions.

Creating an Array

// JavaScript
var x = [];  // or "x = Array()", or "x = new Array"
/* JSAPI */
JSObject *x = JS_NewArrayObject(cx, 0, NULL);
if (x == NULL)
    return JS_FALSE;

Creating an Object

// JavaScript
var x = {};  // or "x = Object()", or "x = new Object"
/* JSAPI */
JSObject *x = JS_NewObject(cx, NULL, NULL, NULL);
if (x == NULL)
    return JS_FALSE;

Constructing an object with new

// JavaScript
var person = new Person("Dave", 24);

It looks so simple in JavaScript, but a JSAPI application has to do three things here:

  • look up the constructor, Person
  • prepare the arguments ("Dave", 24)
  • call JS_New to simulate the new keyword

(If your constructor doesn't take any arguments, you can skip the second step and call JS_New(cx, constructor, 0, NULL) in step 3.)

/* JSAPI */

jsval constructor_val;
JSObject *constructor; /* BUG - not rooted */
JSString *name_str;
jsval argv[2];  /* BUG - not rooted */
JSObject *obj;

/* Step 1 - Get the value of |Person| and check that it is an object. */
if (!JS_GetProperty(cx, JS_GetGlobalObject(cx), "Person", &constructor_val))
    return JS_FALSE;
if (JSVAL_IS_PRIMITIVE(constructor_val)) {
    JS_ReportError(cx, "Person is not a constructor");
    return JS_FALSE;
}
constructor = JSVAL_TO_OBJECT(constructor_val);

/* Step 2 - Set up the arguments. */
name_str = JS_NewStringCopyZ(cx, "Dave");
if (!name_str)
    return JS_FALSE;
argv[0] = STRING_TO_JSVAL(name_str);
argv[1] = INT_TO_JSVAL(24);

/* Step 3 - Call |new Person(...argv)|, passing the arguments. */
obj = JS_New(cx, constructor, 2, argv);
if (!obj)
    return JS_FALSE;

Calling a global JS function

// JavaScript
var r = foo();  // where f is a global function
/* JSAPI
 *
 * Suppose the script defines a global JavaScript
 * function foo() and we want to call it from C.
 */
jsval r;
if (!JS_CallFunctionName(cx, JS_GetGlobalObject(cx), "foo", 0, NULL, &r))
   return JS_FALSE;

Calling a JS function via a local variable

// JavaScript
var r = f();  // where f is a local variable
/* JSAPI
 *
 * Suppose f is a local C variable of type jsval.
 */
jsval r;
if (!JS_CallFunctionValue(cx, NULL, f, 0, NULL, &r)
    return JS_FALSE;

Returning an integer

// JavaScript
return 23;
/* JSAPI
 *
 * Warning: This only works for integers that fit in 32 bits.
 * Otherwise, convert the number to floating point (see the next example).
 */
JS_SET_RVAL(cx, vp, INT_TO_JSVAL(23));
return JS_TRUE;

Returning a floating-point number

// JavaScript
return 3.14159;
/* JSAPI */
jsdouble n = 3.14159;
return JS_NewNumberValue(cx, n, rval);

Exception handling

throw

The most common idiom is to create a new Error object and throw that. JS_ReportError does this. Note that JavaScript exceptions are not the same thing as C++ exceptions. The JSAPI code also has to return JS_FALSE to signal failure to the caller.

// JavaScript
throw new Error("Failed to grow " + varietal + ": too many greenflies.");
/* JSAPI */
JS_ReportError(cx, "Failed to grow %s: too many greenflies.", varietal);
return JS_FALSE;

To internationalize your error messages, and to throw other error types, such as SyntaxError or TypeError, use JS_ReportErrorNumber instead.

JavaScript also supports throwing any value at all, not just Error objects. Use JS_SetPendingException to throw an arbitrary jsval from C/C++.

// JavaScript
throw exc;
/* JSAPI */
JS_SetPendingException(cx, exc);
return JS_FALSE;

When JS_ReportError creates a new Error object, it sets the fileName and lineNumber properties to the line of JavaScript code currently at the top of the stack. This is usually the line of code that called your native function, so it's usually what you want. JSAPI code can override this by creating the Error object directly and passing additional arguments to the constructor:

// JavaScript
throw new Error(message, filename, lineno);
/* JSAPI */
JSBool ThrowError(JSContext *cx, JSObject *global,
                  const char *message, const char *filename, int32 lineno)
{
    JSString *messageStr;
    JSString *filenameStr;
    jsval args[3];
    jsval exc;

    messageStr = JS_NewStringCopyZ(cx, message);
    if (!messageStr)
        return JS_FALSE;
    filenameStr = JS_NewStringCopyZ(cx, filename);
    if (!filenameStr)
        return JS_FALSE;

    args[0] = STRING_TO_JSVAL(messageStr);
    args[1] = STRING_TO_JSVAL(filenameStr);
    args[2] = INT_TO_JSVAL(lineno);
    if (JS_CallFunctionName(cx, global, "Error", 3, args, &exc))
        JS_SetPendingException(cx, exc);
    return JS_FALSE;
}

...

return ThrowError(cx, global, message, __FILE__, __LINE__);

The JSAPI code here is actually simulating throw Error(message) without the new, as new is a bit harder to simulate using the JSAPI. In this case, unless the script has redefined Error, it amounts to the same thing.

catch

// JavaScript
try {
    // try some stuff here; for example:
    foo();
    bar();
} catch (exc) {
    // do error-handling stuff here
}
/* JSAPI */
    jsval exc;

    /* try some stuff here; for example: */
    if (!JS_CallFunctionName(cx, global, "foo", 0, NULL, &r))
        goto catch_block;  /* instead of returning JS_FALSE */
    if (!JS_CallFunctionName(cx, global, "bar", 0, NULL, &r))
        goto catch_block;  /* instead of returning JS_FALSE */
    return JS_TRUE;

catch_block:
    if (!JS_GetPendingException(cx, &exc))
        return JS_FALSE;
    JS_ClearPendingException(cx);
    /* do error-handling stuff here */
    return JS_TRUE;

finally

// JavaScript
try {
   foo();
   bar();
} finally {
   cleanup();
}

If your C/C++ cleanup code doesn't call back into the JSAPI, this is straightforward:

/* JSAPI */
    JSBool success = JS_FALSE;

    if (!JS_CallFunctionName(cx, global, "foo", 0, NULL, &r))
        goto finally_block;  /* instead of returning JS_FALSE immediately */
    if (!JS_CallFunctionName(cx, global, "bar", 0, NULL, &r))
        goto finally_block;
    success = JS_TRUE;
    /* Intentionally fall through to the finally block. */

finally_block:
    cleanup();
    return success;

However, if cleanup() is actually a JavaScript function, there's a catch. When an error occurs, the JSContext's pending exception is set. If this happens in foo() or bar() in the above example, the pending exception will still be set when you call cleanup(), which would be bad. To avoid this, your JSAPI code implementing the finally block must:

  • save the old exception, if any
  • clear the pending exception so that your cleanup code can run
  • do your cleanup
  • restore the old exception, if any
  • return JS_FALSE if an exception occurred, so that the exception is propagated up.
/* JSAPI */
    JSBool success = JS_FALSE;
    JSExceptionState *exc_state;

    if (!JS_CallFunctionName(cx, global, "foo", 0, NULL, &r))
        goto finally_block;  /* instead of returning JS_FALSE immediately */
    if (!JS_CallFunctionName(cx, global, "bar", 0, NULL, &r))
        goto finally_block;
    success = JS_TRUE;
    /* Intentionally fall through to the finally block. */

finally_block:
    exc_state = JS_SaveExceptionState(cx);
    if (exc_state == NULL)
        return JS_FALSE;
    JS_ClearPendingException(cx);

    if (!JS_CallFunctionName(cx, global, "cleanup", 0, NULL, &r)) {
        /* The new error replaces the previous one, so discard the saved exception state. */
        JS_DropExceptionState(cx, exc_state);
        return JS_FALSE;
    }
    JS_RestoreExceptionState(cx, exc_state);
    return success;

Object properties

Getting a property

// JavaScript
var x = y.myprop;

The JSAPI function that does this is JS_GetProperty. It requires a JSObject * argument. Since JavaScript values are usually stored in jsval variables, a cast or conversion is usually needed.

In cases where it is certain that y is an object (that is, not a boolean, number, string, null, or undefined), this is fairly straightforward. Use JSVAL_TO_OBJECT to cast y to type JSObject *.

/* JSAPI */
jsval x;

assert(!JSVAL_IS_PRIMITIVE(y)); 
if (!JS_GetProperty(cx, JSVAL_TO_OBJECT(y), "myprop", &x))
    return JS_FALSE;

That code will crash if y is not an object. That's often unacceptable. An alternative would be to simulate the behavior of the JavaScript . notation exactly. It's a nice thought—JavaScript wouldn't crash, at least—but implementing its exact behavior turns out to be quite complicated, and most of the work is not particularly helpful.

Usually it is best to check for !JSVAL_IS_PRIMITIVE(y) and throw an Error with a nice message.

/* JSAPI */
jsval x;

if (JSVAL_IS_PRIMITIVE(y))
    return ThrowError(cx, global, "Parameter y must be an object.", __FILE__, __LINE__);  /* see the #throw example */
if (!JS_GetProperty(cx, JSVAL_TO_OBJECT(y), "myprop", &x))
    return JS_FALSE;

Setting a property

// JavaScript
y.myprop = x;

See "Getting a property", above, concerning the case where y is not an object.

/* JSAPI */
assert(JSVAL_IS_OBJECT(y));
if (!JS_SetProperty(cx, JSVAL_TO_OBJECT(y), "myprop", &x))
    return JS_FALSE;

Checking for a property

// JavaScript
if ("myprop" in y) {
    // then do something
}

See "Getting a property", above, concerning the case where y is not an object.

/* JSAPI */
JSBool found;

assert(JSVAL_IS_OBJECT(y));
if (!JS_HasProperty(cx, JSVAL_TO_OBJECT(y), "myprop", &found))
    return JS_FALSE;
if (found) {
    // then do something
}

Defining a constant property

This is the first of three examples involving the built-in function Object.defineProperty(), which gives JavaScript code fine-grained control over the behavior of individual properties of any object.

You can use this function to create a constant property, one that can't be overwritten or deleted. Specify writeable: false to make the property read-only and configurable: false to prevent it from being deleted or redefined. The flag enumerable: true causes this property to be seen by for loops.

// JavaScript
Object.defineProperty(obj, "prop", {value: 123,
                                    writeable: false,
                                    enumerable: true,
                                    configurable: false});

The analogous JSAPI function is JS_DefineProperty. The property attribute JSPROP_READONLY corresponds to writeable: false, JSPROP_ENUMERATE to enumerable: true, and JSPROP_PERMANENT to configurable: false. To get the opposite behavior for any of these settings, simply omit the property attribute bits you don't want.

/* JSAPI */
if (!JS_DefineProperty(cx, obj, "prop", INT_TO_JSVAL(123),
                       JS_PropertyStub, JS_PropertyStub,
                       JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT)) {
   return JS_FALSE;
}

Defining a property with a getter and setter

Object.defineProperty() can be used to define properties in terms of two accessor functions.

// JavaScript
Object.defineProperty(obj, "prop", {get: GetPropFunc,
                                    set: SetPropFunc,
                                    enumerable: true});

In the JSAPI version, GetPropFunc and SetPropFunc are C/C++ functions of type JSPropertyOp.

/* JSAPI */
if (!JS_DefineProperty(cx, obj, "prop", JSVAL_VOID,
                       GetPropFunc, SetPropFunc,
                       JSPROP_SHARED | JSPROP_ENUMERATE)) {
    return JS_FALSE;
}

Defining a read-only property with only a getter

// JavaScript
Object.defineProperty(obj, "prop", {get: GetPropFunc,
                                    enumerable: true});

In the JSAPI version, to signify that the property is read-only, pass NULL for the setter and add the JSPROP_READONLY attribute.

/* JSAPI */
if (!JS_DefineProperty(cx, obj, "prop", JSVAL_VOID,
                       GetPropFunc, NULL,
                       JSPROP_SHARED | JSPROP_READONLY | JSPROP_ENUMERATE)) {
    return JS_FALSE;
}

Working with the prototype chain

Defining a native read-only property on the String.prototype

// JavaScript
Object.defineProperty(String.prototype, "md5sum", {get: GetMD5Func,
                                                   enumerable: true});	

The following trick couldn't work if someone has replaced the global String object with something.

/* JSAPI */
JSObject   *string, *string_prototype;
jsval       val;

// get the String object from the current context global object
if (!JS_GetProperty(cx, JS_GetGlobalObject(cx), "String", &val)) {
    return JS_FALSE;
}

// check if String is an object
if (!JSVAL_IS_OBJECT(val)) {
    return JS_FALSE;
}

string = JSVAL_TO_OBJECT(val);

if (!JS_GetProperty(cx, string, "prototype", &val)) {
    return JS_FALSE;
}

// check if the String.prototype is an object too
if (!JSVAL_IS_OBJECT(val)) {
	return JS_FALSE;
}

string_prototype = JSVAL_TO_OBJECT(val);

// and now we can add some new functionality to all the strings
JS_DefineProperty(cx, string_prototype, "md5sum", JSVAL_VOID, GetMD5Func, NULL, JSPROP_READONLY | JSPROP_ENUMERATE));

Wanted

  • Using JS_GetStandardClass in the throw new Error example, so it throws an actual Error even if the program has deleted or assigned to the global property Error.
  • Simulating for and for each.
  • Actually outputting errors.

Revision Source

<p>This article shows the JSAPI equivalent for a tiny handful of common JavaScript idioms.</p>
<div class="note">Note: The <a class="internal" href="/En/SpiderMonkey/FOSS" title="En/SpiderMonkey/FOSS">FOSS</a> wiki page contains a few links to other libraries and programs that can make life easier when using Spidermonkey and JSAPI.</div>
<h2>Basics</h2>
<h3>Finding the global object</h3>
<p>Many of these recipes require finding the current global object first.</p>
<pre class="eval">// <strong>JavaScript</strong>
var global = this;
</pre>
<p>There is a function, <code><a href="/en/SpiderMonkey/JSAPI_Reference/JS_GetGlobalForScopeChain">JS_GetGlobalForScopeChain</a>(cx)</code>, that makes a best guess, and sometimes that is the best that can be done. But in a <code><a href="/en/SpiderMonkey/JSAPI_Reference/JSNative">JSNative</a></code> the correct way to do this is:</p>
<pre class="eval">/* <strong>JSAPI</strong> */
JSBool myNative(JSContext *cx, uintN argc, jsval *vp)
{
    JSObject *global = <a href="/en/SpiderMonkey/JSAPI_Reference/JS_GetGlobalForObject">JS_GetGlobalForObject</a>(cx, <a href="/en/SpiderMonkey/JSAPI_Reference/JS_CALLEE">JS_CALLEE</a>(cx, vp));
    ...
}
</pre> <h3>Defining a function</h3>
<pre class="eval">// <strong>JavaScript</strong>
function justForFun() {
    return null;
}
</pre>
<pre class="eval">/* <strong>JSAPI</strong> */
JSBool justForFun(JSContext *cx, uintN argc, jsval *vp)
{
    JS_SET_RVAL(cx, vp, <a href="/en/SpiderMonkey/JSAPI_Reference/JSVAL_NULL" title="en/SpiderMonkey/JSAPI_Reference/JSVAL_NULL">JSVAL_NULL</a>);
    return JS_TRUE;
}

...

/*
 * Add this to your JSContext setup code.
 * This makes your C function visible as a global function in JavaScript.
 */
if (!<a href="/en/SpiderMonkey/JSAPI_Reference/JS_DefineFunction" title="en/SpiderMonkey/JSAPI_Reference/JS_DefineFunction">JS_DefineFunction</a>(cx, global, "justForFun", &amp;justForFun, 0, 0))
    return JS_FALSE;
</pre>
<p>To define many JSAPI functions at once, use <code><a href="/en/SpiderMonkey/JSAPI_Reference/JS_DefineFunctions" title="en/SpiderMonkey/JSAPI_Reference/JS_DefineFunctions">JS_DefineFunctions</a></code>.</p>
<h3>Creating an Array</h3>
<pre class="eval">// <strong>JavaScript</strong>
var x = [];  // or "x = Array()", or "x = new Array"
</pre>
<pre class="eval">/* <strong>JSAPI</strong> */
JSObject *x = <a href="/en/SpiderMonkey/JSAPI_Reference/JS_NewArrayObject" title="en/SpiderMonkey/JSAPI_Reference/JS_NewArrayObject">JS_NewArrayObject</a>(cx, 0, NULL);
if (x == NULL)
    return JS_FALSE;
</pre>
<h3>Creating an Object</h3>
<pre class="eval">// <strong>JavaScript</strong>
var x = {};  // or "x = Object()", or "x = new Object"
</pre>
<pre class="eval">/* <strong>JSAPI</strong> */
JSObject *x = <a href="/en/SpiderMonkey/JSAPI_Reference/JS_NewObject" title="en/SpiderMonkey/JSAPI_Reference/JS_NewObject">JS_NewObject</a>(cx, NULL, NULL, NULL);
if (x == NULL)
    return JS_FALSE;
</pre>
<h3>Constructing an object with new</h3>
<pre class="eval">// <strong>JavaScript</strong>
var person = new Person("Dave", 24);
</pre>
<p>It looks so simple in JavaScript, but a JSAPI application has to do three things here:</p>
<ul> <li>look up the constructor, <code>Person</code></li> <li>prepare the arguments <code>("Dave", 24)</code></li> <li>call <a href="/en/SpiderMonkey/JSAPI_Reference/JS_New" title="en/SpiderMonkey/JSAPI_Reference/JS_New"><code>JS_New</code></a> to simulate the <code>new</code> keyword</li>
</ul>
<p>(If your constructor doesn't take any arguments, you can skip the second step and call <code><a href="/en/SpiderMonkey/JSAPI_Reference/JS_New" title="en/SpiderMonkey/JSAPI_Reference/JS_New">JS_New</a>(cx, constructor, 0, NULL)</code> in step 3.)</p>
<pre class="eval">/* <strong>JSAPI</strong> */

jsval constructor_val;
JSObject *constructor; /* BUG - not rooted */
JSString *name_str;
jsval argv[2];  /* BUG - not rooted */
JSObject *obj;

/* Step 1 - Get the value of |Person| and check that it is an object. */
if (!<a href="/en/SpiderMonkey/JSAPI_Reference/JS_GetProperty" title="en/SpiderMonkey/JSAPI_Reference/JS_GetProperty">JS_GetProperty</a>(cx, <a href="/en/SpiderMonkey/JSAPI_Reference/JS_GetGlobalObject" title="en/SpiderMonkey/JSAPI_Reference/JS_GetGlobalObject">JS_GetGlobalObject</a>(cx), "Person", &amp;constructor_val))
    return JS_FALSE;
if (<a href="/en/SpiderMonkey/JSAPI_Reference/JSVAL_IS_PRIMITIVE" title="en/SpiderMonkey/JSAPI_Reference/JSVAL_IS_PRIMITIVE">JSVAL_IS_PRIMITIVE</a>(constructor_val)) {
    <a href="/en/SpiderMonkey/JSAPI_Reference/JS_ReportError" title="en/SpiderMonkey/JSAPI_Reference/JS_ReportError">JS_ReportError</a>(cx, "Person is not a constructor");
    return JS_FALSE;
}
constructor = <a href="/en/SpiderMonkey/JSAPI_Reference/JSVAL_TO_OBJECT" title="en/SpiderMonkey/JSAPI_Reference/JSVAL_TO_OBJECT">JSVAL_TO_OBJECT</a>(constructor_val);

/* Step 2 - Set up the arguments. */
name_str = <a href="/en/SpiderMonkey/JSAPI_Reference/JS_NewStringCopyZ" title="en/SpiderMonkey/JSAPI_Reference/JS_NewStringCopyZ">JS_NewStringCopyZ</a>(cx, "Dave");
if (!name_str)
    return JS_FALSE;
argv[0] = <a href="/en/SpiderMonkey/JSAPI_Reference/STRING_TO_JSVAL" title="en/SpiderMonkey/JSAPI_Reference/STRING_TO_JSVAL">STRING_TO_JSVAL</a>(name_str);
argv[1] = <a href="/en/SpiderMonkey/JSAPI_Reference/INT_TO_JSVAL" title="en/SpiderMonkey/JSAPI_Reference/INT_TO_JSVAL">INT_TO_JSVAL</a>(24);

/* Step 3 - Call |new Person(...argv)|, passing the arguments. */
obj = <a href="/en/SpiderMonkey/JSAPI_Reference/JS_New" title="en/SpiderMonkey/JSAPI_Reference/JS_New">JS_New</a>(cx, constructor, 2, argv);
if (!obj)
    return JS_FALSE;
</pre>
<h3>Calling a global JS function</h3>
<pre class="eval">// <strong>JavaScript</strong>
var r = foo();  // where f is a global function
</pre>
<pre class="eval">/* <strong>JSAPI</strong>
 *
 * Suppose the script defines a global JavaScript
 * function foo() and we want to call it from C.
 */
jsval r;
if (!<a href="/en/SpiderMonkey/JSAPI_Reference/JS_CallFunctionName" title="en/SpiderMonkey/JSAPI_Reference/JS_CallFunctionName">JS_CallFunctionName</a>(cx, <a href="/en/SpiderMonkey/JSAPI_Reference/JS_GetGlobalObject" title="en/SpiderMonkey/JSAPI_Reference/JS_GetGlobalObject">JS_GetGlobalObject</a>(cx), "foo", 0, NULL, &amp;r))
   return JS_FALSE;
</pre>
<h3>Calling a JS function via a local variable</h3>
<pre class="eval">// <strong>JavaScript</strong>
var r = f();  // where f is a local variable
</pre>
<pre class="eval">/* <strong>JSAPI</strong>
 *
 * Suppose f is a local C variable of type jsval.
 */
jsval r;
if (!<a href="/en/SpiderMonkey/JSAPI_Reference/JS_CallFunctionValue" title="en/SpiderMonkey/JSAPI_Reference/JS_CallFunctionValue">JS_CallFunctionValue</a>(cx, NULL, f, 0, NULL, &amp;r)
    return JS_FALSE;
</pre>
<h3 name="Returning_an_integer">Returning an integer</h3>
<pre class="eval">// <strong>JavaScript</strong>
return 23;
</pre>
<pre class="eval">/* <strong>JSAPI</strong>
 *
 * Warning: This only works for integers that fit in 32 bits.
 * Otherwise, convert the number to floating point (see the next example).
 */
<a href="/en/SpiderMonkey/JSAPI_Reference/JS_SET_RVAL" title="en/SpiderMonkey/JSAPI_Reference/JS_SET_RVAL">JS_SET_RVAL</a>(cx, vp, <a href="/en/SpiderMonkey/JSAPI_Reference/INT_TO_JSVAL" title="en/SpiderMonkey/JSAPI_Reference/INT_TO_JSVAL">INT_TO_JSVAL</a>(23));
return JS_TRUE;
</pre>
<h3 name="Returning_a_floating-point_number">Returning a floating-point number</h3>
<pre class="eval">// <strong>JavaScript</strong>
return 3.14159;
</pre>
<pre class="eval">/* <strong>JSAPI</strong> */
<a href="/en/SpiderMonkey/JSAPI_Reference/jsdouble" title="en/SpiderMonkey/JSAPI_Reference/jsdouble">jsdouble</a> n = 3.14159;
return <a href="/en/SpiderMonkey/JSAPI_Reference/JS_NewNumberValue" title="en/SpiderMonkey/JSAPI_Reference/JS_NewNumberValue">JS_NewNumberValue</a>(cx, n, rval);
</pre>
<h2 name="Exception_handling">Exception handling</h2>
<h3 name="throw"><code>throw</code></h3>
<p>The most common idiom is to create a new <code>Error</code> object and throw that. <a href="/en/SpiderMonkey/JSAPI_Reference/JS_ReportError" title="en/SpiderMonkey/JSAPI_Reference/JS_ReportError"><code>JS_ReportError</code></a> does this. Note that JavaScript exceptions are not the same thing as C++ exceptions. The JSAPI code also has to return <code>JS_FALSE</code> to signal failure to the caller.</p>
<pre>// <strong>JavaScript</strong>
throw new Error("Failed to grow " + varietal + ": too many greenflies.");
</pre>
<pre>/* <strong>JSAPI</strong> */
<a href="mks://localhost/en/SpiderMonkey/JSAPI_Reference/JS_ReportError">JS_ReportError</a>(cx, "Failed to grow %s: too many greenflies.", varietal);
return JS_FALSE;
</pre>
<p>To internationalize your error messages, and to throw other error types, such as <code>SyntaxError</code> or <code>TypeError</code>, use <a href="/en/SpiderMonkey/JSAPI_Reference/JS_ReportErrorNumber" title="en/SpiderMonkey/JSAPI_Reference/JS_ReportErrorNumber"><code>JS_ReportErrorNumber</code></a> instead.</p>
<p>JavaScript also supports throwing any value at all, not just <code>Error</code> objects. Use <a href="/en/SpiderMonkey/JSAPI_Reference/JS_SetPendingException" title="en/SpiderMonkey/JSAPI_Reference/JS_SetPendingException"><code>JS_SetPendingException</code></a> to throw an arbitrary <code>jsval</code> from C/C++.</p>
<pre class="eval">// <strong>JavaScript</strong>
throw exc;
</pre>
<pre class="eval">/* <strong>JSAPI</strong> */
<a href="/en/SpiderMonkey/JSAPI_Reference/JS_SetPendingException" title="en/SpiderMonkey/JSAPI_Reference/JS_SetPendingException">JS_SetPendingException</a>(cx, exc);
return JS_FALSE;
</pre>
<p>When <code>JS_ReportError</code> creates a new <code>Error</code> object, it sets the <code>fileName</code> and <code>lineNumber</code> properties to the line of JavaScript code currently at the top of the stack. This is usually the line of code that called your native function, so it's usually what you want. JSAPI code can override this by creating the <code>Error</code> object directly and passing additional arguments to the constructor:</p>
<pre class="eval">// <strong>JavaScript</strong>
throw new Error(message, filename, lineno);
</pre>
<pre class="eval">/* <strong>JSAPI</strong> */
JSBool ThrowError(JSContext *cx, JSObject *global,
                  const char *message, const char *filename, int32 lineno)
{
    JSString *messageStr;
    JSString *filenameStr;
    jsval args[3];
    jsval exc;

    messageStr = JS_NewStringCopyZ(cx, message);
    if (!messageStr)
        return JS_FALSE;
    filenameStr = JS_NewStringCopyZ(cx, filename);
    if (!filenameStr)
        return JS_FALSE;

    args[0] = STRING_TO_JSVAL(messageStr);
    args[1] = STRING_TO_JSVAL(filenameStr);
    args[2] = INT_TO_JSVAL(lineno);
    if (<a href="/en/SpiderMonkey/JSAPI_Reference/JS_CallFunctionName" title="en/SpiderMonkey/JSAPI_Reference/JS_CallFunctionName">JS_CallFunctionName</a>(cx, global, "Error", 3, args, &amp;exc))
        <a href="/en/SpiderMonkey/JSAPI_Reference/JS_SetPendingException" title="en/SpiderMonkey/JSAPI_Reference/JS_SetPendingException">JS_SetPendingException</a>(cx, exc);
    return JS_FALSE;
}

...

return ThrowError(cx, global, message, __FILE__, __LINE__);
</pre>
<p>The JSAPI code here is actually simulating <code>throw Error(message)</code> without the <code>new</code>, as <code>new</code> is a bit harder to simulate using the JSAPI. In this case, unless the script has redefined <code>Error</code>, it amounts to the same thing.</p><h3 name="catch"><code>catch</code></h3>
<pre class="eval">// <strong>JavaScript</strong>
try {
    // try some stuff here; for example:
    foo();
    bar();
} catch (exc) {
    // do error-handling stuff here
}
</pre>
<pre class="eval">/* <strong>JSAPI</strong> */
    jsval exc;

    /* try some stuff here; for example: */
    if (!JS_CallFunctionName(cx, global, "foo", 0, NULL, &amp;r))
        goto catch_block;  /* instead of returning JS_FALSE */
    if (!JS_CallFunctionName(cx, global, "bar", 0, NULL, &amp;r))
        goto catch_block;  /* instead of returning JS_FALSE */
    return JS_TRUE;

catch_block:
    if (!<a href="/en/SpiderMonkey/JSAPI_Reference/JS_GetPendingException" title="en/SpiderMonkey/JSAPI_Reference/JS_GetPendingException">JS_GetPendingException</a>(cx, &amp;exc))
        return JS_FALSE;
    <a href="/en/SpiderMonkey/JSAPI_Reference/JS_ClearPendingException" title="en/SpiderMonkey/JSAPI_Reference/JS_ClearPendingException">JS_ClearPendingException</a>(cx);
    /* do error-handling stuff here */
    return JS_TRUE;
</pre>
<h3 name="finally"><code>finally</code></h3>
<pre class="eval">// <strong>JavaScript</strong>
try {
   foo();
   bar();
} finally {
   cleanup();
}
</pre>
<p>If your C/C++ cleanup code doesn't call back into the JSAPI, this is straightforward:</p>
<pre class="eval">/* <strong>JSAPI</strong> */
    <a href="/En/SpiderMonkey/JSAPI_Reference/JSBool" title="En/SpiderMonkey/JSAPI_Reference/JSBool">JSBool</a> success = JS_FALSE;

    if (!JS_CallFunctionName(cx, global, "foo", 0, NULL, &amp;r))
        goto finally_block;  /* instead of returning JS_FALSE immediately */
    if (!JS_CallFunctionName(cx, global, "bar", 0, NULL, &amp;r))
        goto finally_block;
    success = JS_TRUE;
    /* Intentionally fall through to the finally block. */

finally_block:
    cleanup();
    return success;
</pre>
<p>However, if <code>cleanup()</code> is actually a JavaScript function, there's a catch. When an error occurs, the <code>JSContext</code>'s <em>pending exception</em> is set. If this happens in <code>foo()</code> or <code>bar()</code> in the above example, the pending exception will still be set when you call <code>cleanup()</code>, which would be bad. To avoid this, your JSAPI code implementing the <code>finally</code> block must:</p>
<ul> <li>save the old exception, if any</li> <li>clear the pending exception so that your cleanup code can run</li> <li>do your cleanup</li> <li>restore the old exception, if any</li> <li>return JS_FALSE if an exception occurred, so that the exception is propagated up.</li>
</ul>
<pre class="eval">/* <strong>JSAPI</strong> */
    <a href="/En/SpiderMonkey/JSAPI_Reference/JSBool" title="En/SpiderMonkey/JSAPI_Reference/JSBool">JSBool</a> success = JS_FALSE;
    <a href="/en/SpiderMonkey/JSAPI_Reference/JSExceptionState" title="en/SpiderMonkey/JSAPI_Reference/JSExceptionState">JSExceptionState</a> *exc_state;

    if (!JS_CallFunctionName(cx, global, "foo", 0, NULL, &amp;r))
        goto finally_block;  /* instead of returning JS_FALSE immediately */
    if (!JS_CallFunctionName(cx, global, "bar", 0, NULL, &amp;r))
        goto finally_block;
    success = JS_TRUE;
    /* Intentionally fall through to the finally block. */

finally_block:
    exc_state = <a href="/en/SpiderMonkey/JSAPI_Reference/JS_SaveExceptionState" title="en/SpiderMonkey/JSAPI_Reference/JS_SaveExceptionState">JS_SaveExceptionState</a>(cx);
    if (exc_state == NULL)
        return JS_FALSE;
    <a href="/en/SpiderMonkey/JSAPI_Reference/JS_ClearPendingException" title="en/SpiderMonkey/JSAPI_Reference/JS_ClearPendingException">JS_ClearPendingException</a>(cx);

    if (!JS_CallFunctionName(cx, global, "cleanup", 0, NULL, &amp;r)) {
        /* The new error replaces the previous one, so discard the saved exception state. */
        <a href="/en/SpiderMonkey/JSAPI_Reference/JS_DropExceptionState" title="en/SpiderMonkey/JSAPI_Reference/JS_DropExceptionState">JS_DropExceptionState</a>(cx, exc_state);
        return JS_FALSE;
    }
    <a href="/en/SpiderMonkey/JSAPI_Reference/JS_RestoreExceptionState" title="en/SpiderMonkey/JSAPI_Reference/JS_RestoreExceptionState">JS_RestoreExceptionState</a>(cx, exc_state);
    return success;
</pre>
<h2 name="Object_properties">Object properties</h2>
<h3 name="Getting_a_property">Getting a property</h3>
<pre class="eval">// <strong>JavaScript</strong>
var x = y.myprop;
</pre>
<p>The JSAPI function that does this is <code><a href="/en/SpiderMonkey/JSAPI_Reference/JS_GetProperty" title="en/SpiderMonkey/JSAPI_Reference/JS_GetProperty">JS_GetProperty</a></code>. It requires a <code>JSObject *</code> argument. Since JavaScript values are usually stored in <code>jsval</code> variables, a cast or conversion is usually needed.</p>
<p>In cases where it is certain that <code>y</code> is an object (that is, not a boolean, number, string, <code>null</code>, or <code>undefined</code>), this is fairly straightforward. Use <code><a href="/en/SpiderMonkey/JSAPI_Reference/JSVAL_TO_OBJECT" title="en/SpiderMonkey/JSAPI_Reference/JSVAL_TO_OBJECT">JSVAL_TO_OBJECT</a></code> to cast <code>y</code> to type <code>JSObject *</code>.</p>
<pre class="eval">/* <strong>JSAPI</strong> */
jsval x;

assert(<a href="/en/SpiderMonkey/JSAPI_Reference/JSVAL_IS_OBJECT" title="en/SpiderMonkey/JSAPI_Reference/JSVAL_IS_PRIMITIVE">!JSVAL_IS_PRIMITIVE</a>(y)); 
if (!<a href="/en/SpiderMonkey/JSAPI_Reference/JS_GetProperty" title="en/SpiderMonkey/JSAPI_Reference/JS_GetProperty">JS_GetProperty</a>(cx, <a href="/en/SpiderMonkey/JSAPI_Reference/JSVAL_TO_OBJECT" title="en/SpiderMonkey/JSAPI_Reference/JSVAL_TO_OBJECT">JSVAL_TO_OBJECT</a>(y), "myprop", &amp;x))
    return JS_FALSE;
</pre>
<p>That code will crash if <code>y</code> is not an object. That's often unacceptable. An alternative would be to simulate the behavior of the JavaScript <code>.</code> notation exactly. It's a nice thought—JavaScript wouldn't crash, at least—but implementing its exact behavior turns out to be quite complicated, and most of the work is not particularly helpful.</p>
<p>Usually it is best to check for <code>!<a href="/en/SpiderMonkey/JSAPI_Reference/JSVAL_IS_PRIMITIVE" title="en/SpiderMonkey/JSAPI_Reference/JSVAL_IS_PRIMITIVE">JSVAL_IS_PRIMITIVE</a>(y)</code> and throw an <code>Error</code> with a nice message.</p>
<pre class="eval">/* <strong>JSAPI</strong> */
jsval x;

if (<a href="/en/SpiderMonkey/JSAPI_Reference/JSVAL_IS_PRIMITIVE" title="en/SpiderMonkey/JSAPI_Reference/JSVAL_IS_PRIMITIVE">JSVAL_IS_PRIMITIVE</a>(y))
    return ThrowError(cx, global, "Parameter y must be an object.", __FILE__, __LINE__);  /* see the <a href="#throw">#throw</a> example */
if (!<a href="/en/SpiderMonkey/JSAPI_Reference/JS_GetProperty" title="en/SpiderMonkey/JSAPI_Reference/JS_GetProperty">JS_GetProperty</a>(cx, <a href="/en/SpiderMonkey/JSAPI_Reference/JSVAL_TO_OBJECT" title="en/SpiderMonkey/JSAPI_Reference/JSVAL_TO_OBJECT">JSVAL_TO_OBJECT</a>(y), "myprop", &amp;x))
    return JS_FALSE;
</pre>
<h3 name="Setting_a_property">Setting a property</h3>
<pre class="eval">// <strong>JavaScript</strong>
y.myprop = x;
</pre>
<p>See "Getting a property", above, concerning the case where <code>y</code> is not an object.</p>
<pre class="eval">/* <strong>JSAPI</strong> */
assert(<a href="/en/SpiderMonkey/JSAPI_Reference/JSVAL_IS_OBJECT" title="en/SpiderMonkey/JSAPI_Reference/JSVAL_IS_OBJECT">JSVAL_IS_OBJECT</a>(y));
if (!<a href="/en/SpiderMonkey/JSAPI_Reference/JS_SetProperty" title="en/SpiderMonkey/JSAPI_Reference/JS_SetProperty">JS_SetProperty</a>(cx, <a href="/en/SpiderMonkey/JSAPI_Reference/JSVAL_TO_OBJECT" title="en/SpiderMonkey/JSAPI_Reference/JSVAL_TO_OBJECT">JSVAL_TO_OBJECT</a>(y), "myprop", &amp;x))
    return JS_FALSE;
</pre>
<h3 name="Checking_for_a_property">Checking for a property</h3>
<pre class="eval">// <strong>JavaScript</strong>
if ("myprop" in y) {
    // then do something
}
</pre>
<p>See "Getting a property", above, concerning the case where <code>y</code> is not an object.</p>
<pre class="eval">/* <strong>JSAPI</strong> */
JSBool found;

assert(<a href="/en/SpiderMonkey/JSAPI_Reference/JSVAL_IS_OBJECT" title="en/SpiderMonkey/JSAPI_Reference/JSVAL_IS_OBJECT">JSVAL_IS_OBJECT</a>(y));
if (!<a href="/en/SpiderMonkey/JSAPI_Reference/JS_HasProperty" title="en/SpiderMonkey/JSAPI_Reference/JS_HasProperty">JS_HasProperty</a>(cx, <a href="/en/SpiderMonkey/JSAPI_Reference/JSVAL_TO_OBJECT" title="en/SpiderMonkey/JSAPI_Reference/JSVAL_TO_OBJECT">JSVAL_TO_OBJECT</a>(y), "myprop", &amp;found))
    return JS_FALSE;
if (found) {
    // then do something
}
</pre>
<h3>Defining a constant property</h3>
<p>This is the first of three examples involving the built-in function <code>Object.defineProperty()</code>, which gives JavaScript code fine-grained control over the behavior of individual properties of any object.</p>
<p>You can use this function to create a constant property, one that can't be overwritten or deleted. Specify <code>writeable: false</code> to make the property read-only and <code>configurable: false</code> to prevent it from being deleted or redefined. The flag <code>enumerable: true</code> causes this property to be seen by for loops.</p>
<pre class="eval">// <strong>JavaScript</strong>
Object.defineProperty(obj, "prop", {value: 123,
                                    writeable: false,
                                    enumerable: true,
                                    configurable: false});
</pre>
<p>The analogous JSAPI function is <code><a href="/en/SpiderMonkey/JSAPI_Reference/JS_DefineProperty" title="en/SpiderMonkey/JSAPI_Reference/JS_DefineProperty">JS_DefineProperty</a></code>. The property attribute <code>JSPROP_READONLY</code> corresponds to <code>writeable: false</code>, <code>JSPROP_ENUMERATE</code> to <code>enumerable: true</code>, and <code>JSPROP_PERMANENT</code> to <code>configurable: false</code>. To get the opposite behavior for any of these settings, simply omit the property attribute bits you don't want.</p>
<pre class="eval">/* <strong>JSAPI</strong> */
if (!JS_DefineProperty(cx, obj, "prop", INT_TO_JSVAL(123),
                       JS_PropertyStub, JS_PropertyStub,
                       JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT)) {
   return JS_FALSE;
}
</pre>
<h3>Defining a property with a getter and setter</h3>
<p><code>Object.defineProperty()</code> can be used to define properties in terms of two accessor functions.</p>
<pre class="eval">// <strong>JavaScript</strong>
Object.defineProperty(obj, "prop", {get: GetPropFunc,
                                    set: SetPropFunc,
                                    enumerable: true});
</pre>
<p>In the JSAPI version, <code>GetPropFunc</code> and <code>SetPropFunc</code> are C/C++ functions of type <a href="/En/SpiderMonkey/JSAPI_Reference/JSPropertyOp" title="En/SpiderMonkey/JSAPI Reference/JSPropertyOp"><code>JSPropertyOp</code></a>.</p>
<pre class="eval">/* <strong>JSAPI</strong> */
if (!JS_DefineProperty(cx, obj, "prop", JSVAL_VOID,
                       GetPropFunc, SetPropFunc,
                       JSPROP_SHARED | JSPROP_ENUMERATE)) {
    return JS_FALSE;
}
</pre>
<h3>Defining a read-only property with only a getter</h3>
<pre class="eval">// <strong>JavaScript</strong>
Object.defineProperty(obj, "prop", {get: GetPropFunc,
                                    enumerable: true});
</pre>
<p>In the JSAPI version, to signify that the property is read-only, pass <code>NULL</code> for the setter and add the <code>JSPROP_READONLY</code> attribute.</p>
<pre class="eval">/* <strong>JSAPI</strong> */
if (!JS_DefineProperty(cx, obj, "prop", JSVAL_VOID,
                       GetPropFunc, NULL,
                       JSPROP_SHARED | JSPROP_READONLY | JSPROP_ENUMERATE)) {
    return JS_FALSE;
}
</pre>
<h2 name="Prototypes">Working with the prototype chain</h2>
<h3>Defining a native read-only property on the String.prototype</h3>
<pre class="eval">// <strong>JavaScript</strong>
Object.defineProperty(String.prototype, "md5sum", {get: GetMD5Func,
                                                   enumerable: true});	<br></pre>
<p>The following trick couldn't work if someone has replaced the global String object with something.</p>
<pre class="eval">/* <strong>JSAPI</strong> */
JSObject   *string, *string_prototype;
jsval       val;

// get the String object from the current context global object
if (!JS_GetProperty(cx, JS_GetGlobalObject(cx), "String", &amp;val)) {
    return JS_FALSE;
}

// check if String is an object
if (!JSVAL_IS_OBJECT(val)) {
    return JS_FALSE;
}

string = JSVAL_TO_OBJECT(val);

if (!JS_GetProperty(cx, string, "prototype", &amp;val)) {
    return JS_FALSE;
}

// check if the String.prototype is an object too
if (!JSVAL_IS_OBJECT(val)) {
	return JS_FALSE;
}

string_prototype = JSVAL_TO_OBJECT(val);

// and now we can add some new functionality to all the strings
JS_DefineProperty(cx, string_prototype, "md5sum", JSVAL_VOID, GetMD5Func, NULL, JSPROP_READONLY | JSPROP_ENUMERATE));</pre><h2 name="Wanted">Wanted</h2>
<ul> <li>Using <code><a href="/en/JS_GetStandardClass" title="en/JS_GetStandardClass">JS_GetStandardClass</a></code> in the <code>throw new Error</code> example, so it throws an actual <code>Error</code> even if the program has deleted or assigned to the global property <code>Error</code>.</li> <li>Simulating <code>for</code> and <code>for each</code>.</li> <li>Actually outputting errors.</li>
</ul>
Revert to this revision