Fork me on GitHub
Duktape Programmer's Guide

Introduction §

Version: 1.4.0 (2016-01-10)

Document scope §

This guide provides an introduction to using Duktape in your programs. Once you're familiar with the basics, there is a concise API reference for looking up API details. The Duktape Wiki provides more detailed examples and best practices.

This document doesn't cover Duktape internals (see the Duktape repo if you wish to tinker with them).

What is Duktape? §

Duktape is an embeddable Ecmascript E5/E5.1 engine with a focus on portability and compact footprint. By integrating Duktape into your C/C++ program you can easily extend its functionality through scripting. You can also build the main control flow of your program in Ecmascript and use fast C code functions to do heavy lifting.

The terms Ecmascript and Javascript are often considered more or less equivalent, although Javascript and its variants are technically just one environment where the Ecmascript language is used. The line between the two is not very clear in practice: even non-browser Ecmascript environments often provide some browser-specific built-ins. Duktape is no exception, and provides the commonly used print() and alert() built-ins. Even so, we use the term Ecmascript throughout to refer to the language implemented by Duktape.

Conformance §

Duktape conforms to the following Ecmascript specifications:

Duktape borrows a few features from Ecmascript E6:

TypedArray support is based on Khronos TypedArray specification with some ambiguous semantics resolved based on ES6 TypedArray (and comparison to other engines):

Node.js Buffer support is based on:

Features §

Besides standard Ecmascript features, Duktape has the following additional features (some are visible to applications, while others are internal):

Goals §

Compliance. Ecmascript E5/E5.1 and real world compliance. Ecmascript compliance requires regular expression and Unicode support. When possible, implement features from the upcoming Ecmascript E6 specification to minimize Duktape custom features.

Portability. Minimal system dependencies are nice when porting, so Duktape depends on very few system libraries. For example, number formatting and parsing, regular expressions, and Unicode are all implemented internally by Duktape. One of the few dependencies that cannot be fully eliminated is system date/time integration in the Date built-in. Duktape supports major platforms directly but you can also use an external Date provider on exotic platforms.

Easy C interface. The interface between Duktape and C programs should be natural and error-tolerant. As a particular issue, string representation should be UTF-8 with automatic NUL terminators to match common C use.

Small footprint. Code and data footprint should be as small as possible, even for small programs. This is more important than performance, as there are already several very fast engines but fewer very compact, portable engines.

Reasonable performance. Small footprint (and portability, to some extent) probably eliminates the possibility of a competitive JIT-based engine, so there is no practical way of competing with very advanced JIT-based engines like SpiderMonkey (and its optimized variants) or Google V8. Performance should still be reasonable for typical embedded programs. Lua is a good benchmark in this respect. (Adding optional, modular support for JITing or perhaps off-line compilation would be nice.)

ASCII string performance. It's important that operations dealing with plain ASCII strings be very fast: ASCII dominates most embedded use. Operations dealing with non-ASCII strings need to perform reasonably but are not critical. This is a necessary trade-off: using C-compatible strings means essentially using UTF-8 string representation which makes string indexing and many other operations slower than with fixed size character representations. It's still important to support common idioms like iterating strings sequentially (in either direction) efficiently.

Document organization §

Getting started guides you through downloading, compiling, and integrating Duktape into your program. It also provides concrete examples of how you can integrate scripting capabilities into your program.

Programming model, Stack types, and C types discuss core Duktape concepts such as heap, context, value stacks, Duktape API, and Duktape/C functions. Duktape stack types and C type wrappers are discussed in detail.

Duktape specific Ecmascript features are discussed in multiple sections: Type algorithms (for custom types), Duktape built-ins (additional built-ins), Ecmascript E6 features (features borrowed from ES6), Custom behavior (behavior differing from standard), Custom JSON formats, Custom directives, Buffer objects, Error objects (properties and traceback support), Function objects (properties), Debugger, Modules, Logging, Finalization, Coroutines, Virtual properties, Internal properties, Bytecode dump/load, Threading, Sandboxing.

Performance provides a few Duktape-specific tips for improving performance and avoiding performance pitfalls. Memory usage summarizes Duktape memory usage and gives pointers for minimizing it. Compiling describes how to compile Duktape in detail, covering in particular available feature defines. Portability covers platform and compiler specific issues and other portability issues. Compatibility discusses Duktape's compatibility with Ecmascript dialects, extensions, and frameworks. Versioning describes Duktape versioning and what version compatibility to expect. Limitations summarizes currently known limitations and provides possible workarounds.

Comparison to Lua discusses some differences between Lua and Duktape; it may be useful reading if you're already familiar with Lua.

Getting started §

Downloading §

Download the source distributable from the Download page.

Command line tool §

Unpack the distributable:

$ cd /tmp
$ tar xvfJ duktape-<version>.tar.xz

Compile the command line tool using the provided Makefile:

$ cd /tmp/duktape-<version>/
$ make -f Makefile.cmdline
The Makefile assumes you have gcc installed. If you don't, you can just edit the Makefile to match your compiler (the Makefile is quite simple).
The command line tool avoids platform dependencies by default. If you're running a UNIX variant and have readline and the necessary development headers, you can enable line editing support by editing the Makefile:
  • Add -lreadline and -lncurses

You can now run Ecmascript code interactively:

