Movatterモバイル変換


[0]ホーム

URL:


((o) Duktape
Fork me on GitHub
Duktape Programmer's Guide

Introduction§

Version: 2.6.0(2020-10-13)

Document scope§

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

This document doesn't cover Duktape internals, see theDuktape repoif you wish to tinker with them.

What is Duktape?§

Duktape is an embeddable ECMAScript® engine with a focus on portabilityand compact footprint. By integrating Duktape into your C/C++ program youcan easily extend its functionality through scripting. You can also buildthe main control flow of your program in ECMAScript and use fast C codefunctions to do heavy lifting.

Embeddability means that Duktape makes minimal assumptions about theunderlying platform and its capabilities; instead, the application whichembeds Duktape is in control of which native bindings are provided andin what manner. For example, there are no default bindings for printingtext to the console or to interact with the file system. Duktapedistributable package includes example providers which you can easilyintegrate if they suit your needs.

The terms ECMAScript and Javascript are often considered more or lessequivalent, although Javascript and its variants are technically just oneenvironment where the ECMAScript language is used. The line between thetwo is not very clear in practice: even non-browser ECMAScript environmentsoften provide some browser-specific built-ins. Even so, we use the termECMAScript throughout to refer to the language implemented by Duktape.

Conformance§

Duktape conforms to ES5.0/ES5.1 with semantics updated from ES2015 orlater where appropriate:

Duktape tracks the latest ECMAScript specification for semantics andbuilt-ins (however support for ES2015 and later is still incomplete), see:

In some specific cases Duktape may follow a specification draft,see work in progress inTC39/ecma262.This is done when a feature in the latest specifications conflictswith real world code (see e.g.RegExp.prototype issues).

TypedArray support is based on ES2016 TypedArray; initial implementationwas based on Khronos TypedArray specification:

Node.js Buffer support is based on:

TextEncoder() and TextDecoder() bindings are based on:

Performance.now() binding is based on:

Features§

Besides standard ECMAScript features, Duktape has the following additionalfeatures (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 latest or draft ECMAScriptspecifications to minimize Duktape custom features.

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

Easy C interface. The interface between Duktape and C programsshould be natural and error-tolerant. As a particular issue, stringrepresentation should be UTF-8 with automatic NUL terminators to matchcommon C use.

Small footprint. Code and data footprint should be as small aspossible, even for small programs. Duktape is portable even to "bare metal"targets with no standard libraries. 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 someextent) probably eliminates the possibility of a competitive JIT-based engine,so there is no practical way of competing with very advanced JIT-based engineslike SpiderMonkey (and its optimized variants) or Google V8. Performanceshould 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 compilationwould be nice.)

ASCII string performance. It's important that operations dealingwith plain ASCII strings be very fast: ASCII dominates most embedded use.Operations dealing with non-ASCII strings need to perform reasonably but arenot critical. This is a necessary trade-off: using C-compatible strings meansessentially using UTF-8 string representation which makes string indexing andmany 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 concreteexamples of how you can integrate scripting capabilities into your program.

Programming model,Stack types,andC typesdiscuss core Duktape concepts such asheap,context,value stacks,Duktape API, andDuktape/C functions. Duktape stack types and C typewrappers are discussed in detail.

Duktape specific ECMAScript features are discussed in multiple sections:Type algorithms (for custom types),Duktape built-ins (additional built-ins),Post-ES5 features (features implemented from ES2016 and beyond),Custom behavior (behavior differing from standard),Custom JSON formats,Custom directives,Buffer objects,Error objects (properties and traceback support),Function objects (properties),Date and time,Random numbers,Debugger,Modules,Finalization,Coroutines,Virtual properties,Symbols,Bytecode dump/load,Threading,Sandboxing.

Performance provides a few Duktape-specific tipsfor improving performance and avoiding performance pitfalls.Memory usage summarizes Duktape memory usage andgives pointers for minimizing it.Compiling describes how to configure and compile Duktapeas part of your application.Portability covers platform and compiler specificissues and other portability issues.Compatibility discusses Duktape's compatibilitywith ECMAScript dialects, extensions, and frameworks.Versioning describes Duktape versioning and what versioncompatibility to expect.Limitations summarizes currently known limitationsand provides possible workarounds.

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


Getting started§

Downloading§

Download the source distributable from theDownload page.

Command line tool for testing§

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 havegcc installed. If you don't,you can just edit the Makefile to match your compiler (the Makefile isquite simple).

Duktape doesn't provide built-in bindings for file or console I/O to avoidportability issues (for example, some platforms don't have I/O APIs at all).The command line utility providesprint() andalert()bindings usingextras/print-alertto make it easier to play with. There are useful "extras" in the distributableproviding useful (optional) bindings such as:Throughout the guide examples will assume aprint() binding forillustration.
The command line tool avoids platform dependencies by default. You can addline editing support vialinenoiseby editing the Makefile:
  • Add-DDUK_CMDLINE_FANCY
  • Add-Ipath/to/linenoise for thelinenoise.h header
  • Addpath/to/linenoise.c to the source list
  • Linenoise only works in POSIX environments and requires a C compiler (not C++)

You can now run ECMAScript code interactively:

