Download the source distributable from the Download page.
There are currently two distributables: a source distributable for using Duktape in your program, and a full distributable for Duktape development.
Unpack the distributable:
$ cd /tmp $ tar xvfJ duktape-<version>.tar.xz
Compile the command line tool using the provided Makefile (your system
needs to have readline
and ncurses
library and headers
installed):
# NOTE: ensure you have readline and curses library and headers installed $ cd /tmp/duktape-<version>/ $ make -f Makefile.cmdline
readline
by default. If it's
not available or there are other portability issues, try enabling the
-DDUK_CMDLINE_BAREBONES
compile option in the Makefile. The
option minimizes any non-portable dependencies.
You can now run Ecmascript code interactively:
$ ./duk ((o) Duktape [version info] duk> print('Hello world!') Hello world! = undefined
You can also run Ecmascript code from a file which is useful for playing with
features and algorithms. As an example, create fib.js
:
Test the script from the command line:
$ ./duk fib.js 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 Cleaning up...
The command line tool is simply an example of a program which embeds
Duktape. Embedding Duktape into your program is very simple: just add
duktape.c
and duktape.h
to your build, and call the
Duktape API from elsewhere in your program.
The distributable contains a very simple example program, hello.c
,
which illustrates this process. Compile the test program e.g. as:
$ cd /tmp/duktape-<version>/ $ gcc -o hello -Isrc/ src/duktape.c examples/hello/hello.c -lm
The test program creates a Duktape context and uses it to run some Ecmascript code:
$ ./hello Hello world! 2+3=5
Because Duktape is an embeddable interpreter, you don't need to change the basic control flow of your program. The basic approach is:
Let's look at a simple example program. The program reads in a line using a C mainloop, calls an Ecmascript helper to transform the line, and prints out the transformed line. The line processing function can take advantage of Ecmascript goodies like regular expressions, and can be easily modified without recompiling the C program.
The script code will be placed in process.js
. The example
line processing function converts a plain text line into HTML, and
automatically bolds text between stars:
The C code, processlines.c
initializes a Duktape context,
compiles the script, then proceeds to process lines from stdin
,
calling processLine()
for every line:
Let's look at the Duktape specific parts of the example code line by line. Here we need to gloss over some details for brevity, see Programming model for a detailed discussion:
ctx = duk_create_heap_default(); if (!ctx) { exit(1); }
First we create a Duktape context. A context allows us to exchange values with Ecmascript code by pushing and popping values to the value stack. Most calls in the Duktape API operate with the value stack, pushing, popping, and examining values on the stack.
duk_eval_file(ctx, "process.js"); duk_pop(ctx); /* pop eval result */
The first call reads in process.js
and evaluates its contents.
The script registers the processLine()
function into the Ecmascript
global object. The result of the evaluation is pushed on top of the value
stack. Here we don't need the evaluation result, so we pop the value off
the stack.
duk_push_global_object(ctx); duk_get_prop_string(ctx, -1 /*index*/, "processLine");
The first call pushes the Ecmascript global object to the value stack.
The second call looks up processLine
property of the global object.
The script in process.js
has registered a function into the global
object with this name. The -1
argument is an index to the value
stack; negative values refer to stack elements starting from the top, so
-1
refers to the topmost element of the stack, the property
lookup result.
duk_push_string(ctx, line);
Pushes the string pointed to by line
to the value stack. The
string length is automatically determined by scanning for a NUL terminator
(same as strlen()
). Duktape makes a copy of the string when it is
pushed to the stack, so the line
buffer can be freely modified when
the call returns.
duk_call(ctx, 1 /*nargs*/);
At this point the value stack contains: the global object, the processLine
function, and the line
string. This line calls an Ecmascript function
with the specified number of arguments; here the argument count is 1. The
target function is expected to reside just before the argument list on the
value stack. After the API call returns, the arguments have been replaced with
a single return value, so the stack contains: the global object and the call
result.
printf("%s\n", duk_to_string(ctx, -1)); duk_pop(ctx);
The duk_to_string()
call requests Duktape to convert the value stack
element at index -1 (the topmost value on the stack, which is the processLine
function call result here) to a string, returning a const char *
pointing
to the result. This return value is a read-only, NUL terminated UTF-8 value which
C code can use directly as long as the value resides on the value stack. Here we
just print out the string. After we've printed the string, we pop the value off
the value stack.
duk_destroy_heap(ctx);
Destroy the Duktape context, freeing all resources held by the context. In our example we left the global object on the value stack on purpose. This call will free the value stack and all references on the value stack. No memory leaks will occur even if the value stack is not empty.
Compile like above:
$ gcc -o processlines -Isrc/ src/duktape.c processlines.c -lm
Test run:
$ echo "I like *Sam & Max*." | ./processlines I like <b>Sam & Max</b>.
The integration example illustrated how C code can call into Ecmascript to do things which are easy in Ecmascript but difficult in C.
Ecmascript also often needs to call into C when the situation is reversed. For instance, while scripting is useful for many things, it is not optimal for low level byte or character processing. Being able to call optimized C helpers allows you to write most of your script logic in nice Ecmascript but call into C for the performance critical parts. Another reason for using native functions is to provide access to native libraries.
To implement a native function you write an ordinary C function which conforms to a special calling convention, the Duktape/C binding. Duktape/C functions take a single argument, a Duktape context, and return a single value indicating either error or number of return values. The function accesses call arguments and places return values through the Duktape context's value stack, manipulated with the Duktape API. We'll go deeper into Duktape/C binding and the Duktape API later on. Example:
int my_native_func(duk_context *ctx) { double arg = duk_require_number(ctx, 0 /*index*/); duk_push_number(ctx, arg * arg); return 1; }
Let's look at this example line by line:
double arg = duk_require_number(ctx, 0 /*index*/);
Check that the number at value stack index 0 (bottom of the stack, first
argument to function call) is a number; if not, throws an error and never
returns. If the value is a number, return it as a double
.
duk_push_number(ctx, arg * arg);
Compute square of argument and push it to the value stack.
return 1;
Return from the function call, indicating that there is a (single) return
value on top of the value stack. You could also return 0
to indicate
that no return value is given (in which case Duktape defaults to Ecmascript
undefined
). A negative return value which causes an error to be
automatically thrown: this is a shorthand for throwing errors conveniently.
Note that you don't need to pop any values off the stack, Duktape will do that
for you automatically when the function returns.
See Programming model for more details.
We'll use a primality test as an example for using native code to speed up Ecmascript algorithms. More specifically, our test program searches for primes under 1000000 which end with the digits '9999'. The Ecmascript version of the program is:
Note that the program uses the native helper if it's available but falls back to an Ecmascript version if it's not. This allows the Ecmascript code to be used in other containing programs. Also, if the prime check program is ported to another platform where the native version does not compile without changes, the program remains functional (though slower) until the helper is ported. In this case the native helper detection happens when the script is loaded. You can also detect it when the code is actually called which is more flexible.
A native helper with functionality equivalent to primeCheckEcmascript
is quite straightforward to implement. Adding a program main we get
primecheck.c
:
The new calls here are, line by line:
int val = duk_require_int(ctx, 0); int lim = duk_require_int(ctx, 1);
These two calls check the two argument values given to the native helper.
If the values are not of the Ecmascript number type, an error is thrown.
If they are numbers, their value is converted to an integer and assigned to
the val
and lim
locals. The index 0 refers to the first
function argument and index 1 to the second.
duk_push_false(ctx); return 1;
Pushes an Ecmascript false
to the value stack. The C return value
1 indicates that the false
value is returned to the Ecmascript caller.
duk_push_global_object(ctx); duk_push_c_function(ctx, native_prime_check, 2 /*nargs*/); duk_put_prop_string(ctx, -2, "primeCheckNative");
The first call, like before, pushes the Ecmascript global object to the
value stack. The second call creates an Ecmascript Function
object
and pushes it to the value stack. The function object is bound to the
Duktape/C function native_prime_check
: when the Ecmascript function
created here is called from Ecmascript, the C function gets invoked.
The second argument (2
) to the call indicates how many arguments
the C function gets on the value stack. If the caller gives fewer arguments,
the missing arguments are padded with undefined
; if the caller gives
more arguments, the extra arguments are dropped automatically. Finally, the
third call registers the function object into the global object with the
name primeCheckNative
and pops the function value off the stack.
duk_get_prop_string(ctx, -1, "primeTest"); duk_call(ctx, 0); duk_pop(ctx);
When we come here the value stack already contains the global object
at the stack top. The first line looks up the primeTest
function
from the global object (which was defined by the loaded script). The
second line calls the primeTest
function with zero arguments.
The third line pops the call result off the stack; we don't need the return
value here.
Compile like above:
$ gcc -o primecheck -Isrc/ src/duktape.c primecheck.c -lm
Test run:
$ time ./primecheck Have native helper: true 49999 59999 79999 139999 179999 199999 239999 289999 329999 379999 389999 409999 419999 529999 599999 619999 659999 679999 769999 799999 839999 989999 real 0m2.985s user 0m2.976s sys 0m0.000s
Because most execution time is spent in the prime check, the speed-up
compared to plain Ecmascript is significant. You can check this by editing
prime.js
and disabling the use of the native helper:
// Select available helper at load time var primeCheckHelper = primeCheckEcmascript;
Re-compiling and re-running the test:
$ time ./primecheck Have native helper: false 49999 59999 79999 139999 179999 199999 239999 289999 329999 379999 389999 409999 419999 529999 599999 619999 659999 679999 769999 799999 839999 989999 real 0m23.609s user 0m23.573s sys 0m0.000s