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.
440 lines
19 KiB
440 lines
19 KiB
<h2 id="programming">Programming model</h2>
|
|
|
|
<p><b>(This section is under work.)</b></p>
|
|
|
|
<h3>Overview</h3>
|
|
|
|
<p>Programming with Duktape is quite straightforward:</p>
|
|
<ul>
|
|
<li>Add Duktape source (<tt>duktape.c</tt>) and header (<tt>duktape.h</tt>)
|
|
to your build.</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>
|
|
|
|
<h3>Heap and context</h3>
|
|
|
|
<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 <tt>duk_context *</tt>
|
|
in the Duktape API, and is associated with an internal Duktape coroutine (a form
|
|
of a co-operative thread). 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. A coroutine also has an internal <b>catch stack</b> which is used
|
|
to track error catching sites established using e.g. <tt>try-catch-finally</tt>
|
|
blocks. This is not visible to the caller in any way at the moment.</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.</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 additional contexts inside the same heap:</p>
|
|
<pre class="c-code">
|
|
duk_context *new_ctx;
|
|
|
|
(void) duk_push_thread(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 <tt>duk_context *</tt>, 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>
|
|
|
|
<h3>Call stack and catch stack (of a context)</h3>
|
|
|
|
<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>Closely related to the call stack, Duktape also maintains a catch stack
|
|
for keeping track of current error catching sites established using e.g.
|
|
<tt>try-catch-finally</tt>. The catch stack is even less visible to the
|
|
caller than the call stack.</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>
|
|
|
|
<h3>Value stack (of a context) and value stack index</h3>
|
|
|
|
<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:
|
|
<tt>undefined</tt>, <tt>null</tt>, boolean, number, string, object, buffer,
|
|
and pointer. For a detailed discussion of the available tagged types, see
|
|
<a href="#types">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 10 entries
|
|
(absolute indices)
|
|
|
|
.----.
|
|
| 15 |
|
|
| 14 |
|
|
| 13 |
|
|
| 12 | Active stack frame (indices
|
|
| 11 | relative to stack bottom)
|
|
| 10 |
|
|
| 9 | .---.
|
|
| 8 | | 5 | API index 0 is bottom (at value stack index 3).
|
|
| 7 | | 4 |
|
|
| 6 | | 3 | API index 5 is highest used (at value stack index 8).
|
|
| 5 | | 2 |
|
|
| 4 | | 1 | Stack top is 6 (relative to stack bottom).
|
|
| 3 | <--- | 0 |
|
|
| 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 <tt>DUK_INVALID_INDEX</tt> 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>
|
|
|
|
<h3>Growing the value stack</h3>
|
|
|
|
<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 up to size XXX. 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 <tt>duk_check_stack()</tt>
|
|
or (usually more preferably) <tt>duk_require_stack()</tt>. 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="ecmascript-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>
|
|
|
|
<h3>Ecmascript array index</h3>
|
|
|
|
<p>Ecmascript object and array keys can only be strings. Array indices
|
|
(e.g. 0, 1, 2) are represented as canonical string representations of the
|
|
respective numbers. More technically, all canonical string representations
|
|
of the integers in the range [0, 2**32-1] 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>
|
|
|
|
<h3>Duktape API</h3>
|
|
|
|
<p>Duktape API is the collection of user callable API calls defined in
|
|
<tt>duktape.h</tt> 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 <tt>NULL</tt> pointers). However, to minimize
|
|
footprint, the <tt>ctx</tt> argument is not checked, and the caller MUST NOT
|
|
call any Duktape API calls with a <tt>NULL</tt> context.</p>
|
|
|
|
<h3>Duktape/C function</h3>
|
|
|
|
<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">
|
|
int my_func(duk_context *ctx) {
|
|
duk_push_int(ctx, 123);
|
|
return 1;
|
|
}
|
|
</pre>
|
|
|
|
<p>The function gets Ecmascript call argument in the value stack of
|
|
<tt>ctx</tt>, with <tt>duk_get_top(ctx)</tt> indicating the number of
|
|
arguments present on the value stack. 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 <tt>undefined</tt>. A function can also be registered
|
|
as a vararg function (by giving <tt>DUK_VARARGS</tt> 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 <tt>undefined</tt> is returned to caller.</li>
|
|
<li>A negative return value indicates that an error is to be automatically
|
|
thrown. Error codes named <tt>DUK_RET_xxx</tt> map to specific kinds
|
|
of errors (do not confuse these with <tt>DUK_ERR_xxx</tt> which are
|
|
positive values).</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>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">
|
|
int 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>.
|
|
For instance, attempt to delete a non-configurable property using <tt>duk_del_prop()</tt>
|
|
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>As a consequence of Duktape/C function strictness, all Duktape API calls
|
|
made from inside a Duktape/C call obey Ecmascript strict semantics. However,
|
|
when API calls are made from outside a Duktape/C function (when the call stack
|
|
is empty), all API calls obey Ecmascript <i>non-strict</i> semantics, as this
|
|
is the Ecmascript default.</p>
|
|
|
|
<p>Also as a consequence of Duktape/C function strictness, the <tt>this</tt>
|
|
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>
|
|
as is normal for non-strict Ecmascript functions. An example of how coercion
|
|
happens in 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 <tt>new Foo()</tt> expressions. You can check whether
|
|
a function was called in constructor mode as follows:</p>
|
|
<pre class="c-code">
|
|
static int 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>
|
|
|
|
<h3>Storing state for a Duktape/C function</h3>
|
|
|
|
<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 a few ways to achieve this.</p>
|
|
|
|
<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
|
|
magically 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>
|
|
|
|
<p>Another alternative for storing state is to call the Duktape/C function
|
|
as a method and then use the <tt>this</tt> 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 <tt>foo</tt> as its <tt>this</tt>
|
|
binding, and one could store state directly in <tt>foo</tt>. 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 <tt>this</tt> binding is easy:</p>
|
|
<pre class="c-code">
|
|
duk_push_this(ctx);
|
|
duk_get_prop_string(ctx, -1, "my_state_variable");
|
|
</pre>
|
|
|
|
|