$ ./duk((o) Duktape 2.6.0 (v2.6.0)duk> print('Hello world!')Hello world!= undefined

You can also run ECMAScript code from a file which is useful for playing withfeatures and algorithms. As an example, createfib.js:

// fib.jsfunctionfib(n){if(n==0){return0;}if(n==1){return1;}returnfib(n-1)+fib(n-2);}functiontest(){var res=[];for(i=0; i<20; i++){        res.push(fib(i));}print(res.join(' '));}test();

Test the script from the command line:

$ ./duk fib.js0 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 embedsDuktape. Embedding Duktape into your program is very simple:

The Duktape distributable (duktape-N.N.N.tar.xz)src/ directorycontains preconfigured header and source files for the Duktape default configurationwhich can usually be used as is. If needed, the configuration tool allows you to customizeDuktape options, such as optimizing Duktape for low memory targets andenable/disable features. SeeCompiling andConfiguring Duktape for buildfor more details and examples.

The distributable contains a very simple example program,hello.c,which illustrates this process. Compile the test program with the preconfiguredDuktape header and source files e.g. as follows:

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

To customize Duktape configuration useconfigure.py:

$ cd /tmp/duktape-<version>/# Here we disable ECMAScript 6 Proxy object support$ python2 tools/configure.py --output-directory duktape-src -UDUK_USE_ES6_PROXY$ gcc -std=c99 -o hello -Iduktape-src duktape-src/duktape.c examples/hello/hello.c -lm

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

$ ./helloHello world!2+3=5

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

More broadly, there are several approaches to how you can use Duktapewith native code; for example:

See the following Wiki articles for detailed examples:


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 Duktapeheap is a single region for garbage collection. A heapis used to allocate storage for strings, ECMAScript objects, and othervariable size, garbage collected data. Objects in the heap have an internalheap 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 froma garbage collection perspective. For instance, the properties of an ECMAScriptobject reference both the keys and values of the object's property set. You canhave multiple heaps, but objects in different heaps cannot reference each otherdirectly; you need to use serialization to pass values between heaps.

A Duktapecontext is an ECMAScript "thread of execution" which livesin a certain Duktape heap. A context is represented by aduk_context *in the Duktape API, and is associated with an internal Duktape coroutine (a formof a co-operative thread). Each context is also associated with an environmentconsisting of global objects; contexts may share the same global environment butcan also have different environments. The context handle is given to almost everyDuktape API call, and allows the caller to interact with thevalue stack ofthe Duktape coroutine: values can be inserted and queries, functions can be called,and so on.

Each coroutine has acall stack which controls execution, keepingtrack of function calls, native or ECMAScript, within the ECMAScript engine.Each coroutine also has avalue stack which stores all the ECMAScriptvalues of the coroutine's active call stack. The value stack always has anactivestack frame for the most recent function call (when no functioncalls have been made, the active stack frame is the value stack as is).The Duktape API calls operate almost exclusively in the currently activestack frame. There's also internal book-keeping for error catching sitesestablished using e.g.try-catch-finally.

Multiple contexts can share the same Duktapeheap. In more concreteterms this means that multiple contexts can share the same garbage collectionstate, and can exchange object references safely. Contexts in different heapscannot exchange direct object references; all values must be serialized in oneway or another.

Almost every API call provided by the Duktape API takes a context pointeras its first argument: no global variables or states are used, and there areno restrictions on running multiple, independent Duktape heaps and contextsat the same time. There are multi-threading restrictions, however: only onenative thread can execute any code within a single heap at any time, seeThreading.

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

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

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

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

To create a new context inside the same heap, with the context sharing thesame 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 globalobject:

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 aduk_context *, thecorresponding Duktape coroutine MUST be reachable from a garbage collectionpoint of view.

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

duk_destroy_heap(ctx);

This frees all heap objects allocated, and invalidates any pointers tosuch objects. In particular, if the calling program holds string pointersto values which resided on the value stack of a context associated with theheap, such pointers are invalidated and must never be dereferenced afterthe heap destruction call returns.

Call 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 isto facilitate the passing of arguments and results between function callersand callees, and to keep track of how the value stack is divided betweenfunction calls. The call stack also allows Duktape to construct a tracebackfor errors.

Because Duktape supports tail calls, the call stack does not alwaysaccurately 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 relatedto 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 availabletagged types, seeTypes.

The value stack is divided between the currently active function calls(activations) on the coroutine's call stack. At any time, there is an activestack frame which provides an origin for indexing elements on the stack.More concretely, at any time there is abottom which is referredto with the index zero in the Duktape API. There is also a conceptualtop which identifies the stack element right above the highestcurrently 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 |      .---.   Stack top is 6 (relative to stack bottom).|  8 | <--- | 5 |   API index 5 is highest used (at value stack index 8).|  7 |      | 4 ||  6 |      | 3 ||  5 |      | 2 ||  4 |      | 1 ||  3 | <--- | 0 |   API index 0 is bottom (at value stack index 3).|  2 |      `---'|  1 ||  0 |`----'

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 framesare shown horizontally throughout the documentation for space reasons. Forexample, the active stack frame in the figure above would be shown as:

[012345]

Avalue stack index is a signed integer index used in the DuktapeAPI to refer to elements in currently active stack frame, relative to thecurrent frame bottom.

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

[012345]

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

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

The special constantDUK_INVALID_INDEX is a negative integerwhich denotes an invalid stack index. It can be returned from API callsand can also be given to API calls to indicate a "no value".

Thevalue stack top (or just "top") is the non-negative index ofan imaginary element just above the highest used index. For instance, abovethe highest used index is 5, so the stack top is 6. The top indicates thecurrent stack size, and is also the index of the next element pushed to thestack.

[0123456]

API stack operations are always confined to the current stack frame.There is no way to refer to stack entries below the current frame. Thisis intentional, as it protects functions in the call stack from affectingeach 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 certainmaximum number of entries. An attempt to push values beyond the allocatedsize will cause an error to be thrown, it willnot cause the valuestack to be automatically extended. This simplifies the internalimplementation and also improves performance by minimizing reallocationswhen you know, beforehand, that a certain number of entries will be neededduring a function.

When a value stack is created or a Duktape/C function is entered, thevalue stack is always guaranteed to have space for the call arguments andDUK_API_ENTRY_STACK (currently 64) elements. In the typicalcase this is more than sufficient so that the majority of Duktape/Cfunctions don't need to extend the value stack. Only functions that needmore space or perhaps need an input-dependent amount of space need to growthe value stack.

You can extend the stack allocation explicitly withduk_check_stack()or (usually more preferably)duk_require_stack().Once successfullyextended, you are again guaranteed that the specified number of elements canbe pushed to the stack. There is no way to shrink the allocation except byreturning from a Duktape/C function.

Consider, for instance, the following function which will uppercase aninput ASCII string by pushing uppercased characters one-by-one on the stackand then concatenating the result. This example illustrates how the numberof value stack entries required may depend on the input (otherwise this isnot a very good approach for uppercasing a string):

/* uppercase.c */#include<stdio.h>#include<stdlib.h>#include"duktape.h"staticintdummy_upper_case(duk_context*ctx){size_t sz;constchar*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,(constchar*)&ch,1);}duk_concat(ctx, sz);return1;}intmain(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));duk_pop(ctx);duk_destroy_heap(ctx);return0;}

In addition to user reserved elements, Duktape keeps an automatic internalvalue stack reserve to ensure all API calls have enough value stack space towork without further allocations. The value stack is also extended and shrunkin somewhat large steps to minimize memory reallocation activity. As a resultthe internal number of value stack elements available beyond the callerspecified extra varies considerably. The caller does not need to take thisinto account and should never rely on any additional elements being available.

ECMAScript array index§

ECMAScript object and array keys can only be strings or Symbols. Arrayindices (e.g. 0, 1, 2) are represented as canonical string representations ofthe respective numbers (e.g. "0", "1", "2"). More technically, all canonicalstring representations of the integers in the range [0, 2**32-2] are validarray indices.

To illustrate the ECMAScript array index handling, consider the followingexample:

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 indexarguments. This is really just a short hand for denoting a string conversionof that number. For instance, if the API is given the integer 123, thisreally refers to the property name "123".

Internally, Duktape tries to avoid converting numeric indices to actualstrings whenever possible, so it is preferable to use array index API callswhen they are relevant. Similarly, when writing ECMAScript code it ispreferable to use numeric rather than string indices, as the same fast pathapplies for ECMAScript code.

Duktape API§

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

The Duktape API calls are generally error tolerant and will check allarguments for errors (such asNULL pointers). However, tominimize footprint, thectx argument is not checked, and thecaller MUST NOT call any Duktape API calls with aNULL context.

All Duktape API calls are potentially macros. Calling code must not relyon any Duktape API call being available as a function pointer. Theimplementation of a certain API call may change between a macro and anactual function even between compatible releases. The Duktape API providesthe following guarantees for macros:

Duktape/C function§

A C function with a Duktape/C API signature can be associated with anECMAScript function object, and gets called when the ECMAScript functionobject is called. A Duktape/C API function looks as follows:

duk_ret_tmy_func(duk_context*ctx){duk_push_int(ctx,123);return1;}

The function gets ECMAScript call arguments in the value stack ofctx, withduk_get_top()indicating the number ofarguments present on the value stack. Thethis binding is notautomatically pushed to the value stack; useduk_push_this()to access it if necessary. When creating an ECMAScript function objectassociated with a Duktape/C function, one can select the desired numberof arguments. Extra arguments are dropped and missing arguments are replacedwithundefined. A function can also be registered as a varargfunction (by givingDUK_VARARGS as the argument count) inwhich case call arguments are not modified prior to C function entry.

The function can return one of the following:

When a Duktape/C function returns, the value stack is automaticallyunwound so there is no need to manually clean up the value stack whenthe function returns.

A negative error return value is intended to simplify common errorhandling, and is an alternative to constructing and throwing an errorexplicitly with Duktape API calls. No error message can be given; amessage is automatically constructed by Duktape. For example:

duk_ret_tmy_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 consideredstrict in theECMAScript sense.Duktape API calls always obey ECMAScript strict mode semantics, even when the API callsare made outside of any Duktape/C function, i.e. with an empty call stack.For instance, attempt to delete a non-configurable property usingduk_del_prop()will cause an error to be thrown. This is the case with a strict ECMAScript function too:

functionf(){'use strict';var arr=[1,2,3];returndelete 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 thethisbinding given to Duktape/C functions is notcoerced.This is also the case for strict ECMAScript code:

functionstrictFunc(){'use strict';print(typeofthis);}functionnonStrictFunc(){print(typeofthis);}strictFunc.call('foo');// prints 'string' (uncoerced)nonStrictFunc.call('foo');// prints 'object' (coerced)

Duktape/C functions are currently alwaysconstructable, i.e. theycan always be used innew Foo() expressions. You can check whethera function was called in constructor mode as follows:

staticduk_ret_tmy_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 aprototypeproperty by default, so the default object instance (given to the constructorasthis) inherits fromObject.prototype. To use acustom prototype you can defineprototype for the Duktape/Cfunction explicitly. Another approach is to ignore the default object instanceand construct one manually within the Duktape/C call: as with ECMAScriptfunctions, if a constructor returns an object value, that value replaces thedefault object instance and becomes the value of thenewexpression.

Thethis binding is not automatically pushed to the value stack;useduk_push_this()to access it.

Storing state for a Duktape/C function§

Sometimes it would be nice to provide parameters or additional stateto 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 stateor parameters. A certain Duktape/C function (the actual C function)is always represented by an ECMAScript Function object which isinternally associated with the underlying C function. The Functionobject can be used to store properties related to that particularinstance of the function. Note that a certain Duktape/C function canbe associated with multiple independent Function objects and thusindependent states.

Accessing the ECMAScript Function object related to a Duktape/C functionis easy:

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

'this' binding§

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

foo.my_c_func()

When called, the Duktape/C function getsfoo as itsthisbinding, and one could store state directly infoo. The differenceto using the Function object approach is that the same object is shared by allmethods, which has both advantages and disadvantages.

Accessing thethis binding is easy:

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

Hidden Symbol properties§

If data needs to be associated with an object, but hidden from ECMAScriptcode, hiddenSymbols can be used as a property key. Thekey is only accessible via the C API, unless passed to ECMAScript code from theC API. A common use case is to associated backing pointers/data to C memory.Symbols are created as a string, but are differentiated with macros that markthem as Symbols.

For example, setting and getting a hidden symbol onthis:

my_context_data_t*my_context_data=malloc(sizeof(my_context_data_t));duk_push_this(ctx);duk_push_pointer(ctx, my_context_data);duk_put_prop_string(ctx,-2,DUK_HIDDEN_SYMBOL("my_context_data"));/* ... */duk_push_this(ctx);duk_get_prop_string(ctx,-1,DUK_HIDDEN_SYMBOL("my_context_data"));my_context_data_t*my_context_data=duk_get_pointer(ctx,-1);

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 usedto pass flags and/or small values to a Duktape/C function at minimal cost, sothat a single native function can provide slightly varied behavior for multiplefunction 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. */constchar*prefix[4]={"INFO","WARN","ERROR","FATAL"};duk_int_t magic=duk_get_current_magic(ctx);printf("%s: %s", prefix[magic&0x03],duk_safe_to_string(ctx,0));if(magic&0x04){printf("\r\n");}else{printf("\n");}

For an API usage example, see the test casetest-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 itreally matters for footprint. Properties stored on the function object is amore stable alternative.

Heap stash§

The heap stash is an object visible only from C code. It is associatedwith 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 theduk_push_heap_stash()API call.

Global stash§

The global stash is like the heap stash, but is associated with a globalobject. It is accessed with theduk_push_global_stash()API call. There can be several environments with different global objectswithin the same heap.

Thread stash§

The thread stash is like the heap stash, but is associated with a Duktapethread (i.e. actx pointer). It is accessible with theduk_push_thread_stash()API call.

Duktape version specific code§

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

For C code:

#if(DUK_VERSION>=20403)/* Duktape 2.4.3 or later */#elif(DUK_VERSION>=10500)/* Duktape 1.5.0 or later */#else/* Duktape lower than 1.5.0 */#endif

For ECMAScript code (also seeDuktape built-ins):

if(typeof Duktape!=='object'){print('not Duktape');}elseif(Duktape.version>=20403){print('Duktape 2.4.3 or higher');}elseif(Duktape.version>=10500){print('Duktape 1.5.0 or higher (but lower than 2.4.3)');}else{print('Duktape lower than 1.5.0');}

Numeric error codes§

When errors are created or thrown using the Duktape API, the callermust assign a numeric error code to the error. Error codes arepositive integers, with a range restricted to 24 bits at themoment: the allowed error number range is thus [1,16777215]. Built-inerror codes are defined induktape.h, e.g.DUK_ERR_TYPE_ERROR.

The remaining high bits are used internally to carry e.g. additionalflags. Negative error values are used in the Duktape/C API as ashorthand to automatically throw an error.

Error handling§

Error handling in the Duktape API is similar to how ECMAScript handleserrors: errors are thrown either explicitly or implicitly, then caught andhandled. Instead of a try-catch statement application C code usesprotectedDuktape API calls to establish points in C code where errors can be caughtand handled. All Duktape API calls except protected calls may throw errors:most ECMAScript operations may cause error throws in some situations, and anout-of-memory error is possible in almost any situation. The long controltransfer between the throw site and the catch site is based either onsetjmp()/longjmp() (or their platform specificvariants), or a C++ exception throw (whenDUK_USE_CPP_EXCEPTIONSis enabled), seeLong control transfers.

An uncaught error causes the fatal error handler to be called, which isconsidered an unrecoverable situation and should ordinarily be avoided,seeNormal and fatal errors andHow to handle fatal errors. To avoid fatal errors, typical application code should establish anerror catch site before making other Duktape API calls. This is done usingprotected 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. */push_file_as_string(ctx,"myscript.js");if(duk_peval(ctx)!=0){/* Use duk_safe_to_string() to convert error into string.  This API     * call is guaranteed not to throw an error during the coercion.     */printf("Script error: %s\n",duk_safe_to_string(ctx,-1));}duk_pop(ctx);

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. */staticduk_ret_tunsafe_code(duk_context*ctx,void*udata){/* Here we can use unprotected calls freely. */(void) udata;/* 'udata' may be used to pass e.g. a struct pointer */push_file_as_string(ctx,"myscript.js");duk_eval(ctx);/* ... */return0;/* success return, no return value */}/* elsewhere: */if(duk_safe_call(ctx, unsafe_code, NULL/*udata*/,0/*nargs*/,1/*nrets */)!=0){/* The 'nrets' argument should be at least 1 so that an error value     * is left on the stack if an error occurs.  To avoid further errors,     * use duk_safe_to_string() for safe error printing.     */printf("Unexpected error: %s\n",duk_safe_to_string(ctx,-1));}duk_pop(ctx);

Even within protected calls there are some rare cases, such as internalerrors, that will either cause a fatal error or propagate an error outwardsfrom a protected API call. These should only happen in abnormal conditionsand are not considered recoverable. To handle also these cases well, aproduction quality application should always have a fatal error handler witha reasonable strategy for dealing with fatal errors. Such a strategy isnecessarily application dependent, but could be something like:

Note that it may be fine for some applications to make API calls withoutan error catcher and risk throwing uncaught errors leading to a fatal error.It's not safe to continue execution after a fatal error, so suchapplications would typically simply exit when a fatal error occurs. Evenwithout an actual recovery strategy, a fatal error handler should be used toe.g. write fatal error information tostderr before process exit.

Normal and fatal errors§

Anordinary error is caused by athrow statement, aduk_throw()API call (or similar), or by an internal, recoverable Duktape error.Ordinary errors can be caught with atry-catch in ECMAScriptcode or e.g.duk_pcall()(see API calls taggedprotected)in C code.

Afatal error is caused by an uncaught error, an assertion failure(if enabled), an explicit call toduk_fatal(), or an unrecoverableerror inside Duktape.

Each Duktape heap has a heap-wide fatal error handler registered induk_create_heap().If no handler is given a built-in default handler is used:

It isstrongly recommended to both (1) provide a custom fatal errorhandler in heap creation and (2) replace the built-in default fatal errorhandler using theDUK_USE_FATAL_HANDLER() option induk_config.h.

There's no safe way to resume execution after a fatal error, so that afatal error handler must not return or call into the Duktape API. Instead thehandler should e.g. write out the message to the console or a log file andthen exit the process (or restart an embedded device). This also applies whenusing C++ exceptions as the long control transfer mechanism.If theapplication continues execution after a fatal error all bets are off: memoryleaks are possible, and memory safety may be compromised.

Fatal errors may also happen without any heap context, so that Duktapecan't look up a possible heap-specific fatal error handler. Duktape will thenalways call the built-in default fatal error handler (with the handleruserdata argument set to NULL). Fatal errors handled this way are currentlylimited to assertion failures, so that if you don't enable assertions, no sucherrors will currently occur and all fatal error handling goes through theheap-associated fatal error handler which is in direct application control.

SeeHow to handle fatal errorsfor more detail and examples.

Long control transfers§

The specific long control transfer mechanism Duktape uses internallyfor error throwing and catching is not directly visible to application code:application code uses aprotected call to catcherrors, while errors are thrown by Duktape or by the application using avariety of Duktape API calls, e.g.duk_error().

Default configuration, without DUK_USE_CPP_EXCEPTIONS§

In the default configuration a Duktape API protected call usessetjmp() to establish a catch site. The error throwsite useslongjmp() to unwind the native C stack and returnto the (nearest) catch site. On some platforms variants of the calls, suchassigsetjmp() andsiglongjmp are used. There areminor differences between the call variants e.g. with regards to performanceand signal handling. The variant is chosen byduk_config.h.

Alongjmp() unwinds all the native C stack frames between thelongjmp() and thesetjmp(). However, this unwindingprocess won't invoke C++ automatic destructors which may be a significantlimitation for some C++ applications.

For fatal errors, e.g. uncaught errors, the default fatal error handlerusesabort(), seeNormal and fatal errors.

C++ mode, with DUK_USE_CPP_EXCEPTIONS§

WhenDUK_USE_CPP_EXCEPTIONS is enabled the long control transferis based on a C++ exception throw. The protected call uses a C++ try-catchto establish a catch site; note that this happens inside Duktape, and is notvisible to the application. The error throw site throws aduk_internal_exception which is caught by the (nearest) Duktapecatch site.Application code must not catch (or throw) the exception.To minimize that risk, the exception doesn't inherit from any standard exceptionclass so it won't be caught by a boilerplatestd::exception catchsite.

For fatal errors, e.g. uncaught errors, aduk_fatal_exceptionis thrown by the default fatal error handler. The exception inherits fromstd::runtime_error and is intended to be caught by user code.The exception provides a::what() method returning the fatalerror message. Even though the fatal error is catchable, it is still unsafeto continue execution after catching the error. SeeNormal and fatal errors.

For both C++ exception types the C++ native stack unwind process supportsautomatic destructors for the stack frames between the throw site and the catchsite.


Stack types§

Overview§

TypeType constantType mask constantDescription
(none)DUK_TYPE_NONEDUK_TYPE_MASK_NONEno type (missing value, invalid index, etc)
undefinedDUK_TYPE_UNDEFINEDDUK_TYPE_MASK_UNDEFINEDundefined
nullDUK_TYPE_NULLDUK_TYPE_MASK_NULLnull
booleanDUK_TYPE_BOOLEANDUK_TYPE_MASK_BOOLEANtrue andfalse
numberDUK_TYPE_NUMBERDUK_TYPE_MASK_NUMBERIEEE double
stringDUK_TYPE_STRINGDUK_TYPE_MASK_STRINGimmutable (plain) string or (plain) Symbol
objectDUK_TYPE_OBJECTDUK_TYPE_MASK_OBJECTobject with properties
bufferDUK_TYPE_BUFFERDUK_TYPE_MASK_BUFFERmutable (plain) byte buffer, fixed/dynamic/external; mimics an Uint8Array
pointerDUK_TYPE_POINTERDUK_TYPE_MASK_POINTERopaque pointer (void *)
lightfuncDUK_TYPE_LIGHTFUNCDUK_TYPE_MASK_LIGHTFUNCplain Duktape/C function pointer (non-object); mimics a Function

Memory allocations§

The following stack types involve additional heap allocations:

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 arenot relocated in memory while they are reachable from a garbage collectionpoint of view. This is the case for the main heap object, but notnecessarily for any additional allocations related to the object, such asdynamic property tables or dynamic buffer data area. A heap object isreachable e.g. when it resides on the value stack of a reachable thread oris reachable through the global object. Once a heap object becomesunreachable any pointers held by user C code referring to the object areunsafe and should no longer be dereferenced.

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

The data area of a dynamic buffer doesnot have a stable pointer.The buffer itself has a heap header with a stable address but the currentbuffer is allocated separately and potentially relocated when the bufferis resized. It is thus unsafe to hold a pointer to a dynamic buffer's dataarea across a buffer resize, and it's probably best not to hold a pointerafter a Duktape/C function returns (as there would be no easy way of beingsure that the buffer hadn't been resized).

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

Type masks§

Type masks allows calling code to easily check whether a type belongs toa certain type set. For instance, to check that a certain stack value isa 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 moreconveniently:

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 1if(duk_is_number(ctx,-3)||duk_is_string(ctx,-3)||duk_is_object(ctx,-3)){printf("type is number, string, or object\n");}// alt 2int t=duk_get_type(ctx,-3);if(t== DUK_TYPE_NUMBER|| t== DUK_TYPE_STRING|| t== DUK_TYPE_OBJECT){printf("type is number, string, or object\n");}

None§

Thenone type is not actually a type but is used in the API toindicate that a value does not exist, a stack index is invalid, etc.

Undefined§

Theundefined type maps to ECMAScriptundefined, which isdistinguished from anull.

Values read from outside the active value stack range read back asundefined.

Null§

Thenull type maps to ECMAScriptnull.

Boolean§

Theboolean 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 isaccepted as a "true" value. Whenever API calls return boolean values, the value1 is always used for a "true" value. This allows certain C idioms to beused. 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§

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

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

String§

Thestring stack type is used to represent both plain strings andplain Symbols (introduced in ES2015). A string is an arbitrary byte sequenceof a certain length which may contain internal NUL (0x00) values. Strings arealways automatically NUL terminated for C coding convenience. The NUL terminatoris 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 simpleconst char * as longas internal NULs are not an issue for the application; if they are, the explicitbyte length of the string can be queried with the API. Calling code can referdirectly 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 areinternedfor 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 willlead to very mysterious issues which are hard to diagnose.

Calling code most often deals with ECMAScript strings, which may containarbitrary 16-bit codepoints in the range U+0000 to U+FFFF but cannot representnon-BMPcodepoints. This is how strings are defined in the ECMAScript standard.In Duktape, ECMAScript strings are encoded withCESU-8 encoding. CESU-8matchesUTF-8 except that itallows codepoints in the surrogate pair range (U+D800 to U+DFFF) to be encodeddirectly (prohibited in UTF-8). CESU-8, like UTF-8, encodes all 7-bit ASCIIcharacters as-is which is convenient for C code. For example:

Duktape also uses extended strings internally. Codepoints up to U+10FFFFcan be represented with UTF-8, and codepoints above that up to full 32 bitscan be represented withextended UTF-8.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) whilecontinuation 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+FFFF FFFF)

The downside of the encoding for codepoints above U+7FFFFFFF is thatthe leading byte will be0xFE which conflicts with Unicode byte ordermarker encoding. This is not a practical concern in Duktape's internal use.

Finally, invalid extended UTF-8 byte sequences are used for special purposessuch as representing Symbol values. Invalid extended UTF-8/CESU-8 byte sequencesnever conflict with standard ECMAScript strings (which are CESU-8) and will remaincleanly separated within object property tables. For more information seeSymbols andsymbols.rst.

Strings with invalid extended UTF-8 sequences can be pushed on the value stackfrom C code and also passed to ECMAScript functions, with two caveats:

Object§

Theobject type includes ECMAScript objects and arrays, functions,threads (coroutines), and buffer objects. In other words, anything withproperties is an object. Properties are key-value pairs with a string keyand an arbitrary value (includingundefined).

Objects may participate in garbage collection finalization.

Buffer§

The plainbuffer type is a raw buffer for user data. It's morememory efficient than standard buffer object types like Uint8Arrayor Node.js Buffer. There are three plain buffer sub-types:

Buffer sub-typeData pointerResizableMemory managed byDescription
FixedStable, non-NULLNoDuktapeBuffer size is fixed at creation, memory is managed automatically by Duktape. Fixed buffers have an unchanging (stable) non-NULL data pointer.
DynamicUnstable, may be NULLYesDuktapeBuffer size can be changed after creation, memory is managed automatically by Duktape. Requires two memory allocations internally to allow resizing. Data pointer may change if buffer is resized. Zero-size buffer may have a NULL data pointer.
ExternalUnstable, may be NULLYesDuktape and user codeBuffer data is externally allocated. Duktape allocates and manages a heap allocated buffer header structure, while the data area pointer and length are configured by user code explicitly. External buffers are useful to allow a buffer to point to data structures outside Duktape heap, e.g. a frame buffer allocated by a graphics library. Zero-size buffer may have a NULL data pointer.

Unlike strings, buffer data areas are not automatically NUL-terminatedand calling code must never access bytes beyond the currently allocatedbuffer size. The data pointer of a zero-size dynamic or external buffermay be NULL; fixed buffers always have a non-NULL data pointer.

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

Plain buffers mimic Uint8Array objects to a large extent, so they area non-standard, memory efficient alternative to working with e.g. ArrayBufferand typed arrays. The standard buffer objects are implemented on top of plainbuffers, so that e.g. an ArrayBuffer will be backed by a plain buffer. SeeBuffer objects for more discussion.

A few notes:

Pointer§

Thepointer type is a raw, uninterpreted C pointer, essentiallyavoid *. Pointers can be used to point to native objects (memoryallocations, handles, etc), but because Duktape doesn't know their use, theyare not automatically garbage collected. You can, however, put one or morepointers inside an object and use the object finalizer to free thenative resources related to the pointer(s).

Lightfunc§

Thelightfunc type is a plain Duktape/C function pointer and a smallset of control flags packed into a single tagged value which requires no separateheap allocations. The control flags (16 bits currently) encode:(1) number of stack arguments expected by the Duktape/C function (0 to 14 orvarargs), (2) virtuallength property value (0 to 15), and(3) a magic value (-128 to 127).

Lightfuncs are a separate tagged type in the Duktape C API, but behave mostlylike Function objects for ECMAScript code. They have significant limitationscompared to ordinary Function objects, the most important being:

Lightfuncs are useful for very low memory environments where the memoryimpact of ordinary Function objects matters. SeeFunction objects for more discussion.


API C types§

Duktape API uses typedef-wrapped C types such asduk_int_t,duk_idx_t, andduk_ret_t almost exclusively to ensureportability to exotic platforms. On most platforms these map directly to signedor unsignedint; the wrappers make it possible to support platformswhere usual type assumptions (like having a 32-bitint) don't hold.See Wiki articleAPI C typesfor a detailed discussion.

Summary of best practices:


Type algorithms§

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

Notation§

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

ValueDescription
tcompares to true
fcompares to false
ssimple compare: boolean-to-boolean, string-to-string (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 vs. number: coerce string with ToNumber() and retry comparison
2boolean vs. any: coerce boolean with ToNumber() to 0 or 1, and retry comparison
3object vs. string/number: coerce object with ToPrimitive() and retry comparison

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

Equality (non-strict)§

Non-strict equality comparison is specified inThe Abstract Equality Comparison Algorithmfor 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:

undnulboonumstrobjbufptrlfn
undttfffffff
nultfffffff
boos222fff
numn13fff
strs3fff
objpfff
bufpff
ptrsf
lfnL

Strict equality§

Strict equality is much more straightforward and preferable wheneverpossible for simplicity and performance. It is described inThe Strict Equality Comparison Algorithmfor 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:

undnulboonumstrobjbufptrlfn
undtffffffff
nultfffffff
boosffffff
numnfffff
strsffff
objpfff
bufpff
ptrsf
lfnL

SameValue§

TheSameValue algorithm is not easy to invoke from user code.It is used by e.g.Object.defineProperty() when checking whethera property value is about to change. SameValue is even stricter than astrict equality comparison, and most notably differs in how numbers are compared.It is specified inThe SameValue algorithmfor 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:

undnulboonumstrobjbufptrlfn
undtffffffff
nultfffffff
boosffffff
numNfffff
strsffff
objpfff
bufpff
ptrsf
lfnL

Type conversion and testing§

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

bufferpointerlightfunc
DefaultValueUsually"[object Uint8Array]"; like Uint8ArrayTypeError"light_<PTR>_<FLAGS>" (toString/valueOf)
ToPrimitiveUsually"[object Uint8Array]"; like Uint8Arrayidentity"light_<PTR>_<FLAGS>" (toString/valueOf)
ToBooleantruefalse for NULL pointer, true otherwisetrue
ToNumberToNumber(String(buffer)), usually ToNumber("[object Uint8Array]") = NaN0 for NULL pointer, 1 otherwiseNaN
ToIntegersame as ToNumber; usually 0same as ToNumber0
ToInt32same as ToNumber; usually 0same as ToNumber0
ToUint32same as ToNumber; usually 0same as ToNumber0
ToUint16same as ToNumber; usually 0same as ToNumber0
ToStringUsually[object Uint8Array]; like Uint8Arraysprintf() with%p format (platform specific)"light_<PTR>_<FLAGS>"
ToObjectUint8Array object (backs to argument plain buffer)Pointer objectFunction object
CheckObjectCoercibleallow (no error)allow (no error)allow (no error)
IsCallablefalsefalsetrue
SameValue(covered above)(covered above)(covered above)

When a buffer is string coerced it behaves like an Uint8Array, with theresult usually being"[object Uint8Array]". This behavior waschanged in Duktape 2.0. To create a string from buffer contents you can usee.g. the Node.js Buffer binding or the Encoding API.

When a buffer is object coerced a new Uint8Array object is created, witha new ArrayBuffer backing to the plain buffer (no copy is made).

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

When a lightfunc is object coerced, a new Function object is createdand the virtual properties (name andlength andthe 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 buffertype e.g. with theduk_to_buffer() API call. The coercionis as follows:

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

The following table summarizes how different types are handled:

ToBufferToPointer
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 createdusing the Duktape C API.

Addition§

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

Addition is not generally useful for custom types. For example, if two plainbuffers are added, the result is usually"[object Uint8Array][object Uint8Array]",which matches how standard addition behaves for two Uint8Array instances.

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(Uint8Array.prototype orDuktape.Pointer.prototype).This mimics the behavior of standard types.

For example:

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

Lightfuncs have a few non-configurable and non-writable virtual properties(name andlength) and inherit their remainingproperties fromFunction.prototype, which allows ordinary inheritedFunction methods to be called:

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

Duktape built-ins§

This section summarizes Duktape-specific and non-ECMAScript built-inobjects, methods, and values.

Additional global object properties§

PropertyDescription
globalThisReferences the global object itself. Seeproposal-global.
DuktapeThe Duktape built-in object. Contains miscellaneous implementation specific stuff.
CBORExperimentalCBOR API based oncbor-js.
TextEncoderTextEncoder() fromWHATWG Encoding API. Converts a string into a buffer using UTF-8 encoding.
TextDecoderTextDecoder() fromWHATWG Encoding API. Converts a buffer into a string using UTF-8 encoding.
performanceperformance.now() fromHigh Resolution Time Level 2. Bindings like performance.timing fromNavigation Timing are not supported.

globalThis§

References the global object itself, seeproposal-global.(The binding was initially named 'global' but this was changed due tosome web sites breaking.).

The Duktape object§

PropertyDescription
versionDuktape version number:(major * 10000) + (minor * 100) + patch.
envCryptic, version dependent summary of most important effective options like endianness and architecture.
finSet or get finalizer of an object.
encEncode a value (hex, base-64, JX, JC):Duktape.enc('hex', 'foo').
decDecode a value (hex, base-64, JX, JC):Duktape.dec('base64', 'Zm9v').
infoGet internal information (such as heap address and alloc size) of a value in a version specific format. The C API equivalent isduk_inspect_value().
actGet information about call stack entry. The C API equivalent isduk_inspect_callstack_entry().
gcTrigger mark-and-sweep garbage collection.
compactCompact the memory allocated for a value (object).
errCreateCallback to modify/replace a created error.
errThrowCallback to modify/replace an error about to be thrown.
PointerPointer constructor (function).
ThreadThread constructor (function).

version§

Theversion property allows version-based feature detection andbehavior. Version numbers can be compared directly: a logically higher versionwill also be numerically higher. For example:

if(typeof Duktape!=='object'){print('not Duktape');}elseif(Duktape.version>=20403){print('Duktape 2.4.3 or higher');}elseif(Duktape.version>=10500){print('Duktape 1.5.0 or higher (but lower than 2.4.3)');}else{print('Duktape lower than 1.5.0');}

The value ofversion for pre-releases is one less than theactual release, e.g. 1199 for a 0.12.0 pre-release and 10299 for a 1.3.0pre-release. SeeVersioning.

Remember to check for existence ofDuktape when doing featuredetection. 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 missingif(!Duktape){print('not Duktape');}// Better: check through 'this' (bound to global)if(!this.Duktape){print('not Duktape');}// Better: use typeof to check also type explicitlyif(typeof Duktape!=='object'){print('not Duktape');}

env§

env summarizes the most important effective compile optionsin a version specific, quite cryptic manner. The format is version specificand is not intended to be parsed programmatically. This is mostly useful fordevelopers (seeduk_hthread_builtins.c for the code which setsthe 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

For endianness, "l" stands for little endian, "b" for big endian, and "m"for mixed endian (legacy ARM devices, see e.g.The FPA architecture).

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 furtherarguments are format specific.

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

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

For "jx" and "jc" the argument list following the format name is thesame as forJSON.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 ofenc().

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

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

If you prefer a fullUint8Array over a plain buffer, you cancoerce the result as follows:

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

If you wish to get back a string value, you can coerce the plain buffer toa string e.g. as follows:

// Use TextDecoder which decodes the input as UTF-8.  You can also use// the Node.js Buffer binding to achieve a similar result.var result=newTextDecoder().decode(Duktape.dec('base64','Zm9v'));print(typeof result, result);// prints 'string foo'

For "jx" and "jc" the argument list following the format name is the sameas forJSON.parse(): text, reviver (optional). For example:

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

info()§

Duktape.info() returns an object exposing internal informationrelated to its argument value. Seeduk_inspect_value()for description of current fields.

The properties of the result object are not under versioning guarantees and maychange in an incompatible fashion even in minor versions (but not patch versions).

act()§

Get information about a call stack entry. Takes a single number argumentindicating depth in the call stack: -1 is the top (innermost) entry, -2 is theone below that etc. Returns an object describing the call stack entry, orundefined if the entry doesn't exist. Seeduk_inspect_callstack_entry()for description of current fields.

The properties of the result object are not under versioning guarantees and maychange in an incompatible fashion even in minor versions (but not patch versions).

Example:

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

The example, when executed with the command line tool, currently printssomething 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 arelineNumber andfunctionwhich provides e.g. the function name.

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

functiongetCurrentLine(){'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());

gc()§

Trigger a forced mark-and-sweep collection. The call takes an optionalinteger flags field, seeduktape.h for constants.

compact()§

Minimize the memory allocated for a target object. Same as the C API callduk_compact() but accessible from ECMAScript code. If called witha non-object argument, this call is a no-op. The argument value is returned bythe 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 newproperties, 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 areinitially non-existent.

SeeError handlers (errCreate and errThrow) fordetails.

Duktape.Pointer (constructor)§

PropertyDescription
prototypePrototype for Pointer objects.

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

Duktape.Pointer.prototype§

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

toString() andvalueOf accept both plain pointers andPointer objects as theirthis binding. This allows code such as:

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

Duktape.Thread (constructor)§

PropertyDescription
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 anordinary function and as a constructor. The behavior is the same in bothcases:

Duktape.Thread.prototype§

PropertyDescription
No properties at the moment.

CBOR§

CBOR (Concise Binary Object Representation) is a compact binary encodingfor arbitrary structured values. It is faster than JSON and can encode someECMAScript values more accurately. It is suitable as an alternative to JSONfor state serialization, IPC, etc. See:

TheCBOR object provides encode/decode functions to convertarbitrary ECMAScript values to CBOR and vice versa. There's no officialCBOR API yet, so the API is based, for now, oncbor-js.There's also aC API for CBOR.

The binding is currently experimental, and details are likely to change overtime. For example, custom CBOR tags are being registered to serialize ECMAScriptvalues more accurately.

Example:

var enc= CBOR.encode(['foo','bar',{ quux:true}]);print(Duktape.enc('hex', enc));// = 8363666f6f63626172a16471757578f5var dec= CBOR.decode(enc);print(Duktape.enc('jx', dec));// = ["foo","bar",{quux:true}]

TextEncoder§

TextEncoder() is part of theWHATWG Encoding APIand provides a clean way of encoding a string into a buffer (Uint8Array)using UTF-8 encoding. Surrogate pairs are combined during the process.For example:

var str='\u{1f4a9}';// non-BMP codepointprint(str.length);// length is 2, represented as a surrogate pairvar u8=newTextEncoder().encode(str);print(u8.length);// length is 4, a single UTF-8 codepointprint(Duktape.enc('jx', u8));// |f09f92a9|, UTF-8 bytes F0 9F 92 A9

TextDecoder§

TextDecoder() is part of theWHATWG Encoding APIand provides a clean way of decoding a buffer into a string using UTF-8encoding. Non-BMP codepoints are represented as surrogate pairs in theresulting string. For example:

var u8=newUint8Array([0xf0,0x9f,0x92,0xa9]);// a single non-BMP codepointvar str=newTextDecoder().decode(u8);print(str.length);// length is 2, represented as a surrogate pairprint(str.charCodeAt(0));// 55357, high surrogateprint(str.charCodeAt(1));// 56489, low surrogate

performance§

performance.now() provides a monotonic time in milliseconds (includingfractions if available) from an unspecified origin. The return value isfrom DUK_USE_GET_MONOTONIC_TIME() with a fallback to DUK_USE_DATE_GET_NOW().If an actual monotonic time provider is available, the return value isguaranteed to advance in real time without "time jumps" caused by date/timeadjustments. This is useful for performance measurement, scheduling eventsrelative to current time instead of wall clock time, rate limiting, etc.Example:

functiontestFunction(){for(var i=0; i<1e6; i++){}}var t1= performance.now();testFunction();var t2= performance.now();print('test took:',(t2- t1),'milliseconds');

performance.timeOrigin is currently (Duktape 2.2.0) absent on purpose,until its semantics for Duktape are decided.

Navigation Timingbindings like performance.timing are not currently supported.


Post-ES5 features§

Duktape implements features from ES2015 (ES6), ES2016 (ES7), and laterspecification drafts. See Wiki articlePost-ES5 featuresfor current status.

Duktape status is also updated for new releases inkangax/compat-table.


Custom behavior§

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

Duktape built-in and custom types§

TheDuktape built-in is (of course) non-standard and providesaccess to Duktape specific features. Also the buffer, pointer, and lightfunctypes are custom.

Hidden Symbols§

Objects may have properties withhidden Symbol keys.These are similar to ES2015 Symbols but won't be enumerated or returned from evenObject.getOwnPropertySymbols(). Ordinary ECMAScript code cannotrefer to such properties because the keys intentionally use an invalid (extended)UTF-8 representation.

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

Theconst keyword is supported with minimal non-standardsemantics (officially defined in ECMAScript 6). SeeConst variables for more detail.

Additional Error and Function object properties§

SeeError objects andFunction objects.

Non-strict function instances don't have acaller property in theE5/E5.1 specification. Some real world code expects to have this property, so itcan be enabled with the config optionDUK_USE_NONSTD_FUNC_CALLER_PROPERTY.

Function statements§

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

functiontest(){// point Atry{thrownewError('test');}catch(e){// This is a SyntaxError in E5.1functionfunc(){print(typeof e);}// point B}// point C}

These declarations are also referred to as "function statements", and appearquite often in real world code (including the test262 test suite), so they areallowed by Duktape. Unfortunately there are several semantics used by differentJavascript engines (ES2015unfortunately doesn't specify semantics for function statements either).Duktape follows the V8 behavior for function statements:

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

functiontest(){functionfunc(){print(typeof e);}try{thrownewError('test');}catch(e){}}

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

RegExp leniency§

Most ECMAScript engines support more syntax than guaranteed by theECMAScriptE5.1 specification (Section 15.10.1 Patterns). As a result there's quitea lot of code that won't work with strict ECMAScript E5.1 regexp syntax. Muchof the additional syntax expected of web browser engines is documented inES2015 Annex B.1.4 Regular Expression Patterns.However, note that features inAnnex B Additional ECMAScript Features for Web Browsers arenot recommended for new code:"These features are not considered part of the core ECMAScript language.Programmers should not use or assume the existence of these features andbehaviours when writing new ECMAScript code. ECMAScript implementationsare discouraged from implementing these features unless the implementationis part of a web browser or is required to run the same legacy ECMAScriptcode that web browsers encounter."

Duktape also allows some ES2015 Annex B syntax to better support existingcode. You can turn this non-standard behavior off using config options ifyou prefer. Some examples of additional syntax supported:

  /{(\d+)}/    // unescaped left curly, digits, unescaped right curly; ES2015 Annex B  /\{(\d+)\}/  // same, ES5 compliant  /]/          // unescaped right bracket; ES2015 Annex B  /\]/         // same, ES5 compliant  /\$/         // literal dollar using escape; ES2015 Annex B  /\u0024/     // same, ES5 compliant

Setter/getter key argument§

ECMAScript standard behavior is that setters and getters are not giventhe name of the property being accessed. This prevents reusing a singlesetter or a getter for multiple properties; separate functions are neededfor each property which is sometimes inconvenient and wastes memory.

Duktape provides the property key name as a non-standard additionalargument to setter and getter functions. Seetest-dev-nonstd-setget-key-argument.jsandProperty virtualizationfor more discussion. The strict standards compliant behavior can be enabledby disabling the config optionsDUK_USE_NONSTD_GETTER_KEY_ARGUMENTandDUK_USE_NONSTD_SETTER_KEY_ARGUMENT.

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

SeeObject.setPrototypeOf and Object.prototype.__proto__.

Proxy object (ES2015)§

SeeProxy object (subset).

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

JSON.stringify() standard behavior is to output U+2028 andU+2029 without escaping. This leads to counterintuitive behavior when theoutput is used in a web page or parsed witheval(): the U+2028and U+2029 characters are considered line terminators which leads to a syntaxerror (unterminated string). Duktape escapes U+2028 and U+2029 by defaultto avoid this issue; you can turn on the compliant behavior by disabling theconfig optionDUK_USE_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 bettersupport non-BMP strings. You can force the compliant behavior by disablingthe config optipnDUK_USE_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, Duktapedoesn't check Array.prototype for conflicting properties (these are very rare inpractical code), which makes common array writes faster. The behavior isnon-compliant, but there's no outward difference unless Array.prototype hasproperties with numeric keys. You can turn on the compliant behavior bydisaling the config optionsDUK_USE_NONSTD_ARRAY_WRITE andDUK_USE_ARRAY_PROP_FASTPATH. See the following for moredetails on the fast path behavior:test-misc-array-fast-write.js.

TypedArray binding§

Duktape provides the ES2015 TypedArray binding, with some details yetto be fixed, e.g. small differences in argument coercion e.g. for offsetand length values.

The plain buffer custom type behaves mostly like an Uint8Array objectfor ECMAScript code, but has a separate type in the Duktape C API.

Node.js Buffer binding§

Duktape provides a Node.js-likeBuffer binding. There aresome differences between the Node.js behavior and Duktape behavior. Thesedifferences include:

Shebang comment support§

duk_compile() flagDUK_COMPILE_SHEBANG allows shebangcomment parsing:#! on the first column of the first line causes theline to be treated as a comment. For example:

#!/usr/bin/dukprint('Hello world!');

The feature can be disabled by undefiningDUK_USE_SHEBANG_COMMENTS.


Custom JSON formats§

ECMAScript JSON shortcomings§

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

These limitations are part of the ECMAScript specification whichexplicitly prohibits more lenient behavior. Duktape provides two moreprogrammer friendly custom JSON format variants:JX andJC,described below.

Custom JX format§

JX encodes all values in a very readable manner and parses backalmost all values in a faithful manner (function values being the mostimportant exception). Output is pure printable ASCII, codepoints aboveU+FFFF are encoded with a custom escape format, and quotes around objectkeys are omitted in most cases. JX is not JSON compatible but a veryreadable 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.foo, dec.bar, dec.quux);// prints out: 123 undefined NaN

Custom JC format§

JC encodes all values into standard JSON. Values not supported bystandard JSON are encoded as objects with a marker key beginning with anunderscore (e.g.{"_ptr":"0xdeadbeef"}). Such values parseback as ordinary objects. However, you can revive them manually more orless reliably. Output is pure printable ASCII; codepoints above U+FFFFare 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.foo, dec.bar, dec.quux);// prints out: 123 [object Object] [object Object]

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

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

All standard ECMAScript strings are valid CESU-8 data internally, sobehavior for codepoints above U+FFFF never poses compliance issues. However,Duktape strings may containextended UTF-8codepoints 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 resultingstring may look a bit odd, but this behavior is preferable to throwing anerror.

JSON format examples§

The table below summarizes how different values encode in eachencoding:

ValueStandard JSONJXJCNotes
undefinedn/aundefined{"_undef":true}Standard JSON: encoded asnull inside arrays, otherwise omitted
nullnullnullnullstandard JSON
truetruetruetruestandard JSON
falsefalsefalsefalsestandard JSON
123.4123.4123.4123.4standard JSON
+0000standard JSON
-00-0-0Standard JSON allows-0 but serializes negative zero as0 (losing the sign unnecessarily)
NaNnullNaN{"_nan":true}Standard JSON: always encoded asnull
InfinitynullInfinity{"_inf":true}Standard JSON: always encoded asnull
-Infinitynull-Infinity{"_ninf":true}Standard JSON: always encoded asnull
"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"]
buffern/a|deadbeef|{"_buf":"deadbeef"}
pointern/a(0xdeadbeef)
(DEADBEEF)
{"_ptr":"0xdeadbeef"}
{"_ptr":"DEADBEEF"}
Representation inside parentheses or quotes is platform specific
NULL pointern/a(null){"_ptr":"null"}
functionn/a{_func:true}{"_func":true}Standard JSON: encoded asnull inside arrays, otherwise omitted
lightfuncn/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 adirective prologueto allow version or implementation specific features be activated.The standard only provides one such directive,"use strict", whileasm.js uses"use asm". Duktape custom directives are discussedin this section.

use duk notail§

Theuse duk notail directive indicates that the function shouldnever be tail called. Tail calls affect the call stack so they are visible instack traces (usually harmless) and affect functions which inspect the call stackusing e.g.Duktape.act(). This directive may be useful in specialcases to ensure call stack has a known shape. Example:

functionnoTailCall(){'use duk notail';// ...}

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


Buffer objects§

Overview of buffer types§

Duktape provides the following buffer and buffer-related types:

TypeStandardDuktape versionDescription
Plain bufferNo
Duktape specific
1.0Plain, primitive buffer value (not an object), similar to how a plain string relates to a String object. Behaves like an Uint8Array instance where possible, object coerces to an actualUint8Array.
ArrayBuffer objectYes
ES2015
1.3Standard object type for representing a byte array. References an underlying plain buffer.
DataView, typed array objectsYes
ES2015
1.3View objects to access an underlying ArrayBuffer. References an underlying plain buffer.
Node.js Buffer objectNo
Node.js-like
1.3Object withNode.js Buffer API, inherits from Uint8Array.prototype. References an underlying plain buffer.

Seebuffers.rstfor a detailed discussion, including adetailed table of buffer types and their properties.

Plain buffers§

Plain buffers are a non-standard memory efficient way of representingbuffer data. Plain buffers mimic Uint8Array objects so that they inheritfromUint8Array.prototype, are accepted as typed arrayconstructor arguments, and so on. While plain buffers don't have a propertytable and can't hold properties of their own, they have the followingvirtual or inherited properties (example values are for a 24-byte buffer):

Property nameExample valueDescription
[index]0-255Index properties in the range [0, length-1]. Reads and writes behave like forUint8Array.
length24Length of buffer in bytes. Length is not writable, so you can't resize a buffer by assigning its length.
byteOffset0Always 0, present to match typed arrays.
byteLength24Same as.length.
BYTES_PER_ELEMENT1Always 1, present to match typed arrays.
buffer Getter property which returns a new ArrayBuffer instance backing to the plain buffer without making a copy. Because plain buffers don't have a property table, a new ArrayBuffer is created on every property read. Absent if buffer object support is disabled in Duktape configuration.

Buffer objects like ArrayBuffer and Node.js Buffer are implemented on topof plain buffer values and provide additional functionality like view/slicesupport, typed accessors, and methods to manipulate data in different endianness.However, they have more overhead than plain buffers.

For more details, see:

Working with buffers§

Buffer values work in both C and ECMAScript code:

SeeHow to work with buffersfor examples.

In special cases the plain buffer backing a buffer object may not be largeenough to cover the apparent size of the buffer object; the buffer objectis then "uncovered" or "unbacked". Duktape guarantees memory safe behaviorfor such buffers, but other than that behavior varies between calls. Forexample, a call may ignore the situation silently returning undefined, NaN,or zero, or it may throw a TypeError.The behavior for unbacked buffers isnot part of versioning guarantees and may change between minor versions.

Current limitations§


Error objects§

Property summary§

ECMAScript Error objects have very few standard properties, so manyECMAScript implementations have added quite a few custom properties.Duktape uses standard Error properties but also borrows the most usefulproperties used by other implementations. The number of "own" propertiesof 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 usefulfileName andlineNumber issomewhat 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 thecaller does not give a format string for amessage, themessageproperty is set to a numeric error code given in the API call. The type ofmessage will be number in this case; normally error messages arestrings. In minimized Duktape builds all errors generated internally byDuktape use numeric error codes only.

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

Traceback§

Thestack property is an accessor (setter/getter) propertywhich provides a printable traceback related to an error. The tracebackreflects 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(\x82Tracedata), in an internal and version-dependent formatdescribederror-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 betweenversions (for example,tracebacks were improved for the 1.5.0 release).As an example of the current traceback format, the program:

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

would print something like:

URIError: invalid input    at [anon] (duk_bi_global.c:343) internal    at decodeURIComponent () native strict preventsyield    at global (test.js:3) preventsyield

In builds where tracebacks are disabled, thestack accessorwill return the same value as callingtoString() on the errorwould. This means you can always printe.stack and get a usefuloutput.

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

An attempt to write tostack is captured by an inheritedsetter which will create an own property as if a normal assignment was done.This behavior differs from V8 wherestack is an own propertyof the Error instance.

Error handlers (errCreate and errThrow)§

IfDuktape.errCreate has been set, it is called right afterDuktape has added traceback information to an object, and can process theerror further or even replace the error value entirely. The error handleronly gets called withError instances, and its return value isused as the final error value. If the error handler throws an error, thaterror replaces the original error. The error handler is usually called only onceper error. However, in corner cases related to constructors, the error handlercan be called multiple times for a single error value.

An error handler should avoid overwriting any properties alreadypresent in an object, as that would be quite confusing for other code.In general, an error handler should always avoid throwing an error, as thaterror replaces the original error and would also be confusing. As a specificexample, an error handler must not try to add a new property to a non-extensibleobject, as that would cause aTypeError.

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

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

To remove the handler, delete the property (setting it to e.g.nulldoes not work and causes aTypeError when Duktape attempts tocall thenull value):

// Remove error handler for error creationdelete Duktape.errCreate;

Similarly, ifDuktape.errThrow has been set, it is calledright before an error is thrown, and can process or replace the error value.Because ECMAScript allows any value type to be thrown, the error handlermay get called with arbitrary input values (not justErrorinstances). It may also be called more than once for the same value becausean error can be re-thrown multiple times.

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

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

Again, to remove the handler, delete the property:

// Remove error handler for error throwingdelete Duktape.errThrow;

Current limitations§


Function objects§

ECMAScript functions§

Duktape Function objects add a few properties to standard ECMAScriptproperties. The table below summarizes properties assigned to newlycreated function instances (properties can of course be added or removedafterwards):

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. (IfDUK_USE_NONSTD_FUNC_CALLER_PROPERTY is given, non-strict functions will get a non-standardcaller property.)
argumentsstandardAccessor which throws an error. Present for strict functions and bound functions. Not copied to bound functions.
nameDuktapeFunction name, see below. Bound function name is based on this property, with a"bound " prefix (standard ES2015 behavior).
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).

Thename property is assigned to all functions and is alsothe name used in tracebacks. It is assigned as follows:

functionfuncDecl(){/* Function declaration: 'name' is declaration name, here 'funcDecl'. */}var foo=functionnamedFunc(){/* 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 usercreated Functions.

Duktape/C functions§

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

Property nameCompatibilityDescription
lengthstandardFunction argument count, matches argument toduk_push_c_function(), 0 for varargs.Non-writable and non-configurable.

Note in particular that the standardprototype,caller,andarguments properties are missing by default. This is not strictlycompliant but is important to reduce function footprint. User code can of courseassign these properties but is not required to do so.

There's also no (non-standard)name property. Setting it manuallyis useful because it affects how a function appears in tracebacks.

Lightweight Duktape/C functions§

Lightweight Duktape/C functions (lightfuncs) are a very memory efficient wayof representing a native function in the ECMAScript environment. Lightfuncsdon't have a property table so they can't hold properties. However, they inheritfromFunction.prototype and have the following virtual properties(which are non-configurable and non-writable):

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

Thename property is an automatically generated virtualfunction name. <PTR> is a platform dependent dump of the Duktape/Cfunction pointer, and <FLAGS> is a raw hex dump of the 16-bit internalcontrol fields (the format is Duktape internal). You shouldn't rely on aspecific format. For example:

duk> print(myLightFunc.name);light_0805b94c_0511

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

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

For more details, see:


Date and time§

Date and time handling is often a portability issue when adapting Duktapeto more exotic environments. Theduk_config.h configurationheader selects platform specific providers needed to implement theDate built-in. Duktape has built-in providers for mainstreamplatforms (Linux, Windows, OS X) which should usually work without changes.You can also write anexternal Date providerfor more exotic environments. An external Date provider can also be used e.g.when a time offset needs to be applied to the platform time, or when usingtime virtualization.

ECMAScript code interacts with date/time through the standardDate built-in which is, by specification, limited to millisecondresolution. There are currently no Duktape specific ECMAScript date/time APIs.(A custom API may be added later to deal with sub-millisecond resolution.)

C code can of course use platform date/time APIs directly, but the DuktapeC API also provides date/time API calls. These calls see the same time valuesas ECMAScript code which may matter when e.g. time virtualization is used.Using these calls makes your code platform neutral and thus more portable.The Duktape C API allows sub-millisecond resolution for time values. SeeHow to work with time valuesfor more details.


Random numbers§

Random numbers are used internally forMath.random().They are also used currently for random pivot selection in theArray.prototype.sort() implementation.

The default internal random number generator isxoroshiro128+ withSplitMix64 seed mixing.Shamir's three-op PRNGis used on low memory targets and when the compiler doesn't have 64-bittypes. The generators are not suitable for serious statistics algorithmsdue to e.g. limited quality of the seed material, and are not at allsuitable for cryptography.

You can replace the internal random number generator using theDUK_USE_GET_RANDOM_DOUBLE config option.


Debugger§

Duktape has built-in debugger support as an option you can enable duringcompilation. Debugger support adds about 15-20kB of code footprint (dependingon what debugger features are enabled) and has very minimal memory footprint.Debugger features include:

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 custommanagement protocol, etc. Although there is no "standard" transport, a TCPconnection is a useful default. The Duktape distributable includes all thepieces you need to get started with debugging using a TCP transport:

The Node.js based debugger web UI (duk_debug.js) can connectto the Duktape command line, but can also talk directly with any other targetimplementing a TCP transport. You can also customize it to use a differenttransport 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 targetusing the binary debug protocol, or use the JSON proxy provided byduk_debug.js(Node.js) orduk_debug_proxy.js(DukLuv).

Debug targets and debug clients are intended to be mixed and matched:apart from the transport (which is usually either TCP or easy to adapt) thedebug protocol is the same. Core functionality will be the same regardlessof the debug client or the debug target, but some optional features may bemissing. Debug clients and debug targets may also implement applicationspecific commands (AppRequest) and notifications (AppNotify) for richerintegration which can be used when both the client and the target supportthem (they're easy and safe to ignore if not supported). Custom commandsand notifications allow e.g. downloading of source files directly from thetarget, deep inspection of the state of a custom memory allocator, rebootingthe target on command, etc.

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


Modules§

CommonJS module loading frameworks§

There's no built-in module loading framework because it's difficult for asingle framework to match a wide variety of different module loading use cases.The Duktape distributable includes several optional module loader frameworks,for example:

module-duktapeA Duktape 1.x compatible loader based onCommonJS modules version 1.1.1,with additional support formodule.exports and a few Duktape specificmodule object properties. The internals are documented inmodules.rst,seeHow to use modules for examples.This loader was a built-in in Duktape 1.x but was moved into an optional extra in Duktape 2.x.
module-nodeANode.js modules compatibleloader. SeeHow to use Node.js-like modulesfor examples.

You can also implement your own module loader from scratch: the aboveloaders are implemented using the Duktape public API with no special accessto internals.

The module loaders provide arequire() function which allowsmodules to be loaded as follows:

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

The loaders abstract actual module resolution/loading to user-provided hook(s)to allow the loader to be embedded in wide variety of environments. For example:

ES2015 modules§

There's currently no support for ES2015 import/export and ES2015 modules.

C module convention§

There's a recommended (but not mandatory) C module convention which allowsC modules to be loaded and initialized from DLLs:c-module-convention.rst.


Logging§

The Duktape distributable includes alogging frameworkwith a small footprint, reasonable performance, and redirectable output. The frameworkwas a built-in in Duktape 1.x but was moved into an optional extra in Duktape 2.x.

Basic usage example:

var val1='foo';var val2=123;var val3=newDate(123456789e3);var logger=new Duktape.Logger();// or new Duktape.Logger('logger name')logger.info('three values:', val1, val2, val3);

The example would print something like the following tostderr:

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

See the Wiki articleHow to use logging andlogging.rstfor more details.


Finalization§

Overview§

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

SeeHow to use finalizationfor examples.

Getting and setting the current finalizer§

An object which has an internal_Finalizer property in itsprototype chain (or in the object itself) is subject to finalization beforebeing freed. The internal property should not be accessed directly, but canbe 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 thefinalizer are also silently ignored.

Finalizer execution guarantees§

The main finalizer guarantees are:

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

When the Duktape heap is being destroyed there are a few limitations forfinalizer 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 yieldsor finishes (either successfully or through an uncaught error), after whichcoroutine A continues execution with the yield result.

Coroutines are created withnew Duktape.Thread(), which gets as itssole argument the initial function where the new coroutine begins execution onits first resume. The resume argument becomes the initial function's first (andonly) argument value.

A coroutine is resumed usingDuktape.Thread.resume() which takes thefollowing arguments: the coroutine to resume, the resume value, and (optionally)a flag indicating whether the resume value is an ordinary value or an error tobe injected into the target coroutine. Injecting an error means that the resumevalue 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 willseemingly throw an error.

A coroutine yields its current execution usingDuktape.Thread.yield()which takes as its arguments: the value to yield, and (optionally) a flag indicatingwhether the yield value is an ordinary value or an error to be thrown in thecontext of the resuming coroutine. In other words, an error value causes theresume operation to seemingly throw an error instead of returning an ordinaryvalue.

If a coroutine exists successfully, i.e. the initial function finishes byreturning 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 similarlyto a yield with the error: the resume operation will rethrow that error inthe resuming coroutine's context. In either case the coroutine which hasfinished 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 consistsof plain ECMAScript-to-ECMAScript calls. The following prevent a yield ifthey are present anywhere in the yielding coroutine's call stack:

Since Duktape 2.2 constructor calls (new Func()),Function.prototype.call(),Function.prototype.apply(),Reflect.apply(), andReflect.construct() no longer prevent a yield.

SeeHow to use coroutinesfor examples.


Virtual properties§

Duktape provides two mechanisms for interacting with property accessesprogrammatically:

SeeHow to use virtual propertiesfor examples.


Symbols§

Duktape supports ES2015 Symbols and also provides a Duktape specifichidden Symbol variant similar to internal strings in Duktape 1.x.Hidden Symbols differ from ES2015 Symbols in that they're hidden fromordinary ECMAScript code: they can't be created from ECMAScript code,won't be enumerated or JSON-serialized, and won't be returned fromObject.getOwnPropertyNames() or evenObject.getOwnPropertySymbols(). Properties with hidden Symbolkeys can only be accessed by a direct property read/write when holding areference to the hidden Symbol.

Symbols of all kinds are represented internally using invalid UTF-8 bytesequences, seesymbols.rstfor the current formats in use. Application hidden Symbols begin with a 0xFFbyte prefix and are followed by an arbitrary, application selected string. WhenC code pushes a string using e.g.duk_push_string() and the bytesequence matches an internal Symbol format, the string value is automaticallyinterpreted as a Symbol.

Duktape also uses hidden Symbols for various implementation specific purposes,such as storing an object's finalizer reference. As of Duktape 2.2 a differentbyte prefix is used for Duktape's hidden Symbols, so the 0xFF prefix is nowreserved entirely for application use. Application code should never try toaccess Duktape's hidden Symbol keyed properties: the set of such properties canchange arbitrarily between versions.

Note that the internal UTF-8 byte sequences cannot be created from ECMAScriptcode as a valid ECMAScript string. For example, a hidden Symbol might berepresented using\xFFxyz, i.e. the byte sequenceff 78 79 7a, while the ECMAScript string"\u00ffxyz"would be represented as the CESU-8 bytesc3 bf 78 79 7a in memory.

Creating a Symbol is straightforward from C code:

/* Create a hidden Symbol which can then be used to read/write properties. * The Symbol can be passed on to ECMAScript code like any other string or * Symbol. */duk_push_string(ctx,DUK_HIDDEN_SYMBOL("mySymbol"));

Before Duktape 2.2DUK_HIDDEN_SYMBOL() and other symbolliteral macros were not available and the internal representation would beused directly:

/* Terminating a string literal after a hex escape is safest to avoid some * ambiguous cases like "\xffab".  For more discussion, see: *https://github.com/svaarala/duktape/blob/master/misc/c_hex_esc.c */duk_push_string(ctx,"\xff""mySymbol");

Hidden Symbols cannot be created from ECMAScript code using the defaultbuilt-ins alone. Standard ES2015 Symbols can be created using theSymbol built-in, e.g. asSymbol.for('foo').When sandboxing, ensure that application C bindings don't accidentally providea mechanism to create hidden Symbols by e.g. converting an input buffer as-isto a string without applying an encoding.

There's currently no special access control for properties with hiddenSymbol keys: if user code has access to the Symbol, it can read/write theproperty value. This will most likely change in future major versions sothat ECMAScript code cannot access a property with a hidden Symbol key,even when holding a reference to the hidden Symbol value.


Bytecode dump/load§

The API callsduk_dump_function() andduk_load_function() allowcalling C code to (1) serialize an ECMAScript function into a portablebytecode and then (2) load the bytecode to reconstitute the function.

The bytecode format is Duktape version specific and it's unsafe toload bytecode from a different Duktape minor version (patch versiondoesn't affect bytecode compatibility). Duktape configuration optionsmay also affect bytecode compatibility, so dump/load source and targetmust be compiled with the same options. The bytecode format is platformneutral so that you can compile bytecode on one platform and then load iton another, which is useful for cross-platform builds. Duktape does notvalidate loaded bytecode so calling code must ensure bytecode is intactand valid for the running Duktape version.

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

Seebytecode.rstfor more details.


Threading§

Duktape supports a limited form of multithreading:

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

Seethreading.rstandHow to use multiple native threadsfor a detailed discussion of threading limitations and best practices.


Sandboxing§

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

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

Sandboxing support in Duktape 2.x is still a work in progress.

Performance§

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

SeeHow to optimize performancefor discussion of Duktape performance characteristics and hints to optimize codefor performance.

Profile guided optimization (PGO) is strongly recommended if performance isa major concern. For example, GCC -O2 with PGO can be around 20% faster thanGCC -O2 without PGO.


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 80kB for thebuilt-in ECMAScript objects. With specificlow memory options initial memoryusage is about 27kB. This can be further reduced to about 3kB when movingbuilt-in objects and strings to ROM (read-only data section). It's alsopossible to move custom native bindings fully into ROM.

After heap creation additional memory is then allocated as needed forexecuting application scripts. Reference counting ensures there is verylittle unused allocated memory, the only major exception being objects whichparticipate 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 byteswhich are needed for strings, buffers, objects, object property tables, etc.Second, there are much fewer larger allocations needed for e.g. ECMAScriptfunction bytecode, large strings and buffers, value stacks, the global stringtable, and the Duktape heap object.

For most systems memory usage or the memory allocation pattern is not anissue. On low memory environments, e.g. less than 1MB of system RAM, you maywant to use a custom allocator to optimize memory usage. A pool-basedallocator deals well with the small allocation churn without fragmentationissues. The downside is that you need to tune the memory pool sizes to matchthe concrete allocation patterns. You may want to use a pool allocator or ahybrid allocated if the platform allocation primitives perform poorly witha lot of small allocations.

Seelow memory options andlow-memory.rstfor more discussion on what low memory features exists and how to tune thememory pools for low memory systems.

With default options Duktape uses a 32-bit refcount field which maytechnically wrap on 64-bit systems with very large memory sizes. Inpractice this is unlikely to happen and requires the Duktape heap to belarger than 64GB. DisableDUK_USE_REFCOUNT32 to usesize_t for refcount fields.


Compiling§

Overview§

There are two basic steps to compiling Duktape:

While Duktape is usually compiled together with your application, you canalso build it into a static or shared library. Duktape can also be installedas a system-wide library, seesystem-install.rst.

TheDUK_OPT_xxx feature options are no longer supported inDuktape 2.x. All configuration information is embedded induk_config.hand/or autogenerated sources and headers.

Configuring§

Preconfigured sources and default configuration§

The Duktape distributable contains preconfigured sources and headers witha few variants:

These preconfigured sources provide automatic platform, compiler, andarchitecture detection and use the Duktape default configuration:

The preconfigured sources cannot be used to build Duktape into a Windows DLL.Runconfigure.py with the--dll option to do that.

Running configure.py to customize Duktape configuration§

Theconfigure.py utility prepares Duktape source and headerfiles for a specific configuration described using command line options.For example, to prepare Duktape sources for a DLL build with fastintsupport enabled and ECMAScript 6Proxy object support disabled:

# Default output format is single source file (--separate-sources for separate# sources) and no #line directives (--line-directives to enable them).$ python2 tools/configure.py \      --output-directory /tmp/output \      --dll \      -DDUK_USE_FASTINT \      -UDUK_USE_ES6_PROXY# The output directory /tmp/output contains the header and source files to# be included in your build.$ ls /tmp/outputduk_config.h  duk_source_meta.json  duktape.c  duktape.h

Configuration options given toconfigure.py affect severaldifferent aspects of the prepared header and source files, for example:

Theconfigure.py utility requires Python 2.x support. If yourbuild environment doesn't support Python 2.x, you can runconfigure.pyon a different platform and compile the resulting files in your build environment.

Even if the default options are OK, it's recommended that you runconfigure.py as part of your build instead of using thepreconfigured sources. Custom options may be necessary on e.g. low memoryplatforms. SeeConfiguring Duktape for buildfor more practical details.

Commonly needed configuration options§

Some commonly needed configuration options are:

Memory management alternatives§

There are two supported memory management alternatives:

Reference counting relies on mark-and-sweep to handle reference cycles.For example, every ECMAScript function instance is required to be in areference 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 bebroken from user code and only mark-and-sweep can collect such functions.

Compiling§

General guidelines§

Duktape doesn't have an official Makefile or a build script: given thenumber of different portability targets, maintaining an official buildscript would be difficult. Instead, you should add Duktape to your existingbuild 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 betweenplatforms and toolchains. For example, you can:

All Duktape API functions are potentially macros, and the implementationof a certain API primitive may change between a macro and an actual functioneven between compatible releases. Some Duktape configuration options alsoaffect binary compatibility. To ensure binary compatibility:

Recommended compiler options§

Recommended compiler options for GCC/clang, use similar options foryour compiler:

Compilation warnings§

Duktape usually compiles without warnings when using a mainstream compiler(e.g. GCC, Clang, MSVC, or MinGW) in C99 mode with warnings enabled (e.g.-Wall in gcc/clang), and using default Duktape configurationoptions. There may be some warnings when using a non-mainstream compiler,very strict warning levels (like-Wextra in gcc/clang or/W4 in MSVC), or non-default Duktape configuration options.Eliminating compilation warnings for all compilers and all configurationoption combinations is very difficult and is thus explicitly not a projectgoal. You're still encouraged to report warnings so that they can be fixedif possible.

Using a C++ compiler§

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

Theduktape.h header contains the necessary glue to make allof these combinations work. Specifically, all symbols needed by Duktapepublic API are inside aextern "C" { ... } wrapper when compiledwith a C++ compiler. This ensures that such symbols are defined and usedwithout C++ name mangling. Specifically:

If you mix C and C++ compilation, you should do the final linking with theC++ 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 -lmduktape.o:(.eh_frame+0x1ab): undefined reference to `__gxx_personality_v0'collect2: error: ld returned 1 exit status

One fix is to useg++ 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

Becauseduk_config.h selects C/C++ data types needed byDuktape and also does other feature detection, mixing C and C++ compilerscould theoretically cause the C and C++ compilers to end up with differentactive features or data types. If that were to happen, Duktape and theapplication would be binary incompatible which would lead to very difficultto diagnose issues. 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 asRAII)won't work in Duktape/C functions because Duktape useslongjmp() for internal long control transfers, bypassingC++ stack unwind mechanisms. You can useDUK_USE_CPP_EXCEPTIONSto cause Duktape to use C++ exceptions for internal long control transfers,which allows scope-based resource management to work in Duktape/C functions.With MSVC be careful to avoid the/EHsc exception model (use e.g./EHs instead) because the "c" option causes MSVC to assume externC functions cannot throw C++ exceptions. When using MSVC and CMake you can usee.g.:

if (MSVC)    string( REPLACE "/EHsc" "/EHs" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}" )endif()

Portability§

Duktape is widely portable to platforms with at least a C89 compiler(with a few exceptions like requiring snprintf() and vsnprintf()).Because Duktape has very limited dependency on platform functions, it'spossible to Duktape to very exotic platforms. One major platform dependencyis the Date built-in which may need a custom provider for an exotic platform.

SeePortability forplatform 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.1compatible. However, ES5 feature semantics are updated to ES2015 (orlater) where incompatible changes have been made in newer specificationversions. Current level of compatibility should be quite high.

ECMAScript 2015 (E6)§

Duktape implements some features from ECMAScript 2015 (E6), but generally thereis no compatibility with E6 yet.

ECMAScript 2016 (E7)§

Duktape implements some features from ECMAScript 2016 (E7), but generally thereis no compatibility with E7 yet.

ECMAScript E3§

There is no effort to maintainECMAScript E3compatibility, other than required by the E5/E5.1 specification.

CoffeeScript§

CoffeeScript compiles toJavaScript which should be compatible with Duktape. There are no knowncompatibility issues.

Some CoffeeScript examples are included in the distributable. Simplyrunmake inexamples/coffee/. For instance,hello.coffee:

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

compiles to:

(function(){print('Hello world!');print('version: '+ Duktape.version);}).call(this);

