mirror of https://github.com/svaarala/duktape.git
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
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 | <--- | 5 | API index 5 is highest used (at value stack index 8).
|
|
| 7 | | 4 |
|
|
| 6 | | 3 |
|
|
| 5 | | 2 |
|
|
| 4 | | 1 |
|
|
| 3 | <--- | 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 (>= 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 (< 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 & 0x03], duk_safe_to_string(ctx, 0));
|
|
if (magic & 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 >= 20403)
|
|
/* Duktape 2.4.3 or later */
|
|
#elif (DUK_VERSION >= 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 >= 20403) {
|
|
print('Duktape 2.4.3 or higher');
|
|
} else if (Duktape.version >= 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>
|
|
|