You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

835 lines
37 KiB

<h1 id="programming">Programming model</h1>
<h2>Overview</h2>
<p>Programming with Duktape is quite straightforward:</p>
<ul>
<li>Add Duktape source (<code>duktape.c</code>) and headers
(<code>duktape.h</code> and <code>duk_config.h</code>)
to your build. If the default configuration is not appropriate,
use <code>python2 tools/configure.py</code> to prepare Duktape
sources and headers for a custom configuration.</li>
<li>Create a Duktape <b>heap</b> (a garbage collection region) and an initial
<b>context</b> (essentially a thread handle) in your program.</li>
<li>Load the necessary ECMAScript script files, and register your Duktape/C
functions. Duktape/C functions are C functions you can call from
ECMAScript code for better performance, bindings to native libraries, etc.</li>
<li>Use the Duktape API to call ECMAScript functions whenever appropriate.
Duktape API is used to pass values to and from functions. Values are
converted between their C representation and the Duktape internal
(ECMAScript compatible) representation.</li>
<li>Duktape API is also used by Duktape/C functions (called from ECMAScript)
to access call arguments and to provide return values.</li>
</ul>
<p>Let's look at all the steps and their related concepts in more detail.</p>
<h2>Heap and context</h2>
<p>A Duktape <b>heap</b> is a single region for garbage collection. A heap
is used to allocate storage for strings, ECMAScript objects, and other
variable size, garbage collected data. Objects in the heap have an internal
heap header which provides the necessary information for reference counting,
mark-and-sweep garbage collection, object finalization, etc.
Heap objects can reference each other, creating a reachability graph from
a garbage collection perspective. For instance, the properties of an ECMAScript
object reference both the keys and values of the object's property set. You can
have multiple heaps, but objects in different heaps cannot reference each other
directly; you need to use serialization to pass values between heaps.</p>
<p>A Duktape <b>context</b> is an ECMAScript "thread of execution" which lives
in a certain Duktape heap. A context is represented by a <code>duk_context *</code>
in the Duktape API, and is associated with an internal Duktape coroutine (a form
of a co-operative thread). Each context is also associated with an environment
consisting of global objects; contexts may share the same global environment but
can also have different environments. The context handle is given to almost every
Duktape API call, and allows the caller to interact with the <b>value stack</b> of
the Duktape coroutine: values can be inserted and queries, functions can be called,
and so on.</p>
<p>Each coroutine has a <b>call stack</b> which controls execution, keeping
track of function calls, native or ECMAScript, within the ECMAScript engine.
Each coroutine also has a <b>value stack</b> which stores all the ECMAScript
values of the coroutine's active call stack. The value stack always has an
active <b>stack frame</b> for the most recent function call (when no function
calls have been made, the active stack frame is the value stack as is).
The Duktape API calls operate almost exclusively in the currently active
stack frame. There's also internal book-keeping for error catching sites
established using e.g. <code>try-catch-finally</code>.</p>
<p>Multiple contexts can share the same Duktape <b>heap</b>. In more concrete
terms this means that multiple contexts can share the same garbage collection
state, and can exchange object references safely. Contexts in different heaps
cannot exchange direct object references; all values must be serialized in one
way or another.</p>
<p>Almost every API call provided by the Duktape API takes a context pointer
as its first argument: no global variables or states are used, and there are
no restrictions on running multiple, independent Duktape heaps and contexts
at the same time. There are multi-threading restrictions, however: only one
native thread can execute any code within a single heap at any time, see
<a href="#threading">Threading</a>.</p>
<p>To create a Duktape heap and an initial context inside the heap, you can
simply use:</p>
<pre class="c-code">
duk_context *ctx = duk_create_heap_default();
if (!ctx) { exit(1); }
</pre>
<p>If you wish to provide your own memory allocation functions and a fatal
error handler function (recommended), use:</p>
<pre class="c-code">
duk_context *ctx = duk_create_heap(my_alloc,
my_realloc,
my_free,
my_udata,
my_fatal);
if (!ctx) { exit(1); }
</pre>
<p>To create a new context inside the same heap, with the context sharing the
same global objects:</p>
<pre class="c-code">
duk_context *new_ctx;
(void) duk_push_thread(ctx);
new_ctx = duk_get_context(ctx, -1 /*index*/);
</pre>
<p>To create a new context inside the same heap, but with a fresh set of global
object:</p>
<pre class="c-code">
duk_context *new_ctx;
(void) duk_push_thread_new_globalenv(ctx);
new_ctx = duk_get_context(ctx, -1 /*index*/);
</pre>
<p>Contexts are automatically garbage collected when they become unreachable.
This also means that if your C code holds a <code>duk_context *</code>, the
corresponding Duktape coroutine MUST be reachable from a garbage collection
point of view.</p>
<p>A heap must be destroyed explicitly when the caller is done with it:</p>
<pre class="c-code">
duk_destroy_heap(ctx);
</pre>
<p>This frees all heap objects allocated, and invalidates any pointers to
such objects. In particular, if the calling program holds string pointers
to values which resided on the value stack of a context associated with the
heap, such pointers are invalidated and must never be dereferenced after
the heap destruction call returns.</p>
<h2>Call stack (of a context)</h2>
<p>The call stack of a context is not directly visible to the caller.
It keeps track of the chain of function calls, either C or ECMAScript,
currently executing in a context. The main purpose of this book-keeping is
to facilitate the passing of arguments and results between function callers
and callees, and to keep track of how the value stack is divided between
function calls. The call stack also allows Duktape to construct a traceback
for errors.</p>
<p>Because Duktape supports tail calls, the call stack does not always
accurately represent the true call chain: tail calls will be "squashed"
together in the call stack.</p>
<div class="note">Don't confuse with the C stack.</div>
<h2>Value stack (of a context) and value stack index</h2>
<p>The value stack of a context is an array of tagged type values related
to the current execution state of a coroutine. The tagged types used are:
<code>undefined</code>, <code>null</code>, boolean, number, string, object,
buffer, pointer, and lightfunc. For a detailed discussion of the available
tagged types, see <a href="#stacktypes">Types</a>.</p>
<p>The value stack is divided between the currently active function calls
(activations) on the coroutine's call stack. At any time, there is an active
stack frame which provides an origin for indexing elements on the stack.
More concretely, at any time there is a <b>bottom</b> which is referred
to with the index zero in the Duktape API. There is also a conceptual
<b>top</b> which identifies the stack element right above the highest
currently used element. The following diagram illustrates this:</p>
<pre>
Value stack
of 15 entries
(absolute indices)
.----.
| 15 |
| 14 |
| 13 |
| 12 | Active stack frame (indices
| 11 | relative to stack bottom)
| 10 |
| 9 | .---. Stack top is 6 (relative to stack bottom).
| 8 | &lt;--- | 5 | API index 5 is highest used (at value stack index 8).
| 7 | | 4 |
| 6 | | 3 |
| 5 | | 2 |
| 4 | | 1 |
| 3 | &lt;--- | 0 | API index 0 is bottom (at value stack index 3).
| 2 | `---'
| 1 |
| 0 |
`----'
</pre>
<p>There is no direct way to refer to elements in the internal value stack:
Duktape API always deals with the currently active stack frame. Stack frames
are shown horizontally throughout the documentation for space reasons. For
example, the active stack frame in the figure above would be shown as:</p>
<pre class="stack">
[ 0 1 2 3 4 5 ]
</pre>
<p>A <b>value stack index</b> is a signed integer index used in the Duktape
API to refer to elements in currently active stack frame, relative to the
current frame bottom.</p>
<p>Non-negative (&gt;= 0) indices refer to stack entries in the
current stack frame, relative to the frame bottom:</p>
<pre class="stack">
[ 0 1 2 3 4 5! ]
</pre>
<p>Negative (&lt; 0) indices refer to stack entries relative to the top:</p>
<pre class="stack">
[ -6 -5 -4 -3 -2 -1! ]
</pre>
<p>The special constant <code>DUK_INVALID_INDEX</code> is a negative integer
which denotes an invalid stack index. It can be returned from API calls
and can also be given to API calls to indicate a "no value".</p>
<p>The <b>value stack top</b> (or just "top") is the non-negative index of
an imaginary element just above the highest used index. For instance, above
the highest used index is 5, so the stack top is 6. The top indicates the
current stack size, and is also the index of the next element pushed to the
stack.</p>
<pre class="stack">
[ 0 1 2 3 4 5! 6? ]
</pre>
<div class="note">
<p>API stack operations are always confined to the current stack frame.
There is no way to refer to stack entries below the current frame. This
is intentional, as it protects functions in the call stack from affecting
each other's values.</p>
</div>
<div class="note">Don't confuse with the C stack.</div>
<h2>Growing the value stack</h2>
<p>At any time, the value stack of a context is allocated for a certain
maximum number of entries. An attempt to push values beyond the allocated
size will cause an error to be thrown, it will <b>not</b> cause the value
stack to be automatically extended. This simplifies the internal
implementation and also improves performance by minimizing reallocations
when you know, beforehand, that a certain number of entries will be needed
during a function.</p>
<p>When a value stack is created or a Duktape/C function is entered, the
value stack is always guaranteed to have space for the call arguments and
<code>DUK_API_ENTRY_STACK</code> (currently 64) elements. In the typical
case this is more than sufficient so that the majority of Duktape/C
functions don't need to extend the value stack. Only functions that need
more space or perhaps need an input-dependent amount of space need to grow
the value stack.</p>
<p>You can extend the stack allocation explicitly with
<code><a href="api.html#duk_check_stack">duk_check_stack()</a></code>
or (usually more preferably)
<code><a href="api.html#duk_require_stack">duk_require_stack()</a></code>.
Once successfully
extended, you are again guaranteed that the specified number of elements can
be pushed to the stack. There is no way to shrink the allocation except by
returning from a Duktape/C function.</p>
<p>Consider, for instance, the following function which will uppercase an
input ASCII string by pushing uppercased characters one-by-one on the stack
and then concatenating the result. This example illustrates how the number
of value stack entries required may depend on the input (otherwise this is
not a very good approach for uppercasing a string):</p>
<pre class="c-code" include="uppercase.c"></pre>
<p>In addition to user reserved elements, Duktape keeps an automatic internal
value stack reserve to ensure all API calls have enough value stack space to
work without further allocations. The value stack is also extended and shrunk
in somewhat large steps to minimize memory reallocation activity. As a result
the internal number of value stack elements available beyond the caller
specified extra varies considerably. The caller does not need to take this
into account and should never rely on any additional elements being available.</p>
<h2>ECMAScript array index</h2>
<p>ECMAScript object and array keys can only be strings or Symbols. Array
indices (e.g. 0, 1, 2) are represented as canonical string representations of
the respective numbers (e.g. "0", "1", "2"). More technically, all canonical
string representations of the integers in the range [0, 2**32-2] are valid
array indices.</p>
<p>To illustrate the ECMAScript array index handling, consider the following
example:</p>
<pre class="ecmascript-code">
var arr = [ 'foo', 'bar', 'quux' ];
print(arr[1]); // refers to 'bar'
print(arr["1"]); // refers to 'bar'
print(arr[1.0]); // refers to 'bar', canonical encoding is "1"
print(arr["1.0"]); // undefined, not an array index
</pre>
<p>Some API calls operating on ECMAScript arrays accept numeric array index
arguments. This is really just a short hand for denoting a string conversion
of that number. For instance, if the API is given the integer 123, this
really refers to the property name "123".</p>
<p>Internally, Duktape tries to avoid converting numeric indices to actual
strings whenever possible, so it is preferable to use array index API calls
when they are relevant. Similarly, when writing ECMAScript code it is
preferable to use numeric rather than string indices, as the same fast path
applies for ECMAScript code.</p>
<h2 id="duktape-api">Duktape API</h2>
<p>Duktape API is the collection of user callable API calls defined in
<code>duktape.h</code> and documented in the
<a href="api.html">API reference</a>.</p>
<p>The Duktape API calls are generally error tolerant and will check all
arguments for errors (such as <code>NULL</code> pointers). However, to
minimize footprint, the <code>ctx</code> argument is not checked, and the
caller MUST NOT call any Duktape API calls with a <code>NULL</code> context.</p>
<p>All Duktape API calls are potentially macros. Calling code must not rely
on any Duktape API call being available as a function pointer. The
implementation of a certain API call may change between a macro and an
actual function even between compatible releases. The Duktape API provides
the following guarantees for macros:</p>
<ul>
<li>Arguments are never evaluated more than once (unless explicitly mentioned).
However, an argument may not be evaluated at all if an argument is ignored
in the current version.</li>
<li>An API call with a return value can be used as an expression. If the API
macro contains multiple statements, it is implemented as a comma expression
(e.g. <code>(foo, bar, quux)</code>).</li>
<li>An API call with a <code>void</code> return value may not necessarily work
as part of an expression. The API macro may be implemented as a block
statement or as a dummy <code>do {...} while (0)</code> loop.</li>
</ul>
<h2>Duktape/C function</h2>
<p>A C function with a Duktape/C API signature can be associated with an
ECMAScript function object, and gets called when the ECMAScript function
object is called. A Duktape/C API function looks as follows:</p>
<pre class="c-code">
duk_ret_t my_func(duk_context *ctx) {
duk_push_int(ctx, 123);
return 1;
}
</pre>
<p>The function gets ECMAScript call arguments in the value stack of
<code>ctx</code>, with
<code><a href="api.html#duk_get_top">duk_get_top()</a></code>
indicating the number of
arguments present on the value stack. The <code>this</code> binding is not
automatically pushed to the value stack; use
<code><a href="api.html#duk_push_this">duk_push_this()</a></code>
to access it if necessary. When creating an ECMAScript function object
associated with a Duktape/C function, one can select the desired number
of arguments. Extra arguments are dropped and missing arguments are replaced
with <code>undefined</code>. A function can also be registered as a vararg
function (by giving <code>DUK_VARARGS</code> as the argument count) in
which case call arguments are not modified prior to C function entry.</p>
<p>The function can return one of the following:</p>
<ul>
<li>Return value 1 indicates that the value on the stack top is to be
interpreted as a return value.</li>
<li>Return value 0 indicates that there is no explicit return value on
the value stack; an <code>undefined</code> is returned to caller.</li>
<li>A negative return value indicates that an error is to be automatically
thrown. Error codes named <code>DUK_RET_xxx</code> map to specific kinds
of errors (do not confuse these with <code>DUK_ERR_xxx</code> which are
positive values). This is an optional shorthand which has a small
footprint but also some drawbacks (e.g. no error message).</li>
<li>A return value higher than 1 is currently undefined, as ECMAScript
doesn't support multiple return values in Edition 5.1. (Values higher
than 1 may be taken into to support multiple return values in ECMAScript
Edition 6.)</li>
</ul>
<p>When a Duktape/C function returns, the value stack is automatically
unwound so there is no need to manually clean up the value stack when
the function returns.</p>
<p>A negative error return value is intended to simplify common error
handling, and is an alternative to constructing and throwing an error
explicitly with Duktape API calls. No error message can be given; a
message is automatically constructed by Duktape. For example:</p>
<pre class="c-code">
duk_ret_t my_func(duk_context *ctx) {
if (duk_get_top(ctx) == 0) {
/* throw TypeError if no arguments given */
return DUK_RET_TYPE_ERROR;
}
/* ... */
}
</pre>
<p>All Duktape/C functions are considered <b>strict</b> in the
<a href="http://www.ecma-international.org/ecma-262/5.1/#sec-4.2.2">ECMAScript sense</a>.
Duktape API calls always obey ECMAScript strict mode semantics, even when the API calls
are made outside of any Duktape/C function, i.e. with an empty call stack.
For instance, attempt to delete a non-configurable property using
<code><a href="api.html#duk_del_prop">duk_del_prop()</a></code>
will cause an error to be thrown. This is the case with a strict ECMAScript function too:</p>
<pre class="ecmascript-code">
function f() {
'use strict';
var arr = [1, 2, 3];
return delete arr.length; // array 'length' is non-configurable
}
print(f()); // this throws an error because f() is strict
</pre>
<p>Another consequence of Duktape/C function strictness is that the <code>this</code>
binding given to Duktape/C functions is not
<a href="http://www.ecma-international.org/ecma-262/5.1/#sec-10.4.3">coerced</a>.
This is also the case for strict ECMAScript code:</p>
<pre class="ecmascript-code">
function strictFunc() { 'use strict'; print(typeof this); }
function nonStrictFunc() { print(typeof this); }
strictFunc.call('foo'); // prints 'string' (uncoerced)
nonStrictFunc.call('foo'); // prints 'object' (coerced)
</pre>
<p>Duktape/C functions are currently always <b>constructable</b>, i.e. they
can always be used in <code>new Foo()</code> expressions. You can check whether
a function was called in constructor mode as follows:</p>
<pre class="c-code">
static duk_ret_t my_func(duk_context *ctx) {
if (duk_is_constructor_call(ctx)) {
printf("called as a constructor\n");
} else {
printf("called as a function\n");
}
}
</pre>
<p>To save memory, Duktape/C functions don't have a <code>prototype</code>
property by default, so the default object instance (given to the constructor
as <code>this</code>) inherits from <code>Object.prototype</code>. To use a
custom prototype you can define <code>prototype</code> for the Duktape/C
function explicitly. Another approach is to ignore the default object instance
and construct one manually within the Duktape/C call: as with ECMAScript
functions, if a constructor returns an object value, that value replaces the
default object instance and becomes the value of the <code>new</code>
expression.</p>
<div class="note">
The <code>this</code> binding is not automatically pushed to the value stack;
use
<code><a href="api.html#duk_push_this">duk_push_this()</a></code>
to access it.
</div>
<h2>Storing state for a Duktape/C function</h2>
<p>Sometimes it would be nice to provide parameters or additional state
to a Duktape/C function out-of-band, i.e. outside explicit call arguments.
There are several ways to achieve this.</p>
<h3>Properties of function</h3>
<p>First, a Duktape/C function can use its Function object to store state
or parameters. A certain Duktape/C function (the actual C function)
is always represented by an ECMAScript Function object which is
internally associated with the underlying C function. The Function
object can be used to store properties related to that particular
instance of the function. Note that a certain Duktape/C function can
be associated with multiple independent Function objects and thus
independent states.</p>
<p>Accessing the ECMAScript Function object related to a Duktape/C function
is easy:</p>
<pre class="c-code">
duk_push_current_function(ctx);
duk_get_prop_string(ctx, -1, "my_state_variable");
</pre>
<h3>'this' binding</h3>
<p>Another alternative for storing state is to call the Duktape/C function
as a method and then use the <code>this</code> binding for storing state. For
instance, consider a Duktape/C function called as:</p>
<pre class="ecmascript-code">
foo.my_c_func()
</pre>
<p>When called, the Duktape/C function gets <code>foo</code> as its <code>this</code>
binding, and one could store state directly in <code>foo</code>. The difference
to using the Function object approach is that the same object is shared by all
methods, which has both advantages and disadvantages.</p>
<p>Accessing the <code>this</code> binding is easy:</p>
<pre class="c-code">
duk_push_this(ctx);
duk_get_prop_string(ctx, -1, "my_state_variable");
</pre>
<h3 id="hidden-symbol-properties">Hidden Symbol properties</h3>
<p>If data needs to be associated with an object, but hidden from ECMAScript
code, hidden <a href="#symbols">Symbols</a> can be used as a property key. The
key is only accessible via the C API, unless passed to ECMAScript code from the
C API. A common use case is to associated backing pointers/data to C memory.
Symbols are created as a string, but are differentiated with macros that mark
them as Symbols.</p>
<p>For example, setting and getting a hidden symbol on <code>this</code>:</p>
<pre class="c-code">
my_context_data_t *my_context_data = malloc(sizeof(my_context_data_t));
duk_push_this(ctx);
duk_push_pointer(ctx, my_context_data);
duk_put_prop_string(ctx, -2, DUK_HIDDEN_SYMBOL("my_context_data"));
/* ... */
duk_push_this(ctx);
duk_get_prop_string(ctx, -1, DUK_HIDDEN_SYMBOL("my_context_data"));
my_context_data_t *my_context_data = duk_get_pointer(ctx, -1);
</pre>
<h3>Magic value of function</h3>
<p>Duktape/C function objects can store an internal 16-bit signed integer "magic"
value (zero by default) with no extra memory cost. The magic value can be used
to pass flags and/or small values to a Duktape/C function at minimal cost, so
that a single native function can provide slightly varied behavior for multiple
function objects:</p>
<pre class="c-code">
/* Magic value example: two lowest bits are used for a prefix index, bit 2 (0x04)
* is used to select newline style for a log write helper.
*/
const char *prefix[4] = { "INFO", "WARN", "ERROR", "FATAL" };
duk_int_t magic = duk_get_current_magic(ctx);
printf("%s: %s", prefix[magic &amp; 0x03], duk_safe_to_string(ctx, 0));
if (magic &amp; 0x04) {
printf("\r\n");
} else {
printf("\n");
}
</pre>
<p>For an API usage example, see the test case
<a href="https://github.com/svaarala/duktape/blob/master/tests/api/test-get-set-magic.c">test-get-set-magic.c</a>.
Duktape uses magic values a lot internally to minimize size of compiled code, see e.g.
<a href="https://github.com/svaarala/duktape/blob/master/src-input/duk_bi_math.c">duk_bi_math.c</a>.</p>
<div class="note">
The magic value mechanism is liable to change between major Duktape versions,
as the number of available spare bits changes. Use magic values only when it
really matters for footprint. Properties stored on the function object is a
more stable alternative.
</div>
<h3>Heap stash</h3>
<p>The heap stash is an object visible only from C code. It is associated
with the Duktape heap, and allows Duktape/C code to store "under the hood"
state data which is not exposed to ECMAScript code. It is accessed with the
<code><a href="api.html#duk_push_heap_stash">duk_push_heap_stash()</a></code>
API call.</p>
<h3>Global stash</h3>
<p>The global stash is like the heap stash, but is associated with a global
object. It is accessed with the
<code><a href="api.html#duk_push_global_stash">duk_push_global_stash()</a></code>
API call. There can be several environments with different global objects
within the same heap.</p>
<h3>Thread stash</h3>
<p>The thread stash is like the heap stash, but is associated with a Duktape
thread (i.e. a <code>ctx</code> pointer). It is accessible with the
<code><a href="api.html#duk_push_thread_stash">duk_push_thread_stash()</a></code>
API call.</p>
<h2>Duktape version specific code</h2>
<p>The Duktape version is available through the <code>DUK_VERSION</code> define,
with the numeric value <code>(major * 10000) + (minor * 100) + patch</code>.
The same value is available to ECMAScript code through <code>Duktape.version</code>.
Calling code can use this define for Duktape version specific code.</p>
<p>For C code:</p>
<pre class="c-code">
#if (DUK_VERSION &gt;= 20403)
/* Duktape 2.4.3 or later */
#elif (DUK_VERSION &gt;= 10500)
/* Duktape 1.5.0 or later */
#else
/* Duktape lower than 1.5.0 */
#endif
</pre>
<p>For ECMAScript code (also see <a href="#duktapebuiltins">Duktape built-ins</a>):</p>
<pre class="ecmascript-code">
if (typeof Duktape !== 'object') {
print('not Duktape');
} else if (Duktape.version &gt;= 20403) {
print('Duktape 2.4.3 or higher');
} else if (Duktape.version &gt;= 10500) {
print('Duktape 1.5.0 or higher (but lower than 2.4.3)');
} else {
print('Duktape lower than 1.5.0');
}
</pre>
<h2>Numeric error codes</h2>
<p>When errors are created or thrown using the Duktape API, the caller
must assign a numeric error code to the error. Error codes are
positive integers, with a range restricted to 24 bits at the
moment: the allowed error number range is thus [1,16777215]. Built-in
error codes are defined in <code>duktape.h</code>, e.g. <code>DUK_ERR_TYPE_ERROR</code>.</p>
<p>The remaining high bits are used internally to carry e.g. additional
flags. Negative error values are used in the Duktape/C API as a
shorthand to automatically throw an error.</p>
<h2 id="error-handling">Error handling</h2>
<p>Error handling in the Duktape API is similar to how ECMAScript handles
errors: errors are thrown either explicitly or implicitly, then caught and
handled. Instead of a try-catch statement application C code uses
<code><a href="api.html#taglist-protected">protected</a></code>
Duktape API calls to establish points in C code where errors can be caught
and handled. All Duktape API calls except protected calls may throw errors:
most ECMAScript operations may cause error throws in some situations, and an
out-of-memory error is possible in almost any situation. The long control
transfer between the throw site and the catch site is based either on
<code>setjmp()</code>/<code>longjmp()</code> (or their platform specific
variants), or a C++ exception throw (when <code>DUK_USE_CPP_EXCEPTIONS</code>
is enabled), see <a href="#long-control-transfers">Long control transfers</a>.</p>
<p>An uncaught error causes the fatal error handler to be called, which is
considered an unrecoverable situation and should ordinarily be avoided,
see <a href="#normal-and-fatal-errors">Normal and fatal errors</a> and
<a href="http://wiki.duktape.org/HowtoFatalErrors.html">How to handle fatal errors</a>. To avoid fatal errors, typical application code should establish an
error catch site before making other Duktape API calls. This is done using
protected Duktape API calls, for example:</p>
<ul>
<li>Use protected calls to evaluate code
(<a href="api.html#duk_peval">duk_peval()</a>), compile code
(<a href="api.html#duk_pcompile">duk_pcompile()</a>), and call
(<a href="api.html#duk_pcall">duk_pcall()</a>) functions.</li>
<li>Use a single <a href="api.html#duk_safe_call">duk_safe_call()</a> to
establish an error catcher so that you can use unsafe primitives freely
inside the safe call.</li>
</ul>
<p>An example of the first technique:</p>
<pre class="c-code">
/* Use duk_peval() variant to evaluate a file so that script errors are
* handled safely. Both syntax errors and runtime errors are caught.
*/
push_file_as_string(ctx, "myscript.js");
if (duk_peval(ctx) != 0) {
/* Use duk_safe_to_string() to convert error into string. This API
* call is guaranteed not to throw an error during the coercion.
*/
printf("Script error: %s\n", duk_safe_to_string(ctx, -1));
}
duk_pop(ctx);
</pre>
<p>An example of the second technique:</p>
<pre class="c-code">
/* Use duk_safe_call() to wrap all unsafe code into a separate C function.
* This approach has the advantage of covering all API calls automatically
* but is a bit more verbose.
*/
static duk_ret_t unsafe_code(duk_context *ctx, void *udata) {
/* Here we can use unprotected calls freely. */
(void) udata; /* 'udata' may be used to pass e.g. a struct pointer */
push_file_as_string(ctx, "myscript.js");
duk_eval(ctx);
/* ... */
return 0; /* success return, no return value */
}
/* elsewhere: */
if (duk_safe_call(ctx, unsafe_code, NULL /*udata*/, 0 /*nargs*/, 1 /*nrets */) != 0) {
/* The 'nrets' argument should be at least 1 so that an error value
* is left on the stack if an error occurs. To avoid further errors,
* use duk_safe_to_string() for safe error printing.
*/
printf("Unexpected error: %s\n", duk_safe_to_string(ctx, -1));
}
duk_pop(ctx);
</pre>
<p>Even within protected calls there are some rare cases, such as internal
errors, that will either cause a fatal error or propagate an error outwards
from a protected API call. These should only happen in abnormal conditions
and are not considered recoverable. To handle also these cases well, a
production quality application should always have a fatal error handler with
a reasonable strategy for dealing with fatal errors. Such a strategy is
necessarily application dependent, but could be something like:</p>
<ul>
<li>On an embedded device the fatal error handler could write the fatal
error information to a flash file and reboot the device. After the
reboot, the fatal error could be reported to a diagnostics server so
that it can be investigated.</li>
<li>On a UNIX system the fatal error handler could simply exit the process
(the default fatal handler uses <code>abort()</code>) and let a wrapper
script restart the application.</li>
</ul>
<p>Note that it may be fine for some applications to make API calls without
an error catcher and risk throwing uncaught errors leading to a fatal error.
It's not safe to continue execution after a fatal error, so such
applications would typically simply exit when a fatal error occurs. Even
without an actual recovery strategy, a fatal error handler should be used to
e.g. write fatal error information to <code>stderr</code> before process exit.</p>
<a name="error-fatal-panic"></a> <!-- for old links -->
<h2 id="normal-and-fatal-errors">Normal and fatal errors</h2>
<p>An <b>ordinary error</b> is caused by a <code>throw</code> statement, a
<code><a href="api.html#duk_throw">duk_throw()</a></code>
API call (or similar), or by an internal, recoverable Duktape error.
Ordinary errors can be caught with a <code>try-catch</code> in ECMAScript
code or e.g. <code><a href="api.html#duk_pcall">duk_pcall()</a></code>
(see API calls tagged
<code><a href="api.html#taglist-protected">protected</a></code>)
in C code.</p>
<p>A <b>fatal error</b> is caused by an uncaught error, an assertion failure
(if enabled), an explicit call to
<code><a href="api.html#duk_fatal">duk_fatal()</a></code>, or an unrecoverable
error inside Duktape.</p>
<p>Each Duktape heap has a heap-wide fatal error handler registered in
<code><a href="api.html#duk_create_heap">duk_create_heap()</a></code>.
If no handler is given a built-in default handler is used:</p>
<ul>
<li>Default configuration: the built-in default fatal error handler
writes a debug log message but <b>won't</b> write anything to
<code>stdout</code> or <code>stderr</code>. Debug logging is disabled
by default, so that the fatal error message is not displayed by default.
The handler then calls <code>abort()</code>. If the abort() call exits
for some reason, the handler then enters an infinite loop to ensure
execution doesn't resume after a fatal error.</li>
<li><code>DUK_USE_CPP_EXCEPTIONS</code> enabled: the built-in default
fatal error handler throws a <code>duk_fatal_exception</code>. This
exception inherits from <code>std::runtime_error</code> so that it can be
easily caught and provides a <code>::what()</code> method to read the
fatal error message. It is unsafe to continue execution after catching
the fatal error.</li>
<li>If <code>DUK_USE_FATAL_HANDLER</code> is defined, it is always used as the
built-in default fatal error handler, even if C++ exceptions are enabled.</li>
</ul>
<div class="note">
It is <b>strongly recommended</b> to both (1) provide a custom fatal error
handler in heap creation and (2) replace the built-in default fatal error
handler using the <code>DUK_USE_FATAL_HANDLER()</code> option in
<code>duk_config.h</code>.
</div>
<p>There's no safe way to resume execution after a fatal error, so that a
fatal error handler must not return or call into the Duktape API. Instead the
handler should e.g. write out the message to the console or a log file and
then exit the process (or restart an embedded device). This also applies when
using C++ exceptions as the long control transfer mechanism. <b>If the
application continues execution after a fatal error all bets are off: memory
leaks are possible, and memory safety may be compromised.</b></p>
<p>Fatal errors may also happen without any heap context, so that Duktape
can't look up a possible heap-specific fatal error handler. Duktape will then
always call the built-in default fatal error handler (with the handler
userdata argument set to NULL). Fatal errors handled this way are currently
limited to assertion failures, so that if you don't enable assertions, no such
errors will currently occur and all fatal error handling goes through the
heap-associated fatal error handler which is in direct application control.</p>
<p>See <a href="http://wiki.duktape.org/HowtoFatalErrors.html">How to handle fatal errors</a>
for more detail and examples.</p>
<h2 id="long-control-transfers">Long control transfers</h2>
<p>The specific long control transfer mechanism Duktape uses internally
for error throwing and catching is not directly visible to application code:
application code uses a
<code><a href="api.html#taglist-protected">protected</a></code> call to catch
errors, while errors are thrown by Duktape or by the application using a
variety of Duktape API calls, e.g. <code>duk_error()</code>.</p>
<h3>Default configuration, without DUK_USE_CPP_EXCEPTIONS</h3>
<p>In the default configuration a Duktape API protected call uses
<code>setjmp()</code> to establish a catch site. The error throw
site uses <code>longjmp()</code> to unwind the native C stack and return
to the (nearest) catch site. On some platforms variants of the calls, such
as <code>sigsetjmp()</code> and <code>siglongjmp</code> are used. There are
minor differences between the call variants e.g. with regards to performance
and signal handling. The variant is chosen by <code>duk_config.h</code>.</p>
<p>A <code>longjmp()</code> unwinds all the native C stack frames between the
<code>longjmp()</code> and the <code>setjmp()</code>. However, this unwinding
process won't invoke C++ automatic destructors which may be a significant
limitation for some C++ applications.</p>
<p>For fatal errors, e.g. uncaught errors, the default fatal error handler
uses <code>abort()</code>, see <a href="#normal-and-fatal-errors">Normal and fatal errors</a>.</p>
<h3>C++ mode, with DUK_USE_CPP_EXCEPTIONS</h3>
<p>When <code>DUK_USE_CPP_EXCEPTIONS</code> is enabled the long control transfer
is based on a C++ exception throw. The protected call uses a C++ try-catch
to establish a catch site; note that this happens inside Duktape, and is not
visible to the application. The error throw site throws a
<code>duk_internal_exception</code> which is caught by the (nearest) Duktape
catch site. <b>Application code must not catch (or throw) the exception.</b>
To minimize that risk, the exception doesn't inherit from any standard exception
class so it won't be caught by a boilerplate <code>std::exception</code> catch
site.</p>
<p>For fatal errors, e.g. uncaught errors, a <code>duk_fatal_exception</code>
is thrown by the default fatal error handler. The exception inherits from
<code>std::runtime_error</code> and is intended to be caught by user code.
The exception provides a <code>::what()</code> method returning the fatal
error message. Even though the fatal error is catchable, it is still unsafe
to continue execution after catching the error. See
<a href="#normal-and-fatal-errors">Normal and fatal errors</a>.</p>
<p>For both C++ exception types the C++ native stack unwind process supports
automatic destructors for the stack frames between the throw site and the catch
site.</p>