You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

476 lines
19 KiB

<h1 id="gettingstarted">Getting started</h1>
<h2>Downloading</h2>
<p>Download the source distributable from the
<a href="download.html">Download</a> page.</p>
<h2>Command line tool</h2>
<p>Unpack the distributable:</p>
<pre>
$ cd /tmp
$ tar xvfJ duktape-&lt;version&gt;.tar.xz
</pre>
<p>Compile the command line tool using the provided Makefile:</p>
<pre>
$ cd /tmp/duktape-&lt;version&gt;/
$ make -f Makefile.cmdline
</pre>
<p>The Makefile assumes you have <code>gcc</code> installed. If you don't,
you can just edit the Makefile to match your compiler (the Makefile is
quite simple).</p>
<div class="note">
Duktape doesn't provide built-in bindings for file or console I/O to avoid
portability issues (for example, some platforms don't have I/O APIs at all).
The command line utility provides <code>print()</code> and <code>alert()</code>
bindings using <a href="https://github.com/svaarala/duktape/blob/master/extras/print-alert">extras/print-alert</a>
to make it easier to play with. There are useful "extras" in the distributable
providing useful (optional) bindings such as:
<ul>
<li><a href="https://github.com/svaarala/duktape/blob/master/extras/print-alert">print() and alert()</a></li>
<li><a href="https://github.com/svaarala/duktape/blob/master/extras/console">console object, e.g. console.log()</a></li>
</ul>
<b>Throughout the guide examples will assume a <code>print()</code> binding for
illustration.</b>
</div>
<div class="note">
The command line tool avoids platform dependencies by default. You can add
line editing support via <a href="https://github.com/antirez/linenoise">linenoise</a>
by editing the Makefile:
<ul>
<li>Add <code class="nobreak">-DDUK_CMDLINE_FANCY</code></li>
<li>Add <code>-Ipath/to/linenoise</code> for the <code>linenoise.h</code> header</li>
<li>Add <code>path/to/linenoise.c</code> to the source list</li>
<li>Linenoise only works in POSIX environments and requires a C compiler (not C++)</li>
</ul>
</div>
<p>You can now run Ecmascript code interactively:</p>
<pre>
$ ./duk
((o) Duktape 1.5.0 (v1.5.0)
duk&gt; print('Hello world!')
Hello world!
= undefined
</pre>
<p>You can also run Ecmascript code from a file which is useful for playing with
features and algorithms. As an example, create <code>fib.js</code>:</p>
<pre class="ecmascript-code" include="fib.js"></pre>
<p>Test the script from the command line:</p>
<pre>
$ ./duk fib.js
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181
</pre>
<h2>Integrating Duktape into your program</h2>
<p>The command line tool is simply an example of a program which embeds
Duktape. Embedding Duktape into your program is very simple:</p>
<ul>
<li>Run <code>duktape-N.N.N/tools/configure.py</code> to configure Duktape
for build. The result is a directory containing <code>duktape.c</code>,
<code>duktape.h</code>, and <code>duk_config.h</code>.</li>
<li>Add <code>duktape.c</code>, <code>duktape.h</code>, and <code>duk_config.h</code>
to your build, and call the Duktape API from elsewhere in your program.</li>
</ul>
<p>The Duktape distributable (duktape-N.N.N.tar.xz) <code>src/</code> directory
contains preconfigured header and source files for the Duktape default configuration
which can usually be used as is. If needed, the configuration tool allows you to customize
Duktape options, such as optimizing Duktape for low memory targets and
enable/disable features. See <a href="#compiling">Compiling</a> and
<a href="http://wiki.duktape.org/Configuring.html">Configuring Duktape for build</a>
for more details and examples.</p>
<p>The distributable contains a very simple example program, <code>hello.c</code>,
which illustrates this process. Compile the test program with the preconfigured
Duktape header and source files e.g. as follows:</p>
<pre>
$ cd /tmp/duktape-&lt;version&gt;/
$ gcc -std=c99 -o hello -Isrc src/duktape.c examples/hello/hello.c -lm
</pre>
<p>To customize Duktape configuration use <code>configure.py</code>:</p>
<pre>
$ cd /tmp/duktape-&lt;version&gt;/
# 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
</pre>
<p>The test program creates a Duktape context and uses it to run some
Ecmascript code:</p>
<pre>
$ ./hello
Hello world!
2+3=5
</pre>
<p>Because Duktape is an embeddable engine, you don't need to change
the basic control flow of your program. The basic approach is:</p>
<ul>
<li>Create a Duktape context e.g. in program initialization
(or even on-demand when scripting is needed). Usually you
would also load your scripts during initialization, though
that can also be done on-demand.</li>
<li>Identify points in your code where you would like to use scripting
and insert calls to script functions there.</li>
<li>To make a script function call, first push call arguments to the
Duktape context's <i>value stack</i> using the Duktape API.
Then, use another Duktape API call to initiate the actual call.</li>
<li>Once script execution is finished, control is returned to your
program (the API call returns) and a return value is left on the
Duktape context's value stack. C code can then access the return
value using the Duktape API.</li>
</ul>
<p>Let's look at a simple example program. The program reads in a line from
<code>stdin</code> using a C mainloop, calls an Ecmascript helper to transform
the line, and prints out the result. The line processing function can take
advantage of Ecmascript goodies like regular expressions, and can be easily
modified without recompiling the C program.</p>
<p>The script code will be placed in <code>process.js</code>. The example
line processing function converts a plain text line into HTML, and
automatically bolds text between stars:</p>
<pre class="ecmascript-code" include="process.js"></pre>
<p>The C code, <code>processlines.c</code> initializes a Duktape context,
evaluates the script, then proceeds to process lines from <code>stdin</code>,
calling <code>processLine()</code> for every line:</p>
<pre class="c-code" include="processlines.c"></pre>
<p>Let's look at the Duktape specific parts of the example code piece by piece.
Here we need to gloss over some details for brevity, see
<a href="#programming">Programming model</a> for a detailed discussion:</p>
<ul class="breakdown"> <!-- breakdown -->
<li>
<pre class="c-code">
/* For brevity assumes a maximum file length of 16kB. */
static void push_file_as_string(duk_context *ctx, const char *filename) {
FILE *f;
size_t len;
char buf[16384];
f = fopen(filename, "rb");
if (f) {
len = fread((void *) buf, 1, sizeof(buf), f);
fclose(f);
duk_push_lstring(ctx, (const char *) buf, (duk_size_t) len);
} else {
duk_push_undefined(ctx);
}
}
</pre>
<p>Because Duktape is an embeddable engine and makes minimal assumptions
there are no file I/O bindings in the default C or Ecmascript API. The
above helper is an example of how to push the contents of a file as a
string; the example uses a fixed read buffer for brevity, a better
implementation would first check the file size and then allocate a buffer
for it. The Duktape distributable includes "extras" which provide, among
other things, useful C and Ecmascript helpers, including file I/O helpers.</p>
</li>
<li>
<pre class="c-code">
ctx = duk_create_heap_default();
if (!ctx) {
printf("Failed to create a Duktape heap.\n");
exit(1);
}
</pre>
<p>First we create a Duktape context. A context allows us to exchange values
with Ecmascript code by pushing and popping values to the <b>value stack</b>.
Most calls in the Duktape API operate with the value stack, pushing, popping,
and examining values on the stack. For production code you should use
<a href="api.html#duk_create_heap">duk_create_heap()</a> so that you can set
a <b>fatal error handler</b>. See <a href="#error-handling">Error handling</a>
for discussion of error handling best practices.</p>
</li>
<li>
<pre class="c-code">
push_file_as_string(ctx, "process.js");
if (duk_peval(ctx) != 0) {
printf("Error: %s\n", duk_safe_to_string(ctx, -1));
goto finished;
}
duk_pop(ctx); /* ignore result */
</pre>
<p>First we use our file helper to push <code>process.js</code> onto the value
stack as a string. Then we use <code>duk_peval()</code> to compile and run the
script. The script registers <code>processLine()</code> into the Ecmascript
global object for later use. A <b>protected call, duk_peval(),</b> is used for
running the script so that any script errors, such as syntax errors, are caught
and handled without causing a fatal error. If an error occurs, the error message
is coerced safely using <a href="api.html#duk_safe_to_string">duk_safe_to_string()</a>
which is guaranteed not to throw a further error. The result of the string coercion
is a <code>const char *</code> pointing to a read-only, NUL-terminated, UTF-8
encoded string, which can be used directly with <code>printf</code>.</p>
</li>
<li>
<pre class="c-code">
duk_push_global_object(ctx);
duk_get_prop_string(ctx, -1 /*index*/, "processLine");
</pre>
<p>The first call pushes the Ecmascript global object to the value stack.
The second call looks up <code>processLine</code> property of the global object
(which the script in <code>process.js</code> has defined). The <code>-1</code>
argument is an index to the value stack; negative values refer to stack
elements starting from the top, so <code>-1</code> refers to the topmost
element of the stack, the global object.</p>
</li>
<li>
<pre class="c-code">
duk_push_string(ctx, line);
</pre>
<p>Pushes the string pointed to by <code>line</code> to the value stack. The
string length is automatically determined by scanning for a NUL terminator
(same as <code>strlen()</code>). Duktape makes a copy of the string when it is
pushed to the stack, so the <code>line</code> buffer can be freely modified when
the call returns.</p>
</li>
<li>
<pre class="c-code">
if (duk_pcall(ctx, 1 /*nargs*/) != 0) {
printf("Error: %s\n", duk_safe_to_string(ctx, -1));
} else {
printf("%s\n", duk_safe_to_string(ctx, -1));
}
duk_pop(ctx); /* pop result/error */
</pre>
<p>At this point the value stack contains: the global object, the processLine
function, and the <code>line</code> string. The
<a href="api.html#duk_pcall">duk_pcall()</a> method calls a function with a
specified number of arguments given on the value stack, and replaces both the
function and the argument values with the function's return value.
Here the resulting value stack contains: the global object and the call result.
The call is protected so that errors can be caught and printed. The
<a href="api.html#duk_safe_to_string">duk_safe_to_string()</a> API call is
again used to print errors safely. Finally, the result (or error) is popped
off the value stack.</p>
</li>
<li>
<pre class="c-code">
duk_destroy_heap(ctx);
</pre>
<p>Destroy the Duktape context, freeing all resources held by the context.
This call will free the value stack and all references on the value stack.
In our example we left the global object on the value stack on purpose.
This is not a problem: no memory leaks will occur even if the value stack
is not empty when the heap is destroyed.</p>
</li>
</ul> <!-- breakdown -->
<p>Compile like above:</p>
<pre>
$ gcc -std=c99 -o processlines -Isrc/ src/duktape.c processlines.c -lm
</pre>
<p>Test run (ensure that <code>process.js</code> is in the current directory):</p>
<pre>
$ echo "I like *Sam &amp; Max*." | ./processlines
I like &lt;b&gt;Sam &amp;<!-- avoiding double decode is tricky -->&#35;38; Max&lt;/b&gt;.
</pre>
<h2>Calling C code from Ecmascript (Duktape/C bindings)</h2>
<p>The integration example illustrated how C code can call into Ecmascript
to do things which are easy in Ecmascript but difficult in C.</p>
<p>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.</p>
<p>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
<i>value stack</i>, manipulated with the Duktape API. We'll go deeper into
Duktape/C binding and the Duktape API later on. Example:</p>
<pre class="c-code">
duk_ret_t my_native_func(duk_context *ctx) {
double arg = duk_require_number(ctx, 0 /*index*/);
duk_push_number(ctx, arg * arg);
return 1;
}
</pre>
<p>Let's look at this example line by line:</p>
<ul class="breakdown"> <!-- breakdown -->
<li>
<pre class="c-code">
double arg = duk_require_number(ctx, 0 /*index*/);
</pre>
<p>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 <code>double</code>.</p>
</li>
<li>
<pre class="c-code">
duk_push_number(ctx, arg * arg);
</pre>
<p>Compute square of argument and push it to the value stack.</p>
</li>
<li>
<pre class="c-code">
return 1;
</pre>
<p>Return from the function call, indicating that there is a (single) return
value on top of the value stack. You could also return <code>0</code> to indicate
that no return value is given (in which case Duktape defaults to Ecmascript
<code>undefined</code>). 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 <a href="#programming">Programming model</a> for more details.</p>
</li>
</ul> <!-- breakdown -->
<p>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:</p>
<pre class="ecmascript-code" include="prime.js"></pre>
<p>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.</p>
<p>A native helper with functionality equivalent to <code>primeCheckEcmascript</code>
is quite straightforward to implement. Adding a program main and a simple
<code>print()</code> binding into the Ecmascript global object, we get
<code>primecheck.c</code>:</p>
<pre class="c-code" include="primecheck.c"></pre>
<p>The new calls here are, line by line:</p>
<ul class="breakdown"> <!-- breakdown -->
<li>
<pre class="c-code">
int val = duk_require_int(ctx, 0);
int lim = duk_require_int(ctx, 1);
</pre>
<p>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 <code>val</code> and <code>lim</code> locals. The index 0 refers to the first
function argument and index 1 to the second.</p>
<p>
Technically <code>duk_require_int()</code> returns a <code>duk_int_t</code>; this
indirect type is always mapped to an <code>int</code> except on obscure platforms
where an <code>int</code> is only 16 bits wide. In ordinary application code you
don't need to worry about this, see <a href="#ctypes">C types</a> for more discussion.</p>
</li>
<li>
<pre class="c-code">
duk_push_false(ctx);
return 1;
</pre>
<p>Pushes an Ecmascript <code>false</code> to the value stack. The C return value
1 indicates that the <code>false</code> value is returned to the Ecmascript caller.</p>
</li>
<li>
<pre class="c-code">
duk_push_global_object(ctx);
duk_push_c_function(ctx, native_prime_check, 2 /*nargs*/);
duk_put_prop_string(ctx, -2, "primeCheckNative");
</pre>
<p>The first call, like before, pushes the Ecmascript global object to the
value stack. The second call creates an Ecmascript <code>Function</code> object
and pushes it to the value stack. The function object is bound to the
Duktape/C function <code>native_prime_check</code>: when the Ecmascript function
created here is called from Ecmascript, the C function gets invoked.
The second argument (<code>2</code>) 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 <code>undefined</code>; 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 <code>primeCheckNative</code> and pops the function value off the stack.
</p>
</li>
<li>
<pre class="c-code">
duk_get_prop_string(ctx, -1, "primeTest");
if (duk_pcall(ctx, 0) != 0) {
printf("Error: %s\n", duk_safe_to_string(ctx, -1));
}
duk_pop(ctx); /* ignore result */
</pre>
<p>When we come here the value stack already contains the global object
at the stack top. Line 1 looks up the <code>primeTest</code> function
from the global object (which was defined by the loaded script). Lines
2-4 call the <code>primeTest</code> function with zero arguments, and
prints out an error safely if one occurs. Line 5 pops the call result
off the stack; we don't need the return value here.</p>
</li>
</ul> <!-- breakdown -->
<p>Compile like above:</p>
<pre>
$ gcc -std=c99 -o primecheck -Isrc/ src/duktape.c primecheck.c -lm
</pre>
<p>Test run (ensure that <code>prime.js</code> is in the current directory):</p>
<pre>
$ 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
</pre>
<p>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
<code>prime.js</code> and disabling the use of the native helper:</p>
<pre class="ecmascript-code">
// Select available helper at load time
var primeCheckHelper = primeCheckEcmascript;
</pre>
<p>Re-compiling and re-running the test:</p>
<pre>
$ 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
</pre>