Coco§

Like CoffeeScript,Coco compiles toJavascript. There are no known issues.

LiveScript§

Like CoffeeScript,LiveScript compiles toJavascript. There are no known issues.

TypeScript§

TypeScriptcompiles to Javascript. There are no known issues with compiling TypeScriptusing the Microsoft TypeScript compiler (in the ES5/CommonJS mode) andrunning the resulting Javascript using Duktape. It's also possible torun the TypeScript compiler with Duktape.

Underscore.js§

Underscore.js provides a lot ofuseful utilities to plain ECMAScript. Duktape passes almost all of Underscore'stest cases, seeunderscore-status.rstfor current compatibility status.

Test262§

test262 is a test suite fortesting E5.1 compatibility, although it includes also tests outside of standard E5.1.Duktape passes almost all of test262 cases, seetest262-status.rstfor current compatibility status.

Asm.js§

asm.js is a"strict subset of JavaScript that can be used as a low-level, efficienttarget language for compilers". As a subset of JavaScript, functions usingasm.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 slowerthan normal ECMAScript code. The"use asm" directive specifiedby asm.js is ignored by Duktape.

Emscripten§

Emscripten compilesC/C++ into Javascript. Duktape is currently (as of Duktape 1.5.0) Emscriptencompatible and supports ES2015 TypedArray which allows Emscripten fastcompto be used.