$ ./duk
((o) Duktape [no readline] 1.4.0 (v1.4.0)
duk> print('Hello world!')
Hello world!
= undefined

You can also run Ecmascript code from a file which is useful for playing with features and algorithms. As an example, create fib.js:

// fib.js
function fib(n) {
    if (n == 0) { return 0; }
    if (n == 1) { return 1; }
    return fib(n-1) + fib(n-2);

function test() {
    var res = [];
    for (i = 0; i < 20; i++) {
    print(res.join(' '));


Test the script from the command line:

$ ./duk fib.js
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181

Integrating Duktape into your program §

The command line tool is simply an example of a program which embeds Duktape. Embedding Duktape into your program is very simple: just add duktape.c, duktape.h, and duk_config.h to your build, and call the Duktape API from elsewhere in your program.

The distributable contains a very simple example program, hello.c, which illustrates this process. Compile the test program e.g. as (see Compiling for compiler option suggestions):

$ cd /tmp/duktape-<version>/
$ gcc -std=c99 -o hello -Isrc/ src/duktape.c examples/hello/hello.c -lm

The test program creates a Duktape context and uses it to run some Ecmascript code:

$ ./hello
Hello world!

Because Duktape is an embeddable engine, you don't need to change the basic control flow of your program. The basic approach is:

Let's look at a simple example program. The program reads in a line from stdin using a C mainloop, calls an Ecmascript helper to transform the line, and prints out the result. The line processing function can take advantage of Ecmascript goodies like regular expressions, and can be easily modified without recompiling the C program.

The script code will be placed in process.js. The example line processing function converts a plain text line into HTML, and automatically bolds text between stars:

// process.js
function processLine(line) {
    return line.trim()
        .replace(/[<>&"'\u0000-\u001F\u007E-\uFFFF]/g, function(x) {
            // escape HTML characters
            return '&#' + x.charCodeAt(0) + ';'
        .replace(/\*(.*?)\*/g, function(x, m) {
            // automatically bold text between stars
            return '<b>' + m + '</b>';

The C code, processlines.c initializes a Duktape context, evaluates the script, then proceeds to process lines from stdin, calling processLine() for every line:

/* processlines.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "duktape.h"

int main(int argc, const char *argv[]) {
    duk_context *ctx = NULL;
    char line[4096];
    char idx;
    int ch;

    ctx = duk_create_heap_default();
    if (!ctx) {
        printf("Failed to create a Duktape heap.\n");

    if (duk_peval_file(ctx, "process.js") != 0) {
        printf("Error: %s\n", duk_safe_to_string(ctx, -1));
        goto finished;
    duk_pop(ctx);  /* ignore result */

    memset(line, 0, sizeof(line));
    idx = 0;
    for (;;) {
        if (idx >= sizeof(line)) {
            printf("Line too long\n");

        ch = fgetc(stdin);
        if (ch == 0x0a) {
            line[idx++] = '\0';

            duk_get_prop_string(ctx, -1 /*index*/, "processLine");
            duk_push_string(ctx, line);
            if (duk_pcall(ctx, 1 /*nargs*/) != 0) {
                printf("Error: %s\n", duk_safe_to_string(ctx, -1));
            } else {
                printf("%s\n", duk_safe_to_string(ctx, -1));
            duk_pop(ctx);  /* pop result/error */

            idx = 0;
        } else if (ch == EOF) {
        } else {
            line[idx++] = (char) ch;



Let's look at the Duktape specific parts of the example code piece by piece. Here we need to gloss over some details for brevity, see Programming model for a detailed discussion:

Compile like above:

$ gcc -std=c99 -o processlines -Isrc/ src/duktape.c processlines.c -lm

Test run (ensure that process.js is in the current directory):

$ echo "I like *Sam & Max*." | ./processlines
I like <b>Sam &#38; Max</b>.

Calling C code from Ecmascript (Duktape/C bindings) §

The integration example illustrated how C code can call into Ecmascript to do things which are easy in Ecmascript but difficult in C.

Ecmascript also often needs to call into C when the situation is reversed. For instance, while scripting is useful for many things, it is not optimal for low level byte or character processing. Being able to call optimized C helpers allows you to write most of your script logic in nice Ecmascript but call into C for the performance critical parts. Another reason for using native functions is to provide access to native libraries.

To implement a native function you write an ordinary C function which conforms to a special calling convention, the Duktape/C binding. Duktape/C functions take a single argument, a Duktape context, and return a single value indicating either error or number of return values. The function accesses call arguments and places return values through the Duktape context's value stack, manipulated with the Duktape API. We'll go deeper into Duktape/C binding and the Duktape API later on. Example:

duk_ret_t my_native_func(duk_context *ctx) {
    double arg = duk_require_number(ctx, 0 /*index*/);
    duk_push_number(ctx, arg * arg);
    return 1;

Let's look at this example line by line:

We'll use a primality test as an example for using native code to speed up Ecmascript algorithms. More specifically, our test program searches for primes under 1000000 which end with the digits '9999'. The Ecmascript version of the program is:

// prime.js

// Pure Ecmascript version of low level helper
function primeCheckEcmascript(val, limit) {
    for (var i = 2; i <= limit; i++) {
        if ((val % i) == 0) { return false; }
    return true;

// Select available helper at load time
var primeCheckHelper = (this.primeCheckNative || primeCheckEcmascript);

// Check 'val' for primality
function primeCheck(val) {
    if (val == 1 || val == 2) { return true; }
    var limit = Math.ceil(Math.sqrt(val));
    while (limit * limit < val) { limit += 1; }
    return primeCheckHelper(val, limit);

// Find primes below one million ending in '9999'.
function primeTest() {
    var res = [];

    print('Have native helper: ' + (primeCheckHelper !== primeCheckEcmascript));
    for (var i = 1; i < 1000000; i++) {
        if (primeCheck(i) && (i % 10000) == 9999) { res.push(i); }
    print(res.join(' '));

Note that the program uses the native helper if it's available but falls back to an Ecmascript version if it's not. This allows the Ecmascript code to be used in other containing programs. Also, if the prime check program is ported to another platform where the native version does not compile without changes, the program remains functional (though slower) until the helper is ported. In this case the native helper detection happens when the script is loaded. You can also detect it when the code is actually called which is more flexible.

A native helper with functionality equivalent to primeCheckEcmascript is quite straightforward to implement. Adding a program main we get primecheck.c:

/* primecheck.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "duktape.h"

static duk_ret_t native_prime_check(duk_context *ctx) {
    int val = duk_require_int(ctx, 0);
    int lim = duk_require_int(ctx, 1);
    int i;

    for (i = 2; i <= lim; i++) {
        if (val % i == 0) {
            return 1;

    return 1;

int main(int argc, const char *argv[]) {
    duk_context *ctx = NULL;

    ctx = duk_create_heap_default();
    if (!ctx) {
        printf("Failed to create a Duktape heap.\n");

    duk_push_c_function(ctx, native_prime_check, 2 /*nargs*/);
    duk_put_prop_string(ctx, -2, "primeCheckNative");

    if (duk_peval_file(ctx, "prime.js") != 0) {
        printf("Error: %s\n", duk_safe_to_string(ctx, -1));
        goto finished;
    duk_pop(ctx);  /* ignore result */

    duk_get_prop_string(ctx, -1, "primeTest");
    if (duk_pcall(ctx, 0) != 0) {
        printf("Error: %s\n", duk_safe_to_string(ctx, -1));
    duk_pop(ctx);  /* ignore result */



The new calls here are, line by line:

Compile like above:

$ gcc -std=c99 -o primecheck -Isrc/ src/duktape.c primecheck.c -lm

Test run (ensure that prime.js is in the current directory):

$ time ./primecheck
Have native helper: true
49999 59999 79999 139999 179999 199999 239999 289999 329999 379999 389999
409999 419999 529999 599999 619999 659999 679999 769999 799999 839999 989999

real	0m2.985s
user	0m2.976s
sys	0m0.000s

Because most execution time is spent in the prime check, the speed-up compared to plain Ecmascript is significant. You can check this by editing prime.js and disabling the use of the native helper:

// Select available helper at load time
var primeCheckHelper = primeCheckEcmascript;

Re-compiling and re-running the test:

$ time ./primecheck
Have native helper: false
49999 59999 79999 139999 179999 199999 239999 289999 329999 379999 389999
409999 419999 529999 599999 619999 659999 679999 769999 799999 839999 989999

real	0m23.609s
user	0m23.573s
sys	0m0.000s

Programming model §

Overview §

Programming with Duktape is quite straightforward:

Let's look at all the steps and their related concepts in more detail.

Heap and context §

A Duktape heap 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.

A Duktape context is an Ecmascript "thread of execution" which lives in a certain Duktape heap. A context is represented by a duk_context * 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 value stack of the Duktape coroutine: values can be inserted and queries, functions can be called, and so on.

Each coroutine has a call stack which controls execution, keeping track of function calls, native or Ecmascript, within the Ecmascript engine. Each coroutine also has a value stack which stores all the Ecmascript values of the coroutine's active call stack. The value stack always has an active stack frame 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 catch stack which is used to track error catching sites established using e.g. try-catch-finally blocks. This is not visible to the caller in any way at the moment.

Multiple contexts can share the same Duktape heap. 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.

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 Threading.

To create a Duktape heap and an initial context inside the heap, you can simply use:

duk_context *ctx = duk_create_heap_default();
if (!ctx) { exit(1); }

If you wish to provide your own memory allocation functions and a fatal error handler function (recommended), use:

duk_context *ctx = duk_create_heap(my_alloc,
if (!ctx) { exit(1); }

To create a new context inside the same heap, with the context sharing the same global objects:

duk_context *new_ctx;

(void) duk_push_thread(ctx);
new_ctx = duk_get_context(ctx, -1 /*index*/);

To create a new context inside the same heap, but with a fresh set of global object:

duk_context *new_ctx;

(void) duk_push_thread_new_globalenv(ctx);
new_ctx = duk_get_context(ctx, -1 /*index*/);

Contexts are automatically garbage collected when they become unreachable. This also means that if your C code holds a duk_context *, the corresponding Duktape coroutine MUST be reachable from a garbage collection point of view.

A heap must be destroyed explicitly when the caller is done with it:


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.

Call stack and catch stack (of a context) §

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.

Closely related to the call stack, Duktape also maintains a catch stack for keeping track of current error catching sites established using e.g. try-catch-finally. The catch stack is even less visible to the caller than the call stack.

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.

Don't confuse with the C stack.

Value stack (of a context) and value stack index §

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: undefined, null, boolean, number, string, object, buffer, pointer, and lightfunc. For a detailed discussion of the available tagged types, see Types.

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 bottom which is referred to with the index zero in the Duktape API. There is also a conceptual top which identifies the stack element right above the highest currently used element. The following diagram illustrates this:

 Value stack
 of 15 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 |

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:

[ 0 1 2 3 4 5 ]

A value stack index 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.

Non-negative (>= 0) indices refer to stack entries in the current stack frame, relative to the frame bottom:

[ 0 1 2 3 4 5 ]

Negative (< 0) indices refer to stack entries relative to the top:

[ -6 -5 -4 -3 -2 -1 ]

The special constant DUK_INVALID_INDEX 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".

The value stack top (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.

[ 0 1 2 3 4 5 6 ]

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.

Don't confuse with the C stack.

Growing the value stack §

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 not 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.

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 DUK_API_ENTRY_STACK (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.

You can extend the stack allocation explicitly with duk_check_stack() or (usually more preferably) duk_require_stack(). 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.

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):

/* uppercase.c */
#include <stdio.h>
#include <stdlib.h>
#include "duktape.h"

static int dummy_upper_case(duk_context *ctx) {
    size_t sz;
    const char *val = duk_require_lstring(ctx, 0, &sz);
    size_t i;

    /* We're going to need 'sz' additional entries on the stack. */
    duk_require_stack(ctx, sz);

    for (i = 0; i < sz; i++) {
        char ch = val[i];
        if (ch >= 'a' && ch <= 'z') {
            ch = ch - 'a' + 'A';
        duk_push_lstring(ctx, (const char *) &ch, 1);

    duk_concat(ctx, sz);
    return 1;

int main(int argc, char *argv[]) {
    duk_context *ctx;

    if (argc < 2) { exit(1); }

    ctx = duk_create_heap_default();
    if (!ctx) { exit(1); }

    duk_push_c_function(ctx, dummy_upper_case, 1);
    duk_push_string(ctx, argv[1]);
    duk_call(ctx, 1);
    printf("%s -> %s\n", argv[1], duk_to_string(ctx, -1));

    return 0;

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.

Ecmascript array index §

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.

To illustrate the Ecmascript array index handling, consider the following example:

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

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".

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.

Duktape API §

Duktape API is the collection of user callable API calls defined in duktape.h and documented in the API reference.

The Duktape API calls are generally error tolerant and will check all arguments for errors (such as NULL pointers). However, to minimize footprint, the ctx argument is not checked, and the caller MUST NOT call any Duktape API calls with a NULL context.

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:

Duktape/C function §

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:

duk_ret_t my_func(duk_context *ctx) {
    duk_push_int(ctx, 123);
    return 1;

The function gets Ecmascript call argument in the value stack of ctx, with duk_get_top() indicating the number of arguments present on the value stack. The this binding is not automatically pushed to the value stack; use duk_push_this() to access it. 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 undefined. A function can also be registered as a vararg function (by giving DUK_VARARGS as the argument count) in which case call arguments are not modified prior to C function entry.

The function can return one of the following:

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:

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;
    /* ... */

All Duktape/C functions are considered strict in the Ecmascript sense. 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 duk_del_prop() will cause an error to be thrown. This is the case with a strict Ecmascript function too:

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

Another consequence of Duktape/C function strictness is that the this binding given to Duktape/C functions is not coerced. This is also the case for strict Ecmascript code:

function strictFunc() { 'use strict'; print(typeof this); }
function nonStrictFunc() { print(typeof this); }'foo');     // prints 'string' (uncoerced)'foo');  // prints 'object' (coerced)

Duktape/C functions are currently always constructable, i.e. they can always be used in new Foo() expressions. You can check whether a function was called in constructor mode as follows:

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");

To save memory, Duktape/C functions don't have a prototype property by default, so the default object instance (given to the constructor as this) inherits from Object.prototype. To use a custom prototype you can define prototype 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 new expression.

The this binding is not automatically pushed to the value stack; use duk_push_this() to access it.

Storing state for a Duktape/C function §

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.

Properties of function §

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.

Accessing the Ecmascript Function object related to a Duktape/C function is easy:

duk_get_prop_string(ctx, -1, "my_state_variable");

'this' binding §

Another alternative for storing state is to call the Duktape/C function as a method and then use the this binding for storing state. For instance, consider a Duktape/C function called as:


When called, the Duktape/C function gets foo as its this binding, and one could store state directly in foo. The difference to using the Function object approach is that the same object is shared by all methods, which has both advantages and disadvantages.

Accessing the this binding is easy:

duk_get_prop_string(ctx, -1, "my_state_variable");

Magic value of function §

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:

/* 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) {
} else {

For an API usage example, see the test case test-get-set-magic.c. Duktape uses magic values a lot internally to minimize size of compiled code, see e.g. duk_bi_math.c.

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.

Heap stash §

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 duk_push_heap_stash() API call.

Global stash §

The global stash is like the heap stash, but is associated with a global object. It is accessed with the duk_push_global_stash() API call. There can be several environments with different global objects within the same heap.

Thread stash §

The thread stash is like the heap stash, but is associated with a Duktape thread (i.e. a ctx pointer). It is accessible with the duk_push_thread_stash() API call.

Duktape version specific code §

The Duktape version is available through the DUK_VERSION define, with the numeric value (major * 10000) + (minor * 100) + patch. The same value is available to Ecmascript code through Duktape.version. Calling code can use this define for Duktape version specific code.

For C code:

#if (DUK_VERSION >= 10203)
/* Duktape 1.2.3 or later */
#elif (DUK_VERSION >= 800)
/* Duktape 0.8.0 or later */
/* Duktape lower than 0.8.0 */

For Ecmascript code (also see Duktape built-ins):

if (typeof Duktape !== 'object') {
    print('not Duktape');
} else if (Duktape.version >= 10203) {
    print('Duktape 1.2.3 or higher');
} else if (Duktape.version >= 800) {
    print('Duktape 0.8.0 or higher (but lower than 1.2.3)');
} else {
    print('Duktape lower than 0.8.0');

Numeric error codes §

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 duktape.h, e.g. DUK_ERR_TYPE_ERROR.

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.

Error handling §

Error handling in the Duktape API is similar to how Ecmascript handles errors: errors are thrown either explicitly or implicitly, then caught and handled. However, instead of a try-catch statement application code uses protected Duktape API calls to establish points in C code where errors can be caught and handled. An uncaught error causes the fatal error handler to be called, which is considered an unrecoverable situation and should ordinarily be avoided (see Error, fatal, and panic).

To avoid fatal errors, typical application code should establish an error catch point before making other Duktape API calls. This is done using protected Duktape API calls, for example:

An example of the first technique:

/* Use duk_peval() variant to evaluate a file so that script errors are
 * handled safely.  Both syntax errors and runtime errors are caught.

if (duk_peval_file(ctx, "myscript.js") != 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));

An example of the second technique:

/* 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) {
    /* Here we can use unprotected calls freely. */
    duk_eval_file_noresult(ctx, "myscript.js");

    /* ... */

    return 0;  /* success return, no return value */

/* elsewhere: */

if (duk_safe_call(ctx, unsafe_code, 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));

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 context dependent, but could be something like:

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 possible 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 stderr before process exit.

Error, fatal, and panic §

An ordinary error is caused by a throw statement, a duk_throw() API call (or similar), or by an internal, recoverable Duktape error. Ordinary errors can be caught with a try-catch in Ecmascript code or e.g. duk_pcall() (see API calls tagged protected) in C code.

An uncaught error or an explicit call to duk_fatal() causes a fatal error handler to be called. A fatal error handler is associated with every Duktape heap upon creation. There is no reasonable way to resume execution after a fatal error, so the fatal error handler must not return. The default fatal error handler writes an error message to stderr and then escalates the fatal error to a panic (which, by default, abort()s the process). You can provide your own fatal error handler to deal with fatal errors. The most appropriate recovery action is, of course, platform and application specific. The handler could, for instance, write a diagnostic file detailing the situation and then restart the application to recover.

A panic is caused by Duktape assertion code (if included in the build) or by the default fatal error handler. There is no way to induce a panic from user code. The default panic handler writes an error message to stderr and abort()s the process. You can use the DUK_OPT_SEGFAULT_ON_PANIC feature option to cause a deliberate segfault instead of an abort(), which may be useful to get a stack trace from some debugging tools. You can also override the default panic handler entirely with the feature option DUK_OPT_PANIC_HANDLER. The panic handler is decided during build, while the fatal error handler is decided at runtime by the calling application.

If assertions are turned off and the application provides a fatal error handler, no panics will be caused by Duktape code. All errors will then be either ordinary errors or fatal errors, both under application control.

Stack types §

Overview §

TypeType constantType mask constantDescription
(none)DUK_TYPE_NONEDUK_TYPE_MASK_NONEno type (missing value, invalid index, etc)
stringDUK_TYPE_STRINGDUK_TYPE_MASK_STRINGimmutable (plain) string
objectDUK_TYPE_OBJECTDUK_TYPE_MASK_OBJECTobject with properties
bufferDUK_TYPE_BUFFERDUK_TYPE_MASK_BUFFERmutable (plain) byte buffer, fixed/dynamic
pointerDUK_TYPE_POINTERDUK_TYPE_MASK_POINTERopaque pointer (void *)
lightfuncDUK_TYPE_LIGHTFUNCDUK_TYPE_MASK_LIGHTFUNCplain Duktape/C pointer (non-object)

Memory allocations §

The following stack types involve additional heap allocations:

Note that while strings and buffers are considered a primitive (pass-by-value) types, they are a heap allocated type from a memory allocation viewpoint.

Pointer stability §

Heap objects allocated by Duktape have stable pointers: the objects are not relocated in memory while they are reachable from a garbage collection point of view. This is the case for the main heap object, but not necessarily for any additional allocations related to the object, such as dynamic property tables or dynamic buffer data area. A heap object is reachable e.g. when it resides on the value stack of a reachable thread or is reachable through the global object. Once a heap object becomes unreachable any pointers held by user C code referring to the object are unsafe and should no longer be dereferenced.

In practice the only heap allocated data directly referenced by user code are strings, fixed buffers, and dynamic buffers. The data area of strings and fixed buffers is stable; it is safe to keep a C pointer referring to the data even after a Duktape/C function returns as long the string or fixed buffer remains reachable from a garbage collection point of view at all times. Note that this is not the case for Duktape/C value stack arguments, for instance, unless specific arrangements are made.

The data area of a dynamic buffer does not have a stable pointer. The buffer itself has a heap header with a stable address but the current buffer is allocated separately and potentially relocated when the buffer is resized. It is thus unsafe to hold a pointer to a dynamic buffer's data area across a buffer resize, and it's probably best not to hold a pointer after a Duktape/C function returns (as there would be no easy way of being sure that the buffer hadn't been resized). The data area of an external buffer also has a potentially changing pointer, but the pointer is only changed by an explicit user API call.

For external buffers the stability of the data pointer is up to the user code which sets and updates the pointer.

Type masks §

Type masks allows calling code to easily check whether a type belongs to a certain type set. For instance, to check that a certain stack value is a number, string, or an object:

if (duk_get_type_mask(ctx, -3) & (DUK_TYPE_MASK_NUMBER |
                                  DUK_TYPE_MASK_STRING |
                                  DUK_TYPE_MASK_OBJECT)) {
    printf("type is number, string, or object\n");

There is a specific API call for matching a set of types even more conveniently:

if (duk_check_type_mask(ctx, -3, DUK_TYPE_MASK_NUMBER |
                                 DUK_TYPE_MASK_STRING |
                                 DUK_TYPE_MASK_OBJECT)) {
    printf("type is number, string, or object\n");

These are faster and more compact than the alternatives:

// alt 1
if (duk_is_number(ctx, -3) || duk_is_string(ctx, -3) || duk_is_object(ctx, -3)) {
    printf("type is number, string, or object\n");

// alt 2
int t = duk_get_type(ctx, -3);
    printf("type is number, string, or object\n");

None §

The none type is not actually a type but is used in the API to indicate that a value does not exist, a stack index is invalid, etc.

Undefined §

The undefined type maps to Ecmascript undefined, which is distinguished from a null.

Values read from outside the active value stack range read back as undefined.

Null §

The null type maps to Ecmascript null.

Boolean §

The boolean type is represented in the C API as an integer: zero for false, and non-zero for true.

Whenever giving boolean values as arguments in API calls, any non-zero value is accepted as a "true" value. Whenever API calls return boolean values, the value 1 is always used for a "true" value. This allows certain C idioms to be used. For instance, a bitmask can be built directly based on API call return values, as follows:

// this works and generates nice code
int bitmask = (duk_get_boolean(ctx, -3) << 2) |
              (duk_get_boolean(ctx, -2) << 1) |
              duk_get_boolean(ctx, -1);

// more verbose variant not relying on "true" being represented by 1
int bitmask = ((duk_get_boolean(ctx, -3) ? 1 : 0) << 2) |
              ((duk_get_boolean(ctx, -2) ? 1 : 0) << 1) |
              (duk_get_boolean(ctx, -1) ? 1 : 0);

// another verbose variant
int bitmask = (duk_get_boolean(ctx, -3) ? (1 << 2) : 0) |
              (duk_get_boolean(ctx, -2) ? (1 << 1) : 0) |
              (duk_get_boolean(ctx, -1) ? 1 : 0);

Number §

The number type is an IEEE double, including +/- Infinity and NaN values. Zero sign is also preserved. An IEEE double represents all integers up to 53 bits accurately.

IEEE double allows NaN values to have additional signaling bits. Because these bits are used by Duktape internal tagged type representation (when using 8-byte packed values), NaN values in the Duktape API are normalized. Concretely, if you push a certain NaN value to the value stack, another (normalized) NaN value may come out. Don't rely on NaNs preserving their exact form.

String §

The string type is an arbitrary byte sequence of a certain length which may contain internal NUL (0x00) values. Strings are always automatically NUL terminated for C coding convenience. The NUL terminator is not counted as part of the string length. For instance, the string "foo" has byte length 3 and is stored in memory as { 'f', 'o', 'o', '\0' }. Because of the guaranteed NUL termination, strings can always be pointed to using a simple const char * as long as internal NULs are not an issue; if they are, the explicit byte length of the string can be queried with the API. Calling code can refer directly to the string data held by Duktape. Such string data pointers are valid (and stable) for as long as a string is reachable in the Duktape heap.

Strings are interned for efficiency: only a single copy of a certain string ever exists at a time. Strings are immutable and must NEVER be changed by calling C code. Doing so will lead to very mysterious issues which are hard to diagnose.

Calling code most often deals with Ecmascript strings, which may contain arbitrary 16-bit codepoints (the whole range U+0000 to U+FFFF) but cannot represent non-BMP codepoints (this is how strings are defined in the Ecmascript standard). In Duktape, Ecmascript strings are encoded with CESU-8 encoding. CESU-8 matches UTF-8 except that it allows codepoints in the surrogate pair range (U+D800 to U+DFFF) to be encoded directly; these are prohibited in UTF-8. CESU-8, like UTF-8, encodes all 7-bit ASCII characters as-is which is convenient for C code. For example:

Duktape also uses extended strings internally. Codepoints up to U+10FFFF can be represented with UTF-8, and codepoints above that up to full 32 bits can be represented with extended UTF-8. Non-standard strings are used for storing internal object properties; using a non-standard string ensures that such properties never conflict with properties accessible using standard Ecmascript strings. Non-standard strings can be given to Ecmascript built-in functions, but since behavior may not be exactly specified, results may vary.

The extended UTF-8 encoding used by Duktape is described in the table below. The leading byte is shown in binary (with "x" marking data bits) while continuation bytes are marked with "C" (indicating the bit sequence 10xxxxxx):

Codepoint rangeBitsByte sequenceNotes
U+0000 to U+007F70xxxxxxx
U+0080 to U+07FF11110xxxxx C
U+0800 to U+FFFF161110xxxx C CU+D800 to U+DFFF allowed (unlike UTF-8)
U+1 0000 to U+1F FFFF2111110xxx C C CAbove U+10FFFF allowed (unlike UTF-8)
U+20 0000 to U+3FF FFFF26111110xx C C C C
U+400 0000 to U+7FFF FFFF311111110x C C C C C
U+8000 0000 to U+F FFFF FFFF3611111110 C C C C C COnly 32 bits used in practice (up to U+FFFFFFFF)

The downside of the encoding for codepoints above U+7FFFFFFF is that the leading byte will be 0xFE which conflicts with Unicode byte order marker encoding. This is not a practical concern in Duktape's internal use.

The leading 0xFF byte never appears in Duktape's extended UTF-8 encoding, and is used to implement internal properties.

Object §

The object type includes Ecmascript objects and arrays, functions, threads (coroutines), and buffer objects. In other words, anything with properties is an object. Properties are key-value pairs with a string key and an arbitrary value (including undefined).

Objects may participate in garbage collection finalization.

Buffer §

The buffer type is a raw buffer for user data. There are three kinds of buffer values:

The data pointer of a zero-size dynamic buffer may (or may not) be NULL which must be handled by calling code properly: a NULL data pointer only indicates an error if the requested size is non-zero. Unlike strings, buffer data areas are not automatically NUL terminated and calling code must not access the bytes following the allocated buffer size.

Fixed and dynamic buffers are automatically garbage collected. This also means that C code must not hold onto a buffer data pointer unless the buffer is reachable to Duktape, e.g. resides in an active value stack. The data area of an external buffer is not automatically garbage collected so user code is responsible for managing its life cycle. User code must also make sure that no external buffer value is accessed when the external buffer is no longer (or not yet) valid.

Like strings, buffer values have a length property and array index properties for reading and writing individual bytes in the buffer. The value of a indexed byte (buf[123]) is a number in the range 0...255 which represents a byte value (written values are coerced to integer modulo 256). This differs from string behavior where the indexed values are one-character strings (much more expensive). The length property is read-only so you can't resize a string by assigning to the length property. These properties are available for both plain buffer values and buffer object values.

The plain buffer type is not standard Ecmascript. Duktape provides several Object frontends for the plain buffer type: Duktape.Buffer, a Node.js compatible Buffer, and Khronos/ES6 TypedArray. These are all implemented on top of plain buffer values, but provide additional functionality like view/slice support, typed accessors, methods to manipulate data in different endianness, etc; see Buffer objects.

A few notes:

Pointer §

The pointer type is a raw, uninterpreted C pointer, essentially a void *. Pointers can be used to point to native objects (memory allocations, handles, etc), but because Duktape doesn't know their use, they are not automatically garbage collected. You can, however, put one or more pointers inside an object and use the object finalizer to free the native resources related to the pointer(s).

Lightfunc §

The lightfunc type is a plain Duktape/C function pointer and a small set of control flags packed into a single tagged value which requires no further heap allocations. The control flags (16 bits currently) encode: (1) number of stack arguments expected by the Duktape/C function (0 to 14 or varargs), (2) virtual length property value (0 to 15), and (3) a magic value (-128 to 127). Because a lightfunc is a plain tagged value, it cannot hold any actual own property values; it has a few virtual properties and inherits other properties through Function.prototype.

Lightfuncs are a separate tagged type in the Duktape C API, but behave mostly like Function objects for Ecmascript code. They have significant limitations compared to ordinary Function objects, the most important being:

Lightfuncs are useful for very low memory environments where the memory impact of ordinary Function objects matters. For more discussion, see Properties of lightweight Duktape/C functions, Type algorithms, and lightweight-functions.rst.

C types §

Duktape API uses typedef-wrapped C types almost exclusively to ensure portability to exotic platforms. This section provides some background, summarizes the types, and describes how calling code should use types to maximize portability.

Portable C/C++ typing is a complex issue, involving:

Duktape only works on platforms with two's complement arithmetic.

Guidelines for code using the Duktape API §

Wrapped types used in the Duktape API §

For the most part you don't need to worry about these type wrappers: they're intended for exotic environments where some common assumptions about type bit counts and such don't hold.

The API documentation uses the Duktape wrapped typedef names (such as duk_idx_t). The concrete type used by the compiler depends on your platform and compiler. When hovering over a prototype in the API documentation the tool tip will show what concrete types are used when C99/C++11 types are available and the platform int is at least 32 bits wide (which is nowadays almost always the case).

The following table summarizes a few central typedefs and what the concrete type selected will be in various (example) environments. The table also suggests what plain type you should use for printf() and scanf() casts for portable formatting/scanning.

Duktape type C99/C++11 32-bit int Legacy 32-bit int Legacy 16-bit int printf scanf Notes
duk_int_t int int long %ld
All around integer type, range is [DUK_INT_MIN, DUK_INT_MAX]
duk_uint_t unsigned int unsigned int unsigned long %lu
unsigned long
unsigned long
All around unsigned integer type, range is [0, DUK_UINT_MAX]
duk_int32_t int32_t int long %ld
Exact type for ToInt32() coercion
duk_uint32_t uint32_t unsigned int unsigned long %lu
unsigned long
unsigned long
Exact type for ToUint32() coercion
duk_uint16_t uint16_t unsigned short unsigned short %u
unsigned int
unsigned int
Exact type for ToUint16() coercion
duk_idx_t int int long %ld
Value stack index
duk_uarridx_t unsigned int unsigned int unsigned long %lu
unsigned long
unsigned long
Ecmascript array index
duk_codepoint_t int int long %ld
Unicode codepoints
duk_errcode_t int int long %ld
Integer error codes used in the Duktape API (range for user codes is [1,16777215])
duk_bool_t int int int %d
Boolean return values
duk_ret_t int int int %d
Return value from Duktape/C function
duk_size_t size_t size_t size_t %lu
unsigned long
unsigned long
1:1 mapping now, wrapped for future use. Range is [0, DUK_SIZE_MAX]. C99 format specifier is %zu.
duk_double_t double double double %f or %lf
1:1 mapping now, wrapped for future use, e.g. custom software floating point library.

Background on C/C++ typing issues §

This section provides some background and rationale for the C typing.

Bit sizes are not standard (and there's no guaranteed fast 32-bit type) §

Bit sizes of common types like int vary across implementations. C99/C++11 provide standard integer typedefs like int32_t (exact signed 32-bit type) and int_fast32_t (fast integer type which has at least signed 32-bit range). These typedefs are not available in older compilers, so platform dependent type detection is necessary.

Duktape needs an integer type which is convenient for the architecture but still guaranteed to be 32 bits wide. Such a type is needed to represent array indices, Unicode points, etc. However, there is no such standard type and at least the following variations are seen:

As can be seen, no built-in C type would be appropriate, so type detection is needed. Duktape detects and defines duk_int_t type for these purposes (at least 32 bits wide, convenient to the CPU). Normally it is mapped to int if Duktape can reliably detect that int is 32 bits or wider. When this is not the case, int_fast32_t is used if C99 types are available; if C99 is not available, Duktape uses platform specific detection to arrive at an appropriate type. The duk_uint_t is the same but unsigned. Most other types in the API (such as duk_idx_t) are mapped to duk_(u)int_t but this may change in the future if necessary.

Other special types are also needed. For instance, exactly N bits wide integers are also needed to ensure proper overflow behavior in some cases.

Format specifiers §

C/C++ types are often used with printf() and scanf(), with each type having a format specifier. The set of format specifiers is only partially standardized (e.g. %d is used for an int, regardless of its bit size), but custom codes are sometimes used.

When using type wrappers, the correct format code depends on type detection. For instance, duk_int_t is mapped to a convenient integer type which is at least 32 bits wide. On one platform the underlying type might be int (format specifier %d) and on another it might be long (format specifier %ld). Calling code cannot safely use such a value in string formatting without either getting the proper format specified from a preprocessor define or using a fixed format specifier and casting the argument:

duk_int_t val = /* ... */;

/* Cast value to ensure type and format match.  Selecting the appropriate
 * cast target is problematic, and caller must "play it safe".  Without
 * relying on C99 types, "long" is usually good for signed integers.
printf("value is: %ld\n", (long) val);

/* When assuming C99 types (which limits portability), the maxint_t is
 * guaranteed to represent all signed integers and has a standard format
 * specifiers "%jd".  For unsigned values, umaxint_t and "%ju".
printf("value is: %jd\n", (maxint_t) val);

/* Use a preprocessor define to provide the format code.  Code and format
 * specifier are chosen to match during type detection.
printf(DUK_PRIdINT, val);

C99 takes this approach and provides preprocessor defines for C99 types in inttypes.h. For instance, the printf() decimal format specifier for int_fast32_t is PRIdFAST32:

int_fast32_t val = /* ... */;

printf("value is: " PRIdFAST32 "\n", val);

The printf() and scanf() format specifiers may be different. One reason is that float arguments are automatically promoted to double in printf() but they are handled as distinct types by scanf(). See why-does-scanf-need-lf-for-doubles-when-printf-is-okay-with-just-f.

The correct format specifier for a double in printf() is %f (float values are automatically promoted to doubles) but %lf is also accepted. The latter is used in Duktape examples for clarity. See correct-format-specifier-for-double-in-printf.

Type algorithms §

This section describes how type-related Ecmascript algorithms like comparisons and coercions are extended to Duktape custom types. Duktape specific type algorithms (ToBuffer() and ToPointer()) are also discussed.

Notation §

The following shorthand is used to indicate how values are compared:

tcompares to true
fcompares to false
ssimple compare: boolean-to-boolean, string-to-string (string contents compared), buffer-to-buffer (buffer contents compared), buffer-to-string (buffer and string contents compared)
nnumber compare: NaN values compare false, zeroes compare true regardless of sign (e.g. +0 == -0)
Nnumber compare in SameValue: NaN values compare true, zeroes compare with sign (e.g. SameValue(+0,-0) is false)
pheap pointer compare
Llightfunc compare: to be considered equal, Duktape/C function pointers and internal control flags (including the "magic" value) must match
1string/buffer vs. number: coerce string with ToNumber() and retry comparison; a buffer is first coerced to string and then to number (e.g. buffer with "2.5" coerces eventually to number 2.5)
2boolean vs. any: coerce boolean with ToNumber() and retry comparison
3object vs. string/number/buffer: coerce object with ToPrimitive() and retry comparison

Note that Boolean objects, String objects, Number objects, and buffer objects compare as any other objects instead of being automatically unboxed. For example, non-strict equality compares plain buffer values with a byte-by-byte comparison, but buffer objects are compared by object reference (like any other objects).

Equality (non-strict) §

Non-strict equality comparison is specified in The Abstract Equality Comparison Algorithm for standard types. Custom type behavior is as follows:

The standard behavior as well as behavior for Duktape custom types is summarized in the table below:

undt t f f f f f f f
nul t f f f f f f f
boo s 2 2 2 2 f f
num n 1 3 1 f f
str s 3 s f f
obj p 3 f f
buf s f f
ptr s f
lfn L

Strict equality §

Strict equality is much more straightforward and preferable whenever possible for simplicity and performance. It is described in The Strict Equality Comparison Algorithm for standard types. Custom type behavior is as follows:

The standard behavior as well as behavior for Duktape custom types is summarized in the table below:

undt f f f f f f f f
nul t f f f f f f f
boo s f f f f f f
num n f f f f f
str s f f f f
obj p f f f
buf p f f
ptr s f
lfn L

SameValue §

The SameValue algorithm is not easy to invoke from user code. It is used by e.g. Object.defineProperty() when checking whether a property value is about to change. SameValue is even stricter than a strict equality comparison, and most notably differs in how numbers are compared. It is specified in The SameValue algorithm for standard types. Custom type behavior is as follows:

The standard behavior as well as behavior for Duktape custom types is summarized in the table below:

undt f f f f f f f f
nul t f f f f f f f
boo s f f f f f f
num N f f f f f
str s f f f f
obj p f f f
buf p f f
ptr s f
lfn L

Type conversion and testing §

The custom types behave as follows for Ecmascript coercions described Type Conversion and Testing (except SameValue which was already covered above):

DefaultValueTypeErrorTypeError"light_<PTR>_<FLAGS>" (toString/valueOf)
ToPrimitiveidentityidentity "light_<PTR>_<FLAGS>" (toString/valueOf)
ToBooleanfalse for zero-size buffer, true otherwisefalse for NULL pointer, true otherwisetrue
ToNumberlike ToNumber() for a string0 for NULL pointer, 1 otherwiseNaN
ToIntegersame as ToNumbersame as ToNumber0
ToInt32same as ToNumbersame as ToNumber0
ToUint32same as ToNumbersame as ToNumber0
ToUint16same as ToNumbersame as ToNumber0
ToStringstring with bytes from buffer datasprintf() with %p format (platform specific)"light_<PTR>_<FLAGS>"
ToObjectBuffer objectPointer objectFunction object
CheckObjectCoercibleallow (no error)allow (no error)allow (no error)
SameValue(covered above)(covered above)(covered above)

When a buffer is string coerced, the bytes from the buffer are used directly as string data. The bytes will then be interpreted as CESU-8 (or extended UTF-8) from Ecmascript point of view.

When a lightfunc is coerced with ToPrimitive() it behaves like an ordinary function: it gets coerced with Function.prototype.toString() with the result (normally) being the same as ToString() coercion.

When a lightfunc is object coerced, a new Function object is created and the virtual properties (name and length and the internal "magic" value are copied over to the Function object.

Custom coercions (ToBuffer, ToPointer) §

ToBuffer() coercion is used when a value is forced into a buffer type e.g. with the duk_to_buffer() API call. The coercion is as follows:

ToPointer() coercion is used e.g. by the duk_to_pointer() call. The coercion is as follows:

The following table summarizes how different types are handled:

undefinedbuffer with "undefined"NULL
nullbuffer with "null"NULL
booleanbuffer with "true" or "false"NULL
numberbuffer with string coerced numberNULL
stringbuffer with copy of string dataptr to heap hdr
objectbuffer with ToString(value)ptr to heap hdr
bufferidentityptr to heap hdr
pointersprintf() with %p format (platform specific)identity
lightfuncbuffer with ToString(value)NULL
There is currently no ToLightFunc() coercion. Lightfuncs can only be created using the Duktape C API.

Addition §

The Ecmascript addition operator is specified in The Addition operator (+). Addition behaves specially if either argument is a string: the other argument is coerced to a string and the strings are then concatenated. This behavior is extended to custom types as follows:

Property access §

If a plain buffer or pointer is used as a property access base value, properties are looked up from the (initial) built-in prototype object (Duktape.Buffer.prototype or Duktape.Pointer.prototype). This mimics the behavior of standard types.

For example:

duk> buf = Duktape.dec('hex', '414243');  // plain buffer
duk> buf.toString;
= function toString() {/* native */}
duk> typeof buf.toString();
= string

Lightfuncs have a few non-configurable and non-writable virtual properties (name and length) and inherit their remaining properties from Function.prototype, which allows ordinary inherited Function methods to be called:

var bound = myLightFunc.bind('dummy', 123);

Duktape built-ins §

This section describes Duktape-specific built-in objects, methods, and values.

Additional global object properties §

Duktape The Duktape built-in object. Contains miscellaneous implementation specific stuff.
Proxy Proxy constructor borrowed from ES6 (not part of Ecmascript E5/E5.1).
require Non-standard, CommonsJS module loading function.
print Non-standard, browser-like function for writing to stdout.
alert Non-standard, browser-like function for writing to stderr.

require() §

CommonJS module loading function, see Modules.

print() and alert() §

print() writes to stdout with an automatic flush afterwards. The bytes written depend on the arguments:

alert() behaves the same way, but writes to stderr. Unlike a browser alert(), the call does not block.

The Duktape object §

version Duktape version number: (major * 10000) + (minor * 100) + patch.
env Cryptic, version dependent summary of most important effective options like endianness and architecture.
fin Set or get finalizer of an object.
enc Encode a value (hex, base-64, JX, JC): Duktape.enc('hex', 'foo').
dec Decode a value (hex, base-64, JX, JC): Duktape.dec('base64', 'Zm9v').
info Get internal information (such as heap address and alloc size) of a value in a version specific format.
act Get information about call stack entry.
gc Trigger mark-and-sweep garbage collection.
compact Compact the memory allocated for a value (object).
errCreate Callback to modify/replace a created error.
errThrow Callback to modify/replace an error about to be thrown.
modSearch Module search function, must be provided by user code if using modules.
modLoaded Internal tracking table for loaded modules, maps resolved identifier to module metadata object.
Buffer Buffer constructor (function).
Pointer Pointer constructor (function).
Thread Thread constructor (function).
Logger Logger constructor (function).

version §

The version property allows version-based feature detection and behavior. Version numbers can be compared directly: a logically higher version will also be numerically higher. For example:

if (typeof Duktape !== 'object') {
    print('not Duktape');
} else if (Duktape.version >= 10203) {
    print('Duktape 1.2.3 or higher');
} else if (Duktape.version >= 800) {
    print('Duktape 0.8.0 or higher (but lower than 1.2.3)');
} else {
    print('Duktape lower than 0.8.0');

The value of version for pre-releases is one less than the actual release, e.g. 1199 for a 0.12.0 pre-release and 10299 for a 1.3.0 pre-release. See Versioning.

Remember to check for existence of Duktape when doing feature detection. Your code should typically work on as many engines as possible. Avoid the common pitfall of using a direct identifier reference in the check:

// Bad idea: ReferenceError if missing
if (!Duktape) {
    print('not Duktape');

// Better: check through 'this' (bound to global)
if (!this.Duktape) {
    print('not Duktape');

// Better: use typeof to check also type explicitly
if (typeof Duktape !== 'object') {
    print('not Duktape');

env §

env summarizes the most important effective compile options in a version specific, quite cryptic manner. The format is version specific and is not intended to be parsed programmatically. This is mostly useful for developers (see duk_hthread_builtins.c for the code which sets the value).

Example from Duktape 1.1.0:

ll u n p2 a4 x64 linux gcc     // l|b|m integer endianness, l|b|m IEEE double endianness
                               // p|u packed/unpacked tval
                               // n|various, memory optimization options (n = none)
                               // p1|p2|p3 prop memory layout
                               // a1|a4|a8: align target
                               // x64|x86|arm|etc: architecture
                               // linux|windows|etc: operating system
                               // gcc|clang|msvc|etc: compiler

fin() §

When called with a single argument, gets the current finalizer of an object:

var currFin = Duktape.fin(o);

When called with two arguments, sets the finalizer of an object (returns undefined):

Duktape.fin(o, function(x) { print('finalizer called'); });
Duktape.fin(o, undefined);  // disable

enc() §

enc() encodes its argument value into chosen format. The first argument is a format (currently supported are "hex", "base64", "jx" and "jc"), second argument is the value to encode, and any further arguments are format specific.

For "hex" and "base64", buffer values are encoded as is, other values are string coerced and the internal byte representation (extended UTF-8) is then encoded. The result is a string. For example, to encode a string into base64:

var result = Duktape.enc('base64', 'foo');
print(result);  // prints 'Zm9v'

For "jx" and "jc" the argument list following the format name is the same as for JSON.stringify(): value, replacer (optional), space (optional). For example:

var result = Duktape.enc('jx', { foo: 123 }, null, 4);
print(result);  // prints JX encoded {foo:123} with 4-space indent

dec() §

dec() provides the reverse function of enc().

For "hex" and "base64" the input value is first string coerced (it only really makes sense to decode strings). The result is always a buffer. For example:

var result = Duktape.dec('base64', 'Zm9v');
print(typeof result, result);  // prints 'buffer foo'

If you wish to get back a string value, you can simply:

var result = String(Duktape.dec('base64', 'Zm9v'));
print(typeof result, result);  // prints 'string foo'

For "jx" and "jc" the argument list following the format name is the same as for JSON.parse(): text, reviver (optional). For example:

var result = Duktape.dec('jx', "{foo:123}");
print(;  // prints 123

info() §

When given an arbitrary input value, returns an array of values with internal information related to the value. The format of of the values in the array is version specific. This is mainly useful for debugging and diagnosis, e.g. when estimating rough memory usage of objects.

The current result array format is described in the table below. Notes:

undefined type tag internal type tag - - - - - - - -
null type tag internal type tag - - - - - - - -
boolean type tag internal type tag - - - - - - - -
number type tag internal type tag - - - - - - - -
string type tag heap ptr refcount heap hdr size internal type tag - - - - -
object, Ecmascript function type tag heap ptr refcount heap hdr size prop alloc size prop entry count prop entry next prop array count prop hash count func data size
object, Duktape/C function type tag heap ptr refcount heap hdr size prop alloc size prop entry count prop entry next prop array count prop hash count -
object, thread type tag heap ptr refcount heap hdr size prop alloc size prop entry count prop entry next prop array count prop hash count -
object, other type tag heap ptr refcount heap hdr size prop alloc size prop entry count prop entry next prop array count prop hash count -
buffer, fixed type tag heap ptr refcount heap hdr size - - - - - -
buffer, dynamic type tag heap ptr refcount heap hdr size curr buf size - - - - -
buffer, external type tag heap ptr refcount heap hdr size curr buf size - - - - -
pointer type tag internal type tag - - - - - - - -
lightfunc type tag internal type tag - - - - - - - -

act() §

Get information about a call stack entry. Takes a single number argument indicating depth in the call stack: -1 is the top entry, -2 is the one below that etc. Returns an object describing the call stack entry, or undefined if the entry doesn't exist. Example:

function dump() {
    var i, t;
    for (i = -1; ; i--) {
        t = Duktape.act(i);
        if (!t) { break; }
        print(i, t.lineNumber,, Duktape.enc('jx', t));


The example, when executed with the command line tool, currently prints something like:

-1 0 act {lineNumber:0,pc:0,function:{_func:true}}
-2 4 dump {lineNumber:4,pc:16,function:{_func:true}}
-3 10 global {lineNumber:10,pc:5,function:{_func:true}}

The interesting entries are lineNumber and function which provides e.g. the function name.

You can also implement a helper to get the current line number using Duktape.act():

function getCurrentLine() {
    'use duk notail';

    /* Tail calls are prevented to ensure calling activation exists.
     * Call stack indices: -1 = Duktape.act, -2 = getCurrentLine, -3 = caller

    var a = Duktape.act(-3) || {};
    return a.lineNumber;
print('running on line:', getCurrentLine());
The properties provided for call stack entries may change between versions.

gc() §

Trigger a forced mark-and-sweep collection. If mark-and-sweep is disabled, this call is a no-op.

compact() §

Minimize the memory allocated for a target object. Same as the C API call duk_compact() but accessible from Ecmascript code. If called with a non-object argument, this call is a no-op. The argument value is returned by the function, which allows code such as:

var obj = {
    foo: Duktape.compact({ bar:123 })

This call is useful when you know that an object is unlikely to gain new properties, but you don't want to seal or freeze the object in case it does.

errCreate() and errThrow() §

These can be set by user code to process/replace errors when they are created (errCreate) or thrown (errThrow). Both values are initially non-existent.

See Error handlers (errCreate and errThrow) for details.

modSearch() and modLoaded §

modSearch() is a module search function which must be provided by user code to support module loading. modLoaded is an internal module loading tracking table maintained by Duktape which maps a resolved absolute module identifier to the module's module object for modules which are either fully loaded or currently being loaded (module.exports is the exported value returned by require()).

See Modules for details.

Duktape.Buffer (constructor) §

prototypePrototype for Buffer objects.

The Buffer constructor is a function which returns a plain buffer when called as a normal function and a Buffer object when called as a constructor. Otherwise the behavior is the same:

If the argument is a Node.js Buffer, ArrayBuffer, DataView, or a TypedArray, the call behaves as if the argument was a Duktape.Buffer, i.e. the underlying plain buffer is returned or promoted to a Duktape.Buffer. However, any slice byte offset/length information is ignored, so that the full underlying buffer is always returned.

Duktape.Buffer doesn't provide a way to make a copy of a buffer, but you can use the TypedArray bindings to do that: copy = Duktape.Buffer(new Uint8Array(new Duktape.Buffer(input))).

Duktape.Buffer.prototype §

toStringConvert Buffer to a printable string.
valueOfReturn the primitive buffer value held by Buffer.

toString() and valueOf accept both plain buffers and Buffer objects as their this binding. This allows code such as:

var plain_buf = Duktape.Buffer('test');

Duktape.Pointer (constructor) §

prototypePrototype for Pointer objects.

The Pointer constructor is a function which can be called both as an ordinary function and as a constructor:

Duktape.Pointer.prototype §

toStringConvert Pointer to a printable string.
valueOfReturn the primitive pointer value held by Pointer.

toString() and valueOf accept both plain pointers and Pointer objects as their this binding. This allows code such as:

var plain_ptr = Duktape.Pointer({ test: 'object' });

Duktape.Thread (constructor) §

prototypePrototype for Thread objects.
resumeResume target thread with a value or an error. Arguments: target thread, value, flag indicating whether value is to be thrown (optional, default false).
yieldYield a value or an error from current thread. Arguments: value, flag indicating whether value is to be thrown (optional, default false).
currentGet currently running Thread object.

The Thread constructor is a function which can be called both as an ordinary function and as a constructor. The behavior is the same in both cases:

Duktape.Thread.prototype §

No properties at the moment.

Duktape.Logger (constructor) §

prototypePrototype for Logger objects.
clogRepresentative logger for log entries written from C code.

Called as a constructor, creates a new Logger object with a specified name (first argument). If the name is omitted, Logger will automatically assign a name based on the calling function's fileName. If called as a normal function, throws a TypeError.

Logger instances have the following properties:

See Logging on how to use loggers.

Duktape.Logger.prototype §

rawOutput a formatted log line (buffer value), by default writes to stderr.
fmtFormat a single (object) argument.
traceWrite a trace level (level 0, TRC) log entry.
debugWrite a debug level (level 1, DBG) log entry.
infoWrite an info level (level 2, INF) log entry.
warnWrite a warn level (level 3, WRN) log entry.
errorWrite an error level (level 4, ERR) log entry.
fatalWrite a fatal level (level 5, FTL) log entry.
lDefault log level, initial value is 2 (info).
nDefault logger name, initial value is "anon".

Ecmascript E6 features §

This section describes the small set of features Duktape borrows from Ecmascript E6. These features are not fully compliant; the intent is to minimize custom features and to align with the ES6 specification when possible.

Const variables §

Duktape has minimal support for const declarations to allow existing code using const to run. However, const is mostly just an alias for var and currently has the following non-standard semantics:

Object.setPrototypeOf and Object.prototype.__proto__ §

Object.setPrototypeOf allows user to set the internal prototype of an object which is not supported in Ecmascript E5. Ecmascript E6 also provides Object.prototype.__proto__, an accessor property (setter/getter) which provides the same functionality but is compatible with existing code base which has relied on a non-standard __proto__ property for a while. Duktape does not support the __proto__ property name in an object initializer.

These custom features can be disabled with the feature options DUK_OPT_NO_ES6_OBJECT_SETPROTOTYPEOF and DUK_OPT_NO_ES6_OBJECT_PROTO_PROPERTY.

Proxy object (subset) §

The Ecmascript E6 Proxy object allows property virtualization and fine-grained access control for accessing an underlying plain object. Duktape implements a strict subset of the Proxy object from ES6. The following traps are implemented:

hasyesObject.hasOwnProperty() does not invoke the trap at the moment, key in obj does
ownKeysyesObject.keys() enumerability check limitation, trap result validation (non-configurable properties, non-extensible target) not done

Limitations include:

This custom feature can be disabled with the feature option DUK_OPT_NO_ES6_PROXY.

Typed arrays §

Duktape implements Khronos typed array support which is a subset of ES6 typed arrays. Support for typed arrays and Node.js Buffer can be disabled with the feature option DUK_OPT_NO_BUFFEROBJECT_SUPPORT.

Custom behavior §

This section summarizes Duktape behavior which deviates from the E5.1 or other relevant specifications.

Duktape built-in and custom types §

The Duktape built-in is (of course) non-standard and provides access to Duktape specific features. Also the buffer, pointer, and lightfunc types are custom.

Internal properties §

Objects may have internal properties which are essentially hidden from normal code: they won't be enumerated or returned even by e.g. Object.getOwnPropertyNames(). Ordinary Ecmascript code cannot refer to such properties because the property keys intentionally use invalid UTF-8 (0xFF prefix byte).

"use duk notail" directive §

The "use duk notail" directive is non-standard. It prevents a function from being tail called.

"const" treated mostly like "var" §

The const keyword is supported with minimal non-standard semantics (officially defined in Ecmascript 6). See Const variables for more detail.

The global require() function for module loading §

The require() built-in is non-standard, and provided for CommonJS-based module loading, see Modules.

Additional Error and Function object properties §

See Error objects and Function objects.

Non-strict function instances don't have a caller property in the E5/E5.1 specification. Some real world code expects to have this property, so it can be enabled with the feature option DUK_OPT_NONSTD_FUNC_CALLER_PROPERTY.

Function statements §

E5.1 does not allow a function declaration to appear outside program or function top level:

function test() {
    // point A
    try {
        throw new Error('test');
    } catch (e) {
        // This is a SyntaxError in E5.1
        function func() {
            print(typeof e);
        // point B
    // point C

These declarations are also referred to as "function statements", and appear quite often in real world code (including the test262 test suite), so they are allowed by Duktape. Unfortunately there are several semantics used by different Javascript engines. Duktape follows the V8 behavior for function statements:

As an illustration, the above example would behave as the following:

function test() {
    function func() {
        print(typeof e);
    try {
        throw new Error('test');
    } catch (e) {

func() in the above example would already be declared and callable in point A, and would not have access to the e binding in any of the points A, B, or C.

RegExp leniency §

Although not allowed by E5.1, the following escape is allowed in RegExp syntax:

  /\$/       /* matches dollar literally, non-standard */
  /\u0024/   /* same, standard */

This escape occurs in real world code so it is allowed. (More leniency will be added in future versions to deal with real world RegExps; dollar escapes are not the only issue.)

Array.prototype.splice() when deleteCount not given §

When deleteCount (the 2nd argument) is not given to Array.prototype.splice(), the standard behavior is to work as if the 2nd argument was undefined (or 0, which has the same behavior after coercions). A more real world compatible behavior is to treat the missing argument like positive infinity, i.e. to extend the splice operation to the end of the array.

Because the non-standard real world behavior is expected by much existing code, Duktape uses this behavior by default. The strict standards compliant behavior can be enabled with the feature option DUK_OPT_NO_NONSTD_ARRAY_SPLICE_DELCOUNT.

Array.prototype.concat() trailing non-existent elements §

When the result of an array concat() would have trailing non-existent elements, the standard behavior is to ignore them so that they are not reflected in the result length. Real world behavior is to include them in the result value length. See test-bi-array-proto-concat-nonstd-trailing.js.

The real world behavior seems consistent in other engines (V8, Rhino, Spidermonkey at least), so Duktape uses the real world behavior by default. The strict standards compliant behavior can be enabled with the feature option DUK_OPT_NO_NONSTD_ARRAY_CONCAT_TRAILER. trailing non-existent elements §

Similar issue as with Array.prototype.concat(), see test-bi-array-proto-map-nonstd-trailing.js. The strict standards compliant behavior can be enabled with the feature option DUK_OPT_NO_NONSTD_ARRAY_MAP_TRAILER.

Setter/getter key argument §

Ecmascript standard behavior is that setters and getters are not given the name of the property being accessed. This prevents reusing a single setter or a getter for multiple properties; separate functions are needed for each property which is sometimes inconvenient and wastes memory.

Duktape provides the property key name as a non-standard additional argument to setter and getter functions. See test-dev-nonstd-setget-key-argument.js and Property virtualization for more discussion. The strict standards compliant behavior can be enabled with the feature option DUK_OPT_NO_NONSTD_ACCESSOR_KEY_ARGUMENT.

Object.setPrototypeOf and Object.prototype.__proto__ (ES6) §

See Object.setPrototypeOf and Object.prototype.__proto__.

Proxy object (ES6) §

See Proxy object (subset).

JSON.stringify() escapes U+2028 and U+2029 §

JSON.stringify() standard behavior is to output U+2028 and U+2029 without escaping. This leads to counterintuitive behavior when the output is used in a web page or parsed with eval(): the U+2028 and U+2029 characters are considered line terminators which leads to a syntax error (unterminated string). Duktape escapes U+2028 and U+2029 by default to avoid this issue; you can turn on the compliant behavior with the feature option DUK_OPT_NO_NONSTD_JSON_ESC_U2028_U2029.

String.fromCharCode() accepts 32-bit codepoints §

String.fromCharCode() standard behavior is to use ToUInt16() coercion for codepoint values. Duktape uses ToUint32() by default to better support non-BMP strings. You can force the compliant behavior with the feature option DUK_OPT_NO_NONSTD_STRING_FROMCHARCODE_32BIT.

Array instance numeric index writes §

By default Duktape provides a fast path for writing to Array instances. The fast path is active when numeric indices are used (e.g. arr[7] = 'foo') and a few internal conditions are met. When the fast path is taken, Duktape doesn't check Array.prototype for conflicting properties (these are very rare in practical code), which makes common array writes faster. The behavior is non-compliant, but there's no outward difference unless Array.prototype has properties with numeric keys. You can turn on the compliant behavior with the feature option DUK_OPT_NO_NONSTD_ARRAY_WRITE. See the following for more details on the fast path behavior: test-misc-array-fast-write.js.

TypedArray binding §

Duktape provides a Khronos/ES6 TypedArray binding. The scope of the binding is the Khronos specification, not the full ES6 specification. There are some differences to Khronos/ES6, including:

Node.js Buffer binding §

Duktape provides a Node.js-like Buffer binding. There are some differences between the Node.js behavior and Duktape behavior. These differences include:

Custom JSON formats §

Ecmascript JSON shortcomings §

The standard JSON format has a number of shortcomings when used with Ecmascript:

These limitations are part of the Ecmascript specification which explicitly prohibits more lenient behavior. Duktape provides two more programmer friendly custom JSON format variants: JX and JC, described below.

Custom JX format §

JX encodes all values in a very readable manner and parses back almost all values in a faithful manner (function values being the most important exception). Output is pure printable ASCII, codepoints above U+FFFF are encoded with a custom escape format, and quotes around object keys are omitted in most cases. JX is not JSON compatible but a very readable format, most suitable for debugging, logging, etc.

JX is used as follows:

var obj = { foo: 0/0, bar: [ 1, undefined, 3 ] };
print(Duktape.enc('jx', obj));
// prints out: {foo:NaN,bar:[1,undefined,3]}

var dec = Duktape.dec('jx', '{ foo: 123, bar: undefined, quux: NaN }');
print(,, dec.quux);
// prints out: 123 undefined NaN

Custom JC format §

JC encodes all values into standard JSON. Values not supported by standard JSON are encoded as objects with a marker key beginning with an underscore (e.g. {"_ptr":"0xdeadbeef"}). Such values parse back as ordinary objects. However, you can revive them manually more or less reliably. Output is pure printable ASCII; codepoints above U+FFFF are encoded as plain string data with the format "U+nnnnnnnn" (e.g. U+0010fedc).

JC is used as follows:

var obj = { foo: 0/0, bar: [ 1, undefined, 3 ] };
print(Duktape.enc('jc', obj));
// prints out: {"foo":{"_nan":true},"bar":[1,{"_undef":true},3]}

var dec = Duktape.dec('jc', '{ "foo": 123, "bar": {"_undef":true}, "quux": {"_nan":true} }');
print(,, dec.quux);
// prints out: 123 [object Object] [object Object]

The JC decoder is essentially the same as the standard JSON decoder at the moment: all JC outputs are valid JSON and no custom syntax is needed. As shown in the example, custom values (like {"_undef":true}) are not revived automatically. They parse back as ordinary objects instead.

Codepoints above U+FFFF and invalid UTF-8 data §

All standard Ecmascript strings are valid CESU-8 data internally, so behavior for codepoints above U+FFFF never poses compliance issues. However, Duktape strings may contain extended UTF-8 codepoints and may even contain invalid UTF-8 data.

The Duktape JSON implementation, including the standard Ecmascript JSON API, use replacement characters to deal with invalid UTF-8 data. The resulting string may look a bit odd, but this behavior is preferable to throwing an error.

JSON format examples §

The table below summarizes how different values encode in each encoding:

Value Standard JSON JX JC Notes
undefined n/a undefined {"_undef":true} Standard JSON: encoded as null inside arrays, otherwise omitted
null null null null standard JSON
true true true true standard JSON
false false false false standard JSON
123.4 123.4 123.4 123.4 standard JSON
+0 0 0 0 standard JSON
-0 0 -0 -0 Standard JSON allows -0 but serializes negative zero as 0 (losing the sign unnecessarily)
NaN null NaN {"_nan":true} Standard JSON: always encoded as null
Infinity null Infinity {"_inf":true} Standard JSON: always encoded as null
-Infinity null -Infinity {"_ninf":true} Standard JSON: always encoded as null
"köhä" "köhä" "k\xf6h\xe4" "k\u00f6h\u00e4"
U+00FC "\u00fc" "\xfc" "\u00fc"
U+ABCD "\uabcd" "\uabcd" "\uabcd"
U+1234ABCD "U+1234abcd" "\U1234abcd" "U+1234abcd" Non-BMP characters are not standard Ecmascript, JX format borrowed from Python
object {"my_key":123} {my_key:123} {"my_key":123} ASCII keys matching identifer requirements encoded without quotes in JX
array ["foo","bar"] ["foo","bar"] ["foo","bar"]
buffer n/a |deadbeef| {"_buf":"deadbeef"}
pointer n/a (0xdeadbeef)
Representation inside parentheses or quotes is platform specific
NULL pointer n/a (null) {"_ptr":"null"}
function n/a {_func:true} {"_func":true} Standard JSON: encoded as null inside arrays, otherwise omitted
lightfunc n/a {_func:true} {"_func":true} Formats like ordinary functions

Limitations §

Some limitations include:

(See internal documentation for more future work issues.)

Custom directives §

Ecmascript E5/E5.1 employs a directive prologue to allow version or implementation specific features be activated. The standard only provides one such directive, "use strict", while asm.js uses "use asm". Duktape custom directives are discussed in this section.

use duk notail §

The use duk notail directive indicates that the function should never be tail called. Tail calls affect the call stack so they are visible in stack traces (usually harmless) and affect functions which inspect the call stack using e.g. Duktape.act(). This directive may be useful in special cases to ensure call stack has a known shape. Example:

function noTailCall() {
    'use duk notail';

    // ...

Native functions are never tailcalled, so a corresponding declaration is not necessary for them.

Buffer objects §

Overview of buffer types §

Duktape provides the following buffer and buffer-related types:

Type Standard Duktape version Description
Plain buffer No
Duktape specific
1.0 Plain, primitive buffer value (not an object), similar to how a plain string relates to a String object
Duktape.Buffer object No
Duktape specific
1.0 Object wrapped plain buffer, similar to how a String object relates to a plain string
Node.js Buffer object No
1.3 Object with Node.js Buffer API
ArrayBuffer object Yes
1.3 Standard object type for representing a byte array
DataView, typed array objects Yes
1.3 View objects to access an underlying ArrayBuffer

See buffers.rst for a detailed discussion, including a detailed table of buffer types and their properties.

Working with buffers §

Buffer values work in both C and Ecmascript code:

See How to work with buffers for examples.

Current limitations §

Error objects §

Property summary §

Ecmascript Error objects have very few standard properties, so many Ecmascript implementations have added quite a few custom properties. Duktape uses standard Error properties but also borrows the most useful properties used by other implementations. The number of "own" properties of error objects is minimized to keep error objects as small as possible.

Error objects have the following properties (mostly inherited):

Property nameCompatibilityDescription
namestandardName of error, e.g. TypeError, inherited
messagestandardOptional message of error, own property, empty message inherited if absent
fileNameRhinoFilename related to error source, inherited accessor
lineNumberRhinoLinenumber related to error source, inherited accessor
stackV8Traceback as a multi-line human redable string, inherited accessor
Assigning the most useful fileName and lineNumber is somewhat complicated. The related issues and current behavior are described in: error-objects.rst.

If Duktape is compiled with traceback support:

If Duktape is compiled without traceback support:

When error objects are created using the Duktape API from C code and the caller does not give a format string for a message, the message property is set to a numeric error code given in the API call. The type of message will be number in this case; normally error messages are strings. In minimized Duktape builds all errors generated internally by Duktape use numeric error codes only.

An object is considered an "error object" if its internal prototype chain contains the (original) Error.prototype object. Only objects matching this criteria get augmented with e.g. traceback data.

Traceback §

The stack property is an accessor (setter/getter) property which provides a printable traceback related to an error. The traceback reflects the call stack when the error object was created (not thrown). Traceback data is automatically collected and added to an object:

The data used to create the traceback is stored in an internal property (\xFFTracedata), in an internal and version-dependent format described error-objects.rst. You shouldn't access the traceback data directly.

The printable traceback format is intended to be human readable only. You shouldn't rely on an exact traceback format as it may change between versions. As an example of the current traceback format, the program:

// shortened from tests/ecmascript/test-dev-traceback-example.js
try {
    decodeURIComponent('%e1%a9%01');  // invalid utf-8
} catch (e) {

would print something like:

URIError: invalid input
        decodeURIComponent  native strict preventsyield
        global tests/ecmascript/test-dev-traceback-example.js:3 preventsyield

In builds where tracebacks are disabled, the stack accessor will return the same value as calling toString() on the error would. This means you can always print e.stack and get a useful output.

The most portable traceback printing approach is something like:

try {
    decodeURIComponent('%e1%a9%01');  // invalid utf-8
} catch (e) {
    // Print stacktrace on at least Duktape and V8, or a standard error
    // string otherwise.
    print(e.stack || e);

Attempt to write to stack is silently ignored. You can still override the accessor by defining an own property of the same name explicitly with Object.defineProperty(). This behavior differs from V8 where stack is an own property of the Error instance, and if you assign a value to stack, the value reads back as assigned.

Error handlers (errCreate and errThrow) §

If Duktape.errCreate has been set, it is called right after Duktape has added traceback information to an object, and can process the error further or even replace the error value entirely. The error handler only gets called with Error instances, and its return value is used as the final error value. If the error handler throws an error, that error replaces the original error. The error handler is usually called only once per error. However, in corner cases related to constructors, the error handler can be called multiple times for a single error value.

An error handler should avoid overwriting any properties already present in an object, as that would be quite confusing for other code. In general, an error handler should always avoid throwing an error, as that error replaces the original error and would also be confusing. As a specific example, an error handler must not try to add a new property to a non-extensible object, as that would cause a TypeError.

Below is an example error handler for adding a creation timestamp to errors at their creation:

Duktape.errCreate = function (e) {
    if (!(e instanceof Error)) {
        // this check is not really needed because errCreate only gets
        // called with Error instances
        return e;
    if ('created' in e) {
        // already augmented or conflicting property present
        return e;
    if (!Object.isExtensible(e)) {
        // object not extensible, don't try to add a new property
        return e;
    e.created = new Date();
    return e;

To remove the handler, delete the property (setting it to e.g. null does not work and causes a TypeError when Duktape attempts to call the null value):

// Remove error handler for error creation
delete Duktape.errCreate;

Similarly, if Duktape.errThrow has been set, it is called right before an error is thrown, and can process or replace the error value. Because Ecmascript allows any value type to be thrown, the error handler may get called with arbitrary input values (not just Error instances). It may also be called more than once for the same value because an error can be re-thrown multiple times.

For example, to add a throw timestamp (recording the first time the object has been thrown) to errors:

Duktape.errThrow = function (e) {
    if (!(e instanceof Error)) {
        // refuse to touch anything but Error instances
        return e;
    if ('thrown' in e) {
        // already augmented or conflicting property present
        return e;
    if (!Object.isExtensible(e)) {
        // object not extensible, don't try to add a new property
        return e;
    e.thrown = new Date();
    return e;

Again, to remove the handler, delete the property:

// Remove error handler for error throwing
delete Duktape.errThrow;

Current limitations §

Function objects §

Properties of Ecmascript functions §

Duktape Function objects add a few properties to standard Ecmascript properties. The table below summarizes properties assigned to newly created function instances (properties can of course be added or removed afterwards):

Property nameCompatibilityDescription
lengthstandardFunction (nominal) argument count (if relevant). Present for all Function objects, including bound functions.
prototypestandardPrototype used for new objects when called as a constructor. Present for most constructable Function objects, not copied to bound functions.
callerstandardAccessor which throws an error. Present for strict functions and bound functions. Not copied to bound functions. (If DUK_OPT_NONSTD_FUNC_CALLER_PROPERTY is given, non-strict functions will get a non-standard caller property.)
argumentsstandardAccessor which throws an error. Present for strict functions and bound functions. Not copied to bound functions.
nameDuktapeFunction name, see below. Copied to bound function from target function.
fileNameDuktapeFilename or context where function was declared (same name as in error tracebacks). Copied to bound function from target function.
calleen/aNever assigned by default (listed here to clarify relationship to "caller" property).

The name property is assigned to all functions and is also the name used in tracebacks. It is assigned as follows:

function funcDecl() {
    /* Function declaration: 'name' is declaration name, here 'funcDecl'. */

var foo = function namedFunc() {
    /* Named function expression: 'name' is the name used in expression,
     * here 'namedFunc' (not 'foo').

var bar = function () {
    /* Anonymous function expression: 'name' is the empty string. */
Several Ecmascript built-in functions have properties different from user created Functions.

Properties of Duktape/C functions §

User-created Duktape/C functions (duk_push_c_function()) have a different set of properties to reduce Function object memory footprint:

Property nameCompatibilityDescription
lengthstandard Function argument count, matches argument to duk_push_c_function(), 0 for varargs. Non-writable and non-configurable.

Note in particular that the standard prototype, caller, and arguments properties are missing by default. This is not strictly compliant but is important to reduce function footprint. User code can of course assign these but is not required to do so.

Properties of lightweight Duktape/C functions §

Lightweight functions have a set of non-configurable, non-writable virtual properties listed below. Like normal functions, they inherit further properties from Function.prototype.

Property nameCompatibilityDescription
lengthstandardFunction (nominal) argument count.
nameDuktapeFunction name: "light_<PTR>_<FLAGS>".

The name property is an automatically generated virtual function name. <PTR> is a platform dependent dump of the Duktape/C function pointer, and <FLAGS> is a raw hex dump of the 16-bit internal control fields (the format is Duktape internal). You shouldn't rely on a specific format. For example:

duk> print(;

As for ordinary functions, a lightfunc coerces to an implementation dependent string. You shouldn't rely on a specific format. For example:

duk> print(myLightFunc);
function light_0805b94c_0511() {/* light */}

Debugger §

Duktape has built-in debugger support as an option you can enable during compilation. Debugger support adds about 10kB of code footprint and has very minimal memory footprint.

The debugger is based on the following main concepts:

The most appropriate debug transport varies a lot between debug targets; it can be Wi-Fi, Bluetooth, a serial line, a stream embedded into a custom management protocol, etc. Although there is no "standard" transport, a TCP connection is a useful default. The Duktape distributable includes all the pieces you need to get started with debugging using a TCP transport:

The Node.js based debugger web UI (duk_debug.js) can connect to the Duktape command line, but can also talk directly with any other target implementing a TCP transport. You can also customize it to use a different transport or use a proxy which converts between TCP and your custom transport. It's also possible to write your own debug client from scratch and e.g. integrate it to a custom IDE. You can integrate directly with a debug target using the binary debug protocol, or use the JSON proxy provided by duk_debug.js.

For more details on the implementation and how to get started, see:

Modules §

Duktape has a built-in minimal module loading framework based on CommonJS modules version 1.1.1, with additional support for module.exports. The internals are documented in modules.rst. A recommended (but not mandatory) C module convention is described in c-module-convention.rst.

You can load modules from Ecmascript code with the global require() function:

var mod = require('foo/bar');

Modules are defined by Ecmascript code running in a special environment defined by the CommonJS modules specification. Inside this environment, variable/function declarations are local to the module and don't affect the global object. The environment also provides three special symbols related to module loading: exports for exporting module symbols, module for providing module metadata ( in particular), and require() for loading further modules with relative module identifiers resolved in the context of the current module. Example:

// foo/bar.js
var text = 'Hello world!';     // not visible outside the module
var quux = require('./quux');  // loads foo/quux
exports.hello = function () {

Duktape supports module.exports (as of Duktape 1.3) so that you can write modules like:

// foo/bar.js
module.exports = function adder(x, y) { return x + y; };

// main.js
var adder = require('foo/bar');
print('2 + 3 = ' + adder(2, 3));

Because Duktape is embeddable and portable to different environments there is no standard way to search for modules. User code must provide a module search function in Duktape.modSearch for module loading to work. The module search function essentially maps a module identifier to the source code of the module (see below for more details). Example:

// See module search function details below.
Duktape.modSearch = function (id) {
    print('loading module:', id);
    // Return source code for module or throw an error.

See How to use modules for examples.

Logging §

Duktape has a built-in logging framework with a small footprint, reasonable performance, and redirectable output.

Basic usage example:

var val1 = 'foo';
var val2 = 123;
var val3 = new Date(123456789e3);

var logger = new Duktape.Logger();  // or new Duktape.Logger('logger name')'three values:', val1, val2, val3);

The example would print something like the following to stdout:

2014-10-17T19:26:42.141Z INF test.js: three values: foo 123 1973-11-29 23:33:09.000+02:00

See logging.rst for more info.

Finalization §

Overview §

Duktape supports object finalization as a custom feature. A finalizer is called when an object is about to be freed, so that application code can e.g. free native resources associated with the object. The finalizer can be either an Ecmascript function or a Duktape/C function. However, Ecmascript finalizers may interact badly with script timeouts, see below.

See How to use finalization for examples.

Getting and setting the current finalizer §

An object which has an internal _Finalizer property in its prototype chain (or in the object itself) is subject to finalization before being freed. The internal property should not be accessed directly, but can be read/written using the following:

Finalizer function arguments and return value §

The finalizer function is called with two arguments:

The return value of a finalizer is ignored. Any errors thrown by the finalizer are also silently ignored.

Finalizer execution guarantees §

The main finalizer guarantees are:

Together these guarantee that a finalizer gets executed at some point before a heap is destroyed, which allows native resources (such as sockets and files) to be freed reliably. There are two exceptions to this guarantee, see below for more discussion:

When the Duktape heap is being destroyed there are a few limitations for finalizer behavior:

Other current limitations §

Coroutines §

Duktape has a support for simple coroutines. Execution is strictly nesting: coroutine A resumes or initiates coroutine B, coroutine B runs until it yields or finishes (either successfully or through an uncaught error), after which coroutine A continues execution with the yield result.

Coroutines are created with new Duktape.Thread(), which gets as its sole argument the initial function where the new coroutine begins execution on its first resume. The resume argument becomes the initial function's first (and only) argument value.

A coroutine is resumed using Duktape.Thread.resume() which takes the following arguments: the coroutine to resume, the resume value, and (optionally) a flag indicating whether the resume value is an ordinary value or an error to be injected into the target coroutine. Injecting an error means that the resume value will be "thrown" at the site of the target coroutine's last yield operation. In other words, instead of returning with an ordinary value, the yield will seemingly throw an error.

A coroutine yields its current execution using Duktape.Thread.yield() which takes as its arguments: the value to yield, and (optionally) a flag indicating whether the yield value is an ordinary value or an error to be thrown in the context of the resuming coroutine. In other words, an error value causes the resume operation to seemingly throw an error instead of returning an ordinary value.

If a coroutine exists successfully, i.e. the initial function finishes by returning a value, it is handled similarly to a yield with the return value. If a coroutine exists because of an uncaught error, it is handled similarly to a yield with the error: the resume operation will rethrow that error in the resuming coroutine's context. In either case the coroutine which has finished can no longer be resumed; attempt to do so will cause a TypeError.

There are currently strict limitations on when a yield is possible. In short, a coroutine can only yield if its entire active call stack consists of plain Ecmascript-to-Ecmascript calls. The following prevent a yield if they are present anywhere in the yielding coroutine's call stack:

See How to use coroutines for examples.

Virtual properties §

Duktape provides two mechanisms for interacting with property accesses programmatically:

See How to use virtual properties for examples.

Internal properties §

Duktape supports non-standard internal properties which are essentially hidden from user code. They can only be accessed by a direct property read/write, and are never enumerated, serialized by JSON.stringify() or returned from built-in functions such as Object.getOwnPropertyNames().

Duktape uses internal properties for various implementation specific purposes, such as storing an object's finalizer reference, the internal value held by Number and Date, etc. User code can also use internal properties for its own purposes, e.g. to store "hidden state" in objects, as long as the property names never conflict with current or future Duktape internal keys (this is ensured by the naming convention described below). User code should never try to access Duktape's internal properties: the set of internal properties used can change arbitrarily between versions.

Internal properties are distinguished from other properties by the property key: if the byte representation of a property key begins with a 0xFF byte Duktape automatically treats the property as an internal property. Such a string is referred to as an internal string. The initial byte makes the key invalid UTF-8 (even invalid extended UTF-8), which ensures that (1) internal properties never conflict with normal Unicode property names and that (2) ordinary Ecmascript code cannot accidentally access them. The initial prefix byte is often represented by an underscore in documentation for readability, e.g. _Value is used instead of \xFFValue.

The following naming convention is used. The convention ensures that Duktape and user internal properties never conflict:

Type Example (C) Bytes Description
Duktape "\xFF" "Value" ff 56 61 6c 75 65 First character is always uppercase, followed by [a-z0-9_]*.
User "\xFF" "myprop" ff 6d 79 70 72 6f 70 First character must not be uppercase to avoid conflict with current or future Duktape keys.
User "\xFF\xFF" <arbitrary> ff ff <arbitrary> Double 0xFF prefix followed by arbitrary data.

In some cases the internal key needed by user code is not static, e.g. it can be dynamically generated by serializing a pointer or perhaps the bytes are from an external source. In this case it is safest to use two 0xFF prefix bytes as the example above shows.

Note that the 0xFF prefix cannot be expressed as a valid Ecmascript string. For example, the internal string \xFFxyz would appear as the bytes ff 78 79 7a in memory, while the Ecmascript string "\u00ffxyz" would be represented as the CESU-8 bytes c3 bf 78 79 7a in memory.

Creating an internal string is easy from C code:

/* Create an internal string, which can then be used to read/write internal
 * properties, and can be passed on to Ecmascript code like any other string.
 * Terminating a string literal after a hex escape is safest to avoid some
 * ambiguous cases like "\xffab".
duk_push_string(ctx, "\xff" "myprop");

For more discussion on C string hex escaping, see c_hex_esc.c.

Internal strings can also be created from Ecmascript code if one has access to e.g. the Buffer constructor or Duktape.dec() (this must be considered in sandboxing):

// Using Duktape.Buffer()
var buf = new Duktape.Buffer(1);
buf[0] = 255;
var key1 = buf + 'myprop';

// Using Duktape.dec()
var key2 = Duktape.dec('hex', 'ff6d7970726f70');  // \xFFmyprop

There's no special access control for internal properties: if user code has access to the property name (string), it can read/write the property value. Any code with the ability to create or use buffers can potentially create an internal string by converting a buffer into a string. However, standard Ecmascript code with no access to buffer values or ability to create them cannot create internal strings (or any invalid UTF-8 strings in general). When sandboxing, ensure that the sandboxed code has no access to the Duktape built-in or any buffer values.

As a concrete example, the internal value of a Date can be accessed as follows:

// Print the internal timestamp of a Date instance.  User code should NEVER
// actually do this because the internal properties may change between
// versions in an arbitrary manner!

var key = Duktape.dec('hex', 'ff56616c7565');  // \xFFValue
var dt = new Date(123456);
print('internal value is:', dt[key]);  // prints 123456

Bytecode dump/load §

The API calls duk_dump_function() and duk_load_function() allow calling C code to (1) serialize an Ecmascript function into a portable bytecode and then (2) load the bytecode to reconstitute the function.

The bytecode format is Duktape version specific and it's unsafe to load bytecode from a different Duktape minor version (patch version doesn't affect bytecode compatibility). Duktape configuration options may also affect bytecode compatibility, so dump/load source and target must be compiled with the same options. The bytecode format is platform neutral so that you can compile bytecode on one platform and then load it on another, which is useful for cross-platform builds. Duktape does not validate loaded bytecode so calling code must ensure bytecode is intact and valid for the running Duktape version.

Calling code must ensure that bytecode being loaded is intact and valid for the running Duktape version (i.e. it has been compiled with the same version of Duktape and hasn't been modified since). Loading invalid bytecode may lead to memory unsafe behavior; loading maliciously crafted bytecode may even lead to exploitable vulnerabilities.

See bytecode.rst for more details.

Threading §

Duktape supports a limited form of multithreading:

For some background, a Duktape heap is a single memory management region regardless of how many Duktape threads exist in the heap (don't confuse native threads and Duktape threads). Because the Duktape threads in a heap can share object references, multithreading support would need synchronization for garbage collection and all object handling. Synchronization would be a major portability issue, so a practical approach is to limit a Duktape heap to be single threaded. Duktape heaps don't share anything so there are no threading limitations between them as a general rule. However, when some platform features are not available (such as variadic preprocessor macros or re-entrant system calls) there are some limitations.

See threading.rst for a detailed discussion of threading limitations and best practices.

Sandboxing §

Sandboxed environments allow execution of untrusted code with two broad goals in mind:

Duktape provides mechanisms to achieve these goals for untrusted Ecmascript code. All C code is expected to be trusted. See sandboxing.rst for a detailed discussion of how to implement sandboxing.

Sandboxing support in Duktape 1.3 is still a work in progress.

Performance §

Duktape is an interpreted engine with currently no JIT support. It uses reference counting which makes memory usage tight at the cost of some execution performance. Overall Duktape performance should be similar to other interpreted languages.

See How to optimize performance for discussion of Duktape performance characteristics and hints to optimize code for performance.

Memory usage §

Duktape allocates memory on demand and doesn't require a pre-allocated heap. When you create a heap on a 32-bit system, Duktape needs about 46kB for the built-in Ecmascript objects (about 22kB with low memory options). Additional memory is then allocated as needed for executing application scripts. Reference counting ensures there is very little unused allocated memory, the only exception being objects which participate in reference loops; these are collected eventually by mark-and-sweep.

The memory allocations needed by Duktape fall into two basic categories. First, there are a lot of small allocations between roughly 16 to 128 bytes which are needed for strings, buffers, objects, object property tables, etc. Second, there are much fewer larger allocations needed for e.g. Ecmascript function bytecode, large strings and buffers, value stacks, the global string table, and the Duktape heap object.

For most systems memory usage or the memory allocation pattern is not an issue. On low memory environments, e.g. less than 1MB of system RAM, you may want to use a custom allocator to optimize memory usage. A pool-based allocator deals well with the small allocation churn without fragmentation issues. The downside is that you need to tune the memory pool sizes to match the concrete allocation patterns.

See low memory options and low-memory.rst for more discussion on what low memory features exists and how to tune the memory pools for low memory systems.

Compiling §

Overview §

Duktape doesn't have an official Makefile or a build script: given the number of different portability targets, maintaining an official build script would be difficult. Instead, you should add Duktape to your existing build process in whatever way is most natural.

Duktape is compiled with a C or C++ compiler (C99 is recommended) and then linked to your program in some way; the exact details vary between platforms and toolchains. For example, you can:

There are two alternative distribution formats for the Duktape source: a single source file and separate source files. The single source file version consists of duktape.c, duktape.h, and duk_config.h. The separate source files version consists of duktape.h, duk_config.h and a set of separate source files. The single source file version is preferred, but separate files work better with some toolchains.

All Duktape API functions are potentially macros, and the implementation of a certain API primitive may change between a macro and an actual function even between compatible releases. This has two implications:

Duktape has many features which can be controlled during compilation, see feature options below. Some options after binary compatibility of Duktape and the application. Because of this:

Recommended compiler options §

If you compile Duktape with no compiler options, Duktape will detect the compiler and the platform automatically and select defaults appropriate in most cases. Recommended compiler options (for GCC/clang, use similar options in your compiler):

If you're using Duktape on a platform where Duktape's automatic feature detection doesn't (yet) work, you may need to force a specific byte order or alignment requirements with feature options described below.

Duktape feature defaults §

Duktape feature defaults are, at a high level:

Usually these automatic defaults are OK. If you're working on a constrained platform, you may need to add specific options to reduce memory footprint or to minimize garbage collection pauses.

Feature options (DUK_OPT_xxx) §

If you wish to modify the defaults, you can provide feature options in the form of DUK_OPT_xxx compiler defines. These will be taken into account by the default duk_config.h file, which resolves the final internal features based on feature requests, compiler features, and platform features. The full list of feature options is described in Duktape feature options (if you're modifying duk_config.h directly, see Duktape config options).

See Configuring Duktape for build for more practical details.

If you use Duktape feature options, you must define the feature options both when compiling Duktape and when compiling any application code using the duktape.h header. This is necessary because some feature options affect the binary compatibility of the Duktape API.

Suggested feature options for some environments §

Timing sensitive options §

Timing sensitive applications include e.g. games. For such environments steady, predictable performance is important, often more important than absolute performance. Duktape provides some options to improve use in these environments; for instance, you can disable automatic mark-and-sweep and rely on reference counting and manually requested mark-and-sweep for garbage collection.

See timing-sensitive.rst for suggested feature options.

Memory constrained options §

Duktape can work in 256kB flash memory (code footprint) and 96kB system RAM (including Duktape and a minimal OS), and provides a lot of feature options to minimize memory footprint. These feature options are intended for systems with less than 256kB of system RAM.

See low-memory.rst for suggested feature options.

Performance sensitive options §

The default Duktape build has reasonable performance options for most environments. Even so there are some feature options which may be used to improve performance by e.g. enabling fast paths, enabling "fastint" support, or trading some API safety for performance.

See performance-sensitive.rst for suggested feature options.


The default panic handler will print an error message to stdout unless I/O is disabled by DUK_OPT_NO_FILE_IO. It will then call abort() or cause a segfault if DUK_OPT_SEGFAULT_ON_PANIC is defined.

This is not always the best behavior for production applications which may already have better panic recovery mechanisms. To replace the default panic handler, see feature-options.rst.

Memory management alternatives §

There are three supported memory management alternatives:

When using only reference counting it is important to avoid creating unreachable reference cycles. Reference cycles are usually easy to avoid in application code e.g. by using only forward pointers in data structures. Even if reference cycles are necessary, garbage collection can be allowed to work simply by breaking the cycles before deleting the final references to such objects. For example, if you have a tree structure where nodes maintain references to both children and parents (creating reference cycles for each node) you could walk the tree and set the parent reference to null before deleting the final reference to the tree.

Unfortunately every Ecmascript function instance is required to be in a reference loop with an automatic prototype object created for the function. You can break this loop manually if you wish. For internal technical reasons, named function expressions are also in a reference loop; this loop cannot be broken from user code and only mark-and-sweep can collect such functions. See Limitations.

Using an external Date provider §

When porting to new or exotic platforms the Duktape built-in Date support may not work on the platform. In such a case you can implement an external "Date provider" which allows you to provide the necessary date/time primitives without Duktape changes. See datetime.rst.

Using a C++ compiler §

Duktape works with both C and C++ compilers and applications. You can compile Duktape and the application with a C or a C++ compiler in any combination. Even so, it is recommended to compile both Duktape and the application with the same compiler (i.e. both with a C compiler or both with a C++ compiler) and with the same compiler options.

The duktape.h header contains the necessary glue to make all of these combinations work. Specifically, all symbols needed by Duktape public API are inside a extern "C" { ... } wrapper (active only if compiled with a C++ compiler). This ensures that such symbols are defined and used without C++ name mangling. Specifically:

If you mix C and C++ compilation, you should do the final linking with the C++ toolchain. At least when mixing gcc/g++ you may encounter something like:

$ g++ -c -o duktape.o -Isrc/ src/duktape.c
$ gcc -c -o duk_cmdline.o -Isrc/ examples/cmdline/duk_cmdline.c
$ gcc -o duk duktape.o duk_cmdline.o -lm -lreadline -lncurses
duktape.o:(.eh_frame+0x1ab): undefined reference to `__gxx_personality_v0'
collect2: error: ld returned 1 exit status

One fix is to use g++ for linking:

$ g++ -c -o duktape.o -Isrc/ src/duktape.c
$ gcc -c -o duk_cmdline.o -Isrc/ examples/cmdline/duk_cmdline.c
$ g++ -o duk duktape.o duk_cmdline.o -lm -lreadline -lncurses

Because duktape.h selects C/C++ data types needed by Duktape and also does other feature detection, mixing C and C++ compilers could theoretically cause the C and C++ compilers to end up with different active features or data types. If that were to happen, Duktape and the application would be binary incompatible (which would be difficult to diagnose). This is usually not an issue, but to avoid the potential, compile Duktape and the application with the same compiler.

By default scope-based resource management (sometimes referred to as RAII) won't work in Duktape/C functions because Duktape uses longjmp() for internal long control transfers, bypassing C++ stack unwind mechanisms. You can use DUK_OPT_CPP_EXCEPTIONS to cause Duktape to use C++ exceptions for internal long control transfers, which allows scope-based resource management to work in Duktape/C functions.

Installing Duktape as a system-wide library §

Duktape can be installed as a system-wide library; see system-install.rst.

Compiler warnings §

Current goal is for the Duktape compile to be clean when:

There are still some warnings present when you compile with -Wextra or equivalent option.

There may be some warnings when compiling with a pre-C99 compiler (or a C99 compiler without a -std=c99 option or similar).

Portability §

Duktape is widely portable to platforms with at least a C89 compiler. Because Duktape has very limited dependency on platform functions, it's possible to Duktape to very exotic platforms. One major platform dependency is the Date built-in which may need a custom provider for an exotic platform.

See Portability for platform and compiler specific portability issues, porting recommendations, and troubleshooting tips.

Compatibility §

This section discussed Duktape compatibility with Ecmascript dialects, extensions, frameworks, and test suites.

Ecmascript E5 / E5.1 §

The main compatibility goal of Duktape is to be Ecmascript E5/E5.1 compatible. Current level of compatibility should be quite high.

Ecmascript E6 §

Duktape borrows a few features from Ecmascript E6, but generally there is no compatibility with E6 yet.

Ecmascript E3 §

There is no effort to maintain Ecmascript E3 compatibility, other than required by the E5/E5.1 specification.

CoffeeScript §

CoffeeScript compiles to JavaScript which should be compatible with Duktape. There are no known compatibility issues.

Some CoffeeScript examples are included in the distributable. Simply run make in examples/coffee/. For instance,

print 'Hello world!'
print 'version: ' + Duktape.version

compiles to:

(function() {

  print('Hello world!');

  print('version: ' + Duktape.version);


Coco §

Like CoffeeScript, Coco compiles to Javascript. There are no known issues.

LiveScript §

Like CoffeeScript, LiveScript compiles to Javascript. There are no known issues.


TypeScript compiles to Javascript. There are no known issues with compiling TypeScript using the Microsoft TypeScript compiler (in the ES5/CommonJS mode) and running the resulting Javascript using Duktape.

Underscore.js §

Underscore.js provides a lot of useful utilities to plain Ecmascript. Duktape passes almost all of Underscore's test cases, see underscore-status.rst for current compatibility status.

Test262 §

test262 is a test suite for testing E5.1 compatibility, although it includes also tests outside of standard E5.1. Duktape passes almost all of test262 cases, see test262-status.rst for current compatibility status.

Asm.js §

asm.js is a "strict subset of JavaScript that can be used as a low-level, efficient target language for compilers". As a subset of JavaScript, functions using asm.js type annotations should be fully compatible with Duktape. However, Duktape has no specific support for asm.js and won't optimize asm.js code. In fact, asm.js code will generate unnecessary bytecode and execute slower than normal Ecmascript code. The "use asm" directive specified by asm.js is ignored by Duktape. Also, because there is not typed array support yet, no "heap object" can be provided.

Emscripten §

Emscripten compiles C/C++ into Javascript. Duktape is currently Emscripten compatible except for a few RegExp issues, see:

As of Duktape 1.3 there is support for Khronos/ES6 TypedArray which improves Emscripten performance over Duktape 1.2 and allows the Emscripten fastcomp to be used. Duktape can now also execute larger Emscripten-compiled programs and can e.g. run both Emscripten-compiled Lua and Duktape. Large programs may still fail due to Duktape compiler running out of virtual registers, and performance is somewhat limited as Duktape is an interpreted engine. See emscripten-status.rst for current compatibility status.

Because Duktape itself compiles with Emscripten, it is possible to run Duktape inside a web page for instance, see Dukweb REPL.

Lua.js §

lua.js translates Lua code to Javascript. There are no known issues in running the generated Javascript, except that Duktape doesn't provide console.log which lua.js expects. This is easy to remedy, e.g. by prepending the following:

console = { log: function() { print(, ' ')); } };

JS-Interpreter §

JS-Interpreter interprets Javascript in Javascript. JS-Interpreter works with Duktape, except that Duktape doesn't provide window which JS-Interpreter expects. This can be fixed by prepending:

window = {};

Versioning §

Semantic versioning §

Duktape follows Semantic Versioning:

The "public API" to which these rules apply include:

The following are not part of the "public API":

When a patch version is released, the following things are guaranteed:

Experimental features §

Some new features and API calls are marked experimental which means that they may change in an incompatible way even in a minor release.

Features may be marked experimental e.g. because they are useful but incomplete, or because the best design is not obvious and it's useful to gather some feedback before committing to the design. Typically a feature is experimental for one minor release and then, after the necessary changes, becomes a fully supported feature.

Version naming §

Releases use the form (major).(minor).(patch), e.g. 1.0.3.

Development pre-releases use the form (major).(minor).(patch)-alpha.(number), e.g. 1.3.0-alpha.2. The form 0.(minor).0 was used for development pre-releases before the 1.0 release.

DUK_VERSION and Duktape.version §

DUK_VERSION and Duktape.version provide version identification using a single number computed as: (major * 10000 + minor * 100 + patch), then subtracting one for development pre-releases.

Note the limitations for development pre-releases:

Development pre-releases shouldn't be used in production, but the current DUK_VERSION and Duktape.version number provides a useful approximation for version comparison: an alpha release will compare smaller than the actual release, but higher (or equal) than a previous release.

Examples §

The table below provides some examples, in ascending version order:

Version Pre-release? DUK_VERSION &
0.12.0yes1200Pre-release before 1.0 release.
1.3.0-alpha.1yes10299Identified like 1.2.99, first 1.3.0 development pre-release.
1.3.0-alpha.2yes10299Identified like 1.2.99, no difference to 1.3.0-alpha.1.
1.3.3-alpha.6yes10302Identified like 1.3.2, no difference to 1.3.2 release.
2.0.0-alpha.3yes19999Identified like 1.99.99.

Maintenance of stable versions §

There's no long term maintenance policy yet: stable versions will get bug fixes (patch releases) at least until the next stable version has been released, and there has been some time to migrate to it.

Incompatible changes §

The general goal for incompatible changes is that an application relying on old, unsupported features will fail to build. It is preferable to have the build fail rather than to be silently broken. This means for example that:

This is not a hard rule, but the default guideline.

Limitations §

The following is a list of known limitations of the current implementation. Limitations include shortcomings from a semantics perspective, performance limitations, and implementation limits (which are inevitable).

Trivial bugs are not listed unless they are "long term bugs".

No re-entrancy §

A single Duktape heap, i.e. contexts sharing the same garbage collector, is not re-entrant. Only one C/C++ thread can call Duktape APIs at a time for a particular Duktape heap (although the calling thread can change over time). See Threading.

String and buffer limits §

The internal representation allows a maximum length of 2**31-1 (0x7fffffff) bytes (not characters) for strings. 16-bit codepoints encode into 3 bytes of UTF-8 in the worst case, so the maximum string length which is guaranteed to work is about 0.7G characters.

Buffer values are also limited to 2**31-1 (0x7fffffff) bytes.

Property limits §

An object can have at most DUK_HOBJECT_MAX_PROPERTIES (an internal define). Currently this limit is 0x7ffffffff.

Array limits §

When array item indices go over the 2**31-1 limit (0x7fffffff), Duktape has some known bugs with array semantics.

Regexp quantifier over empty match §

The regexp engine gets stuck when a quantifier is used over an empty match but eventually bails out with an internal recursion (or execution step) limit. For instance, the following should produce a "no match" result but hits an internal recursion limit instead:

$ duk
duk> t = /(x*)*/.exec('y');
RangeError: regexp executor recursion limit
        exec (null) native strict preventsyield
        global input:1 preventsyield

Duktape does not fully support locales §

Although Duktape supports the concept of a local time, it doesn't support other locale related features such as: locale specific Date formatting, locale specific string comparison, locale/language specific Unicode rules (such as case conversion rules for Turkish, Azeri, and Lithuanian).

Unicode case conversion is not locale or context sensitive §

E5 Sections to require context and locale processing of Unicode SpecialCasing.txt. However, Duktape doesn't currently have a notion of "current locale".

Array performance when using non-default property attributes §

All array elements are expected to be writable, enumerable, and configurable (default property attributes for new properties). If this assumption is violated, even temporarily, the entire "array part" of an object is abandoned permanently and array entries are moved to the "entry part". This involves interning all used array indices as explicit string keys (e.g. "0", "1", etc). This is not a compliance concern, but degrades performance.

Array performance when writing elements using Object.defineProperty() §

When number indexed array elements are written with Object.defineProperty() the current implementation abandons the internal "array part" which makes later array access much slower. Write array elements with direct assignments such as a[123] = 321 to avoid this.

Global/eval code is slower than function code §

Bytecode generated for global and eval code cannot assign variables statically to registers, and will use explicit name-based variable read/write accesses. Bytecode generated for function code doesn't have this limitation; most variables are assigned statically to registers and direct register references are used used to access them.

This is a minor issue unless you spend a lot of time running top-level global/eval code. The workaround is simple: put code in a function which you call from the top level; for instance:

function main() {
    // ...

There is also a common idiom of using an anonymous function for this purpose:

(function () {
    // ...

Function temporaries may be live for garbage collection longer than expected §

Ecmascript functions are compiled into bytecode with a fixed set of registers. Some registers are reserved for arguments and variable bindings while others are used as temporaries. All registers are considered live from a garbage collection perspective, even temporary registers containing old values which the function actually cannot reference any more. Such temporaries are considered reachable until they are overwritten by the evaluation of another expression or until the function exits. Function exit is the only easily predicted condition to ensure garbage collection.

If you have a function which remains running for a very long time, it should contain the bare minimum of variables and temporaries that could remain live. For instance, you can structure code like:

function runOnce() {
    // run one iteration, lots of temporaries

function foreverLoop() {
    for (;;) {

This is typically not an issue if there are no long-running functions.

Function instances are garbage collected only by mark-and-sweep §

Every Ecmascript function instance is, by default, in a reference loop with an automatic prototype object created for the function. The function instance's prototype property points to the prototype object, and the prototype's constructor property points back to the function instance. Only mark-and-sweep is able to collect these reference loops at the moment. If you build with reference counting only, function instances may appear to leak memory; the memory will be released when the relevant heap is destroyed.

You can break the reference loops manually (although this is a bit cumbersome):

var f = function() { };
var g = function() { };
var h = function() { };
Duktape.fin(f, function() { print('finalizer for f'); });
Duktape.fin(g, function() { print('finalizer for g'); });
Duktape.fin(h, function() { print('finalizer for h'); });

// not collected until heap destruction in a reference counting only build
f = null;            // not collected immediately

// break cycle by deleting 'prototype' reference (alternative 1)
g.prototype = null;
g = null;            // collected immediately, finalizer runs

// break cycle by deleting 'constructor' reference (alternative 2)
h.prototype.constructor = null;
h = null;            // collected immediately, finalizer runs

// no-op with refcount only, with mark-and-sweep finalizer for 'f' runs

For internal technical reasons, named function expressions are also in a reference loop with an internal environment record object. This loop cannot be broken from user code and only mark-and-sweep can collect such functions. Ordinary function declarations and anonymous functions don't have this limitation. Example:

var fn = function myfunc() {
    // myfunc is in reference loop with an internal environment record,
    // and can only be collected with mark-and-sweep.

These issues can be avoided by compiling Duktape with mark-and-sweep enabled (which is the default).

Non-standard function 'caller' property limitations §

When DUK_OPT_NONSTD_FUNC_CALLER_PROPERTY is given, Duktape updates the caller property of non-strict function instances similarly to e.g. V8 and Spidermonkey. There are a few limitations, though:

See the internal test-bi-function-nonstd-caller-prop.js test case for further details.

Comparison to Lua §

Duktape borrows a lot from Lua conceptually. Below are a few notes on what's different in Duktape compared to Lua. This may be useful if you're already familiar with Lua.

Array and stack indices are zero-based §

All array and stack indices are zero-based, not one-based as in Lua. So, bottom of stack is 0, second element from bottom is 1, and top element is -1. Because 0 is no longer available to denote an invalid/non-existent element, the constant DUK_INVALID_INDEX is used instead in Duktape.

String indices are also zero-based, and slices are indicated with an inclusive start index and an exclusive end index (i.e. [start,end[). In Lua, slices are indicated with inclusive indices (i.e. [start,end]).

Object type represents functions and threads §

In Lua functions and threads are a separate type from objects. In Duktape the object type is used for plain objects, Ecmascript and native functions, and threads (coroutines). As a result, all of these have a mutable and extensible set of properties.

Lua userdata and lightuserdata §

The concept closest to Lua userdata is the Duktape buffer type, with the following differences:

Lua lightuserdata and Duktape pointer are essentially the same.

If you need to associate properties with a Duktape buffer, you can use a buffer object instead (or create your own object and store the plain buffer as its property). You can then add a finalizer to the object to free any resources related to the buffer. This works reasonably well as long as nothing else holds a reference to the buffer. If this were the case, the buffer could get used after the object had already been finalized. To safeguard against this, the native C structure should have a flag indicating whether the data structure is open or closed. This is good practice anyway for robust native code.

Garbage collection §

Duktape has a combined reference counting and non-incremental mark-and-sweep garbage collector (mark-and-sweep is needed only for reference cycles). Collection pauses can be avoided by disabling voluntary mark-and-sweep passes (DUK_OPT_NO_VOLUNTARY_GC). Lua has an incremental collector with no pauses, but has no reference counting.

Duktape has an emergency garbage collector. Lua 5.2 has an emergency garbage collector while Lua 5.1 does not (there is an emergency GC patch though).

duk_safe_call() vs. lua_cpcall() §

duk_safe_call() is a protected C function call which operates in the existing value stack frame. The function call is not visible on the call stack all.

lua_cpcall() creates a new stack frame.

Bytecode dump/load §

Starting from Duktape 1.3 Duktape has a bytecode dump/load mechanism similar to Lua lua_dump(). See Bytecode dump/load.

Metatables §

There is no equivalent of Lua metatables in Ecmascript E5/E5.1, but Ecmascript E6 Proxy objects provide similar functionality. To allow property virtualization better than available in E5/E5.1, Duktape implements an ES6 Proxy subset.

lua_next() vs. duk_next() §

lua_next() replaces the previous key and value with a new pair, while duk_next() does not; the caller needs to explicitly pop the key and/or value.

Raw accessors §

There is no equivalent to Lua raw table access functions like lua_rawget. One can use the following Ecmascript built-ins for a similar effect (though not with respect to performance): Object.getOwnPropertyDescriptor ( O, P ), Object.defineProperty ( O, P, Attributes ).

Coroutines §

There are no primitives for coroutine control in the Duktape API (Lua API has e.g. lua_resume). Coroutines can only be controlled using the functions exposed by the Duktape built-in. Further, Duktape has quite many coroutine yield restrictions now; for instance, coroutines cannot yield from inside constructor calls or getter/setter calls.

Multiple return values §

Lua supports multiple return values, Duktape (or Ecmascript) currently doesn't. This may change with Ecmascript E6, which has a syntax for multiple value returns. The Duktape/C API reserves return values above 1 so that they may be later used for multiple return values.

Weak references §

Lua supports weak references. Duktape currently doesn't.

Unicode §

Lua has no built-in Unicode support (strings are byte strings), while Duktape has support for 16-bit Unicode as part of Ecmascript compliance.

Streaming compilation §

Lua has a streaming compilation API which is good when code is read from the disk or perhaps decompressed on-the-fly. Duktape currently does not support streaming compilation because it needs multiple passes over the source code.

Duktape is (C) by its authors and licensed under the MIT license.