Large programs may fail due to Duktape compiler running out of virtualregisters, and performance is somewhat limited as Duktape is an interpretedengine. Seeemscripten-status.rstfor current compatibility status.

Because Duktape itself compiles with Emscripten, it is possible to runDuktape inside a web page for instance, seeDukweb REPL.

Lua.js§

lua.js translates Luacode to Javascript. There are no known issues in running the generatedJavascript, except that Duktape doesn't provideconsole.logwhich lua.js expects. This is easy to remedy, e.g. by prepending thefollowing:

console={ log:function(){print(Array.prototype.join.call(arguments,' '));}};

JS-Interpreter§

JS-Interpreterinterprets Javascript in Javascript. JS-Interpreter works with Duktape,except that Duktape doesn't providewindow which JS-Interpreterexpects. This can be fixed by prepending:

window={};

Versioning§

Semantic versioning§

Duktape followsSemantic Versioning forofficial releases:

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

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

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

Development builds made from Duktape repo are not official releases anddon't follow strict semantic versioning.

Experimental features§

Some new features and API calls are markedexperimental which meansthat they may change in an incompatible way even in a minor release.

Features may be marked experimental e.g. because they are useful butincomplete, or because the best design is not obvious and it's useful togather some feedback before committing to the design. Typically a featureis 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.

DUK_VERSION and Duktape.version§

DUK_VERSION andDuktape.version provide versionidentification using a single number computed as:(major * 10000 + minor * 100 + patch),then subtracting one for development builds (not official releases) made fromDuktape repo.

Note the limitations for development builds:

Development builds shouldn't be used in production, but the currentDUK_VERSION andDuktape.version number providesa useful approximation for version comparison: a development build willcompare smaller than the actual release, but higher (or equal) than aprevious release.

Examples§

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

VersionDUK_VERSION &
Duktape.version
Notes
0.12.01200
1.0.010000
1.2.9910299Development build before 1.3 release.
1.3.010300
1.3.210302
2.0.020000

Maintenance of stable versions§

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

Incompatible changes§

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

This is not a hard rule, and it cannot be achieved in all cases.


Limitations§

The following is a list of known limitations of the current implementation.Limitations include shortcomings from a semantics perspective, performancelimitations, 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,isnot re-entrant. Only one C/C++ thread can call Duktape APIsat a time for a particular Duktape heap (although the calling threadcan change over time). SeeThreading.

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 3bytes of UTF-8 in the worst case, so the maximum string length which isguaranteed 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 mostDUK_HOBJECT_MAX_PROPERTIES (aninternal define). Currently this limit is 0x7ffffffff.

Array limits§

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

Regexp quantifier over empty match§

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

$ dukduk> t = /(x*)*/.exec('y');RangeError: regexp executor recursion limit    at [anon] (duk_regexp_executor.c:145) internal    at exec () native strict preventsyield    at global (input:1) preventsyield

Duktape does not fully support locales§

Although Duktape supports the concept of a local time, it doesn't supportother 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 15.5.4.16 to 15.5.4.19 require context and locale processingof UnicodeSpecialCasing.txt. However, Duktape doesn't currentlyhave 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 permanentlyand array entries are moved to the "entry part". This involves interning all usedarray indices as explicit string keys (e.g. "0", "1", etc). This is not acompliance concern, but degrades performance.

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

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

Global/eval code is slower than function code§

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

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

functionmain(){// ...}main();

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

(function(){// ...})();

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

ECMAScript functions are compiled into bytecode with a fixed set ofregisters. Some registers are reserved for arguments and variablebindings while others are used as temporaries. All registers areconsidered live from a garbage collection perspective, even temporaryregisters containing old values which the function actually cannotreference any more. Such temporaries are considered reachable until theyare overwritten by the evaluation of another expression or until thefunction exits. Function exit is the only easily predicted condition toensure garbage collection.

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

functionrunOnce(){// run one iteration, lots of temporaries}functionforeverLoop(){for(;;){runOnce();}}

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 withan automatic prototype object created for the function. The function instance'sprototype property points to the prototype object, and the prototype'sconstructor property points back to the function instance. Onlymark-and-sweep is able to collect these reference loops at the moment. If youbuild with reference counting only (not recommended), function instances mayappear to leak memory; the memory will be released when the relevant heap isdestroyed.

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 buildf=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// mark-and-sweep triggers finalizer for 'f'Duktape.gc();

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

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

Since Duktape 2.x mark-and-sweep is always enabled so that objectsparticipating in reference loops are eventually freed. You can disableperiodic "voluntary" (non-emergency) mark-and-sweep via config optionsto reduce collection pauses in time sensitive environments.

Non-standard function 'caller' property limitations§

WhenDUK_USE_NONSTD_FUNC_CALLER_PROPERTY is given, Duktapeupdates thecaller property of non-strict function instancessimilarly to e.g. V8 andSpidermonkey.There are a few limitations, though:

See the internaltest-bi-function-nonstd-caller-prop.js testcase for further details.

Garbage collection during debugger paused state§

When debugger support is activated, a debugger session is active, andDuktape is paused, there are a few current limitations:


Comparison to Lua§

Duktape borrows a lot from Lua conceptually. Below are a few notes onwhat's different in Duktape compared to Lua. This may be useful if you'realready 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 constantDUK_INVALID_INDEX is used instead in Duktape.

String indices are also zero-based, and slices are indicated with aninclusive 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 andnative functions, and threads (coroutines). As a result, all of thesehave a mutable and extensible set of properties.

Lua userdata and lightuserdata§

The concept closest to Luauserdata is the Duktapebuffertype, with the following differences:

Lualightuserdata and Duktapepointer are essentiallythe same.

If you need to associate properties with a Duktape buffer, you can usea buffer object instead (or create your own object and store the plain bufferas its property). You can then add a finalizer to the object to free anyresources related to the buffer. This works reasonably well as long as nothingelse holds a reference to the buffer. If this were the case, the buffer couldget used after the object had already been finalized. To safeguard against this,the native C structure should have a flag indicating whether the data structureis 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-sweepgarbage collector (mark-and-sweep is needed only for reference cycles). Collectionpauses can be avoided by disabling voluntary mark-and-sweep passes(disableDUK_USE_VOLUNTARY_GC). Lua has an incremental collector withno pauses, but has no reference counting.

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

duk_safe_call() vs. lua_cpcall()§

duk_safe_call() is a protected C function call whichoperates in the existing value stack frame. The function call isnot 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 mechanismsimilar to Lualua_dump(). SeeBytecode dump/load.

Metatables§

There is no equivalent of Lua metatables in ECMAScript E5/E5.1, butECMAScript ES2015 Proxy objectsprovide similar functionality. To allow property virtualization better than available inE5/E5.1, Duktape implements anES2015 Proxy subset.

lua_next() vs. duk_next()§

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

Raw accessors§

There is no equivalent to Lua raw table access functions likelua_rawget. One can use the following ECMAScript built-insfor 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 controlledusing the functions exposed by theDuktape 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) currentlydoesn't. This may change with ECMAScript ES2015, which has a syntax formultiple value returns. The Duktape/C API reserves return values above 1so 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), whileDuktape 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 fromthe disk or perhaps decompressed on-the-fly. Duktape currently does notsupport streaming compilation because it needs multiple passes over thesource code.


Duktape is (C) by itsauthorsand licensed under theMIT license.


[8]ページ先頭

©2009-2025 Movatter.jp