1
0
mirror of https://github.com/lammertb/libhttp.git synced 2025-08-09 03:22:45 +03:00

Removed duktape support

This commit is contained in:
Lammert Bies
2016-11-16 03:46:31 +01:00
parent 56461e5292
commit 02927f38b8
231 changed files with 4 additions and 190027 deletions

View File

@@ -967,7 +967,6 @@ CIVETWEB_API int mg_get_response(struct mg_connection *conn,
8 support IPv6 (USE_IPV6 set) 8 support IPv6 (USE_IPV6 set)
16 support WebSocket (USE_WEBSOCKET set) 16 support WebSocket (USE_WEBSOCKET set)
32 support Lua scripts and Lua server pages (USE_LUA is set) 32 support Lua scripts and Lua server pages (USE_LUA is set)
64 support server side JavaScript (USE_DUKTAPE is set)
128 support caching (NO_CACHING not set) 128 support caching (NO_CACHING not set)
The result is undefined for all other feature values. The result is undefined for all other feature values.

View File

@@ -59,9 +59,6 @@ unsigned mg_check_feature( unsigned feature ) {
#if defined(USE_LUA) #if defined(USE_LUA)
| 0x0020u | 0x0020u
#endif #endif
#if defined(USE_DUKTAPE)
| 0x0040u
#endif
#if !defined(NO_CACHING) #if !defined(NO_CACHING)
| 0x0080u | 0x0080u
#endif #endif

View File

@@ -479,9 +479,6 @@ enum {
LUA_SCRIPT_EXTENSIONS, LUA_SCRIPT_EXTENSIONS,
LUA_SERVER_PAGE_EXTENSIONS, LUA_SERVER_PAGE_EXTENSIONS,
#endif #endif
#if defined(USE_DUKTAPE)
DUKTAPE_SCRIPT_EXTENSIONS,
#endif
#if defined(USE_WEBSOCKET) #if defined(USE_WEBSOCKET)
WEBSOCKET_ROOT, WEBSOCKET_ROOT,

View File

@@ -640,12 +640,6 @@ struct mg_option XX_httplib_config_options[] = {
{"lua_script_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lua$"}, {"lua_script_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lua$"},
{"lua_server_page_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lp$|**.lsp$"}, {"lua_server_page_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lp$|**.lsp$"},
#endif #endif
#if defined(USE_DUKTAPE)
/* The support for duktape is still in alpha version state.
* The name of this config option might change. */
{"duktape_script_pattern", CONFIG_TYPE_EXT_PATTERN, "**.ssjs$"},
#endif
#if defined(USE_WEBSOCKET) #if defined(USE_WEBSOCKET)
{"websocket_root", CONFIG_TYPE_DIRECTORY, NULL}, {"websocket_root", CONFIG_TYPE_DIRECTORY, NULL},
#endif #endif
@@ -3628,7 +3622,7 @@ interpret_uri(struct mg_connection *conn, /* in: request (must be valid) */
/* Local file path and name, corresponding to requested URI /* Local file path and name, corresponding to requested URI
* is now stored in "filename" variable. */ * is now stored in "filename" variable. */
if (mg_stat(conn, filename, filep)) { if (mg_stat(conn, filename, filep)) {
#if !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE) #if !defined(NO_CGI) || defined(USE_LUA)
/* File exists. Check if it is a script type. */ /* File exists. Check if it is a script type. */
if (0 if (0
#if !defined(NO_CGI) #if !defined(NO_CGI)
@@ -3640,12 +3634,6 @@ interpret_uri(struct mg_connection *conn, /* in: request (must be valid) */
|| match_prefix(conn->ctx->config[LUA_SCRIPT_EXTENSIONS], || match_prefix(conn->ctx->config[LUA_SCRIPT_EXTENSIONS],
strlen(conn->ctx->config[LUA_SCRIPT_EXTENSIONS]), strlen(conn->ctx->config[LUA_SCRIPT_EXTENSIONS]),
filename) > 0 filename) > 0
#endif
#if defined(USE_DUKTAPE)
|| match_prefix(conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS],
strlen(
conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS]),
filename) > 0
#endif #endif
) { ) {
/* The request addresses a CGI script or a Lua script. The URI /* The request addresses a CGI script or a Lua script. The URI
@@ -3660,7 +3648,7 @@ interpret_uri(struct mg_connection *conn, /* in: request (must be valid) */
* generated response. */ * generated response. */
*is_script_resource = !*is_put_or_delete_request; *is_script_resource = !*is_put_or_delete_request;
} }
#endif /* !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE) */ #endif /* !defined(NO_CGI) || defined(USE_LUA) */
*is_found = 1; *is_found = 1;
return; return;
} }
@@ -3691,7 +3679,7 @@ interpret_uri(struct mg_connection *conn, /* in: request (must be valid) */
} }
} }
#if !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE) #if !defined(NO_CGI) || defined(USE_LUA)
/* Support PATH_INFO for CGI scripts. */ /* Support PATH_INFO for CGI scripts. */
for (p = filename + strlen(filename); p > filename + 1; p--) { for (p = filename + strlen(filename); p > filename + 1; p--) {
if (*p == '/') { if (*p == '/') {
@@ -3707,12 +3695,6 @@ interpret_uri(struct mg_connection *conn, /* in: request (must be valid) */
strlen( strlen(
conn->ctx->config[LUA_SCRIPT_EXTENSIONS]), conn->ctx->config[LUA_SCRIPT_EXTENSIONS]),
filename) > 0 filename) > 0
#endif
#if defined(USE_DUKTAPE)
|| match_prefix(
conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS],
strlen(conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS]),
filename) > 0
#endif #endif
) && mg_stat(conn, filename, filep)) { ) && mg_stat(conn, filename, filep)) {
/* Shift PATH_INFO block one character right, e.g. /* Shift PATH_INFO block one character right, e.g.
@@ -3731,7 +3713,7 @@ interpret_uri(struct mg_connection *conn, /* in: request (must be valid) */
} }
} }
} }
#endif /* !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE) */ #endif /* !defined(NO_CGI) || defined(USE_LUA) */
#endif /* !defined(NO_FILES) */ #endif /* !defined(NO_FILES) */
return; return;
@@ -7200,10 +7182,6 @@ mg_unlock_context(struct mg_context *ctx)
#include "mod_lua.inl" #include "mod_lua.inl"
#endif /* USE_LUA */ #endif /* USE_LUA */
#ifdef USE_DUKTAPE
#include "mod_duktape.inl"
#endif /* USE_DUKTAPE */
#if defined(USE_WEBSOCKET) #if defined(USE_WEBSOCKET)
/* START OF SHA-1 code /* START OF SHA-1 code
@@ -8803,14 +8781,6 @@ handle_file_based_request(struct mg_connection *conn,
* entire reply. */ * entire reply. */
mg_exec_lua_script(conn, path, NULL); mg_exec_lua_script(conn, path, NULL);
#endif #endif
#if defined(USE_DUKTAPE)
} else if (match_prefix(conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS],
strlen(
conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS]),
path) > 0) {
/* Call duktape to generate the page */
mg_exec_duktape_script(conn, path);
#endif
#if !defined(NO_CGI) #if !defined(NO_CGI)
} else if (match_prefix(conn->ctx->config[CGI_EXTENSIONS], } else if (match_prefix(conn->ctx->config[CGI_EXTENSIONS],
strlen(conn->ctx->config[CGI_EXTENSIONS]), strlen(conn->ctx->config[CGI_EXTENSIONS]),

View File

@@ -786,38 +786,6 @@ run_lua(const char *file_name)
#endif /* USE_LUA */ #endif /* USE_LUA */
#ifdef USE_DUKTAPE
#include "duktape.h"
static int
run_duktape(const char *file_name)
{
duk_context *ctx = NULL;
#ifdef WIN32
(void)MakeConsole();
#endif /* WIN32 */
ctx = duk_create_heap_default();
if (!ctx) {
fprintf(stderr, "Failed to create a Duktape heap.\n");
goto finished;
}
if (duk_peval_file(ctx, file_name) != 0) {
fprintf(stderr, "%s\n", duk_safe_to_string(ctx, -1));
goto finished;
}
duk_pop(ctx); /* ignore result */
finished:
duk_destroy_heap(ctx);
return 0;
}
#endif /* USE_DUKTAPE */
#if defined(__MINGW32__) || defined(__MINGW64__) #if defined(__MINGW32__) || defined(__MINGW64__)
/* For __MINGW32/64_MAJOR/MINOR_VERSION define */ /* For __MINGW32/64_MAJOR/MINOR_VERSION define */
@@ -975,21 +943,6 @@ start_libhttp(int argc, char *argv[])
#endif /* USE_LUA */ #endif /* USE_LUA */
} }
/* Call Duktape, if -E option is specified */
if (argc > 1 && !strcmp(argv[1], "-E")) {
#ifdef USE_DUKTAPE
if (argc != 3) {
show_usage_and_exit(argv[0]);
}
exit(run_duktape(argv[2]));
#else /* USE_DUKTAPE */
show_server_name();
fprintf(stderr, "\nError: Ecmascript support not enabled\n");
exit(EXIT_FAILURE);
#endif /* USE_DUKTAPE */
}
/* Show usage if -h or --help options are specified */ /* Show usage if -h or --help options are specified */
if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "-H") if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "-H")
|| !strcmp(argv[1], "--help"))) { || !strcmp(argv[1], "--help"))) {

View File

@@ -1,250 +0,0 @@
/* This file is part of the CivetWeb web server.
* See https://github.com/civetweb/civetweb/
* (C) 2015 by the CivetWeb authors, MIT license.
*/
#include "duktape.h"
/* TODO: the mg context should be added to duktape as well */
/* Alternative: redefine a new, clean API from scratch (instead of using mg),
* or at least do not add problematic functions. */
/* For evaluation purposes, currently only "send" is supported.
* All other ~50 functions will be added later. */
/* Note: This is only experimental support, so the API may still change. */
static const char *civetweb_conn_id = "\xFF"
"civetweb_conn";
static const char *civetweb_ctx_id = "\xFF"
"civetweb_ctx";
static void *
mg_duk_mem_alloc(void *udata, duk_size_t size)
{
return mg_malloc(size);
}
static void *
mg_duk_mem_realloc(void *udata, void *ptr, duk_size_t newsize)
{
return mg_realloc(ptr, newsize);
}
static void
mg_duk_mem_free(void *udata, void *ptr)
{
mg_free(ptr);
}
static void
mg_duk_fatal_handler(duk_context *ctx, duk_errcode_t code, const char *msg)
{
/* Script is called "protected" (duk_peval_file), so script errors should
* never yield in a call to this function. Maybe calls prior to executing
* the script could raise a fatal error. */
struct mg_connection *conn;
duk_push_global_stash(ctx);
duk_get_prop_string(ctx, -1, civetweb_conn_id);
conn = (struct mg_connection *)duk_to_pointer(ctx, -1);
mg_cry(conn, "%s", msg);
}
static duk_ret_t
duk_itf_write(duk_context *ctx)
{
struct mg_connection *conn;
duk_double_t ret;
duk_size_t len = 0;
const char *val = duk_require_lstring(ctx, -1, &len);
/*
duk_push_global_stash(ctx);
duk_get_prop_string(ctx, -1, civetweb_conn_id);
conn = (struct mg_connection *)duk_to_pointer(ctx, -1);
*/
duk_push_current_function(ctx);
duk_get_prop_string(ctx, -1, civetweb_conn_id);
conn = (struct mg_connection *)duk_to_pointer(ctx, -1);
if (!conn) {
duk_error(ctx,
DUK_ERR_INTERNAL_ERROR,
"function not available without connection object");
/* probably never reached, but satisfies static code analysis */
return DUK_RET_INTERNAL_ERROR;
}
ret = mg_write(conn, val, len);
duk_push_number(ctx, ret);
return 1;
}
static duk_ret_t
duk_itf_read(duk_context *ctx)
{
struct mg_connection *conn;
char buf[1024];
int len;
duk_push_global_stash(ctx);
duk_get_prop_string(ctx, -1, civetweb_conn_id);
conn = (struct mg_connection *)duk_to_pointer(ctx, -1);
if (!conn) {
duk_error(ctx,
DUK_ERR_INTERNAL_ERROR,
"function not available without connection object");
/* probably never reached, but satisfies static code analysis */
return DUK_RET_INTERNAL_ERROR;
}
len = mg_read(conn, buf, sizeof(buf));
duk_push_lstring(ctx, buf, len);
return 1;
}
static duk_ret_t
duk_itf_getoption(duk_context *ctx)
{
struct mg_context *cv_ctx;
const char *ret;
duk_size_t len = 0;
const char *val = duk_require_lstring(ctx, -1, &len);
duk_push_current_function(ctx);
duk_get_prop_string(ctx, -1, civetweb_ctx_id);
cv_ctx = (struct mg_context *)duk_to_pointer(ctx, -1);
if (!cv_ctx) {
duk_error(ctx,
DUK_ERR_INTERNAL_ERROR,
"function not available without connection object");
/* probably never reached, but satisfies static code analysis */
return DUK_RET_INTERNAL_ERROR;
}
ret = mg_get_option(cv_ctx, val);
if (ret) {
duk_push_string(ctx, ret);
} else {
duk_push_null(ctx);
}
return 1;
}
static void
mg_exec_duktape_script(struct mg_connection *conn, const char *script_name)
{
int i;
duk_context *ctx = NULL;
conn->must_close = 1;
/* Create Duktape interpreter state */
ctx = duk_create_heap(mg_duk_mem_alloc,
mg_duk_mem_realloc,
mg_duk_mem_free,
NULL,
mg_duk_fatal_handler);
if (!ctx) {
mg_cry(conn, "Failed to create a Duktape heap.");
goto exec_duktape_finished;
}
/* Add "conn" object */
duk_push_global_object(ctx);
duk_push_object(ctx); /* create a new table/object ("conn") */
duk_push_c_function(ctx, duk_itf_write, 1 /* 1 = nargs */);
duk_push_pointer(ctx, (void *)conn);
duk_put_prop_string(ctx, -2, civetweb_conn_id);
duk_put_prop_string(ctx, -2, "write"); /* add function conn.write */
duk_push_c_function(ctx, duk_itf_read, 0 /* 0 = nargs */);
duk_push_pointer(ctx, (void *)conn);
duk_put_prop_string(ctx, -2, civetweb_conn_id);
duk_put_prop_string(ctx, -2, "read"); /* add function conn.read */
duk_push_string(ctx, conn->request_info.request_method);
duk_put_prop_string(ctx, -2, "request_method"); /* add string conn.r... */
duk_push_string(ctx, conn->request_info.request_uri);
duk_put_prop_string(ctx, -2, "request_uri");
duk_push_string(ctx, conn->request_info.local_uri);
duk_put_prop_string(ctx, -2, "uri");
duk_push_string(ctx, conn->request_info.http_version);
duk_put_prop_string(ctx, -2, "http_version");
duk_push_string(ctx, conn->request_info.query_string);
duk_put_prop_string(ctx, -2, "query_string");
duk_push_string(ctx, conn->request_info.remote_addr);
duk_put_prop_string(ctx, -2, "remote_addr");
duk_push_int(ctx, conn->request_info.remote_port);
duk_put_prop_string(ctx, -2, "remote_port");
duk_push_int(ctx, ntohs(conn->client.lsa.sin.sin_port));
duk_put_prop_string(ctx, -2, "server_port");
duk_push_object(ctx); /* subfolder "conn.http_headers" */
for (i = 0; i < conn->request_info.num_headers; i++) {
duk_push_string(ctx, conn->request_info.http_headers[i].value);
duk_put_prop_string(ctx, -2, conn->request_info.http_headers[i].name);
}
duk_put_prop_string(ctx, -2, "http_headers");
duk_put_prop_string(ctx, -2, "conn"); /* call the table "conn" */
/* Add "civetweb" object */
duk_push_global_object(ctx);
duk_push_object(ctx); /* create a new table/object ("conn") */
duk_push_string(ctx, CIVETWEB_VERSION);
duk_put_prop_string(ctx, -2, "version");
duk_push_string(ctx, script_name);
duk_put_prop_string(ctx, -2, "script_name");
if (conn->ctx != NULL) {
duk_push_c_function(ctx, duk_itf_getoption, 1 /* 1 = nargs */);
duk_push_pointer(ctx, (void *)(conn->ctx));
duk_put_prop_string(ctx, -2, civetweb_ctx_id);
duk_put_prop_string(ctx, -2, "getoption"); /* add function conn.write */
if (conn->ctx->systemName != NULL) {
duk_push_string(ctx, conn->ctx->systemName);
duk_put_prop_string(ctx, -2, "system");
}
}
duk_put_prop_string(ctx, -2, "civetweb"); /* call the table "civetweb" */
duk_push_global_stash(ctx);
duk_push_pointer(ctx, (void *)conn);
duk_put_prop_string(ctx, -2, civetweb_conn_id);
if (duk_peval_file(ctx, script_name) != 0) {
mg_cry(conn, "%s", duk_safe_to_string(ctx, -1));
goto exec_duktape_finished;
}
duk_pop(ctx); /* ignore result */
exec_duktape_finished:
duk_destroy_heap(ctx);
}

View File

@@ -1,67 +0,0 @@
===============
Duktape authors
===============
Copyright
=========
Duktape copyrights are held by its authors. Each author has a copyright
to their contribution, and agrees to irrevocably license the contribution
under the Duktape ``LICENSE.txt``.
Authors
=======
Please include an e-mail address, a link to your GitHub profile, or something
similar to allow your contribution to be identified accurately.
The following people have contributed code, website contents, or Wiki contents,
and agreed to irrevocably license their contributions under the Duktape
``LICENSE.txt`` (in order of appearance):
* Sami Vaarala <sami.vaarala@iki.fi>
* Niki Dobrev
* Andreas Öman <andreas@lonelycoder.com>
* László Langó <llango.u-szeged@partner.samsung.com>
* Legimet <legimet.calc@gmail.com>
* Karl Skomski <karl@skomski.com>
* Bruce Pascoe <fatcerberus1@gmail.com>
Other contributions
===================
The following people have contributed something other than code (e.g. reported
bugs, provided ideas, etc; roughly in order of appearance):
* Greg Burns
* Anthony Rabine
* Carlos Costa
* Aurélien Bouilland
* Preet Desai (Pris Matic)
* judofyr (http://www.reddit.com/user/judofyr)
* Jason Woofenden
* Michał Przybyś
* Anthony Howe
* Conrad Pankoff
* Jim Schimpf
* Rajaran Gaunker (https://github.com/zimbabao)
* Andreas Öman
* Doug Sanden
* Josh Engebretson (https://github.com/JoshEngebretson)
* Remo Eichenberger (https://github.com/remoe)
* Mamod Mehyar (https://github.com/mamod)
* David Demelier (https://github.com/markand)
* Tim Caswell (https://github.com/creationix)
* Mitchell Blank Jr (https://github.com/mitchblank)
* https://github.com/yushli
* Seo Sanghyeon (https://github.com/sanxiyn)
* Han ChoongWoo (https://github.com/tunz)
* Joshua Peek (https://github.com/josh)
* Bruce E. Pascoe (https://github.com/fatcerberus)
* https://github.com/Kelledin
* https://github.com/sstruchtrup
* Michael Drake (https://github.com/tlsa)
* https://github.com/chris-y
If you are accidentally missing from this list, send me an e-mail
(``sami.vaarala@iki.fi``) and I'll fix the omission.

View File

@@ -1,25 +0,0 @@
===============
Duktape license
===============
(http://opensource.org/licenses/MIT)
Copyright (c) 2013-2015 by Duktape authors (see AUTHORS.rst)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -1,30 +0,0 @@
#
# Example Makefile for building a program with embedded Duktape.
# The example program here is the Duktape command line tool.
#
DUKTAPE_SOURCES = src/duktape.c
DUKTAPE_CMDLINE_SOURCES = \
examples/cmdline/duk_cmdline.c
CC = gcc
CCOPTS = -Os -pedantic -std=c99 -Wall -fstrict-aliasing -fomit-frame-pointer
CCOPTS += -I./src # duktape.h and duk_config.h must be in include path
CCLIBS = -lm
# If you have readline, you may want to enable these. On some platforms
# -lreadline also requires -lncurses (e.g. RHEL), so it is added by default
# (you may be able to remove it)
#CCOPTS += -DDUK_CMDLINE_FANCY
#CCLIBS += -lreadline
#CCLIBS += -lncurses
# Optional feature defines, see: http://duktape.org/guide.html#compiling
CCOPTS += -DDUK_OPT_SELF_TESTS
#CCOPTS += -DDUK_OPT_DEBUG
#CCOPTS += -DDUK_OPT_DPRINT
# ...
duk: $(DUKTAPE_SOURCES) $(DUKTAPE_CMDLINE_SOURCES)
$(CC) -o $@ $(DEFINES) $(CCOPTS) $(DUKTAPE_SOURCES) $(DUKTAPE_CMDLINE_SOURCES) $(CCLIBS)

View File

@@ -1,4 +0,0 @@
codepage:
gcc -o $@ -std=c99 -O2 -Wall -Wextra -Isrc/ \
src/duktape.c examples/codepage-conv/duk_codepage_conv.c \
examples/codepage-conv/test.c -lm

View File

@@ -1,4 +0,0 @@
dummy:
coffee -c examples/coffee/globals.coffee
coffee -c examples/coffee/hello.coffee
coffee -c examples/coffee/mandel.coffee

View File

@@ -1,23 +0,0 @@
#
# Duktape command line tool with debugger support.
#
DUKTAPE_SOURCES = src/duktape.c
DUKTAPE_CMDLINE_SOURCES = \
examples/cmdline/duk_cmdline.c \
examples/debug-trans-socket/duk_trans_socket.c
CC = gcc
CCOPTS = -Os -pedantic -std=c99 -Wall -fstrict-aliasing -fomit-frame-pointer
CCOPTS += -I./src -I./examples/debug-trans-socket
CCOPTS += -DDUK_CMDLINE_DEBUGGER_SUPPORT # enable --debugger in ./duk
CCOPTS += -DDUK_OPT_DEBUGGER_SUPPORT # enable debugger support in Duktape
CCOPTS += -DDUK_OPT_INTERRUPT_COUNTER # prerequisite for debugging
CCOPTS += -DDUK_OPT_DEBUGGER_FWD_PRINTALERT # optional debugger features
CCOPTS += -DDUK_OPT_DEBUGGER_FWD_LOGGING
CCOPTS += -DDUK_OPT_DEBUGGER_DUMPHEAP
CCLIBS = -lm
duk: $(DUKTAPE_SOURCES) $(DUKTAPE_CMDLINE_SOURCES)
$(CC) -o $@ $(DEFINES) $(CCOPTS) $(DUKTAPE_SOURCES) $(DUKTAPE_CMDLINE_SOURCES) $(CCLIBS)

View File

@@ -1,7 +0,0 @@
#
# Example Makefile for building the eval example
#
eval:
gcc -o $@ -std=c99 -O2 -Wall -Wextra -Isrc/ \
src/duktape.c examples/eval/eval.c -lm

View File

@@ -1,22 +0,0 @@
#
# Example Makefile for building the eventloop example
#
evloop:
@echo "NOTE: The eventloop is example is intended to be used on Linux"
@echo " or other common UNIX variants. It is not fully portable."
@echo ""
gcc -o $@ -std=c99 -Wall -Wextra -O2 -Isrc \
examples/eventloop/main.c \
examples/eventloop/c_eventloop.c \
examples/eventloop/poll.c \
examples/eventloop/socket.c \
examples/eventloop/fileio.c \
examples/eventloop/ncurses.c \
src/duktape.c \
-lm -lncurses
@echo ""
@echo "NOTE: You must 'cd examples/eventloop' before you execute the"
@echo " eventloop binary: it relies on finding .js files in CWD"

View File

@@ -1,35 +0,0 @@
#
# Example Makefile for building a program with embedded Duktape.
#
# There are two source sets in the distribution: (1) combined sources where
# you only need duktape.c, duktape.h, and duk_config.h, and (2) separate
# sources where you have a bunch of source and header files. Whichever
# you use, simply include the relevant sources into your C project. This
# Makefile uses the combined source file.
#
DUKTAPE_SOURCES = src/duktape.c
# Compiler options are quite flexible. GCC versions have a significant impact
# on the size of -Os code, e.g. gcc-4.6 is much worse than gcc-4.5.
CC = gcc
CCOPTS = -Os -pedantic -std=c99 -Wall -fstrict-aliasing -fomit-frame-pointer
CCOPTS += -I./src # for combined sources
CCLIBS = -lm
DEFINES =
# If you want a 32-bit build on a 64-bit host
#CCOPTS += -m32
# Optional feature defines, see: http://duktape.org/guide.html#compiling
DEFINES += -DDUK_OPT_SELF_TESTS
#DEFINES += -DDUK_OPT_DEBUG
#DEFINES += -DDUK_OPT_DPRINT
#DEFINES += -DDUK_OPT_NO_TRACEBACKS
# ...
# For debugging, use -O0 -g -ggdb, and don't add -fomit-frame-pointer
hello: $(DUKTAPE_SOURCES) examples/hello/hello.c
$(CC) -o $@ $(DEFINES) $(CCOPTS) $(DUKTAPE_SOURCES) examples/hello/hello.c $(CCLIBS)

View File

@@ -1,8 +0,0 @@
#
# Example Makefile for building the jxpretty example
#
jxpretty:
gcc -o $@ -std=c99 -Wall -Wextra -O2 -Isrc \
src/duktape.c examples/jxpretty/jxpretty.c \
-lm

View File

@@ -1,7 +0,0 @@
#
# Example Makefile for building the sandbox example
#
sandbox:
gcc -o $@ -std=c99 -O2 -Wall -Wextra -Isrc/ \
src/duktape.c examples/sandbox/sandbox.c -lm

View File

@@ -1,103 +0,0 @@
=======
Duktape
=======
Duktape is a small and portable Ecmascript E5/E5.1 implementation. It is
intended to be easily embeddable into C programs, with a C API similar in
spirit to Lua's.
Duktape supports the full E5/E5.1 feature set including errors, Unicode
strings, and regular expressions, a subset of E6 features (e.g. Proxy
objects), Khronos/ES6 ArrayBuffer/TypedView, and Node.js Buffer bindings.
Duktape also provides a number of custom features such as error tracebacks,
additional data types for better C integration, combined reference counting
and mark-and sweep garbage collector, object finalizers, co-operative
threads a.k.a. coroutines, tail calls, built-in logging and module frameworks,
a built-in debugger protocol, function bytecode dump/load, and so on.
You can browse Duktape programmer's API and other documentation at:
* http://duktape.org/
In particular, you should read the getting started section:
* http://duktape.org/guide.html#gettingstarted
More examples and how-to articles are in the Duktape Wiki:
* http://wiki.duktape.org/
Building and integrating Duktape into your project is very straightforward:
* http://duktape.org/guide.html#compiling
See Makefile.hello for a concrete example::
$ cd <dist_root>
$ make -f Makefile.hello
[...]
$ ./hello
Hello world!
2+3=5
To build an example command line tool, use the following::
$ cd <dist_root>
$ make -f Makefile.cmdline
[...]
$ ./duk
((o) Duktape
duk> print('Hello world!');
Hello world!
= undefined
$ ./duk mandel.js
[...]
This distributable contains:
* ``src/``: main Duktape library in a "single source file" format (duktape.c,
duktape.h, and duk_config.h).
* ``src-separate/``: main Duktape library in multiple files format.
* ``config/``: genconfig utility for creating duk_config.h configuration
files, see: http://wiki.duktape.org/Configuring.html.
* ``examples/``: further examples for using Duktape. Although Duktape
itself is widely portable, some of the examples are Linux only.
For instance the ``eventloop`` example illustrates how ``setTimeout()``
and other standard timer functions could be implemented on Unix/Linux.
* ``extras/``: utilities and modules which don't comfortably fit into the
main Duktape library because of footprint or portability concerns.
Extras are maintained and bug fixed code, but don't have the same version
guarantees as the main Duktape library.
* ``polyfills/``: a few replacement suggestions for non-standard Javascript
functions provided by other implementations.
* ``debugger/``: a debugger with a web UI, see ``debugger/README.rst`` and
https://github.com/svaarala/duktape/blob/master/doc/debugger.rst for
details on Duktape debugger support.
* ``licenses/``: licensing information.
You can find release notes at:
* https://github.com/svaarala/duktape/blob/master/RELEASES.rst
This distributable contains Duktape version 1.3.0, created from git
commit 675165f35ea3a5bac34ff4d0a58b007cc2f442dc (v1.3.0).
Duktape is copyrighted by its authors (see ``AUTHORS.rst``) and licensed
under the MIT license (see ``LICENSE.txt``). MurmurHash2 is used internally,
it is also under the MIT license. Duktape module loader is based on the
CommonJS module loading specification (without sharing any code), CommonJS
is under the MIT license.
Have fun!
Sami Vaarala (sami.vaarala@iki.fi)

View File

@@ -1,39 +0,0 @@
=================
Duktape genconfig
=================
Overview
========
``genconfig`` is a helper script for coming up with a ``duk_config.h`` for
compiling Duktape for your platform.
To support this:
* It creates a Duktape 1.2.x compatible ``duk_config.h`` with automatic
platform detection and ``DUK_OPT_xxx`` feature options.
* It helps to create a ``duk_config.h`` for your platform/compiler
combination. You can give a base configuration and then force certain
values manually based on a YAML configuration file.
* It autogenerates documentation for config options (and Duktape 1.2.x
feature options) based on option metadata files written in YAML.
Usage
=====
To create an autodetect duk_config.h header (compatible with Duktape 1.2.x)::
$ python config/genconfig.py --metadata config --output /tmp/duk_config.h \
autodetect-header
To create a barebones duk_config.h header for a specific platform (easier to
edit manually)::
$ python config/genconfig.py --metadata config --output /tmp/duk_config.h \
--platform linux --compiler gcc --architecture x64 \
barebones-header
There are further commands to e.g. autogenerate config option documentation;
see ``genconfig.py`` for details.

File diff suppressed because it is too large Load Diff

View File

@@ -1,80 +0,0 @@
NODE:=$(shell which nodejs node | head -1)
# Try to get a useful default --source-dirs which works both in the Duktape
# repo and in the distributable. We don't want to add '..' because it would
# scan a lot of undesired files in the Duktape repo (e.g. test262 testcases).
ifeq ($(wildcard ../tests/ecmascript/*.js),)
SOURCEDIRS:=../
else
SOURCEDIRS:=../tests/ecmascript
endif
.PHONY: all
all: run
.PHONY: run
run: node_modules static/socket.io-1.2.0.js static/jquery-1.11.1.min.js static/reset.css static/jquery-ui.min.js static/jquery-ui.min.css static/images
$(NODE) duk_debug.js --source-dirs=$(SOURCEDIRS)
.PHONY: runproxy
runproxy: node_modules static/socket.io-1.2.0.js static/jquery-1.11.1.min.js static/reset.css static/jquery-ui.min.js static/jquery-ui.min.css static/images
$(NODE) duk_debug.js --json-proxy
.PHONY: clean
clean:
@rm -f static/socket.io-1.2.0.js
@rm -f static/jquery-1.11.1.min.js
@rm -f static/jquery.syntaxhighlighter.min.js
@rm -f static/jquery.snippet.min.js
@rm -f static/jquery.snippet.min.css
@rm -f static/prefixfree.min.js
@rm -f static/reset.css
@rm -f static/jquery-ui.min.js
@rm -f static/jquery-ui.min.css
@rm -rf static/images
@rm -f jquery-ui-1.11.2.zip
@rm -rf jquery-ui-1.11.2
@rm -rf node_modules
node_modules:
npm install
static/socket.io-1.2.0.js:
wget -O $@ https://cdn.socket.io/socket.io-1.2.0.js
static/jquery-1.11.1.min.js:
wget -O $@ http://code.jquery.com/jquery-1.11.1.min.js
# http://balupton.github.io/jquery-syntaxhighlighter/demo/
static/jquery.syntaxhighlighter.min.js:
wget -O $@ http://balupton.github.com/jquery-syntaxhighlighter/scripts/jquery.syntaxhighlighter.min.js
# http://steamdev.com/snippet/
static/jquery.snippet.min.js:
wget -O $@ http://steamdev.com/snippet/js/jquery.snippet.min.js
static/jquery.snippet.min.css:
wget -O $@ http://steamdev.com/snippet/css/jquery.snippet.min.css
# http://prismjs.com/
# http://prismjs.com/plugins/line-highlight/
#
# XXX: prism download manually?
# https://raw.github.com/LeaVerou/prefixfree/gh-pages/prefixfree.min.js
static/prefixfree.min.js:
wget -O $@ https://raw.github.com/LeaVerou/prefixfree/gh-pages/prefixfree.min.js
# http://meyerweb.com/eric/tools/css/reset/
static/reset.css:
wget -O $@ http://meyerweb.com/eric/tools/css/reset/reset.css
jquery-ui-1.11.2.zip:
wget -O $@ http://jqueryui.com/resources/download/jquery-ui-1.11.2.zip
jquery-ui-1.11.2: jquery-ui-1.11.2.zip
unzip $<
static/jquery-ui.min.js: jquery-ui-1.11.2
cp jquery-ui-1.11.2/jquery-ui.min.js $@
static/jquery-ui.min.css: jquery-ui-1.11.2
cp jquery-ui-1.11.2/jquery-ui.min.css $@
static/images: jquery-ui-1.11.2
cp -r jquery-ui-1.11.2/images static/

View File

@@ -1,268 +0,0 @@
=========================================
Duktape debug client and JSON debug proxy
=========================================
Overview
========
Debugger web UI which connects to the Duktape command line tool or any other
target supporting the example TCP transport (``examples/debug-trans-socket``).
Also provides a JSON debug proxy with a JSON mapping for the Duktape debug
protocol.
For detailed documentation of the debugger internals, see `debugger.rst`__.
__ https://github.com/svaarala/duktape/blob/master/doc/debugger.rst
Using the debugger web UI
=========================
Some prerequisites:
* You'll need Node.js v0.10.x or newer. Older Node.js versions don't support
the required packages.
Compile Duktape command line tool with debugger support (for further options
see ``doc/feature-options.rst``):
* ``DUK_OPT_DEBUGGER_SUPPORT``
* ``DUK_OPT_INTERRUPT_COUNTER``
* ``DUK_CMDLINE_DEBUGGER_SUPPORT``
The source distributable contains a Makefile to build a "duk" command with
debugger support::
$ cd <duktape dist directory>
$ make -f Makefile.dukdebug
The Duktape Git repo "duk" target has debugger support enabled by default::
$ make clean duk
Start Duktape command line tool so that it waits for a debugger connection::
# For now we need to be in the directory containing the source files
# executed so that the 'fileName' properties of functions will match
# that on the debug client.
# Using source distributable
$ cd <duktape dist directory>
$ ./duk --debugger mandel.js
# Using Duktape Git repo
$ cd <duktape checkout>/tests/ecmascript/
$ ../../duk --debugger test-dev-mandel2-func.js
Start the web UI::
# Must be in 'debugger' directory.
$ cd debugger/
$ make # runs 'node duk_debug.js'
Once the required packages are installed, the NodeJS debug client will be
up and running. Open the following in your browser and start debugging:
* http://localhost:9092/
The debug client automatically attaches to the debug target on startup.
If you start the debug target later, you'll need to click "Attach" in the
web UI.
Using the JSON debug proxy
==========================
A JSON debug proxy is also provided by ``duk_debug.js``::
# Same prerequisites as above
$ make runproxy
Start Duktape command line (or whatever your target is)::
$ cd <duktape checkout>/tests/ecmascript/
$ ../../duk --debugger test-dev-mandel2-func.js
You can then connect to localhost:9093 and interact with the proxy.
Here's an example session using telnet and manually typed in commands
The ``-->`` (send) and ``<--`` (receiver) markers have been added for
readability and are not part of the stream::
$ telnet localhost 9093
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
<-- {"notify":"_Connected","args":["1 10199 v1.1.0-275-gbd4d610-dirty duk command built from Duktape repo"]}
<-- {"notify":"Status","command":1,"args":[1,"test-dev-mandel2-func.js","global",58,0]}
--> {"request":"BasicInfo"}
<-- {"reply":true,"args":[10199,"v1.1.0-275-gbd4d610-dirty","duk command built from Duktape repo",1]}
--> {"request":"Eval", "args":[ "print(Math.PI)" ]}
<-- {"notify":"Print","command":2,"args":["3.141592653589793\n"]}
<-- {"reply":true,"args":[0,{"type":"undefined"}]}
--> {"request":"Resume"}
<-- {"reply":true,"args":[]}
<-- {"notify":"Status","command":1,"args":[0,"test-dev-mandel2-func.js","global",58,0]}
<-- {"notify":"Status","command":1,"args":[0,"test-dev-mandel2-func.js","global",58,0]}
<-- {"notify":"Print","command":2,"args":["................................................................................\n"]}
<-- {"notify":"Print","command":2,"args":["................................................................................\n"]}
<-- {"notify":"Print","command":2,"args":["................................................................................\n"]}
[...]
<-- {"notify":"_Disconnecting"}
A telnet connection allows you to experiment with debug commands by simply
copy-pasting debug commands to the telnet session. This is useful even if
you decide to implement the binary protocol directly.
The debug target used by the proxy can be configured with ``duk_debug.js``
command line options.
Source search path
==================
The NodeJS debug client needs to be able to find source code files matching
code running on the target ("duk" command line). **The filenames used on the
target and on the debug client must match exactly**, because e.g. breakpoints
are targeted based on the 'fileName' property of Function objects.
The search path can be set using the ``--source-dirs`` option given to
``duk_debug.js``, with the default search paths including only
``../tests/ecmascript/``.
The default search path means that if a function on the target has fileName
``foo/bar.js`` it would be loaded from (relative to the duk_debug.js working
directory, ``debugger/``)::
../tests/ecmascript/foo/bar.js
Similarly, if the filesystem contained::
../tests/ecmascript/baz/quux.js
the web UI dropdown would show ``baz/quux.js``. If you selected that file
and added a breakpoint, the breakpoint fileName sent to the debug target
would be ``baz/quux.js``.
.. note:: There's much to improve in the search path. For instance, it'd
be nice to add a certain path to search but exclude files based
on paths and patterns, etc.
Architecture
============
::
+-------------------+
| Web browser | [debug UI]
+-------------------+
|
| http (port 9092)
| socket.io
v
+-------------------+
| duk_debug.js | [debug client]
+-------------------+
| /\
| ||
+----------||---- [example tcp transport] (port 9091)
| || (application provides concrete transport)
| ||
| ||---- [debug protocol stream]
| || (between debug client and Duktape)
| ||
+ - - | - - - - -|| - - +
: v || :
 : +-------------||-+ : [target]
: | application || | :
: +-------------||-+ :
: ^ || :
: | || : [debug API]
: +----------||-------- debug transport callbacks
: | || : (read, write, peek, read/write flush)
: | || : implemented by application
: | \/ :
: +----------------+ :
: | Duktape | :
: +----------------+ :
+ - - - - - - - - - - - +
The debug transport is application specific:
* Duktape command line ("duk") and this debug client use an **example** TCP
transport as a concrete example.
* It is entirely up to the application to come up with the most suitable
transport for its environment. Different mechanisms will be needed for
Wi-Fi, serial, etc.
The debug protocol running inside the transport is transport independent:
* The debug protocol is documented in ``doc/debugger.rst``.
* This debug client provides further concrete examples and clarifications
on how the protocol can be used.
Using a custom transport
========================
Quite possibly your target device cannot use the example TCP transport and
you need to implement your own transport. You'll need to implement your
custom transport both for the target device and for the debug client.
Target device
-------------
Implement the debug transport callbacks needed by ``duk_debugger_attach()``.
See ``doc/debugger.rst`` for details and ``examples/debug-trans-socket``
for example running code for a TCP transport.
Debug client alternative 1: duk_debug.js + custom TCP proxy
-----------------------------------------------------------
If you don't want to change ``duk_debug.js`` you can implement a TCP proxy
which accepts a TCP connection from ``duk_debug.js`` and then uses your
custom transport to talk to the target::
+--------------+ TCP +-------+ custom +--------+
| duk_debug.js | ------> | proxy | ---------> | target |
+--------------+ +-------+ +--------+
This is a straightforward option and a proxy can be used with other debug
clients too (perhaps custom scripts talking to the target etc).
You could also use netcat and implement your proxy so that it talks to
``duk_debug.js`` using stdin/stdout.
Debug client alternative 2: duk_debug.js + custom NodeJS stream
---------------------------------------------------------------
To make ``duk_debug.js`` use a custom transport you need to:
* Implement your own transport as NodeJS stream. You can add it directly to
``duk_debug.js`` but it's probably easiest to use a separate module so that
the diff to ``duk_debug.js`` stays minimal.
* Change ``duk_debug.js`` to use the custom transport instead of a TCP
stream. Search for "CUSTOMTRANSPORT" in ``duk_debug.js``.
See:
* http://nodejs.org/api/stream.html
* https://github.com/substack/stream-handbook
Debug client alternative 3: custom debug client
-----------------------------------------------
You can also implement your own debug client and debug UI with support for
your custom transport.
You'll also need to implement the client part of the Duktape debugger
protocol. See ``doc/debugger.rst`` for the specification and ``duk_debug.js``
for example running code which should illustrate the protocol in more detail.
The JSON debug proxy allows you to implement a debug client without needing
to implement the Duktape binary debug protocol. The JSON protocol provides
a roughly 1:1 mapping to the binary protocol but with an easier syntax.

View File

@@ -1,31 +0,0 @@
# Must match C header
- unused
- Arguments
- Array
- Boolean
- Date
- Error
- Function
- JSON
- Math
- Number
- Object
- RegExp
- String
- global
- ObjEnv
- DecEnv
- Buffer
- Pointer
- Thread
- ArrayBuffer
- DataView
- Int8Array
- Uint8Array
- Uint8ClampedArray
- Int16Array
- Uint16Array
- Int32Array
- Uint32Array
- Float32Array
- Float64Array

File diff suppressed because it is too large Load Diff

View File

@@ -1,36 +0,0 @@
# Debug command names. A single map works for now because command names
# provided by client/target don't overlap.
- Reserved_0
- Status
- Print
- Alert
- Log
- Gc
- Reserved_6
- Reserved_7
- Reserved_8
- Reserved_9
- Reserved_10
- Reserved_11
- Reserved_12
- Reserved_13
- Reserved_14
- Reserved_15
- BasicInfo
- TriggerStatus
- Pause
- Resume
- StepInto
- StepOver
- StepOut
- ListBreak
- AddBreak
- DelBreak
- GetVar
- PutVar
- GetCallStack
- GetLocals
- Eval
- Detach
- DumpHeap
- GetBytecode

View File

@@ -1,660 +0,0 @@
# Duktape opcode metadata for debugger.
# - See duk_debug.js for the argument formats (A_R etc).
# - Flag bits are for the whole instruction as a 32-bit integer,
# they are not field shifted
#
# NOTE: Use YAML comments only on comment-only lines (not trailing content):
# Node.js 'yamljs' seems to refuse parsing trailing comments in some cases.
opcodes:
- name: LDREG
args:
- A_R
- BC_R
- name: STREG
args:
- A_R
- BC_R
- name: LDCONST
args:
- A_R
- BC_C
- name: LDINT
args:
- A_R
- BC_LDINT
- name: LDINTX
args:
- A_R
- BC_LDINTX
- name: MPUTOBJ
args:
- A_R
- B_R
- C_I
- name: MPUTOBJI
args:
- A_R
- B_RI
- C_I
- name: MPUTARR
args:
- A_R
- B_R
- C_I
- name: MPUTARRI
args:
- A_R
- B_RI
- C_I
- name: NEW
args:
- B_R
- C_I
- name: NEWI
args:
- B_RI
- C_I
- name: REGEXP
args:
- A_R
- B_RC
- C_RC
- name: CSREG
args:
- A_R
- B_R
- name: CSREGI
args:
- A_RI
- B_R
- name: GETVAR
args:
- A_R
- BC_C
- name: PUTVAR
args:
- A_R
- BC_C
- name: DECLVAR
args:
- A_H
- B_RC
- C_RC
flags:
- mask: 0x40
name: writable
- mask: 0x80
name: enumerable
- mask: 0x100
name: configurable
- mask: 0x200
name: accessor
- mask: 0x400
name: undef_value
- mask: 0x800
name: func_decl
- name: DELVAR
args:
- A_R
- B_RC
- name: CSVAR
args:
- A_R
- B_RC
- name: CSVARI
args:
- A_RI
- B_RC
- name: CLOSURE
args:
- A_R
- BC_I
- name: GETPROP
args:
- A_R
- B_RC
- C_RC
- name: PUTPROP
args:
- A_R
- B_RC
- C_RC
- name: DELPROP
args:
- A_R
- B_R
- C_RC
- name: CSPROP
args:
- A_R
- B_R
- C_RC
- name: CSPROPI
args:
- A_RI
- B_R
- C_RC
- name: ADD
args:
- A_R
- B_RC
- C_RC
- name: SUB
args:
- A_R
- B_RC
- C_RC
- name: MUL
args:
- A_R
- B_RC
- C_RC
- name: DIV
args:
- A_R
- B_RC
- C_RC
- name: MOD
args:
- A_R
- B_RC
- C_RC
- name: BAND
args:
- A_R
- B_RC
- C_RC
- name: BOR
args:
- A_R
- B_RC
- C_RC
- name: BXOR
args:
- A_R
- B_RC
- C_RC
- name: BASL
args:
- A_R
- B_RC
- C_RC
- name: BLSR
args:
- A_R
- B_RC
- C_RC
- name: BASR
args:
- A_R
- B_RC
- C_RC
- name: EQ
args:
- A_R
- B_RC
- C_RC
- name: NEQ
args:
- A_R
- B_RC
- C_RC
- name: SEQ
args:
- A_R
- B_RC
- C_RC
- name: SNEQ
args:
- A_R
- B_RC
- C_RC
- name: GT
args:
- A_R
- B_RC
- C_RC
- name: GE
args:
- A_R
- B_RC
- C_RC
- name: LT
args:
- A_R
- B_RC
- C_RC
- name: LE
args:
- A_R
- B_RC
- C_RC
- name: IF
args:
- A_B
- B_RC
- name: JUMP
args:
- ABC_JUMP
- name: RETURN
args:
- A_H
- B_RC
flags:
- mask: 0x40
name: fast_return
- mask: 0x80
name: have_retval
- name: CALL
args:
- A_H
- B_R
- C_I
flags:
- mask: 0x40
name: tailcall
- mask: 0x80
name: evalcall
- name: CALLI
args:
- A_H
- B_RI
- C_I
- name: TRYCATCH
args:
- A_H
# base register for two consecutive regs (base_reg + 0, base_reg + 1) used for two things:
# - input: either 'with' target register or catch varname constant (base_reg + 0), depending on flags
# - output: when caught, catch value (base_reg + 0) and type (base_reg + 1)
- BC_R
flags:
- mask: 0x40
name: have_catch
- mask: 0x80
name: have_finally
- mask: 0x100
name: catch_binding
- mask: 0x200
name: with_binding
- name: EXTRA
extra: true
- name: PREINCR
args:
- A_R
- BC_R
- name: PREDECR
args:
- A_R
- BC_R
- name: POSTINCR
args:
- A_R
- BC_R
- name: POSTDECR
args:
- A_R
- BC_R
- name: PREINCV
args:
- A_R
- BC_C
- name: PREDECV
args:
- A_R
- BC_C
- name: POSTINCV
args:
- A_R
- BC_C
- name: POSTDECV
args:
- A_R
- BC_C
- name: PREINCP
args:
- A_R
- B_RC
- C_RC
- name: PREDECP
args:
- A_R
- B_RC
- C_RC
- name: POSTINCP
args:
- A_R
- B_RC
- C_RC
- name: POSTDECP
args:
- A_R
- B_RC
- C_RC
extra:
- name: NOP
- name: INVALID
args:
- BC_I
- name: LDTHIS
args:
- BC_R
- name: LDUNDEF
args:
- BC_R
- name: LDNULL
args:
- BC_R
- name: LDTRUE
args:
- BC_R
- name: LDFALSE
args:
- BC_R
- name: NEWOBJ
args:
# XXX: extend to BC?
- B_R
- name: NEWARR
args:
# XXX: extend to BC?
- B_R
- name: SETALEN
args:
- B_R
- C_R
- name: TYPEOF
args:
- BC_R
- name: TYPEOFID
args:
- B_R
# maybe changed to C_C later
- C_RC
- name: INITENUM
args:
- B_R
- C_R
- name: NEXTENUM
args:
- B_R
- C_R
- name: INITSET
args:
- B_R
- C_R
- name: INITSETI
args:
- B_R
- C_RI
- name: INITGET
args:
- B_R
- C_RI
- name: INITGETI
args:
- B_R
- C_RI
- name: ENDTRY
- name: ENDCATCH
- name: ENDFIN
- name: THROW
args:
- BC_R
- name: INVLHS
- name: UNM
args:
- BC_R
- name: UNP
args:
- BC_R
- name: DEBUGGER
- name: BREAK
args:
- BC_I
- name: CONTINUE
args:
- BC_I
- name: BNOT
args:
- BC_R
- name: LNOT
args:
- BC_R
- name: INSTOF
args:
- B_R
- C_RC
- name: IN
args:
- B_R
- C_RC
- name: LABEL
args:
- BC_I
- name: ENDLABEL
args:
- BC_I
- name: EXTRA34
- name: EXTRA35
- name: EXTRA36
- name: EXTRA37
- name: EXTRA38
- name: EXTRA39
- name: EXTRA40
- name: EXTRA41
- name: EXTRA42
- name: EXTRA43
- name: EXTRA44
- name: EXTRA45
- name: EXTRA46
- name: EXTRA47
- name: EXTRA48
- name: EXTRA49
- name: EXTRA50
- name: EXTRA51
- name: EXTRA52
- name: EXTRA53
- name: EXTRA54
- name: EXTRA55
- name: EXTRA56
- name: EXTRA57
- name: EXTRA58
- name: EXTRA59
- name: EXTRA60
- name: EXTRA61
- name: EXTRA62
- name: EXTRA63
- name: EXTRA64
- name: EXTRA65
- name: EXTRA66
- name: EXTRA67
- name: EXTRA68
- name: EXTRA69
- name: EXTRA70
- name: EXTRA71
- name: EXTRA72
- name: EXTRA73
- name: EXTRA74
- name: EXTRA75
- name: EXTRA76
- name: EXTRA77
- name: EXTRA78
- name: EXTRA79
- name: EXTRA80
- name: EXTRA81
- name: EXTRA82
- name: EXTRA83
- name: EXTRA84
- name: EXTRA85
- name: EXTRA86
- name: EXTRA87
- name: EXTRA88
- name: EXTRA89
- name: EXTRA90
- name: EXTRA91
- name: EXTRA92
- name: EXTRA93
- name: EXTRA94
- name: EXTRA95
- name: EXTRA96
- name: EXTRA97
- name: EXTRA98
- name: EXTRA99
- name: EXTRA100
- name: EXTRA101
- name: EXTRA102
- name: EXTRA103
- name: EXTRA104
- name: EXTRA105
- name: EXTRA106
- name: EXTRA107
- name: EXTRA108
- name: EXTRA109
- name: EXTRA110
- name: EXTRA111
- name: EXTRA112
- name: EXTRA113
- name: EXTRA114
- name: EXTRA115
- name: EXTRA116
- name: EXTRA117
- name: EXTRA118
- name: EXTRA119
- name: EXTRA120
- name: EXTRA121
- name: EXTRA122
- name: EXTRA123
- name: EXTRA124
- name: EXTRA125
- name: EXTRA126
- name: EXTRA127
- name: EXTRA128
- name: EXTRA129
- name: EXTRA130
- name: EXTRA131
- name: EXTRA132
- name: EXTRA133
- name: EXTRA134
- name: EXTRA135
- name: EXTRA136
- name: EXTRA137
- name: EXTRA138
- name: EXTRA139
- name: EXTRA140
- name: EXTRA141
- name: EXTRA142
- name: EXTRA143
- name: EXTRA144
- name: EXTRA145
- name: EXTRA146
- name: EXTRA147
- name: EXTRA148
- name: EXTRA149
- name: EXTRA150
- name: EXTRA151
- name: EXTRA152
- name: EXTRA153
- name: EXTRA154
- name: EXTRA155
- name: EXTRA156
- name: EXTRA157
- name: EXTRA158
- name: EXTRA159
- name: EXTRA160
- name: EXTRA161
- name: EXTRA162
- name: EXTRA163
- name: EXTRA164
- name: EXTRA165
- name: EXTRA166
- name: EXTRA167
- name: EXTRA168
- name: EXTRA169
- name: EXTRA170
- name: EXTRA171
- name: EXTRA172
- name: EXTRA173
- name: EXTRA174
- name: EXTRA175
- name: EXTRA176
- name: EXTRA177
- name: EXTRA178
- name: EXTRA179
- name: EXTRA180
- name: EXTRA181
- name: EXTRA182
- name: EXTRA183
- name: EXTRA184
- name: EXTRA185
- name: EXTRA186
- name: EXTRA187
- name: EXTRA188
- name: EXTRA189
- name: EXTRA190
- name: EXTRA191
- name: EXTRA192
- name: EXTRA193
- name: EXTRA194
- name: EXTRA195
- name: EXTRA196
- name: EXTRA197
- name: EXTRA198
- name: EXTRA199
- name: EXTRA200
- name: EXTRA201
- name: EXTRA202
- name: EXTRA203
- name: EXTRA204
- name: EXTRA205
- name: EXTRA206
- name: EXTRA207
- name: EXTRA208
- name: EXTRA209
- name: EXTRA210
- name: EXTRA211
- name: EXTRA212
- name: EXTRA213
- name: EXTRA214
- name: EXTRA215
- name: EXTRA216
- name: EXTRA217
- name: EXTRA218
- name: EXTRA219
- name: EXTRA220
- name: EXTRA221
- name: EXTRA222
- name: EXTRA223
- name: EXTRA224
- name: EXTRA225
- name: EXTRA226
- name: EXTRA227
- name: EXTRA228
- name: EXTRA229
- name: EXTRA230
- name: EXTRA231
- name: EXTRA232
- name: EXTRA233
- name: EXTRA234
- name: EXTRA235
- name: EXTRA236
- name: EXTRA237
- name: EXTRA238
- name: EXTRA239
- name: EXTRA240
- name: EXTRA241
- name: EXTRA242
- name: EXTRA243
- name: EXTRA244
- name: EXTRA245
- name: EXTRA246
- name: EXTRA247
- name: EXTRA248
- name: EXTRA249
- name: EXTRA250
- name: EXTRA251
- name: EXTRA252
- name: EXTRA253
- name: EXTRA254
- name: EXTRA255

View File

@@ -1,27 +0,0 @@
{
"name": "duk-debug",
"version": "0.1.0",
"description": "Duktape debugger",
"author": {
"name": "Sami Vaarala",
"email": "sami.vaarala@iki.fi"
},
"dependencies": {
"bluebird": "~2.6.4",
"minimist": "~1.1.0",
"express": "~4.10.1",
"body-parser": "~1.9.3",
"socket.io": "~1.2.1",
"utf8": "~2.0.0",
"wrench": "~1.5.8",
"sprintf": "~0.1.5",
"events": "~1.0.2",
"stream": "0.0.2",
"readline": "0.0.5",
"util": "~0.10.3",
"http": "0.0.0",
"yamljs": "~0.2.1",
"byline": "~4.2.1"
},
"main": "duk_debug.js"
}

View File

@@ -1,96 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="stylesheet" href="reset.css" type="text/css" />
<link rel="stylesheet" href="jquery-ui.min.css" type="text/css" />
<link rel="stylesheet" href="style.css" type="text/css" />
<title>Duktape debugger</title>
</head>
<body>
<div id="part-header">
((o) Duktape debugger
</div> <!-- #part-header -->
<div id="part-middle">
<div id="left-area">
<button id="stepinto-button">Step&#x00a0;into</button>
<button id="stepover-button">Step&#x00a0;over</button>
<button id="stepout-button">Step&#x00a0;out</button>
<button id="resume-button">Resume</button>
<button id="pause-button">Pause</button>
<br />
<br />
<br />
<br />
<button id="attach-button">Attach</button>
<button id="detach-button">Detach</button>
<button id="about-button">About</button>
<button id="heap-dump-download-button"><a id="heap-dump-download" href="/heapDump.json" target="_blank">Dump&#x00a0;heap</a></button>
<button id="show-bytecode-button">Show bytecode</button>
</div> <!-- #left-area -->
<div id="center-area">
<pre id="source-pre" class="sourcecode"><code id="source-code" class="sourcecode">
// No source loaded
</code></pre>
<div>
<select id="source-select"></select><br />
<!-- <span id="source-filename">?</span> -->
</div>
<div id="exec-status">
<div id="exec-state"><span id="current-state">?</span></div>
<div id="exec-other"><span id="current-fileline">?</span><br /><span id="current-funcpc">?</span></div>
</div> <!-- #exec-status -->
<div id="output">
<div style="color: #dddddd">(output from script, print() and alert() calls)</div>
</div>
</div> <!-- #center-area -->
<div id="right-area">
<div id="callstack">
<div style="color: #dddddd">(callstack)</div>
</div>
<div id="locals">
<div style="color: #dddddd">(locals)</div>
</div>
<div id="breakpoints">
<div style="color: #dddddd">(breakpoints)</div>
</div>
<div id="eval">
<input id="eval-input" value="print('hello world'); 1+2" /><button id="eval-button">Eval</button><input id="eval-watch" type="checkbox" />&#xa0;watch (eval on pause)
<div id="eval-output"></div>
<button id="putvar-button">PutVar</button><button id="getvar-button">GetVar</button><input id="varname-input" value="varname" /><input id="varvalue-input" value="varvalue" />
<div id="var-output"></div>
</div>
</div> <!-- #right-area -->
</div> <!-- #part-middle -->
<div id="part-footer">
<div>DUK_VERSION: <span id="duk-version">?</span>, DUK_GIT_DESCRIBE: <span id="duk-git-describe">?</span>, Target info: <span id="target-info">?</span>, Endianness: <span id="endianness">?</span><br />
Debug protocol stats:
recv <span id="debug-rx-bytes">?</span> (<span id="debug-rx-kbrate">?</span> kB/s), <span id="debug-rx-dvalues">?</span> dvalues, <span id="debug-rx-messages">?</span> messages;
send <span id="debug-tx-bytes">?</span> (<span id="debug-tx-kbrate">?</span> kB/s), <span id="debug-tx-dvalues">?</span> dvalues, <span id="debug-tx-messages">?</span> messages
</div>
</div> <!-- #part-footer -->
<div id="about-dialog" title="About Duktape debugger">
<p>Duktape debugger is a web UI for debugging Ecmascript on a target device.</p>
<p>This web UI talks to a NodeJS debug server using <a href="http://socket.io/" target="_blank">socket.io</a>.
The debug server talks to the target device using the Duktape debug protocol
(see <a href="https://github.com/svaarala/duktape/blob/master/doc/debugger.rst" target="_blank">debugger.rst</a>).</p>
</div> <!-- #about-dialog -->
<div id="bytecode-dialog" title="Bytecode for current function">
<pre id="bytecode-preformatted"></pre>
</div>
<script src="jquery-1.11.1.min.js" type="text/javascript"></script>
<script src="jquery-ui.min.js" type="text/javascript"></script>
<script src="socket.io-1.2.0.js" type="text/javascript"></script>
<script src="webui.js" type="text/javascript"></script>
</body>
</html>

View File

@@ -1,512 +0,0 @@
// http://stackoverflow.com/questions/71074/how-to-remove-firefoxs-dotted-outline-on-buttons-as-well-as-links/3844452#3844452
:focus {
outline: none;
}
::-moz-focus-inner {
border: 0;
}
@keyframes pulsate {
from { opacity: 1; }
to { opacity: 0.25; }
}
#part-header {
background: #444444;
color: #ffffff;
font: 24pt monospace;
border-bottom: 2px solid #cccccc;
padding: 20px 0px 20px 10px;
}
/* http://css-tricks.com/snippets/css/a-guide-to-flexbox/ */
#part-middle {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: space-between;
align-items: stretch;
align-content: stretch;
min-height: 800px;
border-top: 1px solid #ffffff;
padding: 8px;
margin-top: 2px;
}
#left-area {
flex: 0 0 11em;
margin-right: 20px;
margin-bottom: 10px;
}
#center-area {
flex: 1 1 0;
margin-bottom: 10px;
}
#right-area {
flex: 0 0 40em;
margin-left: 20px;
margin-bottom: 10px;
}
#part-footer {
clear: both;
border-top: 2px solid #bbbbbb;
background: #eeeeee;
color: #555555;
text-align: center;
padding-top: 12px;
padding-bottom: 12px;
line-height: 1.5;
}
#exec-status {
margin-top: 25px;
margin-bottom: 25px;
}
#exec-state {
display: inline-block;
vertical-align: middle;
}
#exec-other {
display: inline-block;
vertical-align: middle;
font-size: 125%;
}
#current-state {
background: #228822;
color: #ffffff;
font: 16pt;
padding: 6pt;
border: 5px solid #228822;
border-radius: 10px;
font-size: 200%;
font-weight: bold;
margin-right: 10px;
}
#current-state.notrunning {
background: #882222;
border: 5px solid #882222;
border-radius: 10px;
animation: pulsate 0.7s cubic-bezier(0.75, 0, 0.75, 1) infinite alternate;
}
#exec-other:hover {
text-decoration: underline;
color: #9999ff;
}
#left-area button {
display: inline-block;
width: 100%;
min-width: 8em;
background: #226622;
color: #ffffff;
font: 16pt sans-serif;
font-weight: bold;
text-decoration: none;
margin: 10px 0 0 0;
padding: 0.4em;
border: 2px solid #000000;
border-radius: 4px;
}
#left-area button a {
color: #ffffff;
text-decoration: none;
}
#left-area button:hover {
background: #55aa55;
}
#left-area button:disabled {
background: #555555;
color: #888888;
}
#left-area button:disabled a {
background: #555555;
color: #888888;
}
#pause-button.pending {
background: #5555ff;
animation: pulsate 0.2s cubic-bezier(0.75, 0, 0.75, 1) infinite alternate;
}
#attach-button {
}
#attach-button.enabled {
animation: pulsate 0.7s cubic-bezier(0.75, 0, 0.75, 1) infinite alternate;
}
.duktape-exec-line {
outline: 2px solid red;
background: #550000;
}
.duktape-break-line {
outline: 2px solid white;
}
#output {
font: 9pt monospace;
color: #000000;
border: 2px solid #cccccc;
border-radius: 5px;
padding: 3px;
height: 30ex;
overflow: scroll;
overflow-x: auto;
overflow-y: scroll;
white-space: pre;
}
#output .alert {
color: #ff0000;
}
/* Default color (should be overridden by level) */
#output .log {
color: #00ff00;
}
/* Trace */
#output .loglevel0 {
color: #cccccc;
}
/* Debug */
#output .loglevel1 {
color: #cccccc;
}
/* Info */
#output .loglevel2 {
color: #888888;
font-weight: bold;
}
/* Warn */
#output .loglevel3 {
color: #ff4444;
font-weight: bold;
}
/* Error */
#output .loglevel4 {
color: #ff0000;
font-weight: bold;
}
/* Fatal */
#output .loglevel5 {
background: #000000;
color: #ff0000;
font-weight: bold;
}
#output .debugger-info {
color: #880000;
font-weight: bold;
font-style: italic;
}
#output .debugger-debug {
color: #888888;
font-weight: bold;
font-style: italic;
}
#callstack {
font: 9pt monospace;
color: #000000;
margin-top: 10px;
border: 2px solid #cccccc;
border-radius: 5px;
padding: 3px;
height: 14ex;
overflow: scroll;
overflow-x: auto;
overflow-y: scroll;
white-space: pre;
}
#callstack div:nth-child(2n) {
background: #eeeeee;
}
#callstack .func {
}
#callstack .rest {
float: right;
color: #6666ff;
}
#callstack .rest:hover {
text-decoration: underline;
color: #9999ff;
}
#locals {
font: 9pt monospace;
color: #000000;
margin-top: 10px;
border: 2px solid #cccccc;
border-radius: 5px;
padding: 10px;
height: 30ex;
overflow: scroll;
overflow-x: auto;
overflow-y: scroll;
white-space: pre;
}
#locals div:nth-child(2n) {
background: #eeeeee;
}
#locals .key {
}
#locals .value {
float: right;
color: #888888;
}
#breakpoints {
color: #000000;
margin-top: 10px;
border: 2px solid #cccccc;
border-radius: 5px;
padding: 3px;
height: 15ex;
overflow: scroll;
overflow-x: auto;
overflow-y: scroll;
white-space: pre;
}
#breakpoints div {
margin: 2px 0 2px 0;
}
#breakpoints div:nth-child(2n) {
background: #eeeeee;
}
#breakpoints a {
font: 9pt monospace;
color: #6666ff;
}
#breakpoints a:hover {
text-decoration: underline;
color: #9999ff;
}
.breakpoint-line {
clear: both;
padding-top: 2px;
padding-bottom: 2px;
}
#add-breakpoint-file {
font: 10pt monospace;
width: 10em;
padding: 5px;
}
#add-breakpoint-line {
font: 10pt monospace;
width: 3em;
margin-left: 3px;
padding: 5px;
}
#delete-all-breakpoints-button {
float: right;
font: 10pt sans-serif;
padding: 5px;
border: 1px solid #888888;
background: #ddffdd;
color: #000000;
}
#delete-all-breakpoints-button:hover {
background: #f8fff8;
}
#delete-all-breakpoints-button:disabled {
background: #dddddd;
color: #444444;
}
#add-breakpoint-button {
font: 10pt sans-serif;
margin-left: 10px;
padding: 5px;
border: 1px solid #888888;
background: #ddffdd;
color: #000000;
}
#add-breakpoint-button:hover {
background: #f8fff8;
}
#add-breakpoint-button:disabled {
background: #dddddd;
color: #444444;
}
#breakpoint-hint {
color: #aaaaaa;
font-style: italic;
margin-left: 10px;
}
.delete-breakpoint-button {
float: right;
display: inline;
font: 9pt sans-serif;
padding: 3px;
border: none;
background: none;
color: #6666ff;
}
.delete-breakpoint-button {
font: 9pt sans-serif;
}
.delete-breakpoint-button:hover {
text-decoration: underline;
color: #9999ff;
}
.delete-breakpoint-button:disabled {
color: #888888;
}
#about-dialog p {
margin: 10px 0 10px 0;
}
#bytecode-dialog p {
margin: 10px 0 10px 0;
}
#bytecode-dialog pre {
font: 14pt monospace;
color: #000000;
}
#eval {
color: #000000;
margin-top: 10px;
border: 2px solid #cccccc;
border-radius: 5px;
padding: 3px;
height: 30ex;
overflow: scroll;
overflow-x: auto;
overflow-y: scroll;
white-space: pre;
}
#eval-input {
display: inline;
font: 10pt monospace;
width: 20em;
padding: 5px;
}
#eval-button {
display: inline;
margin-left: 10px;
padding: 5px;
border: 1px solid #888888;
font: 10pt sans-serif;
background: #ddffdd;
color: #000000;
}
#eval-button {
}
#eval-button:hover {
background: #f8fff8;
}
#eval-button:disabled {
background: #dddddd;
color: #444444;
}
#eval-button.pending {
background: #5555ff;
animation: pulsate 0.2s cubic-bezier(0.75, 0, 0.75, 1) infinite alternate;
}
#eval-watch {
margin-left: 20px;
vertical-align: middle;
}
#eval-output {
font: 10pt monospace;
white-space: pre;
padding: 5px;
border: 1px solid #888888;
min-height: 4ex;
margin-top: 5px;
}
#varname-input {
font: 10pt monospace;
width: 10em;
padding: 5px;
}
#varvalue-input {
margin-left: 10px;
font: 10pt monospace;
width: 20em;
padding: 5px;
}
#getvar-button,
#putvar-button {
display: inline;
float: right;
margin-left: 10px;
padding: 5px;
border: 1px solid #888888;
font: 10pt sans-serif;
background: #ddffdd;
color: #000000;
}
#getvar-button:hover,
#putvar-button:hover {
background: #f8fff8;
}
#getvar-button:disabled,
#putvar-button:disabled {
background: #dddddd;
color: #444444;
}
#var-output {
font: 10pt monospace;
white-space: pre;
padding: 5px;
border: 1px solid #888888;
min-height: 4ex;
margin-top: 5px;
}
#source-pre {
margin-top: 10px;
border: 2px solid #cccccc;
border-radius: 5px;
height: 400px;
overflow: scroll;
overflow-x: auto;
overflow-y: scroll;
}
#source-pre.running {
background: #eeeeee;
color: #888888;
}
#source-pre.running #source-code {
background: #eeeeee;
color: #888888;
}
#source-filename {
font-size: 125%;
color: #888888;
}
code.sourcecode {
counter-reset: source-line;
}
code.sourcecode div {
font: 10pt monospace;
padding: 2px 5px 2px 5px;
white-space: pre;
border-bottom: 1px solid #eeeeee;
}
code.sourcecode div:before {
display: inline-block;
content: counter(source-line);
counter-increment: source-line;
width: 4em;
color: #888888;
text-align: right;
margin-right: 20px;
}
code.sourcecode div.breakpoint:before {
margin-right: 0px;
border-right: 20px solid #ff0000;
}
code.sourcecode div.highlight {
background: #aaaaaa;
color: #000000;
}
code.sourcecode div.execution {
background: #000000;
color: #ffffff;
}
#source-select {
margin-top: 5px;
}

View File

@@ -1,745 +0,0 @@
/*
* Duktape debugger web client
*
* Talks to the NodeJS server using socket.io.
*
* http://unixpapa.com/js/key.html
*/
// Update interval for custom source highlighting.
var SOURCE_UPDATE_INTERVAL = 350;
// Source view
var activeFileName = null; // file that we want to be loaded in source view
var activeLine = null; // scroll to line once file has been loaded
var activeHighlight = null; // line that we want to highlight (if any)
var loadedFileName = null; // currently loaded (shown) file
var loadedLineCount = 0; // currently loaded file line count
var loadedFileExecuting = false; // true if currFileName (loosely) matches loadedFileName
var loadedLinePending = null; // if set, scroll loaded file to requested line
var highlightLine = null; // highlight line
var sourceEditedLines = []; // line numbers which have been modified
// (added classes etc, tracked for removing)
var sourceUpdateInterval = null; // timer for updating source view
var sourceFetchXhr = null; // current AJAX request for fetching a source file (if any)
var forceButtonUpdate = false; // hack to reset button states
// Execution state
var prevState = null; // previous execution state ('paused', 'running', etc)
var prevAttached = null; // previous debugger attached state (true, false, null)
var currFileName = null; // current filename being executed
var currFuncName = null; // current function name being executed
var currLine = 0; // current line being executed
var currPc = 0; // current bytecode PC being executed
var currState = 0; // current execution state ('paused', 'running', 'detached', etc)
var currAttached = false; // current debugger attached state (true or false)
var currLocals = []; // current local variables
var currCallstack = []; // current callstack (from top to bottom)
var currBreakpoints = []; // current breakpoints
var startedRunning = 0; // timestamp when last started running (if running)
// (used to grey out the source file if running for long enough)
/*
* Helpers
*/
function formatBytes(x) {
if (x < 1024) {
return String(x) + ' bytes';
} else if (x < 1024 * 1024) {
return (x / 1024).toPrecision(3) + ' kB';
} else {
return (x / (1024 * 1024)).toPrecision(3) + ' MB';
}
}
/*
* Source view periodic update handling
*/
function doSourceUpdate() {
var elem;
// Remove previously added custom classes
sourceEditedLines.forEach(function (linenum) {
elem = $('#source-code div')[linenum - 1];
if (elem) {
elem.classList.remove('breakpoint');
elem.classList.remove('execution');
elem.classList.remove('highlight');
}
});
sourceEditedLines.length = 0;
// If we're executing the file shown, highlight current line
if (loadedFileExecuting) {
elem = $('#source-code div')[currLine - 1];
if (elem) {
sourceEditedLines.push(currLine);
elem.classList.add('execution');
}
}
// Add breakpoints
currBreakpoints.forEach(function (bp) {
if (bp.fileName === loadedFileName) {
elem = $('#source-code div')[bp.lineNumber - 1];
if (elem) {
sourceEditedLines.push(bp.lineNumber);
elem.classList.add('breakpoint');
}
}
});
if (highlightLine !== null) {
elem = $('#source-code div')[highlightLine - 1];
if (elem) {
sourceEditedLines.push(highlightLine);
elem.classList.add('highlight');
}
}
// If no-one requested us to scroll to a specific line, finish.
if (loadedLinePending == null) {
return;
}
var reqLine = loadedLinePending;
loadedLinePending = null;
// Scroll to requested line. This is not very clean, so a better solution
// should be found:
// https://developer.mozilla.org/en-US/docs/Web/API/Element.scrollIntoView
// http://erraticdev.blogspot.fi/2011/02/jquery-scroll-into-view-plugin-with.html
// http://flesler.blogspot.fi/2007/10/jqueryscrollto.html
var tmpLine = Math.max(reqLine - 5, 0);
elem = $('#source-code div')[tmpLine];
if (elem) {
elem.scrollIntoView();
}
}
// Source is updated periodically. Other code can also call doSourceUpdate()
// directly if an immediate update is needed.
sourceUpdateInterval = setInterval(doSourceUpdate, SOURCE_UPDATE_INTERVAL);
/*
* UI update handling when exec-status update arrives
*/
function doUiUpdate() {
var now = Date.now();
// Note: loadedFileName can be either from target or from server, but they
// must match exactly. We could do a loose match here, but exact matches
// are needed for proper breakpoint handling anyway.
loadedFileExecuting = (loadedFileName === currFileName);
// If we just started running, store a timestamp so we can grey out the
// source view only if we execute long enough (i.e. we're not just
// stepping).
if (currState !== prevState && currState === 'running') {
startedRunning = now;
}
// If we just became paused, check for eval watch
if (currState !== prevState && currState === 'paused') {
if ($('#eval-watch').is(':checked')) {
submitEval(); // don't clear eval input
}
}
// Update current execution state
if (currFileName === '' && currLine === 0) {
$('#current-fileline').text('');
} else {
$('#current-fileline').text(String(currFileName) + ':' + String(currLine));
}
if (currFuncName === '' && currPc === 0) {
$('#current-funcpc').text('');
} else {
$('#current-funcpc').text(String(currFuncName) + '() pc ' + String(currPc));
}
$('#current-state').text(String(currState));
// Update buttons
if (currState !== prevState || currAttached !== prevAttached || forceButtonUpdate) {
$('#stepinto-button').prop('disabled', !currAttached || currState !== 'paused');
$('#stepover-button').prop('disabled', !currAttached || currState !== 'paused');
$('#stepout-button').prop('disabled', !currAttached || currState !== 'paused');
$('#resume-button').prop('disabled', !currAttached || currState !== 'paused');
$('#pause-button').prop('disabled', !currAttached || currState !== 'running');
$('#attach-button').prop('disabled', currAttached);
if (currAttached) {
$('#attach-button').removeClass('enabled');
} else {
$('#attach-button').addClass('enabled');
}
$('#detach-button').prop('disabled', !currAttached);
$('#eval-button').prop('disabled', !currAttached);
$('#add-breakpoint-button').prop('disabled', !currAttached);
$('#delete-all-breakpoints-button').prop('disabled', !currAttached);
$('.delete-breakpoint-button').prop('disabled', !currAttached);
$('#putvar-button').prop('disabled', !currAttached);
$('#getvar-button').prop('disabled', !currAttached);
$('#heap-dump-download-button').prop('disabled', !currAttached);
}
if (currState !== 'running' || forceButtonUpdate) {
// Remove pending highlight once we're no longer running.
$('#pause-button').removeClass('pending');
$('#eval-button').removeClass('pending');
}
forceButtonUpdate = false;
// Make source window grey when running for a longer time, use a small
// delay to avoid flashing grey when stepping.
if (currState === 'running' && now - startedRunning >= 500) {
$('#source-pre').removeClass('notrunning');
$('#current-state').removeClass('notrunning');
} else {
$('#source-pre').addClass('notrunning');
$('#current-state').addClass('notrunning');
}
// Force source view to match currFileName only when running or when
// just became paused (from running or detached).
var fetchSource = false;
if (typeof currFileName === 'string') {
if (currState === 'running' ||
(prevState !== 'paused' && currState === 'paused') ||
(currAttached !== prevAttached)) {
if (activeFileName !== currFileName) {
fetchSource = true;
activeFileName = currFileName;
activeLine = currLine;
activeHighlight = null;
requestSourceRefetch();
}
}
}
// Force line update (scrollTop) only when running or just became paused.
// Otherwise let user browse and scroll source files freely.
if (!fetchSource) {
if ((prevState !== 'paused' && currState === 'paused') ||
currState === 'running') {
loadedLinePending = currLine || 0;
}
}
}
/*
* Init socket.io and add handlers
*/
var socket = io(); // returns a Manager
setInterval(function () {
socket.emit('keepalive', {
userAgent: (navigator || {}).userAgent
});
}, 30000);
socket.on('connect', function () {
$('#socketio-info').text('connected');
currState = 'connected';
fetchSourceList();
});
socket.on('disconnect', function () {
$('#socketio-info').text('not connected');
currState = 'disconnected';
});
socket.on('reconnecting', function () {
$('#socketio-info').text('reconnecting');
currState = 'reconnecting';
});
socket.on('error', function (err) {
$('#socketio-info').text(err);
});
socket.on('replaced', function () {
// XXX: how to minimize the chance we'll further communciate with the
// server or reconnect to it? socket.reconnection()?
// We'd like to window.close() here but can't (not allowed from scripts).
// Alert is the next best thing.
alert('Debugger connection replaced by a new one, do you have multiple tabs open? If so, please close this tab.');
});
socket.on('keepalive', function (msg) {
// Not really interesting in the UI
// $('#server-info').text(new Date() + ': ' + JSON.stringify(msg));
});
socket.on('basic-info', function (msg) {
$('#duk-version').text(String(msg.duk_version));
$('#duk-git-describe').text(String(msg.duk_git_describe));
$('#target-info').text(String(msg.target_info));
$('#endianness').text(String(msg.endianness));
});
socket.on('exec-status', function (msg) {
currFileName = msg.fileName;
currFuncName = msg.funcName;
currLine = msg.line;
currPc = msg.pc;
currState = msg.state;
currAttached = msg.attached;
// Duktape now restricts execution status updates quite effectively so
// there's no need to rate limit UI updates now.
doUiUpdate();
prevState = currState;
prevAttached = currAttached;
});
// Update the "console" output based on lines sent by the server. The server
// rate limits these updates to keep the browser load under control. Even
// better would be for the client to pull this (and other stuff) on its own.
socket.on('output-lines', function (msg) {
var elem = $('#output');
var i, n, ent;
elem.empty();
for (i = 0, n = msg.length; i < n; i++) {
ent = msg[i];
if (ent.type === 'print') {
elem.append($('<div></div>').text(ent.message));
} else if (ent.type === 'alert') {
elem.append($('<div class="alert"></div>').text(ent.message));
} else if (ent.type === 'log') {
elem.append($('<div class="log loglevel' + ent.level + '"></div>').text(ent.message));
} else if (ent.type === 'debugger-info') {
elem.append($('<div class="debugger-info"><div>').text(ent.message));
} else if (ent.type === 'debugger-debug') {
elem.append($('<div class="debugger-debug"><div>').text(ent.message));
} else {
elem.append($('<div></div>').text(ent.message));
}
}
// http://stackoverflow.com/questions/14918787/jquery-scroll-to-bottom-of-div-even-after-it-updates
// Stop queued animations so that we always scroll quickly to bottom
$('#output').stop(true);
$('#output').animate({ scrollTop: $('#output')[0].scrollHeight}, 1000);
});
socket.on('callstack', function (msg) {
var elem = $('#callstack');
var s1, s2, div;
currCallstack = msg.callstack;
elem.empty();
msg.callstack.forEach(function (e) {
s1 = $('<a class="rest"></a>').text(e.fileName + ':' + e.lineNumber + ' (pc ' + e.pc + ')'); // float
s1.on('click', function () {
activeFileName = e.fileName;
activeLine = e.lineNumber || 1;
activeHighlight = activeLine;
requestSourceRefetch();
});
s2 = $('<span class="func"></span>').text(e.funcName + '()');
div = $('<div></div>');
div.append(s1);
div.append(s2);
elem.append(div);
});
});
socket.on('locals', function (msg) {
var elem = $('#locals');
var s1, s2, div;
var i, n, e;
currLocals = msg.locals;
elem.empty();
for (i = 0, n = msg.locals.length; i < n; i++) {
e = msg.locals[i];
s1 = $('<span class="value"></span>').text(e.value); // float
s2 = $('<span class="key"></span>').text(e.key);
div = $('<div></div>');
div.append(s1);
div.append(s2);
elem.append(div);
}
});
socket.on('debug-stats', function (msg) {
$('#debug-rx-bytes').text(formatBytes(msg.rxBytes));
$('#debug-rx-dvalues').text(msg.rxDvalues);
$('#debug-rx-messages').text(msg.rxMessages);
$('#debug-rx-kbrate').text((msg.rxBytesPerSec / 1024).toFixed(2));
$('#debug-tx-bytes').text(formatBytes(msg.txBytes));
$('#debug-tx-dvalues').text(msg.txDvalues);
$('#debug-tx-messages').text(msg.txMessages);
$('#debug-tx-kbrate').text((msg.txBytesPerSec / 1024).toFixed(2));
});
socket.on('breakpoints', function (msg) {
var elem = $('#breakpoints');
var div;
var sub;
currBreakpoints = msg.breakpoints;
elem.empty();
// First line is special
div = $('<div></div>');
sub = $('<button id="delete-all-breakpoints-button"></button>').text('Delete all breakpoints');
sub.on('click', function () {
socket.emit('delete-all-breakpoints');
});
div.append(sub);
sub = $('<input id="add-breakpoint-file"></input>').val('file.js');
div.append(sub);
sub = $('<span></span>').text(':');
div.append(sub);
sub = $('<input id="add-breakpoint-line"></input>').val('123');
div.append(sub);
sub = $('<button id="add-breakpoint-button"></button>').text('Add breakpoint');
sub.on('click', function () {
socket.emit('add-breakpoint', {
fileName: $('#add-breakpoint-file').val(),
lineNumber: Number($('#add-breakpoint-line').val())
});
});
div.append(sub);
sub = $('<span id="breakpoint-hint"></span>').text('or dblclick source');
div.append(sub);
elem.append(div);
// Active breakpoints follow
msg.breakpoints.forEach(function (bp) {
var div;
var sub;
div = $('<div class="breakpoint-line"></div>');
sub = $('<button class="delete-breakpoint-button"></button>').text('Delete');
sub.on('click', function () {
socket.emit('delete-breakpoint', {
fileName: bp.fileName,
lineNumber: bp.lineNumber
});
});
div.append(sub);
sub = $('<a></a>').text((bp.fileName || '?') + ':' + (bp.lineNumber || 0));
sub.on('click', function () {
activeFileName = bp.fileName || '';
activeLine = bp.lineNumber || 1;
activeHighlight = activeLine;
requestSourceRefetch();
});
div.append(sub);
elem.append(div);
});
forceButtonUpdate = true;
doUiUpdate();
});
socket.on('eval-result', function (msg) {
$('#eval-output').text((msg.error ? 'ERROR: ' : '') + msg.result);
// Remove eval button "pulsating" glow when we get a result
$('#eval-button').removeClass('pending');
});
socket.on('getvar-result', function (msg) {
$('#var-output').text(msg.found ? msg.result : 'NOTFOUND');
});
socket.on('bytecode', function (msg) {
$('#bytecode-preformatted').text(msg.preformatted);
$('#bytecode-dialog').dialog('open');
});
$('#stepinto-button').click(function () {
socket.emit('stepinto', {});
});
$('#stepover-button').click(function () {
socket.emit('stepover', {});
});
$('#stepout-button').click(function () {
socket.emit('stepout', {});
});
$('#pause-button').click(function () {
socket.emit('pause', {});
// Pause may take seconds to complete so indicate it is pending.
$('#pause-button').addClass('pending');
});
$('#resume-button').click(function () {
socket.emit('resume', {});
});
$('#attach-button').click(function () {
socket.emit('attach', {});
});
$('#detach-button').click(function () {
socket.emit('detach', {});
});
$('#about-button').click(function () {
$('#about-dialog').dialog('open');
});
$('#show-bytecode-button').click(function () {
socket.emit('get-bytecode', {});
});
function submitEval() {
socket.emit('eval', { input: $('#eval-input').val() });
// Eval may take seconds to complete so indicate it is pending.
$('#eval-button').addClass('pending');
}
$('#eval-button').click(function () {
submitEval();
$('#eval-input').val('');
});
$('#getvar-button').click(function () {
socket.emit('getvar', { varname: $('#varname-input').val() });
});
$('#putvar-button').click(function () {
// The variable value is parsed as JSON right now, but it'd be better to
// also be able to parse buffer values etc.
var val = JSON.parse($('#varvalue-input').val());
socket.emit('putvar', { varname: $('#varname-input').val(), varvalue: val });
});
$('#source-code').dblclick(function (event) {
var target = event.target;
var elems = $('#source-code div');
var i, n;
var line = 0;
// XXX: any faster way; elems doesn't have e.g. indexOf()
for (i = 0, n = elems.length; i < n; i++) {
if (target === elems[i]) {
line = i + 1;
}
}
socket.emit('toggle-breakpoint', {
fileName: loadedFileName,
lineNumber: line
});
});
function setSourceText(data) {
var elem, div;
elem = $('#source-code');
elem.empty();
data.split('\n').forEach(function (line) {
div = $('<div></div>');
div.text(line);
elem.append(div);
});
sourceEditedLines = [];
}
function setSourceSelect(fileName) {
var elem;
var i, n, t;
if (fileName == null) {
$('#source-select').val('__none__');
return;
}
elem = $('#source-select option');
for (i = 0, n = elem.length; i < n; i++) {
// Exact match is required.
t = $(elem[i]).val();
if (t === fileName) {
$('#source-select').val(t);
return;
}
}
}
/*
* AJAX request handling to fetch source files
*/
function requestSourceRefetch() {
// If previous update is pending, abort and start a new one.
if (sourceFetchXhr) {
sourceFetchXhr.abort();
sourceFetchXhr = null;
}
// Make copies of the requested file/line so that we have the proper
// values in case they've changed.
var fileName = activeFileName;
var lineNumber = activeLine;
// AJAX request for the source.
sourceFetchXhr = $.ajax({
type: 'POST',
url: '/source',
data: JSON.stringify({ fileName: fileName }),
contentType: 'application/json',
success: function (data, status, jqxhr) {
var elem;
sourceFetchXhr = null;
loadedFileName = fileName;
loadedLineCount = data.split('\n').length; // XXX: ignore issue with last empty line for now
loadedFileExecuting = (loadedFileName === currFileName);
setSourceText(data);
setSourceSelect(fileName);
loadedLinePending = activeLine || 1;
highlightLine = activeHighlight; // may be null
activeLine = null;
activeHighlight = null;
doSourceUpdate();
// XXX: hacky transition, make source change visible
$('#source-pre').fadeTo('fast', 0.25, function () {
$('#source-pre').fadeTo('fast', 1.0);
});
},
error: function (jqxhr, status, err) {
// Not worth alerting about because source fetch errors happen
// all the time, e.g. for dynamically evaluated code.
sourceFetchXhr = null;
// XXX: prevent retry of no-such-file by negative caching?
loadedFileName = fileName;
loadedLineCount = 1;
loadedFileExecuting = false;
setSourceText('// Cannot load source file: ' + fileName);
setSourceSelect(null);
loadedLinePending = 1;
activeLine = null;
activeHighlight = null;
doSourceUpdate();
// XXX: error transition here
$('#source-pre').fadeTo('fast', 0.25, function () {
$('#source-pre').fadeTo('fast', 1.0);
});
},
dataType: 'text'
});
}
/*
* AJAX request for fetching the source list
*/
function fetchSourceList() {
$.ajax({
type: 'POST',
url: '/sourceList',
data: JSON.stringify({}),
contentType: 'application/json',
success: function (data, status, jqxhr) {
var elem = $('#source-select');
data = JSON.parse(data);
elem.empty();
var opt = $('<option></option>').attr({ 'value': '__none__' }).text('No source file selected');
elem.append(opt);
data.forEach(function (ent) {
var opt = $('<option></option>').attr({ 'value': ent }).text(ent);
elem.append(opt);
});
elem.change(function () {
activeFileName = elem.val();
activeLine = 1;
requestSourceRefetch();
});
},
error: function (jqxhr, status, err) {
// This is worth alerting about as the UI is somewhat unusable
// if we don't get a source list.
alert('Failed to load source list: ' + err);
},
dataType: 'text'
});
}
/*
* Initialization
*/
$(document).ready(function () {
var showAbout = true;
// About dialog, shown automatically on first startup.
$('#about-dialog').dialog({
autoOpen: false,
hide: 'fade', // puff
show: 'fade', // slide, puff
width: 500,
height: 300
});
// Bytecode dialog
$('#bytecode-dialog').dialog({
autoOpen: false,
hide: 'fade', // puff
show: 'fade', // slide, puff
width: 1000,
height: 800
});
// http://diveintohtml5.info/storage.html
if (typeof localStorage !== 'undefined') {
if (localStorage.getItem('about-shown')) {
showAbout = false;
} else {
localStorage.setItem('about-shown', 'yes');
}
}
if (showAbout) {
$('#about-dialog').dialog('open');
}
// onclick handler for exec status text
function loadCurrFunc() {
activeFileName = currFileName;
activeLine = currLine;
requestSourceRefetch();
}
$('#exec-other').on('click', loadCurrFunc);
// Enter handling for eval input
// https://forum.jquery.com/topic/bind-html-input-to-enter-key-keypress
$('#eval-input').keypress(function (event) {
if (event.keyCode == 13) {
submitEval();
$('#eval-input').val('');
}
});
// Eval watch handling
$('#eval-watch').change(function () {
// nop
});
forceButtonUpdate = true;
doUiUpdate();
});

View File

@@ -1,838 +0,0 @@
{
"builtin_strings": [
"Logger",
"Thread",
"Pointer",
"DecEnv",
"ObjEnv",
"Float64Array",
"Float32Array",
"Uint32Array",
"Int32Array",
"Uint16Array",
"Int16Array",
"Uint8ClampedArray",
"Uint8Array",
"Int8Array",
"DataView",
"ArrayBuffer",
"Buffer",
"",
"global",
"Arguments",
"JSON",
"Math",
"Error",
"RegExp",
"Date",
"Number",
"Boolean",
"String",
"Array",
"Function",
"Object",
"Null",
"Undefined",
"{_func:true}",
"{\"_func\":true}",
"{\"_ninf\":true}",
"{\"_inf\":true}",
"{\"_nan\":true}",
"{\"_undef\":true}",
"toLogString",
"clog",
"l",
"n",
"fatal",
"error",
"warn",
"debug",
"trace",
"raw",
"fmt",
"current",
"resume",
"compact",
"jc",
"jx",
"base64",
"hex",
"dec",
"enc",
"fin",
"gc",
"act",
"info",
"version",
"env",
"modLoaded",
"modSearch",
"errThrow",
"errCreate",
"compile",
"\u0000Regbase",
"\u0000Thread",
"\u0000Handler",
"\u0000Finalizer",
"\u0000Callee",
"\u0000Map",
"\u0000Args",
"\u0000This",
"\u0000Pc2line",
"\u0000Source",
"\u0000Varenv",
"\u0000Lexenv",
"\u0000Varmap",
"\u0000Formals",
"\u0000Bytecode",
"\u0000Next",
"\u0000Target",
"\u0000Value",
"pointer",
"\u0000Tracedata",
"lineNumber",
"fileName",
"pc",
"stack",
"ThrowTypeError",
"Duktape",
"setFloat64",
"setFloat32",
"setUint32",
"setInt32",
"setUint16",
"setInt16",
"setUint8",
"setInt8",
"getFloat64",
"getFloat32",
"getUint32",
"getInt32",
"getUint16",
"getInt16",
"getUint8",
"getInt8",
"subarray",
"BYTES_PER_ELEMENT",
"byteOffset",
"buffer",
"isView",
"data",
"type",
"writeIntBE",
"writeIntLE",
"writeUIntBE",
"writeUIntLE",
"writeDoubleBE",
"writeDoubleLE",
"writeFloatBE",
"writeFloatLE",
"writeInt32BE",
"writeInt32LE",
"writeUInt32BE",
"writeUInt32LE",
"writeInt16BE",
"writeInt16LE",
"writeUInt16BE",
"writeUInt16LE",
"writeInt8",
"writeUInt8",
"readIntBE",
"readIntLE",
"readUIntBE",
"readUIntLE",
"readDoubleBE",
"readDoubleLE",
"readFloatBE",
"readFloatLE",
"readInt32BE",
"readInt32LE",
"readUInt32BE",
"readUInt32LE",
"readInt16BE",
"readInt16LE",
"readUInt16BE",
"readUInt16LE",
"readInt8",
"readUInt8",
"copy",
"equals",
"fill",
"write",
"compare",
"byteLength",
"isBuffer",
"isEncoding",
"exports",
"id",
"require",
"__proto__",
"setPrototypeOf",
"ownKeys",
"enumerate",
"deleteProperty",
"has",
"Proxy",
"callee",
"Invalid Date",
"[...]",
"\n\t",
" ",
",",
"-0",
"+0",
"0",
"-Infinity",
"+Infinity",
"Infinity",
"object",
"string",
"number",
"boolean",
"undefined",
"stringify",
"tan",
"sqrt",
"sin",
"round",
"random",
"pow",
"min",
"max",
"log",
"floor",
"exp",
"cos",
"ceil",
"atan2",
"atan",
"asin",
"acos",
"abs",
"SQRT2",
"SQRT1_2",
"PI",
"LOG10E",
"LOG2E",
"LN2",
"LN10",
"E",
"message",
"name",
"input",
"index",
"(?:)",
"lastIndex",
"multiline",
"ignoreCase",
"source",
"test",
"exec",
"toGMTString",
"setYear",
"getYear",
"toJSON",
"toISOString",
"toUTCString",
"setUTCFullYear",
"setFullYear",
"setUTCMonth",
"setMonth",
"setUTCDate",
"setDate",
"setUTCHours",
"setHours",
"setUTCMinutes",
"setMinutes",
"setUTCSeconds",
"setSeconds",
"setUTCMilliseconds",
"setMilliseconds",
"setTime",
"getTimezoneOffset",
"getUTCMilliseconds",
"getMilliseconds",
"getUTCSeconds",
"getSeconds",
"getUTCMinutes",
"getMinutes",
"getUTCHours",
"getHours",
"getUTCDay",
"getDay",
"getUTCDate",
"getDate",
"getUTCMonth",
"getMonth",
"getUTCFullYear",
"getFullYear",
"getTime",
"toLocaleTimeString",
"toLocaleDateString",
"toTimeString",
"toDateString",
"now",
"UTC",
"parse",
"toPrecision",
"toExponential",
"toFixed",
"POSITIVE_INFINITY",
"NEGATIVE_INFINITY",
"NaN",
"MIN_VALUE",
"MAX_VALUE",
"substr",
"trim",
"toLocaleUpperCase",
"toUpperCase",
"toLocaleLowerCase",
"toLowerCase",
"substring",
"split",
"search",
"replace",
"match",
"localeCompare",
"charCodeAt",
"charAt",
"fromCharCode",
"reduceRight",
"reduce",
"filter",
"map",
"forEach",
"some",
"every",
"lastIndexOf",
"indexOf",
"unshift",
"splice",
"sort",
"slice",
"shift",
"reverse",
"push",
"pop",
"join",
"concat",
"isArray",
"arguments",
"caller",
"bind",
"call",
"apply",
"propertyIsEnumerable",
"isPrototypeOf",
"hasOwnProperty",
"valueOf",
"toLocaleString",
"toString",
"constructor",
"set",
"get",
"enumerable",
"configurable",
"writable",
"value",
"keys",
"isExtensible",
"isFrozen",
"isSealed",
"preventExtensions",
"freeze",
"seal",
"defineProperties",
"defineProperty",
"create",
"getOwnPropertyNames",
"getOwnPropertyDescriptor",
"getPrototypeOf",
"prototype",
"length",
"alert",
"print",
"unescape",
"escape",
"encodeURIComponent",
"encodeURI",
"decodeURIComponent",
"decodeURI",
"isFinite",
"isNaN",
"parseFloat",
"parseInt",
"eval",
"URIError",
"TypeError",
"SyntaxError",
"ReferenceError",
"RangeError",
"EvalError",
"break",
"case",
"catch",
"continue",
"debugger",
"default",
"delete",
"do",
"else",
"finally",
"for",
"function",
"if",
"in",
"instanceof",
"new",
"return",
"switch",
"this",
"throw",
"try",
"typeof",
"var",
"void",
"while",
"with",
"class",
"const",
"enum",
"export",
"extends",
"import",
"super",
"null",
"true",
"false",
"implements",
"interface",
"let",
"package",
"private",
"protected",
"public",
"static",
"yield"
],
"builtin_strings_base64": [
"TG9nZ2Vy",
"VGhyZWFk",
"UG9pbnRlcg==",
"RGVjRW52",
"T2JqRW52",
"RmxvYXQ2NEFycmF5",
"RmxvYXQzMkFycmF5",
"VWludDMyQXJyYXk=",
"SW50MzJBcnJheQ==",
"VWludDE2QXJyYXk=",
"SW50MTZBcnJheQ==",
"VWludDhDbGFtcGVkQXJyYXk=",
"VWludDhBcnJheQ==",
"SW50OEFycmF5",
"RGF0YVZpZXc=",
"QXJyYXlCdWZmZXI=",
"QnVmZmVy",
"",
"Z2xvYmFs",
"QXJndW1lbnRz",
"SlNPTg==",
"TWF0aA==",
"RXJyb3I=",
"UmVnRXhw",
"RGF0ZQ==",
"TnVtYmVy",
"Qm9vbGVhbg==",
"U3RyaW5n",
"QXJyYXk=",
"RnVuY3Rpb24=",
"T2JqZWN0",
"TnVsbA==",
"VW5kZWZpbmVk",
"e19mdW5jOnRydWV9",
"eyJfZnVuYyI6dHJ1ZX0=",
"eyJfbmluZiI6dHJ1ZX0=",
"eyJfaW5mIjp0cnVlfQ==",
"eyJfbmFuIjp0cnVlfQ==",
"eyJfdW5kZWYiOnRydWV9",
"dG9Mb2dTdHJpbmc=",
"Y2xvZw==",
"bA==",
"bg==",
"ZmF0YWw=",
"ZXJyb3I=",
"d2Fybg==",
"ZGVidWc=",
"dHJhY2U=",
"cmF3",
"Zm10",
"Y3VycmVudA==",
"cmVzdW1l",
"Y29tcGFjdA==",
"amM=",
"ang=",
"YmFzZTY0",
"aGV4",
"ZGVj",
"ZW5j",
"Zmlu",
"Z2M=",
"YWN0",
"aW5mbw==",
"dmVyc2lvbg==",
"ZW52",
"bW9kTG9hZGVk",
"bW9kU2VhcmNo",
"ZXJyVGhyb3c=",
"ZXJyQ3JlYXRl",
"Y29tcGlsZQ==",
"/1JlZ2Jhc2U=",
"/1RocmVhZA==",
"/0hhbmRsZXI=",
"/0ZpbmFsaXplcg==",
"/0NhbGxlZQ==",
"/01hcA==",
"/0FyZ3M=",
"/1RoaXM=",
"/1BjMmxpbmU=",
"/1NvdXJjZQ==",
"/1ZhcmVudg==",
"/0xleGVudg==",
"/1Zhcm1hcA==",
"/0Zvcm1hbHM=",
"/0J5dGVjb2Rl",
"/05leHQ=",
"/1RhcmdldA==",
"/1ZhbHVl",
"cG9pbnRlcg==",
"/1RyYWNlZGF0YQ==",
"bGluZU51bWJlcg==",
"ZmlsZU5hbWU=",
"cGM=",
"c3RhY2s=",
"VGhyb3dUeXBlRXJyb3I=",
"RHVrdGFwZQ==",
"c2V0RmxvYXQ2NA==",
"c2V0RmxvYXQzMg==",
"c2V0VWludDMy",
"c2V0SW50MzI=",
"c2V0VWludDE2",
"c2V0SW50MTY=",
"c2V0VWludDg=",
"c2V0SW50OA==",
"Z2V0RmxvYXQ2NA==",
"Z2V0RmxvYXQzMg==",
"Z2V0VWludDMy",
"Z2V0SW50MzI=",
"Z2V0VWludDE2",
"Z2V0SW50MTY=",
"Z2V0VWludDg=",
"Z2V0SW50OA==",
"c3ViYXJyYXk=",
"QllURVNfUEVSX0VMRU1FTlQ=",
"Ynl0ZU9mZnNldA==",
"YnVmZmVy",
"aXNWaWV3",
"ZGF0YQ==",
"dHlwZQ==",
"d3JpdGVJbnRCRQ==",
"d3JpdGVJbnRMRQ==",
"d3JpdGVVSW50QkU=",
"d3JpdGVVSW50TEU=",
"d3JpdGVEb3VibGVCRQ==",
"d3JpdGVEb3VibGVMRQ==",
"d3JpdGVGbG9hdEJF",
"d3JpdGVGbG9hdExF",
"d3JpdGVJbnQzMkJF",
"d3JpdGVJbnQzMkxF",
"d3JpdGVVSW50MzJCRQ==",
"d3JpdGVVSW50MzJMRQ==",
"d3JpdGVJbnQxNkJF",
"d3JpdGVJbnQxNkxF",
"d3JpdGVVSW50MTZCRQ==",
"d3JpdGVVSW50MTZMRQ==",
"d3JpdGVJbnQ4",
"d3JpdGVVSW50OA==",
"cmVhZEludEJF",
"cmVhZEludExF",
"cmVhZFVJbnRCRQ==",
"cmVhZFVJbnRMRQ==",
"cmVhZERvdWJsZUJF",
"cmVhZERvdWJsZUxF",
"cmVhZEZsb2F0QkU=",
"cmVhZEZsb2F0TEU=",
"cmVhZEludDMyQkU=",
"cmVhZEludDMyTEU=",
"cmVhZFVJbnQzMkJF",
"cmVhZFVJbnQzMkxF",
"cmVhZEludDE2QkU=",
"cmVhZEludDE2TEU=",
"cmVhZFVJbnQxNkJF",
"cmVhZFVJbnQxNkxF",
"cmVhZEludDg=",
"cmVhZFVJbnQ4",
"Y29weQ==",
"ZXF1YWxz",
"ZmlsbA==",
"d3JpdGU=",
"Y29tcGFyZQ==",
"Ynl0ZUxlbmd0aA==",
"aXNCdWZmZXI=",
"aXNFbmNvZGluZw==",
"ZXhwb3J0cw==",
"aWQ=",
"cmVxdWlyZQ==",
"X19wcm90b19f",
"c2V0UHJvdG90eXBlT2Y=",
"b3duS2V5cw==",
"ZW51bWVyYXRl",
"ZGVsZXRlUHJvcGVydHk=",
"aGFz",
"UHJveHk=",
"Y2FsbGVl",
"SW52YWxpZCBEYXRl",
"Wy4uLl0=",
"Cgk=",
"IA==",
"LA==",
"LTA=",
"KzA=",
"MA==",
"LUluZmluaXR5",
"K0luZmluaXR5",
"SW5maW5pdHk=",
"b2JqZWN0",
"c3RyaW5n",
"bnVtYmVy",
"Ym9vbGVhbg==",
"dW5kZWZpbmVk",
"c3RyaW5naWZ5",
"dGFu",
"c3FydA==",
"c2lu",
"cm91bmQ=",
"cmFuZG9t",
"cG93",
"bWlu",
"bWF4",
"bG9n",
"Zmxvb3I=",
"ZXhw",
"Y29z",
"Y2VpbA==",
"YXRhbjI=",
"YXRhbg==",
"YXNpbg==",
"YWNvcw==",
"YWJz",
"U1FSVDI=",
"U1FSVDFfMg==",
"UEk=",
"TE9HMTBF",
"TE9HMkU=",
"TE4y",
"TE4xMA==",
"RQ==",
"bWVzc2FnZQ==",
"bmFtZQ==",
"aW5wdXQ=",
"aW5kZXg=",
"KD86KQ==",
"bGFzdEluZGV4",
"bXVsdGlsaW5l",
"aWdub3JlQ2FzZQ==",
"c291cmNl",
"dGVzdA==",
"ZXhlYw==",
"dG9HTVRTdHJpbmc=",
"c2V0WWVhcg==",
"Z2V0WWVhcg==",
"dG9KU09O",
"dG9JU09TdHJpbmc=",
"dG9VVENTdHJpbmc=",
"c2V0VVRDRnVsbFllYXI=",
"c2V0RnVsbFllYXI=",
"c2V0VVRDTW9udGg=",
"c2V0TW9udGg=",
"c2V0VVRDRGF0ZQ==",
"c2V0RGF0ZQ==",
"c2V0VVRDSG91cnM=",
"c2V0SG91cnM=",
"c2V0VVRDTWludXRlcw==",
"c2V0TWludXRlcw==",
"c2V0VVRDU2Vjb25kcw==",
"c2V0U2Vjb25kcw==",
"c2V0VVRDTWlsbGlzZWNvbmRz",
"c2V0TWlsbGlzZWNvbmRz",
"c2V0VGltZQ==",
"Z2V0VGltZXpvbmVPZmZzZXQ=",
"Z2V0VVRDTWlsbGlzZWNvbmRz",
"Z2V0TWlsbGlzZWNvbmRz",
"Z2V0VVRDU2Vjb25kcw==",
"Z2V0U2Vjb25kcw==",
"Z2V0VVRDTWludXRlcw==",
"Z2V0TWludXRlcw==",
"Z2V0VVRDSG91cnM=",
"Z2V0SG91cnM=",
"Z2V0VVRDRGF5",
"Z2V0RGF5",
"Z2V0VVRDRGF0ZQ==",
"Z2V0RGF0ZQ==",
"Z2V0VVRDTW9udGg=",
"Z2V0TW9udGg=",
"Z2V0VVRDRnVsbFllYXI=",
"Z2V0RnVsbFllYXI=",
"Z2V0VGltZQ==",
"dG9Mb2NhbGVUaW1lU3RyaW5n",
"dG9Mb2NhbGVEYXRlU3RyaW5n",
"dG9UaW1lU3RyaW5n",
"dG9EYXRlU3RyaW5n",
"bm93",
"VVRD",
"cGFyc2U=",
"dG9QcmVjaXNpb24=",
"dG9FeHBvbmVudGlhbA==",
"dG9GaXhlZA==",
"UE9TSVRJVkVfSU5GSU5JVFk=",
"TkVHQVRJVkVfSU5GSU5JVFk=",
"TmFO",
"TUlOX1ZBTFVF",
"TUFYX1ZBTFVF",
"c3Vic3Ry",
"dHJpbQ==",
"dG9Mb2NhbGVVcHBlckNhc2U=",
"dG9VcHBlckNhc2U=",
"dG9Mb2NhbGVMb3dlckNhc2U=",
"dG9Mb3dlckNhc2U=",
"c3Vic3RyaW5n",
"c3BsaXQ=",
"c2VhcmNo",
"cmVwbGFjZQ==",
"bWF0Y2g=",
"bG9jYWxlQ29tcGFyZQ==",
"Y2hhckNvZGVBdA==",
"Y2hhckF0",
"ZnJvbUNoYXJDb2Rl",
"cmVkdWNlUmlnaHQ=",
"cmVkdWNl",
"ZmlsdGVy",
"bWFw",
"Zm9yRWFjaA==",
"c29tZQ==",
"ZXZlcnk=",
"bGFzdEluZGV4T2Y=",
"aW5kZXhPZg==",
"dW5zaGlmdA==",
"c3BsaWNl",
"c29ydA==",
"c2xpY2U=",
"c2hpZnQ=",
"cmV2ZXJzZQ==",
"cHVzaA==",
"cG9w",
"am9pbg==",
"Y29uY2F0",
"aXNBcnJheQ==",
"YXJndW1lbnRz",
"Y2FsbGVy",
"YmluZA==",
"Y2FsbA==",
"YXBwbHk=",
"cHJvcGVydHlJc0VudW1lcmFibGU=",
"aXNQcm90b3R5cGVPZg==",
"aGFzT3duUHJvcGVydHk=",
"dmFsdWVPZg==",
"dG9Mb2NhbGVTdHJpbmc=",
"dG9TdHJpbmc=",
"Y29uc3RydWN0b3I=",
"c2V0",
"Z2V0",
"ZW51bWVyYWJsZQ==",
"Y29uZmlndXJhYmxl",
"d3JpdGFibGU=",
"dmFsdWU=",
"a2V5cw==",
"aXNFeHRlbnNpYmxl",
"aXNGcm96ZW4=",
"aXNTZWFsZWQ=",
"cHJldmVudEV4dGVuc2lvbnM=",
"ZnJlZXpl",
"c2VhbA==",
"ZGVmaW5lUHJvcGVydGllcw==",
"ZGVmaW5lUHJvcGVydHk=",
"Y3JlYXRl",
"Z2V0T3duUHJvcGVydHlOYW1lcw==",
"Z2V0T3duUHJvcGVydHlEZXNjcmlwdG9y",
"Z2V0UHJvdG90eXBlT2Y=",
"cHJvdG90eXBl",
"bGVuZ3Ro",
"YWxlcnQ=",
"cHJpbnQ=",
"dW5lc2NhcGU=",
"ZXNjYXBl",
"ZW5jb2RlVVJJQ29tcG9uZW50",
"ZW5jb2RlVVJJ",
"ZGVjb2RlVVJJQ29tcG9uZW50",
"ZGVjb2RlVVJJ",
"aXNGaW5pdGU=",
"aXNOYU4=",
"cGFyc2VGbG9hdA==",
"cGFyc2VJbnQ=",
"ZXZhbA==",
"VVJJRXJyb3I=",
"VHlwZUVycm9y",
"U3ludGF4RXJyb3I=",
"UmVmZXJlbmNlRXJyb3I=",
"UmFuZ2VFcnJvcg==",
"RXZhbEVycm9y",
"YnJlYWs=",
"Y2FzZQ==",
"Y2F0Y2g=",
"Y29udGludWU=",
"ZGVidWdnZXI=",
"ZGVmYXVsdA==",
"ZGVsZXRl",
"ZG8=",
"ZWxzZQ==",
"ZmluYWxseQ==",
"Zm9y",
"ZnVuY3Rpb24=",
"aWY=",
"aW4=",
"aW5zdGFuY2VvZg==",
"bmV3",
"cmV0dXJu",
"c3dpdGNo",
"dGhpcw==",
"dGhyb3c=",
"dHJ5",
"dHlwZW9m",
"dmFy",
"dm9pZA==",
"d2hpbGU=",
"d2l0aA==",
"Y2xhc3M=",
"Y29uc3Q=",
"ZW51bQ==",
"ZXhwb3J0",
"ZXh0ZW5kcw==",
"aW1wb3J0",
"c3VwZXI=",
"bnVsbA==",
"dHJ1ZQ==",
"ZmFsc2U=",
"aW1wbGVtZW50cw==",
"aW50ZXJmYWNl",
"bGV0",
"cGFja2FnZQ==",
"cHJpdmF0ZQ==",
"cHJvdGVjdGVk",
"cHVibGlj",
"c3RhdGlj",
"eWllbGQ="
],
"comment": "Metadata for Duktape build",
"duk_version": 10300,
"duk_version_string": "1.3.0",
"git_describe": "v1.3.0"
}

View File

@@ -1,10 +0,0 @@
================
Duktape examples
================
Examples for using Duktape. These support user documentation and are
intended as informative illustrations only.
Examples are unmaintained and are not production quality code. Bugs are
not not necessarily fixed, unless the bug makes the example misleading
as documentation.

View File

@@ -1,10 +0,0 @@
=====================
Hybrid pool allocator
=====================
Example allocator that tries to satisfy memory allocations for small sizes
from a set of fixed pools, but always falls back to malloc/realloc/free if
a larger size is requested or the pools have been exhausted.
This may be useful to reduce memory churn when the platform allocator does
not handle allocations for a lot of small memory areas efficiently.

View File

@@ -1,293 +0,0 @@
/*
* Example memory allocator with pool allocation for small sizes and
* fallback into malloc/realloc/free for larger sizes or when the pools
* are exhausted.
*
* Useful to reduce memory churn or work around a platform allocator
* that doesn't handle a lot of small allocations efficiently.
*/
#include "duktape.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
/* Define to enable some debug printfs. */
/* #define DUK_ALLOC_HYBRID_DEBUG */
typedef struct {
size_t size;
int count;
} pool_size_spec;
static pool_size_spec pool_sizes[] = {
{ 32, 1024 },
{ 48, 2048 },
{ 64, 2048 },
{ 128, 2048 },
{ 256, 512 },
{ 1024, 64 },
{ 2048, 32 }
};
#define NUM_POOLS (sizeof(pool_sizes) / sizeof(pool_size_spec))
/* This must fit into the smallest pool entry. */
struct pool_free_entry;
typedef struct pool_free_entry pool_free_entry;
struct pool_free_entry {
pool_free_entry *next;
};
typedef struct {
pool_free_entry *free;
char *alloc_start;
char *alloc_end;
size_t size;
int count;
} pool_header;
typedef struct {
pool_header headers[NUM_POOLS];
size_t pool_max_size;
char *alloc_start;
char *alloc_end;
} pool_state;
#define ADDR_IN_STATE_ALLOC(st,p) \
((char *) (p) >= (st)->alloc_start && (char *) (p) < (st)->alloc_end)
#define ADDR_IN_HEADER_ALLOC(hdr,p) \
((char *) (p) >= (hdr)->alloc_start && (char *) (p) < (hdr)->alloc_end)
#ifdef DUK_ALLOC_HYBRID_DEBUG
static void dump_pool_state(pool_state *st) {
pool_free_entry *free;
int free_len;
int i;
printf("=== Pool state: st=%p\n", (void *) st);
for (i = 0; i < (int) NUM_POOLS; i++) {
pool_header *hdr = st->headers + i;
for (free = hdr->free, free_len = 0; free != NULL; free = free->next) {
free_len++;
}
printf("[%d]: size %ld, count %ld, used %ld, free list len %ld\n",
i, (long) hdr->size, (long) hdr->count,
(long) (hdr->count - free_len),
(long) free_len);
}
}
#else
static void dump_pool_state(pool_state *st) {
(void) st;
}
#endif
void *duk_alloc_hybrid_init(void) {
pool_state *st;
size_t total_size, max_size;
int i, j;
char *p;
st = (pool_state *) malloc(sizeof(pool_state));
if (!st) {
return NULL;
}
memset((void *) st, 0, sizeof(pool_state));
st->alloc_start = NULL;
st->alloc_end = NULL;
for (i = 0, total_size = 0, max_size = 0; i < (int) NUM_POOLS; i++) {
#ifdef DUK_ALLOC_HYBRID_DEBUG
printf("Pool %d: size %ld, count %ld\n", i, (long) pool_sizes[i].size, (long) pool_sizes[i].count);
#endif
total_size += pool_sizes[i].size * pool_sizes[i].count;
if (pool_sizes[i].size > max_size) {
max_size = pool_sizes[i].size;
}
}
#ifdef DUK_ALLOC_HYBRID_DEBUG
printf("Total size %ld, max pool size %ld\n", (long) total_size, (long) max_size);
#endif
st->alloc_start = (char *) malloc(total_size);
if (!st->alloc_start) {
free(st);
return NULL;
}
st->alloc_end = st->alloc_start + total_size;
st->pool_max_size = max_size;
memset((void *) st->alloc_start, 0, total_size);
for (i = 0, p = st->alloc_start; i < (int) NUM_POOLS; i++) {
pool_header *hdr = st->headers + i;
hdr->alloc_start = p;
hdr->alloc_end = p + pool_sizes[i].size * pool_sizes[i].count;
hdr->free = (pool_free_entry *) (void *) p;
hdr->size = pool_sizes[i].size;
hdr->count = pool_sizes[i].count;
for (j = 0; j < pool_sizes[i].count; j++) {
pool_free_entry *ent = (pool_free_entry *) (void *) p;
if (j == pool_sizes[i].count - 1) {
ent->next = (pool_free_entry *) NULL;
} else {
ent->next = (pool_free_entry *) (void *) (p + pool_sizes[i].size);
}
p += pool_sizes[i].size;
}
}
dump_pool_state(st);
/* Use 'st' as udata. */
return (void *) st;
}
void *duk_alloc_hybrid(void *udata, duk_size_t size) {
pool_state *st = (pool_state *) udata;
int i;
void *new_ptr;
#if 0
dump_pool_state(st);
#endif
if (size == 0) {
return NULL;
}
if (size > st->pool_max_size) {
#ifdef DUK_ALLOC_HYBRID_DEBUG
printf("alloc fallback: %ld\n", (long) size);
#endif
return malloc(size);
}
for (i = 0; i < (int) NUM_POOLS; i++) {
pool_header *hdr = st->headers + i;
if (hdr->size < size) {
continue;
}
if (hdr->free) {
#if 0
printf("alloc from pool: %ld -> pool size %ld\n", (long) size, (long) hdr->size);
#endif
new_ptr = (void *) hdr->free;
hdr->free = hdr->free->next;
return new_ptr;
} else {
#ifdef DUK_ALLOC_HYBRID_DEBUG
printf("alloc out of pool entries: %ld -> pool size %ld\n", (long) size, (long) hdr->size);
#endif
break;
}
}
#ifdef DUK_ALLOC_HYBRID_DEBUG
printf("alloc fallback (out of pool): %ld\n", (long) size);
#endif
return malloc(size);
}
void *duk_realloc_hybrid(void *udata, void *ptr, duk_size_t size) {
pool_state *st = (pool_state *) udata;
void *new_ptr;
int i;
#if 0
dump_pool_state(st);
#endif
if (ADDR_IN_STATE_ALLOC(st, ptr)) {
/* 'ptr' cannot be NULL. */
for (i = 0; i < (int) NUM_POOLS; i++) {
pool_header *hdr = st->headers + i;
if (ADDR_IN_HEADER_ALLOC(hdr, ptr)) {
if (size <= hdr->size) {
/* Still fits, no shrink support. */
#if 0
printf("realloc original from pool: still fits, size %ld, pool size %ld\n",
(long) size, (long) hdr->size);
#endif
return ptr;
}
new_ptr = duk_alloc_hybrid(udata, size);
if (!new_ptr) {
#ifdef DUK_ALLOC_HYBRID_DEBUG
printf("realloc original from pool: needed larger size, failed to alloc\n");
#endif
return NULL;
}
memcpy(new_ptr, ptr, hdr->size);
((pool_free_entry *) ptr)->next = hdr->free;
hdr->free = (pool_free_entry *) ptr;
#if 0
printf("realloc original from pool: size %ld, pool size %ld\n", (long) size, (long) hdr->size);
#endif
return new_ptr;
}
}
#ifdef DUK_ALLOC_HYBRID_DEBUG
printf("NEVER HERE\n");
#endif
return NULL;
} else if (ptr != NULL) {
if (size == 0) {
free(ptr);
return NULL;
} else {
#ifdef DUK_ALLOC_HYBRID_DEBUG
printf("realloc fallback: size %ld\n", (long) size);
#endif
return realloc(ptr, size);
}
} else {
#if 0
printf("realloc NULL ptr, call alloc: %ld\n", (long) size);
#endif
return duk_alloc_hybrid(udata, size);
}
}
void duk_free_hybrid(void *udata, void *ptr) {
pool_state *st = (pool_state *) udata;
int i;
#if 0
dump_pool_state(st);
#endif
if (!ADDR_IN_STATE_ALLOC(st, ptr)) {
if (ptr == NULL) {
return;
}
#if 0
printf("free out of pool: %p\n", (void *) ptr);
#endif
free(ptr);
return;
}
for (i = 0; i < (int) NUM_POOLS; i++) {
pool_header *hdr = st->headers + i;
if (ADDR_IN_HEADER_ALLOC(hdr, ptr)) {
((pool_free_entry *) ptr)->next = hdr->free;
hdr->free = (pool_free_entry *) ptr;
#if 0
printf("free from pool: %p\n", ptr);
#endif
return;
}
}
#ifdef DUK_ALLOC_HYBRID_DEBUG
printf("NEVER HERE\n");
#endif
}

View File

@@ -1,11 +0,0 @@
#ifndef DUK_ALLOC_HYBRID_H_INCLUDED
#define DUK_ALLOC_HYBRID_H_INCLUDED
#include "duktape.h"
void *duk_alloc_hybrid_init(void);
void *duk_alloc_hybrid(void *udata, duk_size_t size);
void *duk_realloc_hybrid(void *udata, void *ptr, duk_size_t size);
void duk_free_hybrid(void *udata, void *ptr);
#endif /* DUK_ALLOC_HYBRID_H_INCLUDED */

View File

@@ -1,7 +0,0 @@
======================
Allocator with logging
======================
Example allocator that writes all memory alloc/realloc/free calls into a
log file so that memory usage can replayed later. This is useful to e.g.
optimize pool sizes.

View File

@@ -1,138 +0,0 @@
/*
* Example memory allocator with machine parseable logging.
*
* Also sizes for reallocs and frees are logged so that the memory
* behavior can be essentially replayed to accurately determine e.g.
* optimal pool sizes for a pooled allocator.
*
* Allocation structure:
*
* [ alloc_hdr | user area ]
*
* ^ ^
* | `--- pointer returned to Duktape
* `--- underlying malloc ptr
*/
#include "duktape.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#define ALLOC_LOG_FILE "/tmp/duk-alloc-log.txt"
typedef struct {
/* The double value in the union is there to ensure alignment is
* good for IEEE doubles too. In many 32-bit environments 4 bytes
* would be sufficiently aligned and the double value is unnecessary.
*/
union {
size_t sz;
double d;
} u;
} alloc_hdr;
static FILE *log_file = NULL;
static void write_log(const char *fmt, ...) {
va_list ap;
if (!log_file) {
log_file = fopen(ALLOC_LOG_FILE, "wb");
if (!log_file) {
return;
}
}
va_start(ap, fmt);
vfprintf(log_file, fmt, ap);
va_end(ap);
}
void *duk_alloc_logging(void *udata, duk_size_t size) {
alloc_hdr *hdr;
void *ret;
(void) udata; /* Suppress warning. */
if (size == 0) {
write_log("A NULL %ld\n", (long) size);
return NULL;
}
hdr = (alloc_hdr *) malloc(size + sizeof(alloc_hdr));
if (!hdr) {
write_log("A FAIL %ld\n", (long) size);
return NULL;
}
hdr->u.sz = size;
ret = (void *) (hdr + 1);
write_log("A %p %ld\n", ret, (long) size);
return ret;
}
void *duk_realloc_logging(void *udata, void *ptr, duk_size_t size) {
alloc_hdr *hdr;
size_t old_size;
void *t;
void *ret;
(void) udata; /* Suppress warning. */
/* Handle the ptr-NULL vs. size-zero cases explicitly to minimize
* platform assumptions. You can get away with much less in specific
* well-behaving environments.
*/
if (ptr) {
hdr = (alloc_hdr *) (void *) ((unsigned char *) ptr - sizeof(alloc_hdr));
old_size = hdr->u.sz;
if (size == 0) {
free((void *) hdr);
write_log("R %p %ld NULL 0\n", ptr, (long) old_size);
return NULL;
} else {
t = realloc((void *) hdr, size + sizeof(alloc_hdr));
if (!t) {
write_log("R %p %ld FAIL %ld\n", ptr, (long) old_size, (long) size);
return NULL;
}
hdr = (alloc_hdr *) t;
hdr->u.sz = size;
ret = (void *) (hdr + 1);
write_log("R %p %ld %p %ld\n", ptr, (long) old_size, ret, (long) size);
return ret;
}
} else {
if (size == 0) {
write_log("R NULL 0 NULL 0\n");
return NULL;
} else {
hdr = (alloc_hdr *) malloc(size + sizeof(alloc_hdr));
if (!hdr) {
write_log("R NULL 0 FAIL %ld\n", (long) size);
return NULL;
}
hdr->u.sz = size;
ret = (void *) (hdr + 1);
write_log("R NULL 0 %p %ld\n", ret, (long) size);
return ret;
}
}
}
void duk_free_logging(void *udata, void *ptr) {
alloc_hdr *hdr;
(void) udata; /* Suppress warning. */
if (!ptr) {
write_log("F NULL 0\n");
return;
}
hdr = (alloc_hdr *) (void *) ((unsigned char *) ptr - sizeof(alloc_hdr));
write_log("F %p %ld\n", ptr, (long) hdr->u.sz);
free((void *) hdr);
}

View File

@@ -1,10 +0,0 @@
#ifndef DUK_ALLOC_LOGGING_H_INCLUDED
#define DUK_ALLOC_LOGGING_H_INCLUDED
#include "duktape.h"
void *duk_alloc_logging(void *udata, duk_size_t size);
void *duk_realloc_logging(void *udata, void *ptr, duk_size_t size);
void duk_free_logging(void *udata, void *ptr);
#endif /* DUK_ALLOC_LOGGING_H_INCLUDED */

View File

@@ -1,41 +0,0 @@
#!/usr/bin/python
#
# Analyze allocator logs and write total-bytes-in-use after every
# operation to stdout. The output can be gnuplotted as:
#
# $ python log2gnuplot.py </tmp/duk-alloc-log.txt >/tmp/output.txt
# $ gnuplot
# > plot "output.txt" with lines
#
import os
import sys
def main():
allocated = 0
for line in sys.stdin:
line = line.strip()
parts = line.split(' ')
# A ptr/NULL/FAIL size
# F ptr/NULL size
# R ptr/NULL oldsize ptr/NULL/FAIL newsize
# Note: ajduk doesn't log oldsize (uses -1 instead)
if parts[0] == 'A':
if parts[1] != 'NULL' and parts[1] != 'FAIL':
allocated += long(parts[2])
elif parts[0] == 'F':
allocated -= long(parts[2])
elif parts[0] == 'R':
allocated -= long(parts[2])
if parts[3] != 'NULL' and parts[3] != 'FAIL':
allocated += long(parts[4])
print(allocated)
print(allocated)
if __name__ == '__main__':
main()

View File

@@ -1,10 +0,0 @@
==========================================
Allocator with memory wiping and red zones
==========================================
Example allocator that wipes memory on free and checks that no out-of-bounds
writes have been made to bytes just before and after the allocated area.
Valgrind is a better tool for detecting these memory issues, but it's not
available for all targets so you can use something like this to detect
memory lifecycle or out-of-bounds issues.

View File

@@ -1,182 +0,0 @@
/*
* Example torture memory allocator with memory wiping and check for
* out-of-bounds writes.
*
* Allocation structure:
*
* [ alloc_hdr | red zone before | user area | red zone after ]
*
* ^ ^
* | `--- pointer returned to Duktape
* `--- underlying malloc ptr
*/
#include "duktape.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#define RED_ZONE_SIZE 16
#define RED_ZONE_BYTE 0x5a
#define INIT_BYTE 0xa5
#define WIPE_BYTE 0x27
typedef struct {
/* The double value in the union is there to ensure alignment is
* good for IEEE doubles too. In many 32-bit environments 4 bytes
* would be sufficiently aligned and the double value is unnecessary.
*/
union {
size_t sz;
double d;
} u;
} alloc_hdr;
static void check_red_zone(alloc_hdr *hdr) {
size_t size;
int i;
int err;
unsigned char *p;
unsigned char *userptr;
size = hdr->u.sz;
userptr = (unsigned char *) hdr + sizeof(alloc_hdr) + RED_ZONE_SIZE;
err = 0;
p = (unsigned char *) hdr + sizeof(alloc_hdr);
for (i = 0; i < RED_ZONE_SIZE; i++) {
if (p[i] != RED_ZONE_BYTE) {
err = 1;
}
}
if (err) {
fprintf(stderr, "RED ZONE CORRUPTED BEFORE ALLOC: hdr=%p ptr=%p size=%ld\n",
(void *) hdr, (void *) userptr, (long) size);
fflush(stderr);
}
err = 0;
p = (unsigned char *) hdr + sizeof(alloc_hdr) + RED_ZONE_SIZE + size;
for (i = 0; i < RED_ZONE_SIZE; i++) {
if (p[i] != RED_ZONE_BYTE) {
err = 1;
}
}
if (err) {
fprintf(stderr, "RED ZONE CORRUPTED AFTER ALLOC: hdr=%p ptr=%p size=%ld\n",
(void *) hdr, (void *) userptr, (long) size);
fflush(stderr);
}
}
void *duk_alloc_torture(void *udata, duk_size_t size) {
unsigned char *p;
(void) udata; /* Suppress warning. */
if (size == 0) {
return NULL;
}
p = (unsigned char *) malloc(size + sizeof(alloc_hdr) + 2 * RED_ZONE_SIZE);
if (!p) {
return NULL;
}
((alloc_hdr *) (void *) p)->u.sz = size;
p += sizeof(alloc_hdr);
memset((void *) p, RED_ZONE_BYTE, RED_ZONE_SIZE);
p += RED_ZONE_SIZE;
memset((void *) p, INIT_BYTE, size);
p += size;
memset((void *) p, RED_ZONE_BYTE, RED_ZONE_SIZE);
p -= size;
return (void *) p;
}
void *duk_realloc_torture(void *udata, void *ptr, duk_size_t size) {
unsigned char *p, *old_p;
size_t old_size;
(void) udata; /* Suppress warning. */
/* Handle the ptr-NULL vs. size-zero cases explicitly to minimize
* platform assumptions. You can get away with much less in specific
* well-behaving environments.
*/
if (ptr) {
old_p = (unsigned char *) ptr - sizeof(alloc_hdr) - RED_ZONE_SIZE;
old_size = ((alloc_hdr *) (void *) old_p)->u.sz;
check_red_zone((alloc_hdr *) (void *) old_p);
if (size == 0) {
memset((void *) old_p, WIPE_BYTE, old_size + sizeof(alloc_hdr) + 2 * RED_ZONE_SIZE);
free((void *) old_p);
return NULL;
} else {
/* Force address change on every realloc. */
p = (unsigned char *) malloc(size + sizeof(alloc_hdr) + 2 * RED_ZONE_SIZE);
if (!p) {
return NULL;
}
((alloc_hdr *) (void *) p)->u.sz = size;
p += sizeof(alloc_hdr);
memset((void *) p, RED_ZONE_BYTE, RED_ZONE_SIZE);
p += RED_ZONE_SIZE;
if (size > old_size) {
memcpy((void *) p, (void *) (old_p + sizeof(alloc_hdr) + RED_ZONE_SIZE), old_size);
memset((void *) (p + old_size), INIT_BYTE, size - old_size);
} else {
memcpy((void *) p, (void *) (old_p + sizeof(alloc_hdr) + RED_ZONE_SIZE), size);
}
p += size;
memset((void *) p, RED_ZONE_BYTE, RED_ZONE_SIZE);
p -= size;
memset((void *) old_p, WIPE_BYTE, old_size + sizeof(alloc_hdr) + 2 * RED_ZONE_SIZE);
free((void *) old_p);
return (void *) p;
}
} else {
if (size == 0) {
return NULL;
} else {
p = (unsigned char *) malloc(size + sizeof(alloc_hdr) + 2 * RED_ZONE_SIZE);
if (!p) {
return NULL;
}
((alloc_hdr *) (void *) p)->u.sz = size;
p += sizeof(alloc_hdr);
memset((void *) p, RED_ZONE_BYTE, RED_ZONE_SIZE);
p += RED_ZONE_SIZE;
memset((void *) p, INIT_BYTE, size);
p += size;
memset((void *) p, RED_ZONE_BYTE, RED_ZONE_SIZE);
p -= size;
return (void *) p;
}
}
}
void duk_free_torture(void *udata, void *ptr) {
unsigned char *p;
size_t old_size;
(void) udata; /* Suppress warning. */
if (!ptr) {
return;
}
p = (unsigned char *) ptr - sizeof(alloc_hdr) - RED_ZONE_SIZE;
old_size = ((alloc_hdr *) (void *) p)->u.sz;
check_red_zone((alloc_hdr *) (void *) p);
memset((void *) p, WIPE_BYTE, old_size + sizeof(alloc_hdr) + 2 * RED_ZONE_SIZE);
free((void *) p);
}

View File

@@ -1,10 +0,0 @@
#ifndef DUK_ALLOC_TORTURE_H_INCLUDED
#define DUK_ALLOC_TORTURE_H_INCLUDED
#include "duktape.h"
void *duk_alloc_torture(void *udata, duk_size_t size);
void *duk_realloc_torture(void *udata, void *ptr, duk_size_t size);
void duk_free_torture(void *udata, void *ptr);
#endif /* DUK_ALLOC_TORTURE_H_INCLUDED */

View File

@@ -1,6 +0,0 @@
====================
Duktape command line
====================
Ecmascript command line execution tool, useful for running Ecmascript code
from a file, stdin, or interactively. Also used by automatic testing.

View File

@@ -1,873 +0,0 @@
/*
* Command line execution tool. Useful for test cases and manual testing.
*
* To enable readline and other fancy stuff, compile with -DDUK_CMDLINE_FANCY.
* It is not the default to maximize portability. You can also compile in
* support for example allocators, grep for DUK_CMDLINE_*.
*/
#ifndef DUK_CMDLINE_FANCY
#define NO_READLINE
#define NO_RLIMIT
#define NO_SIGNAL
#endif
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || \
defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
/* Suppress warnings about plain fopen() etc. */
#define _CRT_SECURE_NO_WARNINGS
#endif
#define GREET_CODE(variant) \
"print('((o) Duktape" variant " ' + " \
"Math.floor(Duktape.version / 10000) + '.' + " \
"Math.floor(Duktape.version / 100) % 100 + '.' + " \
"Duktape.version % 100" \
", '(" DUK_GIT_DESCRIBE ")');"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef NO_SIGNAL
#include <signal.h>
#endif
#ifndef NO_RLIMIT
#include <sys/resource.h>
#endif
#ifndef NO_READLINE
#include <readline/readline.h>
#include <readline/history.h>
#endif
#ifdef DUK_CMDLINE_ALLOC_LOGGING
#include "duk_alloc_logging.h"
#endif
#ifdef DUK_CMDLINE_ALLOC_TORTURE
#include "duk_alloc_torture.h"
#endif
#ifdef DUK_CMDLINE_ALLOC_HYBRID
#include "duk_alloc_hybrid.h"
#endif
#include "duktape.h"
#ifdef DUK_CMDLINE_AJSHEAP
/* Defined in duk_cmdline_ajduk.c or alljoyn.js headers. */
void ajsheap_init(void);
void ajsheap_dump(void);
void ajsheap_register(duk_context *ctx);
void ajsheap_start_exec_timeout(void);
void ajsheap_clear_exec_timeout(void);
void *ajsheap_alloc_wrapped(void *udata, duk_size_t size);
void *ajsheap_realloc_wrapped(void *udata, void *ptr, duk_size_t size);
void ajsheap_free_wrapped(void *udata, void *ptr);
void *AJS_Alloc(void *udata, duk_size_t size);
void *AJS_Realloc(void *udata, void *ptr, duk_size_t size);
void AJS_Free(void *udata, void *ptr);
#endif
#ifdef DUK_CMDLINE_DEBUGGER_SUPPORT
#include "duk_trans_socket.h"
#endif
#define MEM_LIMIT_NORMAL (128*1024*1024) /* 128 MB */
#define MEM_LIMIT_HIGH (2047*1024*1024) /* ~2 GB */
#define LINEBUF_SIZE 65536
static int interactive_mode = 0;
#ifndef NO_RLIMIT
static void set_resource_limits(rlim_t mem_limit_value) {
int rc;
struct rlimit lim;
rc = getrlimit(RLIMIT_AS, &lim);
if (rc != 0) {
fprintf(stderr, "Warning: cannot read RLIMIT_AS\n");
return;
}
if (lim.rlim_max < mem_limit_value) {
fprintf(stderr, "Warning: rlim_max < mem_limit_value (%d < %d)\n", (int) lim.rlim_max, (int) mem_limit_value);
return;
}
lim.rlim_cur = mem_limit_value;
lim.rlim_max = mem_limit_value;
rc = setrlimit(RLIMIT_AS, &lim);
if (rc != 0) {
fprintf(stderr, "Warning: setrlimit failed\n");
return;
}
#if 0
fprintf(stderr, "Set RLIMIT_AS to %d\n", (int) mem_limit_value);
#endif
}
#endif /* NO_RLIMIT */
#ifndef NO_SIGNAL
static void my_sighandler(int x) {
fprintf(stderr, "Got signal %d\n", x);
fflush(stderr);
}
static void set_sigint_handler(void) {
(void) signal(SIGINT, my_sighandler);
}
#endif /* NO_SIGNAL */
static int get_stack_raw(duk_context *ctx) {
if (!duk_is_object(ctx, -1)) {
return 1;
}
if (!duk_has_prop_string(ctx, -1, "stack")) {
return 1;
}
if (!duk_is_error(ctx, -1)) {
/* Not an Error instance, don't read "stack". */
return 1;
}
duk_get_prop_string(ctx, -1, "stack"); /* caller coerces */
duk_remove(ctx, -2);
return 1;
}
/* Print error to stderr and pop error. */
static void print_pop_error(duk_context *ctx, FILE *f) {
/* Print error objects with a stack trace specially.
* Note that getting the stack trace may throw an error
* so this also needs to be safe call wrapped.
*/
(void) duk_safe_call(ctx, get_stack_raw, 1 /*nargs*/, 1 /*nrets*/);
fprintf(f, "%s\n", duk_safe_to_string(ctx, -1));
fflush(f);
duk_pop(ctx);
}
static int wrapped_compile_execute(duk_context *ctx) {
const char *src_data;
duk_size_t src_len;
int comp_flags;
/* XXX: Here it'd be nice to get some stats for the compilation result
* when a suitable command line is given (e.g. code size, constant
* count, function count. These are available internally but not through
* the public API.
*/
/* Use duk_compile_lstring_filename() variant which avoids interning
* the source code. This only really matters for low memory environments.
*/
/* [ ... bytecode_filename src_data src_len filename ] */
src_data = (const char *) duk_require_pointer(ctx, -3);
src_len = (duk_size_t) duk_require_uint(ctx, -2);
if (src_data != NULL && src_len >= 2 && src_data[0] == (char) 0xff) {
/* Bytecode. */
duk_push_lstring(ctx, src_data, src_len);
duk_to_buffer(ctx, -1, NULL);
duk_load_function(ctx);
} else {
/* Source code. */
comp_flags = 0;
duk_compile_lstring_filename(ctx, comp_flags, src_data, src_len);
}
/* [ ... bytecode_filename src_data src_len function ] */
/* Optional bytecode dump. */
if (duk_is_string(ctx, -4)) {
FILE *f;
void *bc_ptr;
duk_size_t bc_len;
size_t wrote;
duk_dup_top(ctx);
duk_dump_function(ctx);
bc_ptr = duk_require_buffer(ctx, -1, &bc_len);
f = fopen(duk_require_string(ctx, -5), "wb");
if (!f) {
duk_error(ctx, DUK_ERR_ERROR, "failed to open bytecode output file");
}
wrote = fwrite(bc_ptr, 1, (size_t) bc_len, f); /* XXX: handle partial writes */
(void) fclose(f);
if (wrote != bc_len) {
duk_error(ctx, DUK_ERR_ERROR, "failed to write all bytecode");
}
return 0; /* duk_safe_call() cleans up */
}
#if 0
/* Manual test for bytecode dump/load cycle: dump and load before
* execution. Enable manually, then run "make qecmatest" for a
* reasonably good coverage of different functions and programs.
*/
duk_dump_function(ctx);
duk_load_function(ctx);
#endif
#if defined(DUK_CMDLINE_AJSHEAP)
ajsheap_start_exec_timeout();
#endif
duk_push_global_object(ctx); /* 'this' binding */
duk_call_method(ctx, 0);
#if defined(DUK_CMDLINE_AJSHEAP)
ajsheap_clear_exec_timeout();
#endif
if (interactive_mode) {
/*
* In interactive mode, write to stdout so output won't
* interleave as easily.
*
* NOTE: the ToString() coercion may fail in some cases;
* for instance, if you evaluate:
*
* ( {valueOf: function() {return {}},
* toString: function() {return {}}});
*
* The error is:
*
* TypeError: failed to coerce with [[DefaultValue]]
* duk_api.c:1420
*
* These are handled now by the caller which also has stack
* trace printing support. User code can print out errors
* safely using duk_safe_to_string().
*/
fprintf(stdout, "= %s\n", duk_to_string(ctx, -1));
fflush(stdout);
} else {
/* In non-interactive mode, success results are not written at all.
* It is important that the result value is not string coerced,
* as the string coercion may cause an error in some cases.
*/
}
return 0; /* duk_safe_call() cleans up */
}
static int handle_fh(duk_context *ctx, FILE *f, const char *filename, const char *bytecode_filename) {
char *buf = NULL;
int len;
size_t got;
int rc;
int retval = -1;
if (fseek(f, 0, SEEK_END) < 0) {
goto error;
}
len = (int) ftell(f);
if (fseek(f, 0, SEEK_SET) < 0) {
goto error;
}
buf = (char *) malloc(len);
if (!buf) {
goto error;
}
got = fread((void *) buf, (size_t) 1, (size_t) len, f);
duk_push_string(ctx, bytecode_filename);
duk_push_pointer(ctx, (void *) buf);
duk_push_uint(ctx, (duk_uint_t) got);
duk_push_string(ctx, filename);
interactive_mode = 0; /* global */
rc = duk_safe_call(ctx, wrapped_compile_execute, 4 /*nargs*/, 1 /*nret*/);
#if defined(DUK_CMDLINE_AJSHEAP)
ajsheap_clear_exec_timeout();
#endif
free(buf);
buf = NULL;
if (rc != DUK_EXEC_SUCCESS) {
print_pop_error(ctx, stderr);
goto error;
} else {
duk_pop(ctx);
retval = 0;
}
/* fall thru */
cleanup:
if (buf) {
free(buf);
}
return retval;
error:
fprintf(stderr, "error in executing file %s\n", filename);
fflush(stderr);
goto cleanup;
}
static int handle_file(duk_context *ctx, const char *filename, const char *bytecode_filename) {
FILE *f = NULL;
int retval;
f = fopen(filename, "rb");
if (!f) {
fprintf(stderr, "failed to open source file: %s\n", filename);
fflush(stderr);
goto error;
}
retval = handle_fh(ctx, f, filename, bytecode_filename);
fclose(f);
return retval;
error:
return -1;
}
static int handle_eval(duk_context *ctx, const char *code) {
int rc;
int retval = -1;
duk_push_pointer(ctx, (void *) code);
duk_push_uint(ctx, (duk_uint_t) strlen(code));
duk_push_string(ctx, "eval");
interactive_mode = 0; /* global */
rc = duk_safe_call(ctx, wrapped_compile_execute, 3 /*nargs*/, 1 /*nret*/);
#if defined(DUK_CMDLINE_AJSHEAP)
ajsheap_clear_exec_timeout();
#endif
if (rc != DUK_EXEC_SUCCESS) {
print_pop_error(ctx, stderr);
} else {
duk_pop(ctx);
retval = 0;
}
return retval;
}
#ifdef NO_READLINE
static int handle_interactive(duk_context *ctx) {
const char *prompt = "duk> ";
char *buffer = NULL;
int retval = 0;
int rc;
int got_eof = 0;
duk_eval_string(ctx, GREET_CODE(" [no readline]"));
duk_pop(ctx);
buffer = (char *) malloc(LINEBUF_SIZE);
if (!buffer) {
fprintf(stderr, "failed to allocated a line buffer\n");
fflush(stderr);
retval = -1;
goto done;
}
while (!got_eof) {
size_t idx = 0;
fwrite(prompt, 1, strlen(prompt), stdout);
fflush(stdout);
for (;;) {
int c = fgetc(stdin);
if (c == EOF) {
got_eof = 1;
break;
} else if (c == '\n') {
break;
} else if (idx >= LINEBUF_SIZE) {
fprintf(stderr, "line too long\n");
fflush(stderr);
retval = -1;
goto done;
} else {
buffer[idx++] = (char) c;
}
}
duk_push_pointer(ctx, (void *) buffer);
duk_push_uint(ctx, (duk_uint_t) idx);
duk_push_string(ctx, "input");
interactive_mode = 1; /* global */
rc = duk_safe_call(ctx, wrapped_compile_execute, 3 /*nargs*/, 1 /*nret*/);
#if defined(DUK_CMDLINE_AJSHEAP)
ajsheap_clear_exec_timeout();
#endif
if (rc != DUK_EXEC_SUCCESS) {
/* in interactive mode, write to stdout */
print_pop_error(ctx, stdout);
retval = -1; /* an error 'taints' the execution */
} else {
duk_pop(ctx);
}
}
done:
if (buffer) {
free(buffer);
buffer = NULL;
}
return retval;
}
#else /* NO_READLINE */
static int handle_interactive(duk_context *ctx) {
const char *prompt = "duk> ";
char *buffer = NULL;
int retval = 0;
int rc;
duk_eval_string(ctx, GREET_CODE(""));
duk_pop(ctx);
/*
* Note: using readline leads to valgrind-reported leaks inside
* readline itself. Execute code from an input file (and not
* through stdin) for clean valgrind runs.
*/
rl_initialize();
for (;;) {
if (buffer) {
free(buffer);
buffer = NULL;
}
buffer = readline(prompt);
if (!buffer) {
break;
}
if (buffer && buffer[0] != (char) 0) {
add_history(buffer);
}
duk_push_pointer(ctx, (void *) buffer);
duk_push_uint(ctx, (duk_uint_t) strlen(buffer));
duk_push_string(ctx, "input");
interactive_mode = 1; /* global */
rc = duk_safe_call(ctx, wrapped_compile_execute, 3 /*nargs*/, 1 /*nret*/);
#if defined(DUK_CMDLINE_AJSHEAP)
ajsheap_clear_exec_timeout();
#endif
if (buffer) {
free(buffer);
buffer = NULL;
}
if (rc != DUK_EXEC_SUCCESS) {
/* in interactive mode, write to stdout */
print_pop_error(ctx, stdout);
retval = -1; /* an error 'taints' the execution */
} else {
duk_pop(ctx);
}
}
if (buffer) {
free(buffer);
buffer = NULL;
}
return retval;
}
#endif /* NO_READLINE */
#ifdef DUK_CMDLINE_DEBUGGER_SUPPORT
static void debugger_detached(void *udata) {
fprintf(stderr, "Debugger detached, udata: %p\n", (void *) udata);
fflush(stderr);
}
#endif
#define ALLOC_DEFAULT 0
#define ALLOC_LOGGING 1
#define ALLOC_TORTURE 2
#define ALLOC_HYBRID 3
#define ALLOC_AJSHEAP 4
static duk_context *create_duktape_heap(int alloc_provider, int debugger) {
duk_context *ctx;
ctx = NULL;
if (!ctx && alloc_provider == ALLOC_LOGGING) {
#ifdef DUK_CMDLINE_ALLOC_LOGGING
ctx = duk_create_heap(duk_alloc_logging,
duk_realloc_logging,
duk_free_logging,
(void *) 0xdeadbeef,
NULL);
#else
fprintf(stderr, "Warning: option --alloc-logging ignored, no logging allocator support\n");
fflush(stderr);
#endif
}
if (!ctx && alloc_provider == ALLOC_TORTURE) {
#ifdef DUK_CMDLINE_ALLOC_TORTURE
ctx = duk_create_heap(duk_alloc_torture,
duk_realloc_torture,
duk_free_torture,
(void *) 0xdeadbeef,
NULL);
#else
fprintf(stderr, "Warning: option --alloc-torture ignored, no torture allocator support\n");
fflush(stderr);
#endif
}
if (!ctx && alloc_provider == ALLOC_HYBRID) {
#ifdef DUK_CMDLINE_ALLOC_HYBRID
void *udata = duk_alloc_hybrid_init();
if (!udata) {
fprintf(stderr, "Failed to init hybrid allocator\n");
fflush(stderr);
} else {
ctx = duk_create_heap(duk_alloc_hybrid,
duk_realloc_hybrid,
duk_free_hybrid,
udata,
NULL);
}
#else
fprintf(stderr, "Warning: option --alloc-hybrid ignored, no hybrid allocator support\n");
fflush(stderr);
#endif
}
if (!ctx && alloc_provider == ALLOC_AJSHEAP) {
#ifdef DUK_CMDLINE_AJSHEAP
ajsheap_init();
ctx = duk_create_heap(
ajsheap_log ? ajsheap_alloc_wrapped : AJS_Alloc,
ajsheap_log ? ajsheap_realloc_wrapped : AJS_Realloc,
ajsheap_log ? ajsheap_free_wrapped : AJS_Free,
(void *) 0xdeadbeef, /* heap_udata: ignored by AjsHeap, use as marker */
NULL
); /* fatal_handler */
#else
fprintf(stderr, "Warning: option --alloc-ajsheap ignored, no ajsheap allocator support\n");
fflush(stderr);
#endif
}
if (!ctx && alloc_provider == ALLOC_DEFAULT) {
ctx = duk_create_heap_default();
}
if (!ctx) {
fprintf(stderr, "Failed to create Duktape heap\n");
fflush(stderr);
exit(-1);
}
#ifdef DUK_CMDLINE_AJSHEAP
if (alloc_provider == ALLOC_AJSHEAP) {
fprintf(stdout, "Pool dump after heap creation\n");
ajsheap_dump();
}
#endif
#ifdef DUK_CMDLINE_AJSHEAP
if (alloc_provider == ALLOC_AJSHEAP) {
ajsheap_register(ctx);
}
#endif
if (debugger) {
#ifdef DUK_CMDLINE_DEBUGGER_SUPPORT
fprintf(stderr, "Debugger enabled, create socket and wait for connection\n");
fflush(stderr);
duk_trans_socket_init();
duk_trans_socket_waitconn();
fprintf(stderr, "Debugger connected, call duk_debugger_attach() and then execute requested file(s)/eval\n");
fflush(stderr);
duk_debugger_attach(ctx,
duk_trans_socket_read_cb,
duk_trans_socket_write_cb,
duk_trans_socket_peek_cb,
duk_trans_socket_read_flush_cb,
duk_trans_socket_write_flush_cb,
debugger_detached,
(void *) 0xbeef1234);
#else
fprintf(stderr, "Warning: option --debugger ignored, no debugger support\n");
fflush(stderr);
#endif
}
#if 0
/* Manual test for duk_debugger_cooperate() */
{
for (i = 0; i < 60; i++) {
printf("cooperate: %d\n", i);
usleep(1000000);
duk_debugger_cooperate(ctx);
}
}
#endif
return ctx;
}
static void destroy_duktape_heap(duk_context *ctx, int alloc_provider) {
(void) alloc_provider;
#ifdef DUK_CMDLINE_AJSHEAP
if (alloc_provider == ALLOC_AJSHEAP) {
fprintf(stdout, "Pool dump before duk_destroy_heap(), before forced gc\n");
ajsheap_dump();
duk_gc(ctx, 0);
fprintf(stdout, "Pool dump before duk_destroy_heap(), after forced gc\n");
ajsheap_dump();
}
#endif
if (ctx) {
duk_destroy_heap(ctx);
}
#ifdef DUK_CMDLINE_AJSHEAP
if (alloc_provider == ALLOC_AJSHEAP) {
fprintf(stdout, "Pool dump after duk_destroy_heap() (should have zero allocs)\n");
ajsheap_dump();
}
#endif
}
int main(int argc, char *argv[]) {
duk_context *ctx = NULL;
int retval = 0;
int have_files = 0;
int have_eval = 0;
int interactive = 0;
int memlimit_high = 1;
int alloc_provider = ALLOC_DEFAULT;
int ajsheap_log = 0;
int debugger = 0;
int recreate_heap = 0;
int verbose = 0;
const char *compile_filename = NULL;
int i;
#ifdef DUK_CMDLINE_AJSHEAP
alloc_provider = ALLOC_AJSHEAP;
#endif
(void) ajsheap_log;
/*
* Signal handling setup
*/
#ifndef NO_SIGNAL
set_sigint_handler();
/* This is useful at the global level; libraries should avoid SIGPIPE though */
/*signal(SIGPIPE, SIG_IGN);*/
#endif
/*
* Parse options
*/
for (i = 1; i < argc; i++) {
char *arg = argv[i];
if (!arg) {
goto usage;
}
if (strcmp(arg, "--restrict-memory") == 0) {
memlimit_high = 0;
} else if (strcmp(arg, "-i") == 0) {
interactive = 1;
} else if (strcmp(arg, "-c") == 0) {
if (i == argc - 1) {
goto usage;
}
i++;
compile_filename = argv[i];
} else if (strcmp(arg, "-e") == 0) {
have_eval = 1;
if (i == argc - 1) {
goto usage;
}
i++; /* skip code */
} else if (strcmp(arg, "--alloc-default") == 0) {
alloc_provider = ALLOC_DEFAULT;
} else if (strcmp(arg, "--alloc-logging") == 0) {
alloc_provider = ALLOC_LOGGING;
} else if (strcmp(arg, "--alloc-torture") == 0) {
alloc_provider = ALLOC_TORTURE;
} else if (strcmp(arg, "--alloc-hybrid") == 0) {
alloc_provider = ALLOC_HYBRID;
} else if (strcmp(arg, "--alloc-ajsheap") == 0) {
alloc_provider = ALLOC_AJSHEAP;
} else if (strcmp(arg, "--ajsheap-log") == 0) {
ajsheap_log = 1;
} else if (strcmp(arg, "--debugger") == 0) {
debugger = 1;
} else if (strcmp(arg, "--recreate-heap") == 0) {
recreate_heap = 1;
} else if (strcmp(arg, "--verbose") == 0) {
verbose = 1;
} else if (strlen(arg) >= 1 && arg[0] == '-') {
goto usage;
} else {
have_files = 1;
}
}
if (!have_files && !have_eval) {
interactive = 1;
}
/*
* Memory limit
*/
#ifndef NO_RLIMIT
set_resource_limits(memlimit_high ? MEM_LIMIT_HIGH : MEM_LIMIT_NORMAL);
#else
if (memlimit_high == 0) {
fprintf(stderr, "Warning: option --restrict-memory ignored, no rlimit support\n");
fflush(stderr);
}
#endif
/*
* Create heap
*/
ctx = create_duktape_heap(alloc_provider, debugger);
/*
* Execute any argument file(s)
*/
for (i = 1; i < argc; i++) {
char *arg = argv[i];
if (!arg) {
continue;
} else if (strlen(arg) == 2 && strcmp(arg, "-e") == 0) {
/* Here we know the eval arg exists but check anyway */
if (i == argc - 1) {
retval = 1;
goto cleanup;
}
if (handle_eval(ctx, argv[i + 1]) != 0) {
retval = 1;
goto cleanup;
}
i++; /* skip code */
continue;
} else if (strlen(arg) == 2 && strcmp(arg, "-c") == 0) {
i++; /* skip filename */
continue;
} else if (strlen(arg) >= 1 && arg[0] == '-') {
continue;
}
if (verbose) {
fprintf(stderr, "*** Executing file: %s\n", arg);
fflush(stderr);
}
if (handle_file(ctx, arg, compile_filename) != 0) {
retval = 1;
goto cleanup;
}
if (recreate_heap) {
if (verbose) {
fprintf(stderr, "*** Recreating heap...\n");
fflush(stderr);
}
destroy_duktape_heap(ctx, alloc_provider);
ctx = create_duktape_heap(alloc_provider, debugger);
}
}
/*
* Enter interactive mode if options indicate it
*/
if (interactive) {
if (handle_interactive(ctx) != 0) {
retval = 1;
goto cleanup;
}
}
/*
* Cleanup and exit
*/
cleanup:
if (interactive) {
fprintf(stderr, "Cleaning up...\n");
fflush(stderr);
}
if (ctx) {
destroy_duktape_heap(ctx, alloc_provider);
}
ctx = NULL;
return retval;
/*
* Usage
*/
usage:
fprintf(stderr, "Usage: duk [options] [<filenames>]\n"
"\n"
" -i enter interactive mode after executing argument file(s) / eval code\n"
" -e CODE evaluate code\n"
" -c FILE compile into bytecode (use with only one file argument)\n"
" --verbose verbose messages to stderr\n"
" --restrict-memory use lower memory limit (used by test runner)\n"
" --alloc-default use Duktape default allocator\n"
#ifdef DUK_CMDLINE_ALLOC_LOGGING
" --alloc-logging use logging allocator (writes to /tmp)\n"
#endif
#ifdef DUK_CMDLINE_ALLOC_TORTURE
" --alloc-torture use torture allocator\n"
#endif
#ifdef DUK_CMDLINE_ALLOC_HYBRID
" --alloc-hybrid use hybrid allocator\n"
#endif
#ifdef DUK_CMDLINE_AJSHEAP
" --alloc-ajsheap use ajsheap allocator (enabled by default with 'ajduk')\n"
" --ajsheap-log write alloc log to /tmp/ajduk-alloc-log.txt\n"
#endif
#ifdef DUK_CMDLINE_DEBUGGER_SUPPORT
" --debugger start example debugger\n"
#endif
" --recreate-heap recreate heap after every file\n"
"\n"
"If <filename> is omitted, interactive mode is started automatically.\n");
fflush(stderr);
exit(1);
}

View File

@@ -1,892 +0,0 @@
/*
* 'ajduk' specific functionality, examples for low memory techniques
*/
#ifdef DUK_CMDLINE_AJSHEAP
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "ajs.h"
#include "ajs_heap.h"
extern uint8_t dbgHEAPDUMP;
/*
* Helpers
*/
static void safe_print_chars(const char *p, duk_size_t len, int until_nul) {
duk_size_t i;
printf("\"");
for (i = 0; i < len; i++) {
unsigned char x = (unsigned char) p[i];
if (until_nul && x == 0U) {
break;
}
if (x < 0x20 || x >= 0x7e || x == '"' || x == '\'' || x == '\\') {
printf("\\x%02x", (int) x);
} else {
printf("%c", (char) x);
}
}
printf("\"");
}
/*
* Heap initialization when using AllJoyn.js pool allocator (without any
* other AllJoyn.js integration). This serves as an example of how to
* integrate Duktape with a pool allocator and is useful for low memory
* testing.
*
* The pool sizes are not optimized here. The sizes are chosen so that
* you can look at the high water mark (hwm) and use counts (use) and see
* how much allocations are needed for each pool size. To optimize pool
* sizes more accurately, you can use --alloc-logging and inspect the memory
* allocation log which provides exact byte counts etc.
*
* https://git.allseenalliance.org/cgit/core/alljoyn-js.git
* https://git.allseenalliance.org/cgit/core/alljoyn-js.git/tree/ajs.c
*/
static const AJS_HeapConfig ajsheap_config[] = {
{ 8, 10, AJS_POOL_BORROW, 0 },
{ 12, 10, AJS_POOL_BORROW, 0 },
{ 16, 200, AJS_POOL_BORROW, 0 },
{ 20, 400, AJS_POOL_BORROW, 0 },
{ 24, 400, AJS_POOL_BORROW, 0 },
{ 28, 200, AJS_POOL_BORROW, 0 },
{ 32, 200, AJS_POOL_BORROW, 0 },
{ 40, 200, AJS_POOL_BORROW, 0 },
{ 48, 50, AJS_POOL_BORROW, 0 },
{ 52, 50, AJS_POOL_BORROW, 0 },
{ 56, 50, AJS_POOL_BORROW, 0 },
{ 60, 50, AJS_POOL_BORROW, 0 },
{ 64, 50, AJS_POOL_BORROW, 0 },
{ 128, 80, AJS_POOL_BORROW, 0 },
{ 256, 16, AJS_POOL_BORROW, 0 },
{ 512, 16, AJS_POOL_BORROW, 0 },
{ 1024, 6, AJS_POOL_BORROW, 0 },
{ 1360, 1, AJS_POOL_BORROW, 0 }, /* duk_heap, with heap ptr compression */
{ 2048, 5, AJS_POOL_BORROW, 0 },
{ 4096, 3, 0, 0 },
{ 8192, 3, 0, 0 },
{ 16384, 1, 0, 0 },
{ 32768, 1, 0, 0 }
};
uint8_t *ajsheap_ram = NULL;
void ajsheap_init(void) {
size_t heap_sz[1];
uint8_t *heap_array[1];
uint8_t num_pools, i;
AJ_Status ret;
num_pools = (uint8_t) (sizeof(ajsheap_config) / sizeof(AJS_HeapConfig));
heap_sz[0] = AJS_HeapRequired(ajsheap_config, /* heapConfig */
num_pools, /* numPools */
0); /* heapNum */
ajsheap_ram = (uint8_t *) malloc(heap_sz[0]);
if (ajsheap_ram == NULL) {
fprintf(stderr, "Failed to allocate AJS heap\n");
fflush(stderr);
exit(1);
}
heap_array[0] = ajsheap_ram;
fprintf(stderr, "Allocated AJS heap of %ld bytes, pools:", (long) heap_sz[0]);
for (i = 0; i < num_pools; i++) {
fprintf(stderr, " (sz:%ld,num:%ld,brw:%ld,idx:%ld)",
(long) ajsheap_config[i].size, (long) ajsheap_config[i].entries,
(long) ajsheap_config[i].borrow, (long) ajsheap_config[i].heapIndex);
}
fprintf(stderr, "\n");
fflush(stderr);
ret = AJS_HeapInit((void **) heap_array, /* heap */
(size_t *) heap_sz, /* heapSz */
ajsheap_config, /* heapConfig */
num_pools, /* numPools */
1); /* numHeaps */
fprintf(stderr, "AJS_HeapInit() -> %ld\n", (long) ret);
fflush(stderr);
/* Enable heap dumps */
dbgHEAPDUMP = 1;
}
/* AjsHeap.dump(), allows Ecmascript code to dump heap status at suitable
* points.
*/
duk_ret_t ajsheap_dump_binding(duk_context *ctx) {
AJS_HeapDump();
fflush(stdout);
return 0;
}
void ajsheap_dump(void) {
AJS_HeapDump();
fflush(stdout);
}
void ajsheap_register(duk_context *ctx) {
duk_push_object(ctx);
duk_push_c_function(ctx, ajsheap_dump_binding, 0);
duk_put_prop_string(ctx, -2, "dump");
duk_put_global_string(ctx, "AjsHeap");
}
/*
* Wrapped ajs_heap.c alloc functions
*
* Used to write an alloc log.
*/
static FILE *ajsheap_alloc_log = NULL;
static void ajsheap_write_alloc_log(const char *fmt, ...) {
va_list ap;
char buf[256];
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
buf[sizeof(buf) - 1] = (char) 0;
va_end(ap);
if (ajsheap_alloc_log == NULL) {
ajsheap_alloc_log = fopen("/tmp/ajduk-alloc-log.txt", "wb");
if (ajsheap_alloc_log == NULL) {
fprintf(stderr, "WARNING: failed to write alloc log, ignoring\n");
fflush(stderr);
return;
}
}
(void) fwrite((const void *) buf, 1, strlen(buf), ajsheap_alloc_log);
(void) fflush(ajsheap_alloc_log);
}
void *ajsheap_alloc_wrapped(void *udata, duk_size_t size) {
void *ret = AJS_Alloc(udata, size);
if (size > 0 && ret == NULL) {
ajsheap_write_alloc_log("A FAIL %ld\n", (long) size);
} else if (ret == NULL) {
ajsheap_write_alloc_log("A NULL %ld\n", (long) size);
} else {
ajsheap_write_alloc_log("A %p %ld\n", ret, (long) size);
}
return ret;
}
void *ajsheap_realloc_wrapped(void *udata, void *ptr, duk_size_t size) {
void *ret = AJS_Realloc(udata, ptr, size);
if (size > 0 && ret == NULL) {
if (ptr == NULL) {
ajsheap_write_alloc_log("R NULL -1 FAIL %ld\n", (long) size);
} else {
ajsheap_write_alloc_log("R %p -1 FAIL %ld\n", ptr, (long) size);
}
} else if (ret == NULL) {
if (ptr == NULL) {
ajsheap_write_alloc_log("R NULL -1 NULL %ld\n", (long) size);
} else {
ajsheap_write_alloc_log("R %p -1 NULL %ld\n", ptr, (long) size);
}
} else {
if (ptr == NULL) {
ajsheap_write_alloc_log("R NULL -1 %p %ld\n", ret, (long) size);
} else {
ajsheap_write_alloc_log("R %p -1 %p %ld\n", ptr, ret, (long) size);
}
}
return ret;
}
void ajsheap_free_wrapped(void *udata, void *ptr) {
AJS_Free(udata, ptr);
if (ptr == NULL) {
} else {
ajsheap_write_alloc_log("F %p -1\n", ptr);
}
}
/*
* Example pointer compression functions.
*
* 'base' is chosen so that no non-NULL pointer results in a zero result
* which is reserved for NULL pointers.
*/
duk_uint16_t ajsheap_enc16(void *ud, void *p) {
duk_uint32_t ret;
char *base = (char *) ajsheap_ram - 4;
/* Userdata is not needed in this case but would be useful if heap
* pointer compression were used for multiple heaps. The userdata
* allows the callback to distinguish between heaps and their base
* pointers.
*
* If not needed, the userdata can be left out during compilation
* by simply ignoring the userdata argument of the pointer encode
* and decode macros. It is kept here so that any bugs in actually
* providing the value inside Duktape are revealed during compilation.
*/
(void) ud;
#if 1
/* Ensure that we always get the heap_udata given in heap creation.
* (Useful for Duktape development, not needed for user programs.)
*/
if (ud != (void *) 0xdeadbeef) {
fprintf(stderr, "invalid udata for ajsheap_enc16: %p\n", ud);
fflush(stderr);
}
#endif
if (p == NULL) {
ret = 0;
} else {
ret = (duk_uint32_t) (((char *) p - base) >> 2);
}
#if 0
printf("ajsheap_enc16: %p -> %u\n", p, (unsigned int) ret);
#endif
if (ret > 0xffffUL) {
fprintf(stderr, "Failed to compress pointer\n");
fflush(stderr);
abort();
}
return (duk_uint16_t) ret;
}
void *ajsheap_dec16(void *ud, duk_uint16_t x) {
void *ret;
char *base = (char *) ajsheap_ram - 4;
/* See userdata discussion in ajsheap_enc16(). */
(void) ud;
#if 1
/* Ensure that we always get the heap_udata given in heap creation. */
if (ud != (void *) 0xdeadbeef) {
fprintf(stderr, "invalid udata for ajsheap_dec16: %p\n", ud);
fflush(stderr);
}
#endif
if (x == 0) {
ret = NULL;
} else {
ret = (void *) (base + (((duk_uint32_t) x) << 2));
}
#if 0
printf("ajsheap_dec16: %u -> %p\n", (unsigned int) x, ret);
#endif
return ret;
}
/*
* Simplified example of an external strings strategy where incoming strings
* are written sequentially into a fixed, memory mapped flash area.
*
* The example first scans if the string is already in the flash (which may
* happen if the same string is interned multiple times), then adds it to
* flash if there is space.
*
* This example is too slow to be used in a real world application: there
* should be e.g. a hash table to quickly check for strings that are already
* present in the string data (similarly to how string interning works in
* Duktape itself).
*/
static uint8_t ajsheap_strdata[65536];
static size_t ajsheap_strdata_used = 0;
const void *ajsheap_extstr_check_1(const void *ptr, duk_size_t len) {
uint8_t *p, *p_end;
uint8_t initial;
uint8_t *ret;
size_t left;
(void) safe_print_chars; /* potentially unused */
if (len <= 3) {
/* It's not worth it to make very small strings external, as
* they would take the same space anyway. Also avoids zero
* length degenerate case.
*/
return NULL;
}
/*
* Check if we already have the string. Be careful to compare for
* NUL terminator too, it is NOT present in 'ptr'. This algorithm
* is too simplistic and way too slow for actual use.
*/
initial = ((const uint8_t *) ptr)[0];
for (p = ajsheap_strdata, p_end = p + ajsheap_strdata_used; p != p_end; p++) {
if (*p != initial) {
continue;
}
left = (size_t) (p_end - p);
if (left >= len + 1 &&
memcmp(p, ptr, len) == 0 &&
p[len] == 0) {
ret = p;
#if 0
printf("ajsheap_extstr_check_1: ptr=%p, len=%ld ",
(void *) ptr, (long) len);
safe_print_chars((const char *) ptr, len, 0 /*until_nul*/);
printf(" -> existing %p (used=%ld)\n",
(void *) ret, (long) ajsheap_strdata_used);
#endif
return ret;
}
}
/*
* Not present yet, check if we have space. Again, be careful to
* ensure there is space for a NUL following the input data.
*/
if (ajsheap_strdata_used + len + 1 > sizeof(ajsheap_strdata)) {
#if 0
printf("ajsheap_extstr_check_1: ptr=%p, len=%ld ", (void *) ptr, (long) len);
safe_print_chars((const char *) ptr, len, 0 /*until_nul*/);
printf(" -> no space (used=%ld)\n", (long) ajsheap_strdata_used);
#endif
return NULL;
}
/*
* There is space, add the string to our collection, being careful
* to append the NUL.
*/
ret = ajsheap_strdata + ajsheap_strdata_used;
memcpy(ret, ptr, len);
ret[len] = (uint8_t) 0;
ajsheap_strdata_used += len + 1;
#if 0
printf("ajsheap_extstr_check_1: ptr=%p, len=%ld -> ", (void *) ptr, (long) len);
safe_print_chars((const char *) ptr, len, 0 /*until_nul*/);
printf(" -> %p (used=%ld)\n", (void *) ret, (long) ajsheap_strdata_used);
#endif
return (const void *) ret;
}
void ajsheap_extstr_free_1(const void *ptr) {
(void) ptr;
#if 0
printf("ajsheap_extstr_free_1: freeing extstr %p -> ", ptr);
safe_print_chars((const char *) ptr, DUK_SIZE_MAX, 1 /*until_nul*/);
printf("\n");
#endif
}
/*
* Simplified example of an external strings strategy where a set of strings
* is gathered during application compile time and baked into the application
* binary.
*
* Duktape built-in strings are available from duk_build_meta.json, see
* util/duk_meta_to_strarray.py. There may also be a lot of application
* specific strings, e.g. those used by application specific APIs. These
* must be gathered through some other means, see e.g. util/scan_strings.py.
*/
static const char *strdata_duk_builtin_strings[] = {
/*
* These strings are from util/duk_meta_to_strarray.py
*/
"Logger",
"Thread",
"Pointer",
"Buffer",
"DecEnv",
"ObjEnv",
"",
"global",
"Arguments",
"JSON",
"Math",
"Error",
"RegExp",
"Date",
"Number",
"Boolean",
"String",
"Array",
"Function",
"Object",
"Null",
"Undefined",
"{_func:true}",
"{\x22" "_func\x22" ":true}",
"{\x22" "_ninf\x22" ":true}",
"{\x22" "_inf\x22" ":true}",
"{\x22" "_nan\x22" ":true}",
"{\x22" "_undef\x22" ":true}",
"toLogString",
"clog",
"l",
"n",
"fatal",
"error",
"warn",
"debug",
"trace",
"raw",
"fmt",
"current",
"resume",
"compact",
"jc",
"jx",
"base64",
"hex",
"dec",
"enc",
"fin",
"gc",
"act",
"info",
"version",
"env",
"modLoaded",
"modSearch",
"errThrow",
"errCreate",
"compile",
"\xff" "Regbase",
"\xff" "Thread",
"\xff" "Handler",
"\xff" "Finalizer",
"\xff" "Callee",
"\xff" "Map",
"\xff" "Args",
"\xff" "This",
"\xff" "Pc2line",
"\xff" "Source",
"\xff" "Varenv",
"\xff" "Lexenv",
"\xff" "Varmap",
"\xff" "Formals",
"\xff" "Bytecode",
"\xff" "Next",
"\xff" "Target",
"\xff" "Value",
"pointer",
"buffer",
"\xff" "Tracedata",
"lineNumber",
"fileName",
"pc",
"stack",
"ThrowTypeError",
"Duktape",
"id",
"require",
"__proto__",
"setPrototypeOf",
"ownKeys",
"enumerate",
"deleteProperty",
"has",
"Proxy",
"callee",
"Invalid Date",
"[...]",
"\x0a" "\x09",
" ",
",",
"-0",
"+0",
"0",
"-Infinity",
"+Infinity",
"Infinity",
"object",
"string",
"number",
"boolean",
"undefined",
"stringify",
"tan",
"sqrt",
"sin",
"round",
"random",
"pow",
"min",
"max",
"log",
"floor",
"exp",
"cos",
"ceil",
"atan2",
"atan",
"asin",
"acos",
"abs",
"SQRT2",
"SQRT1_2",
"PI",
"LOG10E",
"LOG2E",
"LN2",
"LN10",
"E",
"message",
"name",
"input",
"index",
"(?:)",
"lastIndex",
"multiline",
"ignoreCase",
"source",
"test",
"exec",
"toGMTString",
"setYear",
"getYear",
"toJSON",
"toISOString",
"toUTCString",
"setUTCFullYear",
"setFullYear",
"setUTCMonth",
"setMonth",
"setUTCDate",
"setDate",
"setUTCHours",
"setHours",
"setUTCMinutes",
"setMinutes",
"setUTCSeconds",
"setSeconds",
"setUTCMilliseconds",
"setMilliseconds",
"setTime",
"getTimezoneOffset",
"getUTCMilliseconds",
"getMilliseconds",
"getUTCSeconds",
"getSeconds",
"getUTCMinutes",
"getMinutes",
"getUTCHours",
"getHours",
"getUTCDay",
"getDay",
"getUTCDate",
"getDate",
"getUTCMonth",
"getMonth",
"getUTCFullYear",
"getFullYear",
"getTime",
"toLocaleTimeString",
"toLocaleDateString",
"toTimeString",
"toDateString",
"now",
"UTC",
"parse",
"toPrecision",
"toExponential",
"toFixed",
"POSITIVE_INFINITY",
"NEGATIVE_INFINITY",
"NaN",
"MIN_VALUE",
"MAX_VALUE",
"substr",
"trim",
"toLocaleUpperCase",
"toUpperCase",
"toLocaleLowerCase",
"toLowerCase",
"substring",
"split",
"search",
"replace",
"match",
"localeCompare",
"charCodeAt",
"charAt",
"fromCharCode",
"reduceRight",
"reduce",
"filter",
"map",
"forEach",
"some",
"every",
"lastIndexOf",
"indexOf",
"unshift",
"splice",
"sort",
"slice",
"shift",
"reverse",
"push",
"pop",
"join",
"concat",
"isArray",
"arguments",
"caller",
"bind",
"call",
"apply",
"propertyIsEnumerable",
"isPrototypeOf",
"hasOwnProperty",
"valueOf",
"toLocaleString",
"toString",
"constructor",
"set",
"get",
"enumerable",
"configurable",
"writable",
"value",
"keys",
"isExtensible",
"isFrozen",
"isSealed",
"preventExtensions",
"freeze",
"seal",
"defineProperties",
"defineProperty",
"create",
"getOwnPropertyNames",
"getOwnPropertyDescriptor",
"getPrototypeOf",
"prototype",
"length",
"alert",
"print",
"unescape",
"escape",
"encodeURIComponent",
"encodeURI",
"decodeURIComponent",
"decodeURI",
"isFinite",
"isNaN",
"parseFloat",
"parseInt",
"eval",
"URIError",
"TypeError",
"SyntaxError",
"ReferenceError",
"RangeError",
"EvalError",
"break",
"case",
"catch",
"continue",
"debugger",
"default",
"delete",
"do",
"else",
"finally",
"for",
"function",
"if",
"in",
"instanceof",
"new",
"return",
"switch",
"this",
"throw",
"try",
"typeof",
"var",
"void",
"while",
"with",
"class",
"const",
"enum",
"export",
"extends",
"import",
"super",
"null",
"true",
"false",
"implements",
"interface",
"let",
"package",
"private",
"protected",
"public",
"static",
"yield",
/*
* These strings are manually added, and would be gathered in some
* application specific manner.
*/
"foo",
"bar",
"quux",
"enableFrob",
"disableFrob"
/* ... */
};
const void *ajsheap_extstr_check_2(const void *ptr, duk_size_t len) {
int i, n;
(void) safe_print_chars; /* potentially unused */
/* Linear scan. An actual implementation would need some acceleration
* structure, e.g. select a sublist based on first character.
*
* NOTE: input string (behind 'ptr' with 'len' bytes) DOES NOT have a
* trailing NUL character. Any strings returned from this function
* MUST have a trailing NUL character.
*/
n = (int) (sizeof(strdata_duk_builtin_strings) / sizeof(const char *));
for (i = 0; i < n; i++) {
const char *str;
str = strdata_duk_builtin_strings[i];
if (strlen(str) == len && memcmp(ptr, (const void *) str, len) == 0) {
#if 0
printf("ajsheap_extstr_check_2: ptr=%p, len=%ld ",
(void *) ptr, (long) len);
safe_print_chars((const char *) ptr, len, 0 /*until_nul*/);
printf(" -> constant string index %ld\n", (long) i);
#endif
return (void *) strdata_duk_builtin_strings[i];
}
}
#if 0
printf("ajsheap_extstr_check_2: ptr=%p, len=%ld ",
(void *) ptr, (long) len);
safe_print_chars((const char *) ptr, len, 0 /*until_nul*/);
printf(" -> not found\n");
#endif
return NULL;
}
void ajsheap_extstr_free_2(const void *ptr) {
(void) ptr;
#if 0
printf("ajsheap_extstr_free_2: freeing extstr %p -> ", ptr);
safe_print_chars((const char *) ptr, DUK_SIZE_MAX, 1 /*until_nul*/);
printf("\n");
#endif
}
/*
* External strings strategy intended for valgrind testing: external strings
* are allocated using malloc()/free() so that valgrind can be used to ensure
* that strings are e.g. freed exactly once.
*/
const void *ajsheap_extstr_check_3(const void *ptr, duk_size_t len) {
duk_uint8_t *ret;
(void) safe_print_chars; /* potentially unused */
ret = malloc((size_t) len + 1);
if (ret == NULL) {
#if 0
printf("ajsheap_extstr_check_3: ptr=%p, len=%ld ",
(void *) ptr, (long) len);
safe_print_chars((const char *) ptr, len, 0 /*until_nul*/);
printf(" -> malloc failed, return NULL\n");
#endif
return (const void *) NULL;
}
if (len > 0) {
memcpy((void *) ret, ptr, (size_t) len);
}
ret[len] = (duk_uint8_t) 0;
#if 0
printf("ajsheap_extstr_check_3: ptr=%p, len=%ld ",
(void *) ptr, (long) len);
safe_print_chars((const char *) ptr, len, 0 /*until_nul*/);
printf(" -> %p\n", (void *) ret);
#endif
return (const void *) ret;
}
void ajsheap_extstr_free_3(const void *ptr) {
(void) ptr;
#if 0
printf("ajsheap_extstr_free_3: freeing extstr %p -> ", ptr);
safe_print_chars((const char *) ptr, DUK_SIZE_MAX, 1 /*until_nul*/);
printf("\n");
#endif
free((void *) ptr);
}
/*
* Execution timeout example
*/
#define AJSHEAP_EXEC_TIMEOUT 5 /* seconds */
static time_t curr_pcall_start = 0;
static long exec_timeout_check_counter = 0;
void ajsheap_start_exec_timeout(void) {
curr_pcall_start = time(NULL);
}
void ajsheap_clear_exec_timeout(void) {
curr_pcall_start = 0;
}
duk_bool_t ajsheap_exec_timeout_check(void *udata) {
time_t now = time(NULL);
time_t diff = now - curr_pcall_start;
(void) udata; /* not needed */
exec_timeout_check_counter++;
#if 0
printf("exec timeout check %ld: now=%ld, start=%ld, diff=%ld\n",
(long) exec_timeout_check_counter, (long) now, (long) curr_pcall_start, (long) diff);
fflush(stdout);
#endif
if (curr_pcall_start == 0) {
/* protected call not yet running */
return 0;
}
if (diff > AJSHEAP_EXEC_TIMEOUT) {
return 1;
}
return 0;
}
#else /* DUK_CMDLINE_AJSHEAP */
int ajs_dummy = 0; /* to avoid empty source file */
#endif /* DUK_CMDLINE_AJSHEAP */

View File

@@ -1,8 +0,0 @@
Codepage conversion example
===========================
Example of how to convert an 8-bit input string (e.g. ISO-8859-1 or Windows
codepage 1252) into CESU-8 without using an external library like iconv.
This is useful e.g. when compiling non-UTF-8 source code which cannot be
converted to UTF-8 (CESU-8) at build time.

View File

@@ -1,54 +0,0 @@
/*
* Convert an 8-bit input string (e.g. ISO-8859-1) into CESU-8.
* Calling code supplies the "code page" as a 256-entry array of
* codepoints for the conversion.
*
* This is useful when input data is in non-UTF-8 format and must
* be converted at runtime, e.g. when compiling non-UTF-8 source
* code. Another alternative is to use e.g. iconv.
*/
#include "duktape.h"
/* Decode an 8-bit string using 'codepage' into Unicode codepoints and
* re-encode into CESU-8. Codepage argument must point to a 256-entry
* table. Only supports BMP (codepoints U+0000 to U+FFFF).
*/
void duk_decode_string_codepage(duk_context *ctx, const char *str, size_t len, unsigned int *codepage) {
unsigned char *tmp;
size_t tmplen, i;
unsigned char *p;
unsigned int cp;
tmplen = 3 * len; /* max expansion is 1 input byte -> 3 output bytes */
if (tmplen / 3 != len) {
/* Temporary buffer length wraps. */
duk_error(ctx, DUK_ERR_RANGE_ERROR, "input string too long");
return;
}
tmp = (unsigned char *) duk_push_fixed_buffer(ctx, tmplen);
for (i = 0, p = tmp; i < len; i++) {
cp = codepage[((unsigned char *) str)[i]] & 0xffffUL;
if (cp < 0x80UL) {
*p++ = (unsigned char) cp;
} else if (cp < 0x800UL) {
*p++ = (unsigned char) (0xc0 + ((cp >> 6) & 0x1f));
*p++ = (unsigned char) (0x80 + (cp & 0x3f));
} else {
/* In CESU-8 all codepoints in [0x0000,0xFFFF] are
* allowed, including surrogates.
*/
*p++ = (unsigned char) (0xe0 + ((cp >> 12) & 0x0f));
*p++ = (unsigned char) (0x80 + ((cp >> 6) & 0x3f));
*p++ = (unsigned char) (0x80 + (cp & 0x3f));
}
}
duk_push_lstring(ctx, (const char *) tmp, (duk_size_t) (p - tmp));
/* [ ... tmp res ] */
duk_remove(ctx, -2);
}

View File

@@ -1,8 +0,0 @@
#ifndef DUK_CODEPAGE_CONV_H_INCLUDED
#define DUK_CODEPAGE_CONV_H_INCLUDED
#include "duktape.h"
void duk_decode_string_codepage(duk_context *ctx, const char *str, size_t len, unsigned int *codepage);
#endif /* DUK_CODEPAGE_CONV_H_INCLUDED */

View File

@@ -1,286 +0,0 @@
#include "duktape.h"
#include "duk_codepage_conv.h"
/* http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1252.TXT */
unsigned int cp1252[256] = {
(unsigned int) 0x0000,
(unsigned int) 0x0001,
(unsigned int) 0x0002,
(unsigned int) 0x0003,
(unsigned int) 0x0004,
(unsigned int) 0x0005,
(unsigned int) 0x0006,
(unsigned int) 0x0007,
(unsigned int) 0x0008,
(unsigned int) 0x0009,
(unsigned int) 0x000A,
(unsigned int) 0x000B,
(unsigned int) 0x000C,
(unsigned int) 0x000D,
(unsigned int) 0x000E,
(unsigned int) 0x000F,
(unsigned int) 0x0010,
(unsigned int) 0x0011,
(unsigned int) 0x0012,
(unsigned int) 0x0013,
(unsigned int) 0x0014,
(unsigned int) 0x0015,
(unsigned int) 0x0016,
(unsigned int) 0x0017,
(unsigned int) 0x0018,
(unsigned int) 0x0019,
(unsigned int) 0x001A,
(unsigned int) 0x001B,
(unsigned int) 0x001C,
(unsigned int) 0x001D,
(unsigned int) 0x001E,
(unsigned int) 0x001F,
(unsigned int) 0x0020,
(unsigned int) 0x0021,
(unsigned int) 0x0022,
(unsigned int) 0x0023,
(unsigned int) 0x0024,
(unsigned int) 0x0025,
(unsigned int) 0x0026,
(unsigned int) 0x0027,
(unsigned int) 0x0028,
(unsigned int) 0x0029,
(unsigned int) 0x002A,
(unsigned int) 0x002B,
(unsigned int) 0x002C,
(unsigned int) 0x002D,
(unsigned int) 0x002E,
(unsigned int) 0x002F,
(unsigned int) 0x0030,
(unsigned int) 0x0031,
(unsigned int) 0x0032,
(unsigned int) 0x0033,
(unsigned int) 0x0034,
(unsigned int) 0x0035,
(unsigned int) 0x0036,
(unsigned int) 0x0037,
(unsigned int) 0x0038,
(unsigned int) 0x0039,
(unsigned int) 0x003A,
(unsigned int) 0x003B,
(unsigned int) 0x003C,
(unsigned int) 0x003D,
(unsigned int) 0x003E,
(unsigned int) 0x003F,
(unsigned int) 0x0040,
(unsigned int) 0x0041,
(unsigned int) 0x0042,
(unsigned int) 0x0043,
(unsigned int) 0x0044,
(unsigned int) 0x0045,
(unsigned int) 0x0046,
(unsigned int) 0x0047,
(unsigned int) 0x0048,
(unsigned int) 0x0049,
(unsigned int) 0x004A,
(unsigned int) 0x004B,
(unsigned int) 0x004C,
(unsigned int) 0x004D,
(unsigned int) 0x004E,
(unsigned int) 0x004F,
(unsigned int) 0x0050,
(unsigned int) 0x0051,
(unsigned int) 0x0052,
(unsigned int) 0x0053,
(unsigned int) 0x0054,
(unsigned int) 0x0055,
(unsigned int) 0x0056,
(unsigned int) 0x0057,
(unsigned int) 0x0058,
(unsigned int) 0x0059,
(unsigned int) 0x005A,
(unsigned int) 0x005B,
(unsigned int) 0x005C,
(unsigned int) 0x005D,
(unsigned int) 0x005E,
(unsigned int) 0x005F,
(unsigned int) 0x0060,
(unsigned int) 0x0061,
(unsigned int) 0x0062,
(unsigned int) 0x0063,
(unsigned int) 0x0064,
(unsigned int) 0x0065,
(unsigned int) 0x0066,
(unsigned int) 0x0067,
(unsigned int) 0x0068,
(unsigned int) 0x0069,
(unsigned int) 0x006A,
(unsigned int) 0x006B,
(unsigned int) 0x006C,
(unsigned int) 0x006D,
(unsigned int) 0x006E,
(unsigned int) 0x006F,
(unsigned int) 0x0070,
(unsigned int) 0x0071,
(unsigned int) 0x0072,
(unsigned int) 0x0073,
(unsigned int) 0x0074,
(unsigned int) 0x0075,
(unsigned int) 0x0076,
(unsigned int) 0x0077,
(unsigned int) 0x0078,
(unsigned int) 0x0079,
(unsigned int) 0x007A,
(unsigned int) 0x007B,
(unsigned int) 0x007C,
(unsigned int) 0x007D,
(unsigned int) 0x007E,
(unsigned int) 0x007F,
(unsigned int) 0x20AC,
(unsigned int) 0xFFFD, /* undefined */
(unsigned int) 0x201A,
(unsigned int) 0x0192,
(unsigned int) 0x201E,
(unsigned int) 0x2026,
(unsigned int) 0x2020,
(unsigned int) 0x2021,
(unsigned int) 0x02C6,
(unsigned int) 0x2030,
(unsigned int) 0x0160,
(unsigned int) 0x2039,
(unsigned int) 0x0152,
(unsigned int) 0xFFFD, /* undefined */
(unsigned int) 0x017D,
(unsigned int) 0xFFFD, /* undefined */
(unsigned int) 0xFFFD, /* undefined */
(unsigned int) 0x2018,
(unsigned int) 0x2019,
(unsigned int) 0x201C,
(unsigned int) 0x201D,
(unsigned int) 0x2022,
(unsigned int) 0x2013,
(unsigned int) 0x2014,
(unsigned int) 0x02DC,
(unsigned int) 0x2122,
(unsigned int) 0x0161,
(unsigned int) 0x203A,
(unsigned int) 0x0153,
(unsigned int) 0xFFFD, /* undefined */
(unsigned int) 0x017E,
(unsigned int) 0x0178,
(unsigned int) 0x00A0,
(unsigned int) 0x00A1,
(unsigned int) 0x00A2,
(unsigned int) 0x00A3,
(unsigned int) 0x00A4,
(unsigned int) 0x00A5,
(unsigned int) 0x00A6,
(unsigned int) 0x00A7,
(unsigned int) 0x00A8,
(unsigned int) 0x00A9,
(unsigned int) 0x00AA,
(unsigned int) 0x00AB,
(unsigned int) 0x00AC,
(unsigned int) 0x00AD,
(unsigned int) 0x00AE,
(unsigned int) 0x00AF,
(unsigned int) 0x00B0,
(unsigned int) 0x00B1,
(unsigned int) 0x00B2,
(unsigned int) 0x00B3,
(unsigned int) 0x00B4,
(unsigned int) 0x00B5,
(unsigned int) 0x00B6,
(unsigned int) 0x00B7,
(unsigned int) 0x00B8,
(unsigned int) 0x00B9,
(unsigned int) 0x00BA,
(unsigned int) 0x00BB,
(unsigned int) 0x00BC,
(unsigned int) 0x00BD,
(unsigned int) 0x00BE,
(unsigned int) 0x00BF,
(unsigned int) 0x00C0,
(unsigned int) 0x00C1,
(unsigned int) 0x00C2,
(unsigned int) 0x00C3,
(unsigned int) 0x00C4,
(unsigned int) 0x00C5,
(unsigned int) 0x00C6,
(unsigned int) 0x00C7,
(unsigned int) 0x00C8,
(unsigned int) 0x00C9,
(unsigned int) 0x00CA,
(unsigned int) 0x00CB,
(unsigned int) 0x00CC,
(unsigned int) 0x00CD,
(unsigned int) 0x00CE,
(unsigned int) 0x00CF,
(unsigned int) 0x00D0,
(unsigned int) 0x00D1,
(unsigned int) 0x00D2,
(unsigned int) 0x00D3,
(unsigned int) 0x00D4,
(unsigned int) 0x00D5,
(unsigned int) 0x00D6,
(unsigned int) 0x00D7,
(unsigned int) 0x00D8,
(unsigned int) 0x00D9,
(unsigned int) 0x00DA,
(unsigned int) 0x00DB,
(unsigned int) 0x00DC,
(unsigned int) 0x00DD,
(unsigned int) 0x00DE,
(unsigned int) 0x00DF,
(unsigned int) 0x00E0,
(unsigned int) 0x00E1,
(unsigned int) 0x00E2,
(unsigned int) 0x00E3,
(unsigned int) 0x00E4,
(unsigned int) 0x00E5,
(unsigned int) 0x00E6,
(unsigned int) 0x00E7,
(unsigned int) 0x00E8,
(unsigned int) 0x00E9,
(unsigned int) 0x00EA,
(unsigned int) 0x00EB,
(unsigned int) 0x00EC,
(unsigned int) 0x00ED,
(unsigned int) 0x00EE,
(unsigned int) 0x00EF,
(unsigned int) 0x00F0,
(unsigned int) 0x00F1,
(unsigned int) 0x00F2,
(unsigned int) 0x00F3,
(unsigned int) 0x00F4,
(unsigned int) 0x00F5,
(unsigned int) 0x00F6,
(unsigned int) 0x00F7,
(unsigned int) 0x00F8,
(unsigned int) 0x00F9,
(unsigned int) 0x00FA,
(unsigned int) 0x00FB,
(unsigned int) 0x00FC,
(unsigned int) 0x00FD,
(unsigned int) 0x00FE,
(unsigned int) 0x00FF
};
/* Exercise all 3 byte lengths: any ASCII character is 1 byte, 0xFC maps to
* U+00FC which is 2 bytes, and 0x80 maps to U+20AC which is 3 bytes.
*/
static const char *example_source = "print('Hello w\xfcrld - \x80');";
/* Example: compile and run test source encoded in Windows codepage 1252. */
int main(int argc, char *argv[]) {
duk_context *ctx;
(void) argc; (void) argv;
ctx = duk_create_heap_default();
if (!ctx) {
printf("Failed to create Duktape heap.\n");
return 1;
}
duk_decode_string_codepage(ctx, example_source, strlen(example_source), cp1252);
duk_eval_noresult(ctx);
duk_destroy_heap(ctx);
return 0;
}

View File

@@ -1,10 +0,0 @@
=====================
Coffeescript examples
=====================
A few tests to see how CoffeeScript works with Duktape. Just convert the
Coffeescript files to Javascript with the ``Makefile.coffee`` in the
distributable, or manually::
$ coffee -c hello.coffee
$ cat hello.js

View File

@@ -1,7 +0,0 @@
print '*** All globals'
print(name) for name in Object.getOwnPropertyNames(this)
print '*** Globals with a short name (<= 8 chars)'
print(name) for name in Object.getOwnPropertyNames(this) when name.length <= 8

View File

@@ -1,2 +0,0 @@
print 'Hello world!'
print 'version: ' + Duktape.version

View File

@@ -1,28 +0,0 @@
mandel = (x0, y0, x1, y1, w, h, maxiter) ->
[dx, dy] = [(x1 - x0) / w, (y1 - y0) / h]
res = []
y = y0
for yc in [0..h-1]
x = x0
for xc in [0..w-1]
[xx, yy] = [x, y]
c = '*'
for i in [0..maxiter-1]
# (xx+i*yy)^2 + (x+i*y) = xx^2 + i*2*xx*yy - yy^2 + x + i*y
# = (xx^2 - yy^2 + x) + i*(2*xx*yy + y)
[xx2, yy2] = [xx*xx, yy*yy]
if xx2 + yy2 >= 4.0
c = '.'
break
[xx, yy] = [xx2 - yy2 + x, 2*xx*yy + y]
res.push(c)
x += dx
res.push('\n')
y += dy
print(res.join(''))
return
mandel(-2, 2, 2, -2, 200, 100, 1000)

View File

@@ -1,17 +0,0 @@
# Set DUKTAPE_SRC to 'src' dir of Duktape distributable.
# The default is for the dist environment.
DUKTAPE_SRC=../../src
DUKTAPE_OPTS=
DUKTAPE_OPTS+=-DDUK_OPT_ASSERTIONS
DUKTAPE_OPTS+=-DDUK_OPT_DEBUGGER_SUPPORT -DDUK_OPT_INTERRUPT_COUNTER
DUKTAPE_OPTS+=-DDUK_OPT_DEBUGGER_FWD_PRINTALERT
DUKTAPE_OPTS+=-DDUK_OPT_DEBUGGER_DUMPHEAP
#DUKTAPE_OPTS+=-DDUK_OPT_DEBUGGER_TRANSPORT_TORTURE
TRANS_OPTS=
#TRANS_OPTS+=-DDEBUG_PRINTS
test: test.c duk_trans_dvalue.c duk_trans_dvalue.h
echo $(DUKTAPE_SRC)
gcc -O0 -g -ggdb -Wall -Wextra -std=c99 -o test -I$(DUKTAPE_SRC) -I. \
$(DUKTAPE_OPTS) $(TRANS_OPTS) \
$(DUKTAPE_SRC)/duktape.c duk_trans_dvalue.c test.c -lm

View File

@@ -1,8 +0,0 @@
===========================================================
Debug transport with local debug protocol encoding/decoding
===========================================================
This example implements a debug transport which decodes/encodes the Duktape
debug protocol locally into a more easy to use C interface, which is useful
for debug clients implemented locally on the target. The example also
demonstrates how to trial parse dvalues in C.

File diff suppressed because it is too large Load Diff

View File

@@ -1,113 +0,0 @@
#ifndef DUK_TRANS_DVALUE_H_INCLUDED
#define DUK_TRANS_DVALUE_H_INCLUDED
#include "duktape.h"
typedef struct duk_dvalue duk_dvalue;
typedef struct duk_trans_buffer duk_trans_buffer;
typedef struct duk_trans_dvalue_ctx duk_trans_dvalue_ctx;
typedef void (*duk_trans_dvalue_received_function)(duk_trans_dvalue_ctx *ctx, duk_dvalue *dv);
typedef void (*duk_trans_dvalue_cooperate_function)(duk_trans_dvalue_ctx *ctx, int block);
typedef void (*duk_trans_dvalue_handshake_function)(duk_trans_dvalue_ctx *ctx, const char *handshake_line);
typedef void (*duk_trans_dvalue_detached_function)(duk_trans_dvalue_ctx *ctx);
/* struct duk_dvalue 'tag' values, note that these have nothing to do with
* Duktape debug protocol inital byte. Struct fields used with the type
* are noted next to the define.
*/
#define DUK_DVALUE_EOM 1 /* no fields */
#define DUK_DVALUE_REQ 2 /* no fields */
#define DUK_DVALUE_REP 3 /* no fields */
#define DUK_DVALUE_ERR 4 /* no fields */
#define DUK_DVALUE_NFY 5 /* no fields */
#define DUK_DVALUE_INTEGER 6 /* i: 32-bit signed integer */
#define DUK_DVALUE_STRING 7 /* buf: string data, len: string length */
#define DUK_DVALUE_BUFFER 8 /* buf: buffer data, len: buffer length */
#define DUK_DVALUE_UNUSED 9 /* no fields */
#define DUK_DVALUE_UNDEFINED 10 /* no fields */
#define DUK_DVALUE_NULL 11 /* no fields */
#define DUK_DVALUE_TRUE 12 /* no fields */
#define DUK_DVALUE_FALSE 13 /* no fields */
#define DUK_DVALUE_NUMBER 14 /* d: ieee double */
#define DUK_DVALUE_OBJECT 15 /* i: class number, buf: pointer data, len: pointer length */
#define DUK_DVALUE_POINTER 16 /* buf: pointer data, len: pointer length */
#define DUK_DVALUE_LIGHTFUNC 17 /* i: lightfunc flags, buf: pointer data, len: pointer length */
#define DUK_DVALUE_HEAPPTR 18 /* buf: pointer data, len: pointer length */
struct duk_dvalue {
/* Could use a union for the value but the gain would be relatively small. */
int tag;
int i;
double d;
size_t len;
unsigned char *buf;
};
struct duk_trans_buffer {
unsigned char *base;
size_t write_offset;
size_t read_offset;
size_t alloc_size;
};
struct duk_trans_dvalue_ctx {
duk_trans_dvalue_received_function received;
duk_trans_dvalue_cooperate_function cooperate;
duk_trans_dvalue_handshake_function handshake;
duk_trans_dvalue_detached_function detached;
duk_trans_buffer send_buf; /* sending towards Duktape (duktape read callback) */
duk_trans_buffer recv_buf; /* receiving from Duktape (duktape write callback) */
int handshake_done;
int double_byteorder; /* 0=little endian, 1=big endian, 2=mixed endian */
};
/* Buffer size needed by duk_dvalue_to_string(). */
#define DUK_DVALUE_TOSTRING_BUFLEN 256
/* Dvalue handling. */
duk_dvalue *duk_dvalue_alloc(void);
void duk_dvalue_free(duk_dvalue *dv);
void duk_dvalue_to_string(duk_dvalue *dv, char *buf);
duk_dvalue *duk_dvalue_make_tag(int tag);
duk_dvalue *duk_dvalue_make_tag_int(int tag, int intval);
duk_dvalue *duk_dvalue_make_tag_double(int tag, double dblval);
duk_dvalue *duk_dvalue_make_tag_data(int tag, const char *buf, size_t len);
duk_dvalue *duk_dvalue_make_tag_int_data(int tag, int intval, const char *buf, size_t len);
/* Initializing and freeing the transport context. */
duk_trans_dvalue_ctx *duk_trans_dvalue_init(void);
void duk_trans_dvalue_free(duk_trans_dvalue_ctx *ctx);
/* Sending dvalues towards Duktape. */
void duk_trans_dvalue_send(duk_trans_dvalue_ctx *ctx, duk_dvalue *dv);
void duk_trans_dvalue_send_eom(duk_trans_dvalue_ctx *ctx);
void duk_trans_dvalue_send_req(duk_trans_dvalue_ctx *ctx);
void duk_trans_dvalue_send_rep(duk_trans_dvalue_ctx *ctx);
void duk_trans_dvalue_send_err(duk_trans_dvalue_ctx *ctx);
void duk_trans_dvalue_send_nfy(duk_trans_dvalue_ctx *ctx);
void duk_trans_dvalue_send_integer(duk_trans_dvalue_ctx *ctx, int val);
void duk_trans_dvalue_send_string(duk_trans_dvalue_ctx *ctx, const char *str);
void duk_trans_dvalue_send_lstring(duk_trans_dvalue_ctx *ctx, const char *str, size_t len);
void duk_trans_dvalue_send_buffer(duk_trans_dvalue_ctx *ctx, const char *buf, size_t len);
void duk_trans_dvalue_send_unused(duk_trans_dvalue_ctx *ctx);
void duk_trans_dvalue_send_undefined(duk_trans_dvalue_ctx *ctx);
void duk_trans_dvalue_send_null(duk_trans_dvalue_ctx *ctx);
void duk_trans_dvalue_send_true(duk_trans_dvalue_ctx *ctx);
void duk_trans_dvalue_send_false(duk_trans_dvalue_ctx *ctx);
void duk_trans_dvalue_send_number(duk_trans_dvalue_ctx *ctx, double val);
void duk_trans_dvalue_send_object(duk_trans_dvalue_ctx *ctx, int classnum, const char *ptr_data, size_t ptr_len);
void duk_trans_dvalue_send_pointer(duk_trans_dvalue_ctx *ctx, const char *ptr_data, size_t ptr_len);
void duk_trans_dvalue_send_lightfunc(duk_trans_dvalue_ctx *ctx, int lf_flags, const char *ptr_data, size_t ptr_len);
void duk_trans_dvalue_send_heapptr(duk_trans_dvalue_ctx *ctx, const char *ptr_data, size_t ptr_len);
void duk_trans_dvalue_send_req_cmd(duk_trans_dvalue_ctx *ctx, int cmd);
/* Duktape debug callbacks provided by the transport. */
duk_size_t duk_trans_dvalue_read_cb(void *udata, char *buffer, duk_size_t length);
duk_size_t duk_trans_dvalue_write_cb(void *udata, const char *buffer, duk_size_t length);
duk_size_t duk_trans_dvalue_peek_cb(void *udata);
void duk_trans_dvalue_read_flush_cb(void *udata);
void duk_trans_dvalue_write_flush_cb(void *udata);
void duk_trans_dvalue_detached_cb(void *udata);
#endif /* DUK_TRANS_DVALUE_H_INCLUDED */

View File

@@ -1,236 +0,0 @@
/*
* Example program using the dvalue debug transport.
*/
#include <stdio.h>
#include <stdlib.h>
#include "duktape.h"
#include "duk_trans_dvalue.h"
void my_cooperate(duk_trans_dvalue_ctx *ctx, int block) {
static int first_blocked = 1;
if (!block) {
/* Duktape is not blocked; you can cooperate with e.g. a user
* interface here and send dvalues to Duktape, but don't block.
*/
return;
}
/* Duktape is blocked on a read and won't continue until debug
* command(s) are sent.
*
* Normally you'd enter your own event loop here, and process
* events until something needs to be sent to Duktape. For
* example, the user might press a "Step over" button in the
* UI which would cause dvalues to be sent. You can then
* return from this callback.
*
* The code below sends some example messages for testing the
* dvalue handling of the transport.
*
* If you create dvalues manually and send them using
* duk_trans_dvalue_send(), you must free the dvalues after
* the send call returns using duk_dvalue_free().
*/
if (first_blocked) {
char *tmp;
int i;
/* First time Duktape becomes blocked, send DumpHeap which
* exercises a lot of parsing code.
*
* NOTE: Valgrind may complain about reading uninitialized
* bytes. This is caused by the DumpHeap command writing out
* verbatim duk_tval values which are intentionally not
* always fully initialized for performance reasons.
*/
first_blocked = 0;
fprintf(stderr, "Duktape is blocked, send DumpHeap\n");
fflush(stderr);
duk_trans_dvalue_send_req(ctx);
duk_trans_dvalue_send_integer(ctx, 0x20); /* DumpHeap */
duk_trans_dvalue_send_eom(ctx);
/* Also send a dummy TriggerStatus request with trailing dvalues
* ignored by Duktape; Duktape will parse the dvalues to be able to
* skip them, so that the dvalue encoding is exercised.
*/
tmp = malloc(100000); /* long buffer, >= 65536 chars */
for (i = 0; i < 100000; i++) {
tmp[i] = (char) i;
}
duk_trans_dvalue_send_req(ctx);
duk_trans_dvalue_send_integer(ctx, 0x11); /* TriggerStatus */
duk_trans_dvalue_send_string(ctx, "dummy"); /* short, <= 31 chars */
duk_trans_dvalue_send_string(ctx, "123456789012345678901234567890foobar"); /* medium, >= 32 chars */
duk_trans_dvalue_send_lstring(ctx, (const char *) tmp, 65535UL);
duk_trans_dvalue_send_lstring(ctx, (const char *) tmp, 65536UL);
duk_trans_dvalue_send_lstring(ctx, (const char *) tmp, 100000UL);
duk_trans_dvalue_send_buffer(ctx, (const char *) tmp, 255U);
duk_trans_dvalue_send_buffer(ctx, (const char *) tmp, 65535UL);
duk_trans_dvalue_send_buffer(ctx, (const char *) tmp, 65536UL);
duk_trans_dvalue_send_buffer(ctx, (const char *) tmp, 100000UL);
duk_trans_dvalue_send_unused(ctx);
duk_trans_dvalue_send_undefined(ctx);
duk_trans_dvalue_send_null(ctx);
duk_trans_dvalue_send_true(ctx);
duk_trans_dvalue_send_false(ctx);
duk_trans_dvalue_send_number(ctx, 123.456);
duk_trans_dvalue_send_object(ctx, 12 /*classnum*/, (const char *) tmp, 8); /* fake ptr len */
duk_trans_dvalue_send_pointer(ctx, (const char *) tmp, 8); /* fake ptr len */
duk_trans_dvalue_send_lightfunc(ctx, 0xdabc /*lf_flags*/, (const char *) tmp, 8); /* fake ptr len */
duk_trans_dvalue_send_heapptr(ctx, (const char *) tmp, 8); /* fake ptr len */
duk_trans_dvalue_send_eom(ctx);
}
fprintf(stderr, "Duktape is blocked, send Eval and StepInto to resume execution\n");
fflush(stderr);
/* duk_trans_dvalue_send_req_cmd() sends a REQ dvalue followed by
* an integer dvalue (command) for convenience.
*/
duk_trans_dvalue_send_req_cmd(ctx, 0x1e); /* 0x1e = Eval */
duk_trans_dvalue_send_string(ctx, "evalMe");
duk_trans_dvalue_send_eom(ctx);
duk_trans_dvalue_send_req_cmd(ctx, 0x14); /* 0x14 = StepOver */
duk_trans_dvalue_send_eom(ctx);
}
void my_received(duk_trans_dvalue_ctx *ctx, duk_dvalue *dv) {
char buf[DUK_DVALUE_TOSTRING_BUFLEN];
(void) ctx;
duk_dvalue_to_string(dv, buf);
fprintf(stderr, "Received dvalue: %s\n", buf);
fflush(stderr);
/* Here a normal debug client would wait for dvalues until an EOM
* dvalue was received (which completes a debug message). The
* debug message would then be handled, possibly causing UI changes
* and/or causing debug commands to be sent to Duktape.
*
* The callback is responsible for eventually freeing the dvalue.
* Here we free it immediately, but an actual client would probably
* gather dvalues into an array or linked list to handle when the
* debug message was complete.
*/
duk_dvalue_free(dv);
}
void my_handshake(duk_trans_dvalue_ctx *ctx, const char *line) {
(void) ctx;
/* The Duktape handshake line is given in 'line' (without LF).
* The 'line' argument can be accessed for the duration of the
* callback (read only). Don't free 'line' here, the transport
* handles that.
*/
fprintf(stderr, "Received handshake line: '%s'\n", line);
fflush(stderr);
}
void my_detached(duk_trans_dvalue_ctx *ctx) {
(void) ctx;
/* Detached call forwarded as is. */
fprintf(stderr, "Debug transport detached\n");
fflush(stderr);
}
int main(int argc, char *argv[]) {
duk_context *ctx;
duk_trans_dvalue_ctx *trans_ctx;
int exitval = 0;
(void) argc; (void) argv; /* suppress warning */
ctx = duk_create_heap_default();
if (!ctx) {
fprintf(stderr, "Failed to create Duktape heap\n");
fflush(stderr);
exitval = 1;
goto cleanup;
}
trans_ctx = duk_trans_dvalue_init();
if (!trans_ctx) {
fprintf(stderr, "Failed to create debug transport context\n");
fflush(stderr);
exitval = 1;
goto cleanup;
}
trans_ctx->cooperate = my_cooperate;
trans_ctx->received = my_received;
trans_ctx->handshake = my_handshake;
trans_ctx->detached = my_detached;
/* Attach debugger; this will fail with a fatal error here unless
* debugger support is compiled in. To fail more gracefully, call
* this under a duk_safe_call() to catch the error.
*/
duk_debugger_attach(ctx,
duk_trans_dvalue_read_cb,
duk_trans_dvalue_write_cb,
duk_trans_dvalue_peek_cb,
duk_trans_dvalue_read_flush_cb,
duk_trans_dvalue_write_flush_cb,
duk_trans_dvalue_detached_cb,
(void *) trans_ctx);
fprintf(stderr, "Debugger attached, running eval\n");
fflush(stderr);
/* Evaluate simple test code, callbacks will "step over" until end.
*
* The test code here is just for exercising the debug transport.
* The 'evalMe' variable is evaluated (using debugger command Eval)
* before every step to force different dvalues to be carried over
* the transport.
*/
duk_eval_string(ctx,
"var evalMe;\n"
"\n"
"print('Hello world!');\n"
"[ undefined, null, true, false, 123, -123, 123.1, 0, -0, 1/0, 0/0, -1/0, \n"
" 'foo', Duktape.Buffer('bar'), Duktape.Pointer('dummy'), Math.cos, \n"
"].forEach(function (val) {\n"
" print(val);\n"
" evalMe = val;\n"
"});\n"
"\n"
"var str = 'xxx'\n"
"for (i = 0; i < 10; i++) {\n"
" print(i, str);\n"
" evalMe = str;\n"
" evalMe = Duktape.Buffer(str);\n"
" str = str + str;\n"
"}\n"
);
duk_pop(ctx);
duk_debugger_detach(ctx);
cleanup:
if (trans_ctx) {
duk_trans_dvalue_free(trans_ctx);
trans_ctx = NULL;
}
if (ctx) {
duk_destroy_heap(ctx);
}
return exitval;
}

View File

@@ -1,6 +0,0 @@
================================================
Debug transport using a simple socket connection
================================================
This example implements an example debug transport which uses a Linux server
socket on the debug target.

View File

@@ -1,313 +0,0 @@
/*
* Example debug transport using a TCP socket
*
* The application has a server socket which can be connected to.
* After that data is just passed through.
*
* NOTE: This is Linux specific on purpose, as it's just an example how
* a debug transport can be concretely implemented.
*/
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <poll.h>
#include <errno.h>
#include "duktape.h"
#ifndef DUK_DEBUG_PORT
#define DUK_DEBUG_PORT 9091
#endif
#if 0
#define DEBUG_PRINTS
#endif
static int server_sock = -1;
static int client_sock = -1;
/*
* Transport init
*/
void duk_trans_socket_init(void) {
struct sockaddr_in addr;
int on;
server_sock = socket(AF_INET, SOCK_STREAM, 0);
if (server_sock < 0) {
fprintf(stderr, "%s: failed to create server socket: %s\n", __FILE__, strerror(errno));
fflush(stderr);
goto fail;
}
on = 1;
if (setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, (const char *) &on, sizeof(on)) < 0) {
fprintf(stderr, "%s: failed to set SO_REUSEADDR for server socket: %s\n", __FILE__, strerror(errno));
fflush(stderr);
goto fail;
}
memset((void *) &addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(DUK_DEBUG_PORT);
if (bind(server_sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
fprintf(stderr, "%s: failed to bind server socket: %s\n", __FILE__, strerror(errno));
fflush(stderr);
goto fail;
}
listen(server_sock, 1 /*backlog*/);
return;
fail:
if (server_sock >= 0) {
(void) close(server_sock);
server_sock = -1;
}
}
void duk_trans_socket_waitconn(void) {
struct sockaddr_in addr;
socklen_t sz;
if (server_sock < 0) {
fprintf(stderr, "%s: no server socket, skip waiting for connection\n", __FILE__);
fflush(stderr);
return;
}
if (client_sock >= 0) {
(void) close(client_sock);
client_sock = -1;
}
fprintf(stderr, "Waiting for debug connection on port %d\n", (int) DUK_DEBUG_PORT);
fflush(stderr);
sz = (socklen_t) sizeof(addr);
client_sock = accept(server_sock, (struct sockaddr *) &addr, &sz);
if (client_sock < 0) {
fprintf(stderr, "%s: accept() failed, skip waiting for connection: %s\n", __FILE__, strerror(errno));
fflush(stderr);
goto fail;
}
fprintf(stderr, "Debug connection established\n");
fflush(stderr);
/* XXX: For now, close the listen socket because we won't accept new
* connections anyway. A better implementation would allow multiple
* debug attaches.
*/
if (server_sock >= 0) {
(void) close(server_sock);
server_sock = -1;
}
return;
fail:
if (client_sock >= 0) {
(void) close(client_sock);
client_sock = -1;
}
}
/*
* Duktape callbacks
*/
/* Duktape debug transport callback: partial read */
duk_size_t duk_trans_socket_read_cb(void *udata, char *buffer, duk_size_t length) {
ssize_t ret;
(void) udata; /* not needed by the example */
#if defined(DEBUG_PRINTS)
fprintf(stderr, "%s: udata=%p, buffer=%p, length=%ld\n",
__func__, (void *) udata, (void *) buffer, (long) length);
fflush(stderr);
#endif
if (client_sock < 0) {
return 0;
}
if (length == 0) {
/* This shouldn't happen. */
fprintf(stderr, "%s: read request length == 0, closing connection\n", __FILE__);
fflush(stderr);
goto fail;
}
if (buffer == NULL) {
/* This shouldn't happen. */
fprintf(stderr, "%s: read request buffer == NULL, closing connection\n", __FILE__);
fflush(stderr);
goto fail;
}
/* In a production quality implementation there would be a sanity
* timeout here to recover from "black hole" disconnects.
*/
ret = read(client_sock, (void *) buffer, (size_t) length);
if (ret < 0) {
fprintf(stderr, "%s: debug read failed, errno %d, closing connection: %s\n", __FILE__, errno, strerror(errno));
fflush(stderr);
goto fail;
} else if (ret == 0) {
fprintf(stderr, "%s: debug read failed, ret == 0 (EOF), closing connection\n", __FILE__);
fflush(stderr);
goto fail;
} else if (ret > (ssize_t) length) {
fprintf(stderr, "%s: debug read failed, ret too large (%ld > %ld), closing connection\n", __FILE__, (long) ret, (long) length);
fflush(stderr);
goto fail;
}
return (duk_size_t) ret;
fail:
if (client_sock >= 0) {
(void) close(client_sock);
client_sock = -1;
}
return 0;
}
/* Duktape debug transport callback: partial write */
duk_size_t duk_trans_socket_write_cb(void *udata, const char *buffer, duk_size_t length) {
ssize_t ret;
(void) udata; /* not needed by the example */
#if defined(DEBUG_PRINTS)
fprintf(stderr, "%s: udata=%p, buffer=%p, length=%ld\n",
__func__, (void *) udata, (void *) buffer, (long) length);
fflush(stderr);
#endif
if (client_sock < 0) {
return 0;
}
if (length == 0) {
/* This shouldn't happen. */
fprintf(stderr, "%s: write request length == 0, closing connection\n", __FILE__);
fflush(stderr);
goto fail;
}
if (buffer == NULL) {
/* This shouldn't happen. */
fprintf(stderr, "%s: write request buffer == NULL, closing connection\n", __FILE__);
fflush(stderr);
goto fail;
}
/* In a production quality implementation there would be a sanity
* timeout here to recover from "black hole" disconnects.
*/
ret = write(client_sock, (const void *) buffer, (size_t) length);
if (ret <= 0 || ret > (ssize_t) length) {
fprintf(stderr, "%s: debug write failed, closing connection: %s\n", __FILE__, strerror(errno));
fflush(stderr);
goto fail;
}
return (duk_size_t) ret;
fail:
if (client_sock >= 0) {
(void) close(client_sock);
client_sock = -1;
}
return 0;
}
duk_size_t duk_trans_socket_peek_cb(void *udata) {
struct pollfd fds[1];
int poll_rc;
(void) udata; /* not needed by the example */
#if defined(DEBUG_PRINTS)
fprintf(stderr, "%s: udata=%p\n", __func__, (void *) udata);
fflush(stderr);
#endif
fds[0].fd = client_sock;
fds[0].events = POLLIN;
fds[0].revents = 0;
poll_rc = poll(fds, 1, 0);
if (poll_rc < 0) {
fprintf(stderr, "%s: poll returned < 0, closing connection: %s\n", __FILE__, strerror(errno));
fflush(stderr);
goto fail; /* also returns 0, which is correct */
} else if (poll_rc > 1) {
fprintf(stderr, "%s: poll returned > 1, treating like 1\n", __FILE__);
fflush(stderr);
return 1; /* should never happen */
} else if (poll_rc == 0) {
return 0; /* nothing to read */
} else {
return 1; /* something to read */
}
fail:
if (client_sock >= 0) {
(void) close(client_sock);
client_sock = -1;
}
return 0;
}
void duk_trans_socket_read_flush_cb(void *udata) {
#if defined(DEBUG_PRINTS)
fprintf(stderr, "%s: udata=%p\n", __func__, (void *) udata);
fflush(stderr);
#endif
(void) udata; /* not needed by the example */
/* Read flush: Duktape may not be making any more read calls at this
* time. If the transport maintains a receive window, it can use a
* read flush as a signal to update the window status to the remote
* peer. A read flush is guaranteed to occur before Duktape stops
* reading for a while; it may occur in other situations as well so
* it's not a 100% reliable indication.
*/
/* This TCP transport requires no read flush handling so ignore.
* You can also pass a NULL to duk_debugger_attach() and not
* implement this callback at all.
*/
}
void duk_trans_socket_write_flush_cb(void *udata) {
#if defined(DEBUG_PRINTS)
fprintf(stderr, "%s: udata=%p\n", __func__, (void *) udata);
fflush(stderr);
#endif
(void) udata; /* not needed by the example */
/* Write flush. If the transport combines multiple writes
* before actually sending, a write flush is an indication
* to write out any pending bytes: Duktape may not be doing
* any more writes on this occasion.
*/
/* This TCP transport requires no write flush handling so ignore.
* You can also pass a NULL to duk_debugger_attach() and not
* implement this callback at all.
*/
return;
}

View File

@@ -1,14 +0,0 @@
#ifndef DUK_TRANS_SOCKET_H_INCLUDED
#define DUK_TRANS_SOCKET_H_INCLUDED
#include "duktape.h"
void duk_trans_socket_init(void);
void duk_trans_socket_waitconn(void);
duk_size_t duk_trans_socket_read_cb(void *udata, char *buffer, duk_size_t length);
duk_size_t duk_trans_socket_write_cb(void *udata, const char *buffer, duk_size_t length);
duk_size_t duk_trans_socket_peek_cb(void *udata);
void duk_trans_socket_read_flush_cb(void *udata);
void duk_trans_socket_write_flush_cb(void *udata);
#endif /* DUK_TRANS_SOCKET_H_INCLUDED */

View File

@@ -1,5 +0,0 @@
====================================
Dummy external Date provider example
====================================
This example implements a dummy, minimal external Date provider.

View File

@@ -1,27 +0,0 @@
/*
* Dummy Date provider
*
* There are two minimally required macros which you must provide in
* duk_config.h:
*
* extern duk_double_t dummy_get_now(void);
*
* #define DUK_USE_DATE_GET_NOW(ctx) dummy_get_now()
* #define DUK_USE_DATE_GET_LOCAL_TZOFFSET(d) 0
*
* Note that since the providers are macros, you don't need to use
* all arguments. Similarly, you can "return" fixed values as
* constants. Above, local timezone offset is always zero i.e.
* we're always in UTC.
*
* You can also provide optional macros to parse and format timestamps
* in a platform specific format. If not provided, Duktape will use
* ISO 8601 only (which is often good enough).
*/
#include "duktape.h"
duk_double_t dummy_get_now(void) {
/* Return a fixed time here as a dummy example. */
return -11504520000.0;
}

View File

@@ -1,5 +0,0 @@
============
Eval example
============
Evaluate expressions from command line.

View File

@@ -1,48 +0,0 @@
/*
* Very simple example program for evaluating expressions from
* command line
*/
#include "duktape.h"
#include <stdio.h>
static int eval_raw(duk_context *ctx) {
duk_eval(ctx);
return 1;
}
static int tostring_raw(duk_context *ctx) {
duk_to_string(ctx, -1);
return 1;
}
static void usage_exit(void) {
fprintf(stderr, "Usage: eval <expression> [<expression>] ...\n");
fflush(stderr);
exit(1);
}
int main(int argc, char *argv[]) {
duk_context *ctx;
int i;
const char *res;
if (argc < 2) {
usage_exit();
}
ctx = duk_create_heap_default();
for (i = 1; i < argc; i++) {
printf("=== eval: '%s' ===\n", argv[i]);
duk_push_string(ctx, argv[i]);
duk_safe_call(ctx, eval_raw, 1 /*nargs*/, 1 /*nrets*/);
duk_safe_call(ctx, tostring_raw, 1 /*nargs*/, 1 /*nrets*/);
res = duk_get_string(ctx, -1);
printf("%s\n", res ? res : "null");
duk_pop(ctx);
}
duk_destroy_heap(ctx);
return 0;
}

View File

@@ -1,76 +0,0 @@
==================
Eventloop examples
==================
Overview and usage
==================
A few examples on how an event loop can be implemented with Duktape, mainly
illlustrating how the Duktape interface works (not how event loops should be
built otherwise).
To test (Linux only, perhaps other Unix)::
$ make
$ ./evloop curses-timers.js # run with Ecmascript eventloop
$ ./evloop -c curses-timers.js # run with C eventloop
Implementation approaches
=========================
There are several approaches to implementation timers. Here we demonstrate
two main approaches:
1. Using a C eventloop which calls into Javascript. All the event loop state
like timers, sockets, etc, is held in C structures.
(See ``c_eventloop.c`` and ``c_eventloop.js``.)
2. Using an Ecmascript eventloop which never returns. All the event loop state
can be managed with Ecmascript code instead of C structures. The Ecmascript
eventloop calls a Duktape/C helper to do the lowest level poll() call.
(See ``ecma_eventloop.js``.)
Services provided
=================
The event loop API provided by both examples is the same, and includes:
* Timers: setTimeout, clearTimeout, setInterval, clearInterval
* Sockets: simple network sockets
In addition there are a few synchronous API bindings which are not event loop
related:
* File I/O
* Curses, for doing beautiful character graphics
Limitations
===========
This is **not** a production quality event loop. This is on purpose, to
keep the example somewhat simple. Some shortcomings include:
* A production quality event loop would track its internal state (active
timers and sockets) much more efficiently. In general memory usage and
code footprint can be reduced.
* Buffer churn caused by allocating a new buffer for every socket read
should be eliminated by reusing buffers where appropriate. Although
churn doesn't increase memory footprint with reference counting, it
is slower than reusing buffers and might increase memory fragmentation.
* There is no way to suspend reading or writing in the example. Adding
them is straightforward: the poll set needs to be managed dynamically.
* The example uses poll() while one should use epoll() on Linux, kqueue()
on BSD systems, etc.
* Timers are not very accurate, e.g. setInterval() does not try to guarantee
a steady schedule. Instead, the next interval is scheduled after the
current callback has finished. This is not the best behavior for some
environments, but avoids bunching callbacks.
* Error handling is mostly missing. Debug prints don't interact well
with curses.

View File

@@ -1,17 +0,0 @@
/*
* A few basic tests
*/
var count = 0;
var intervalId;
setTimeout(function (x) { print('timer 1', x); }, 1234, 'foo');
setTimeout('print("timer 2");', 4321);
setTimeout(function () { print('timer 3'); }, 2345);
intervalId = setInterval(function (x, y) {
print('interval', ++count, x, y);
if (count >= 10) {
clearInterval(intervalId);
}
}, 400, 'foo', 'bar');

View File

@@ -1,618 +0,0 @@
/*
* C eventloop example.
*
* Timer management is similar to eventloop.js but implemented in C.
* In particular, timer insertion is an O(n) operation; in a real world
* eventloop based on a heap insertion would be O(log N).
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <sys/time.h>
#include <poll.h>
#include "duktape.h"
#define MAX_TIMERS 4096 /* this is quite excessive for embedded use, but good for testing */
#define MIN_DELAY 1.0
#define MIN_WAIT 1.0
#define MAX_WAIT 60000.0
#define MAX_EXPIRYS 10
#define MAX_FDS 256
typedef struct {
int64_t id; /* numeric ID (returned from e.g. setTimeout); zero if unused */
double target; /* next target time */
double delay; /* delay/interval */
int oneshot; /* oneshot=1 (setTimeout), repeated=0 (setInterval) */
int removed; /* timer has been requested for removal */
/* The callback associated with the timer is held in the "global stash",
* in <stash>.eventTimers[String(id)]. The references must be deleted
* when a timer struct is deleted.
*/
} ev_timer;
/* Active timers. Dense list, terminates to end of list or first unused timer.
* The list is sorted by 'target', with lowest 'target' (earliest expiry) last
* in the list. When a timer's callback is being called, the timer is moved
* to 'timer_expiring' as it needs special handling should the user callback
* delete that particular timer.
*/
static ev_timer timer_list[MAX_TIMERS];
static ev_timer timer_expiring;
static int timer_count; /* last timer at timer_count - 1 */
static int64_t timer_next_id = 1;
/* Socket poll state. */
static struct pollfd poll_list[MAX_FDS];
static int poll_count = 0;
/* Misc */
static int exit_requested = 0;
/* Get Javascript compatible 'now' timestamp (millisecs since 1970). */
static double get_now(void) {
struct timeval tv;
int rc;
rc = gettimeofday(&tv, NULL);
if (rc != 0) {
/* Should never happen, so return whatever. */
return 0.0;
}
return ((double) tv.tv_sec) * 1000.0 + ((double) tv.tv_usec) / 1000.0;
}
static ev_timer *find_nearest_timer(void) {
/* Last timer expires first (list is always kept sorted). */
if (timer_count <= 0) {
return NULL;
}
return timer_list + timer_count - 1;
}
/* Bubble last timer on timer list backwards until it has been moved to
* its proper sorted position (based on 'target' time).
*/
static void bubble_last_timer(void) {
int i;
int n = timer_count;
ev_timer *t;
ev_timer tmp;
for (i = n - 1; i > 0; i--) {
/* Timer to bubble is at index i, timer to compare to is
* at i-1 (both guaranteed to exist).
*/
t = timer_list + i;
if (t->target <= (t-1)->target) {
/* 't' expires earlier than (or same time as) 't-1', so we're done. */
break;
} else {
/* 't' expires later than 't-1', so swap them and repeat. */
memcpy((void *) &tmp, (void *) (t - 1), sizeof(ev_timer));
memcpy((void *) (t - 1), (void *) t, sizeof(ev_timer));
memcpy((void *) t, (void *) &tmp, sizeof(ev_timer));
}
}
}
static void expire_timers(duk_context *ctx) {
ev_timer *t;
int sanity = MAX_EXPIRYS;
double now;
int rc;
/* Because a user callback can mutate the timer list (by adding or deleting
* a timer), we expire one timer and then rescan from the end again. There
* is a sanity limit on how many times we do this per expiry round.
*/
duk_push_global_stash(ctx);
duk_get_prop_string(ctx, -1, "eventTimers");
/* [ ... stash eventTimers ] */
now = get_now();
while (sanity-- > 0) {
/*
* If exit has been requested, exit without running further
* callbacks.
*/
if (exit_requested) {
#if 0
fprintf(stderr, "exit requested, exiting timer expiry loop\n");
fflush(stderr);
#endif
break;
}
/*
* Expired timer(s) still exist?
*/
if (timer_count <= 0) {
break;
}
t = timer_list + timer_count - 1;
if (t->target > now) {
break;
}
/*
* Move the timer to 'expiring' for the duration of the callback.
* Mark a one-shot timer deleted, compute a new target for an interval.
*/
memcpy((void *) &timer_expiring, (void *) t, sizeof(ev_timer));
memset((void *) t, 0, sizeof(ev_timer));
timer_count--;
t = &timer_expiring;
if (t->oneshot) {
t->removed = 1;
} else {
t->target = now + t->delay; /* XXX: or t->target + t->delay? */
}
/*
* Call timer callback. The callback can operate on the timer list:
* add new timers, remove timers. The callback can even remove the
* expired timer whose callback we're calling. However, because the
* timer being expired has been moved to 'timer_expiring', we don't
* need to worry about the timer's offset changing on the timer list.
*/
#if 0
fprintf(stderr, "calling user callback for timer id %d\n", (int) t->id);
fflush(stderr);
#endif
duk_push_number(ctx, (double) t->id);
duk_get_prop(ctx, -2); /* -> [ ... stash eventTimers func ] */
rc = duk_pcall(ctx, 0 /*nargs*/); /* -> [ ... stash eventTimers retval ] */
if (rc != 0) {
#if 0
fprintf(stderr, "timer callback failed for timer %d: %s\n", (int) t->id, duk_to_string(ctx, -1));
fflush(stderr);
#endif
}
duk_pop(ctx); /* ignore errors for now -> [ ... stash eventTimers ] */
if (t->removed) {
/* One-shot timer (always removed) or removed by user callback. */
#if 0
fprintf(stderr, "deleting callback state for timer %d\n", (int) t->id);
fflush(stderr);
#endif
duk_push_number(ctx, (double) t->id);
duk_del_prop(ctx, -2);
} else {
/* Interval timer, not removed by user callback. Queue back to
* timer list and bubble to its final sorted position.
*/
#if 0
fprintf(stderr, "queueing timer %d back into active list\n", (int) t->id);
fflush(stderr);
#endif
if (timer_count >= MAX_TIMERS) {
duk_error(ctx, DUK_ERR_RANGE_ERROR, "out of timer slots");
}
memcpy((void *) (timer_list + timer_count), (void *) t, sizeof(ev_timer));
timer_count++;
bubble_last_timer();
}
}
memset((void *) &timer_expiring, 0, sizeof(ev_timer));
duk_pop_2(ctx); /* -> [ ... ] */
}
static void compact_poll_list(void) {
int i, j, n;
/* i = input index
* j = output index (initially same as i)
*/
n = poll_count;
for (i = 0, j = 0; i < n; i++) {
struct pollfd *pfd = poll_list + i;
if (pfd->fd == 0) {
/* keep output index the same */
#if 0
fprintf(stderr, "remove pollfd (index %d): fd=%d, events=%d, revents=%d\n",
i, pfd->fd, pfd->events, pfd->revents),
fflush(stderr);
#endif
continue;
}
#if 0
fprintf(stderr, "keep pollfd (index %d -> %d): fd=%d, events=%d, revents=%d\n",
i, j, pfd->fd, pfd->events, pfd->revents),
fflush(stderr);
#endif
if (i != j) {
/* copy only if indices have diverged */
memcpy((void *) (poll_list + j), (void *) (poll_list + i), sizeof(struct pollfd));
}
j++;
}
if (j < poll_count) {
/* zeroize unused entries for sanity */
memset((void *) (poll_list + j), 0, (poll_count - j) * sizeof(struct pollfd));
}
poll_count = j;
}
int eventloop_run(duk_context *ctx) {
ev_timer *t;
double now;
double diff;
int timeout;
int rc;
int i, n;
int idx_eventloop;
int idx_fd_handler;
/* The Ecmascript poll handler is passed through EventLoop.fdPollHandler
* which c_eventloop.js sets before we come here.
*/
duk_push_global_object(ctx);
duk_get_prop_string(ctx, -1, "EventLoop");
duk_get_prop_string(ctx, -1, "fdPollHandler"); /* -> [ global EventLoop fdPollHandler ] */
idx_fd_handler = duk_get_top_index(ctx);
idx_eventloop = idx_fd_handler - 1;
for (;;) {
/*
* Expire timers.
*/
expire_timers(ctx);
/*
* If exit requested, bail out as fast as possible.
*/
if (exit_requested) {
#if 0
fprintf(stderr, "exit requested, exiting event loop\n");
fflush(stderr);
#endif
break;
}
/*
* Compact poll list by removing pollfds with fd == 0.
*/
compact_poll_list();
/*
* Determine poll() timeout (as close to poll() as possible as
* the wait is relative).
*/
now = get_now();
t = find_nearest_timer();
if (t) {
diff = t->target - now;
if (diff < MIN_WAIT) {
diff = MIN_WAIT;
} else if (diff > MAX_WAIT) {
diff = MAX_WAIT;
}
timeout = (int) diff; /* clamping ensures that fits */
} else {
if (poll_count == 0) {
#if 0
fprintf(stderr, "no timers and no sockets to poll, exiting\n");
fflush(stderr);
#endif
break;
}
timeout = (int) MAX_WAIT;
}
/*
* Poll for activity or timeout.
*/
#if 0
fprintf(stderr, "going to poll, timeout %d ms, pollfd count %d\n", timeout, poll_count);
fflush(stderr);
#endif
rc = poll(poll_list, poll_count, timeout);
#if 0
fprintf(stderr, "poll rc: %d\n", rc);
fflush(stderr);
#endif
if (rc < 0) {
/* error */
} else if (rc == 0) {
/* timeout */
} else {
/* 'rc' fds active */
}
/*
* Check socket activity, handle all sockets. Handling is offloaded to
* Ecmascript code (fd + revents).
*
* If FDs are removed from the poll list while we're processing callbacks,
* the entries are simply marked unused (fd set to 0) without actually
* removing them from the poll list. This ensures indices are not
* disturbed. The poll list is compacted before next poll().
*/
n = (rc == 0 ? 0 : poll_count); /* if timeout, no need to check pollfd */
for (i = 0; i < n; i++) {
struct pollfd *pfd = poll_list + i;
if (pfd->fd == 0) {
/* deleted, perhaps by previous callback */
continue;
}
if (pfd->revents) {
#if 0
fprintf(stderr, "fd %d has revents: %d\n", (int) pfd->fd, (int) pfd->revents);
fflush(stderr);
#endif
duk_dup(ctx, idx_fd_handler);
duk_dup(ctx, idx_eventloop);
duk_push_int(ctx, pfd->fd);
duk_push_int(ctx, pfd->revents);
rc = duk_pcall_method(ctx, 2 /*nargs*/);
if (rc) {
#if 0
fprintf(stderr, "fd callback failed for fd %d: %s\n", (int) pfd->fd, duk_to_string(ctx, -1));
fflush(stderr);
#endif
}
duk_pop(ctx);
pfd->revents = 0;
}
}
}
duk_pop_n(ctx, 3);
return 0;
}
static int create_timer(duk_context *ctx) {
double delay;
int oneshot;
int idx;
int64_t timer_id;
double now;
ev_timer *t;
now = get_now();
/* indexes:
* 0 = function (callback)
* 1 = delay
* 2 = boolean: oneshot
*/
delay = duk_require_number(ctx, 1);
if (delay < MIN_DELAY) {
delay = MIN_DELAY;
}
oneshot = duk_require_boolean(ctx, 2);
if (timer_count >= MAX_TIMERS) {
duk_error(ctx, DUK_ERR_RANGE_ERROR, "out of timer slots");
}
idx = timer_count++;
timer_id = timer_next_id++;
t = timer_list + idx;
memset((void *) t, 0, sizeof(ev_timer));
t->id = timer_id;
t->target = now + delay;
t->delay = delay;
t->oneshot = oneshot;
t->removed = 0;
/* Timer is now at the last position; use swaps to "bubble" it to its
* correct sorted position.
*/
bubble_last_timer();
/* Finally, register the callback to the global stash 'eventTimers' object. */
duk_push_global_stash(ctx);
duk_get_prop_string(ctx, -1, "eventTimers"); /* -> [ func delay oneshot stash eventTimers ] */
duk_push_number(ctx, (double) timer_id);
duk_dup(ctx, 0);
duk_put_prop(ctx, -3); /* eventTimers[timer_id] = callback */
/* Return timer id. */
duk_push_number(ctx, (double) timer_id);
#if 0
fprintf(stderr, "created timer id: %d\n", (int) timer_id);
fflush(stderr);
#endif
return 1;
}
static int delete_timer(duk_context *ctx) {
int i, n;
int64_t timer_id;
ev_timer *t;
int found = 0;
/* indexes:
* 0 = timer id
*/
timer_id = (int64_t) duk_require_number(ctx, 0);
/*
* Unlike insertion, deletion needs a full scan of the timer list
* and an expensive remove. If no match is found, nothing is deleted.
* Caller gets a boolean return code indicating match.
*
* When a timer is being expired and its user callback is running,
* the timer has been moved to 'timer_expiring' and its deletion
* needs special handling: just mark it to-be-deleted and let the
* expiry code remove it.
*/
t = &timer_expiring;
if (t->id == timer_id) {
t->removed = 1;
duk_push_true(ctx);
#if 0
fprintf(stderr, "deleted expiring timer id: %d\n", (int) timer_id);
fflush(stderr);
#endif
return 1;
}
n = timer_count;
for (i = 0; i < n; i++) {
t = timer_list + i;
if (t->id == timer_id) {
found = 1;
/* Shift elements downwards to keep the timer list dense
* (no need if last element).
*/
if (i < timer_count - 1) {
memmove((void *) t, (void *) (t + 1), (timer_count - i - 1) * sizeof(ev_timer));
}
/* Zero last element for clarity. */
memset((void *) (timer_list + n - 1), 0, sizeof(ev_timer));
/* Update timer_count. */
timer_count--;
/* The C state is now up-to-date, but we still need to delete
* the timer callback state from the global 'stash'.
*/
duk_push_global_stash(ctx);
duk_get_prop_string(ctx, -1, "eventTimers"); /* -> [ timer_id stash eventTimers ] */
duk_push_number(ctx, (double) timer_id);
duk_del_prop(ctx, -2); /* delete eventTimers[timer_id] */
#if 0
fprintf(stderr, "deleted timer id: %d\n", (int) timer_id);
fflush(stderr);
#endif
break;
}
}
#if 0
if (!found) {
fprintf(stderr, "trying to delete timer id %d, but not found; ignoring\n", (int) timer_id);
fflush(stderr);
}
#endif
duk_push_boolean(ctx, found);
return 1;
}
static int listen_fd(duk_context *ctx) {
int fd = duk_require_int(ctx, 0);
int events = duk_require_int(ctx, 1);
int i, n;
struct pollfd *pfd;
#if 0
fprintf(stderr, "listen_fd: fd=%d, events=%d\n", fd, events);
fflush(stderr);
#endif
/* events == 0 means stop listening to the FD */
n = poll_count;
for (i = 0; i < n; i++) {
pfd = poll_list + i;
if (pfd->fd == fd) {
#if 0
fprintf(stderr, "listen_fd: fd found at index %d\n", i);
fflush(stderr);
#endif
if (events == 0) {
/* mark to-be-deleted, cleaned up by next poll */
pfd->fd = 0;
} else {
pfd->events = events;
}
return 0;
}
}
/* not found, append to list */
#if 0
fprintf(stderr, "listen_fd: fd not found on list, add new entry\n");
fflush(stderr);
#endif
if (poll_count >= MAX_FDS) {
duk_error(ctx, DUK_ERR_ERROR, "out of fd slots");
}
pfd = poll_list + poll_count;
pfd->fd = fd;
pfd->events = events;
pfd->revents = 0;
poll_count++;
return 0;
}
static int request_exit(duk_context *ctx) {
(void) ctx;
exit_requested = 1;
return 0;
}
static duk_function_list_entry eventloop_funcs[] = {
{ "createTimer", create_timer, 3 },
{ "deleteTimer", delete_timer, 1 },
{ "listenFd", listen_fd, 2 },
{ "requestExit", request_exit, 0 },
{ NULL, NULL, 0 }
};
void eventloop_register(duk_context *ctx) {
memset((void *) timer_list, 0, MAX_TIMERS * sizeof(ev_timer));
memset((void *) &timer_expiring, 0, sizeof(ev_timer));
memset((void *) poll_list, 0, MAX_FDS * sizeof(struct pollfd));
/* Set global 'EventLoop'. */
duk_push_global_object(ctx);
duk_push_object(ctx);
duk_put_function_list(ctx, -1, eventloop_funcs);
duk_put_prop_string(ctx, -2, "EventLoop");
duk_pop(ctx);
/* Initialize global stash 'eventTimers'. */
duk_push_global_stash(ctx);
duk_push_object(ctx);
duk_put_prop_string(ctx, -2, "eventTimers");
duk_pop(ctx);
}

View File

@@ -1,179 +0,0 @@
/*
* C eventloop example (c_eventloop.c).
*
* Ecmascript code to initialize the exposed API (setTimeout() etc) when
* using the C eventloop.
*
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Timers
*/
/*
* Timer API
*/
function setTimeout(func, delay) {
var cb_func;
var bind_args;
var timer_id;
if (typeof delay !== 'number') {
throw new TypeError('delay is not a number');
}
if (typeof func === 'string') {
// Legacy case: callback is a string.
cb_func = eval.bind(this, func);
} else if (typeof func !== 'function') {
throw new TypeError('callback is not a function/string');
} else if (arguments.length > 2) {
// Special case: callback arguments are provided.
bind_args = Array.prototype.slice.call(arguments, 2); // [ arg1, arg2, ... ]
bind_args.unshift(this); // [ global(this), arg1, arg2, ... ]
cb_func = func.bind.apply(func, bind_args);
} else {
// Normal case: callback given as a function without arguments.
cb_func = func;
}
timer_id = EventLoop.createTimer(cb_func, delay, true /*oneshot*/);
return timer_id;
}
function clearTimeout(timer_id) {
if (typeof timer_id !== 'number') {
throw new TypeError('timer ID is not a number');
}
var success = EventLoop.deleteTimer(timer_id); /* retval ignored */
}
function setInterval(func, delay) {
var cb_func;
var bind_args;
var timer_id;
if (typeof delay !== 'number') {
throw new TypeError('delay is not a number');
}
if (typeof func === 'string') {
// Legacy case: callback is a string.
cb_func = eval.bind(this, func);
} else if (typeof func !== 'function') {
throw new TypeError('callback is not a function/string');
} else if (arguments.length > 2) {
// Special case: callback arguments are provided.
bind_args = Array.prototype.slice.call(arguments, 2); // [ arg1, arg2, ... ]
bind_args.unshift(this); // [ global(this), arg1, arg2, ... ]
cb_func = func.bind.apply(func, bind_args);
} else {
// Normal case: callback given as a function without arguments.
cb_func = func;
}
timer_id = EventLoop.createTimer(cb_func, delay, false /*oneshot*/);
return timer_id;
}
function clearInterval(timer_id) {
if (typeof timer_id !== 'number') {
throw new TypeError('timer ID is not a number');
}
EventLoop.deleteTimer(timer_id);
}
function requestEventLoopExit() {
EventLoop.requestExit();
}
/*
* Socket handling
*
* Ideally this would be implemented more in C than here for more speed
* and smaller footprint: C code would directly maintain the callback state
* and such.
*
* Also for more optimal I/O, the buffer churn caused by allocating and
* freeing a lot of buffer values could be eliminated by reusing buffers.
* Socket reads would then go into a pre-allocated buffer, for instance.
*/
EventLoop.socketListening = {};
EventLoop.socketReading = {};
EventLoop.socketConnecting = {};
EventLoop.fdPollHandler = function(fd, revents) {
var data;
var cb;
var rc;
var acc_res;
//print('activity on fd', fd, 'revents', revents);
if (revents & Poll.POLLIN) {
cb = this.socketReading[fd];
if (cb) {
data = Socket.read(fd); // no size control now
//print('READ', Duktape.enc('jx', data));
if (data.length === 0) {
this.close(fd);
return;
}
cb(fd, data);
} else {
cb = this.socketListening[fd];
if (cb) {
acc_res = Socket.accept(fd);
//print('ACCEPT:', Duktape.enc('jx', acc_res));
cb(acc_res.fd, acc_res.addr, acc_res.port);
} else {
//print('UNKNOWN');
}
}
}
if (revents & Poll.POLLOUT) {
// Connected
cb = this.socketConnecting[fd];
if (cb) {
delete this.socketConnecting[fd];
cb(fd);
}
}
if ((revents & ~(Poll.POLLIN | Poll.POLLOUT)) !== 0) {
//print('unexpected revents, close fd');
this.close(fd);
}
}
EventLoop.server = function(address, port, cb_accepted) {
var fd = Socket.createServerSocket(address, port);
this.socketListening[fd] = cb_accepted;
this.listenFd(fd, Poll.POLLIN);
}
EventLoop.connect = function(address, port, cb_connected) {
var fd = Socket.connect(address, port);
this.socketConnecting[fd] = cb_connected;
this.listenFd(fd, Poll.POLLOUT);
}
EventLoop.close = function(fd) {
EventLoop.listenFd(fd, 0);
delete this.socketListening[fd];
delete this.socketReading[fd];
delete this.socketConnecting[fd];
Socket.close(fd);
}
EventLoop.setReader = function(fd, cb_read) {
this.socketReading[fd] = cb_read;
this.listenFd(fd, Poll.POLLIN);
}
EventLoop.write = function(fd, data) {
// This simple example doesn't have support for write blocking / draining
var rc = Socket.write(fd, Duktape.Buffer(data));
}

View File

@@ -1,24 +0,0 @@
var HOST = 'localhost';
var PORT = 80;
var EXIT_TIMEOUT = 300e3;
print('automatic exit after ' + (EXIT_TIMEOUT / 1e3) + ' seconds');
setTimeout(function () {
print('exit timer');
EventLoop.requestExit();
}, EXIT_TIMEOUT);
EventLoop.connect(HOST, PORT, function (fd) {
print('connected to ' + HOST + ':' + PORT + ', fd', fd);
EventLoop.setReader(fd, function (fd, data) {
print('read from fd', fd);
print(data);
EventLoop.close(fd);
});
EventLoop.write(fd, "GET / HTTP/1.1\r\n" +
"Host: " + HOST + "\r\n" +
"User-Agent: client-socket-test.js\r\n" +
"\r\n");
});

View File

@@ -1,79 +0,0 @@
/*
* Test using timers and intervals with curses.
*/
if (typeof Ncurses !== 'object') {
throw new Error('Ncurses required');
}
function fillScreen(ch) {
var size, w, h;
var i, j;
size = Ncurses.getmaxyx();
h = size[0];
w = size[1];
for (i = 0; i < h; i++) {
for (j = 0; j < w; j++) {
Ncurses.mvprintw(i, j, ch);
}
}
Ncurses.refresh();
}
function main() {
var i, j;
var counters = [];
var size, w, h;
Ncurses.initscr();
size = Ncurses.getmaxyx();
h = size[0];
w = size[1];
fillScreen('.');
setInterval(function () {
Ncurses.mvprintw(1, 4, new Date().toISOString());
Ncurses.refresh();
}, 1000);
function addCounter(row, index, interval) {
counters[index] = 0;
setInterval(function () {
counters[index]++;
Ncurses.mvprintw(row, 4, '' + Date.now() + ' ' + counters[index]);
Ncurses.refresh();
}, interval);
}
function addRandomChar(row, col, interval) {
setTimeout(function () {
Ncurses.mvprintw(row, col, String.fromCharCode(Math.random() * 64 + 0x20));
Ncurses.refresh();
}, interval);
}
for (i = 0; i < h - 5; i++) {
addCounter(3 + i, i, 363 * i + 400);
}
/* Here the inserts take a lot of time because the underlying timer manager
* data structure has O(n) insertion performance.
*/
for (i = 0; i < h - 5; i++) {
for (j = 0; j < w - 50; j++) {
// Math.exp(0)...Math.exp(8) is an uneven distribution between 1...~2980.
addRandomChar(3 + i, 28 + j, 58000 - Math.exp(Math.random() * 8) * 20);
}
}
setTimeout(function () {
Ncurses.endwin();
Ncurses.delscreen();
requestEventLoopExit();
}, 120e3);
}
main();

View File

@@ -1,466 +0,0 @@
/*
* Pure Ecmascript eventloop example.
*
* Timer state handling is inefficient in this trivial example. Timers are
* kept in an array sorted by their expiry time which works well for expiring
* timers, but has O(n) insertion performance. A better implementation would
* use a heap or some other efficient structure for managing timers so that
* all operations (insert, remove, get nearest timer) have good performance.
*
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Timers
*/
/*
* Event loop
*
* Timers are sorted by 'target' property which indicates expiry time of
* the timer. The timer expiring next is last in the array, so that
* removals happen at the end, and inserts for timers expiring in the
* near future displace as few elements in the array as possible.
*/
EventLoop = {
// timers
timers: [], // active timers, sorted (nearest expiry last)
expiring: null, // set to timer being expired (needs special handling in clearTimeout/clearInterval)
nextTimerId: 1,
minimumDelay: 1,
minimumWait: 1,
maximumWait: 60000,
maxExpirys: 10,
// sockets
socketListening: {}, // fd -> callback
socketReading: {}, // fd -> callback
socketConnecting: {}, // fd -> callback
// misc
exitRequested: false
};
EventLoop.dumpState = function() {
print('TIMER STATE:');
this.timers.forEach(function(t) {
print(' ' + Duktape.enc('jx', t));
});
if (this.expiring) {
print(' EXPIRING: ' + Duktape.enc('jx', this.expiring));
}
}
// Get timer with lowest expiry time. Since the active timers list is
// sorted, it's always the last timer.
EventLoop.getEarliestTimer = function() {
var timers = this.timers;
n = timers.length;
return (n > 0 ? timers[n - 1] : null);
}
EventLoop.getEarliestWait = function() {
var t = this.getEarliestTimer();
return (t ? t.target - Date.now() : null);
}
EventLoop.insertTimer = function(timer) {
var timers = this.timers;
var i, n, t;
/*
* Find 'i' such that we want to insert *after* timers[i] at index i+1.
* If no such timer, for-loop terminates with i-1, and we insert at -1+1=0.
*/
n = timers.length;
for (i = n - 1; i >= 0; i--) {
t = timers[i];
if (timer.target <= t.target) {
// insert after 't', to index i+1
break;
}
}
timers.splice(i + 1 /*start*/, 0 /*deleteCount*/, timer);
}
// Remove timer/interval with a timer ID. The timer/interval can reside
// either on the active list or it may be an expired timer (this.expiring)
// whose user callback we're running when this function gets called.
EventLoop.removeTimerById = function(timer_id) {
var timers = this.timers;
var i, n, t;
t = this.expiring;
if (t) {
if (t.id === timer_id) {
// Timer has expired and we're processing its callback. User
// callback has requested timer deletion. Mark removed, so
// that the timer is not reinserted back into the active list.
// This is actually a common case because an interval may very
// well cancel itself.
t.removed = true;
return;
}
}
n = timers.length;
for (i = 0; i < n; i++) {
t = timers[i];
if (t.id === timer_id) {
// Timer on active list: mark removed (not really necessary, but
// nice for dumping), and remove from active list.
t.removed = true;
this.timers.splice(i /*start*/, 1 /*deleteCount*/);
return;
}
}
// no such ID, ignore
}
EventLoop.processTimers = function() {
var now = Date.now();
var timers = this.timers;
var sanity = this.maxExpirys;
var n, t;
/*
* Here we must be careful with mutations: user callback may add and
* delete an arbitrary number of timers.
*
* Current solution is simple: check whether the timer at the end of
* the list has expired. If not, we're done. If it has expired,
* remove it from the active list, record it in this.expiring, and call
* the user callback. If user code deletes the this.expiring timer,
* there is special handling which just marks the timer deleted so
* it won't get inserted back into the active list.
*
* This process is repeated at most maxExpirys times to ensure we don't
* get stuck forever; user code could in principle add more and more
* already expired timers.
*/
while (sanity-- > 0) {
// If exit requested, don't call any more callbacks. This allows
// a callback to do cleanups and request exit, and can be sure that
// no more callbacks are processed.
if (this.exitRequested) {
//print('exit requested, exit');
break;
}
// Timers to expire?
n = timers.length;
if (n <= 0) {
break;
}
t = timers[n - 1];
if (now <= t.target) {
// Timer has not expired, and no other timer could have expired
// either because the list is sorted.
break;
}
timers.pop();
// Remove the timer from the active list and process it. The user
// callback may add new timers which is not a problem. The callback
// may also delete timers which is not a problem unless the timer
// being deleted is the timer whose callback we're running; this is
// why the timer is recorded in this.expiring so that clearTimeout()
// and clearInterval() can detect this situation.
if (t.oneshot) {
t.removed = true; // flag for removal
} else {
t.target = now + t.delay;
}
this.expiring = t;
try {
t.cb();
} catch (e) {
print('timer callback failed, ignored: ' + e);
}
this.expiring = null;
// If the timer was one-shot, it's marked 'removed'. If the user callback
// requested deletion for the timer, it's also marked 'removed'. If the
// timer is an interval (and is not marked removed), insert it back into
// the timer list.
if (!t.removed) {
// Reinsert interval timer to correct sorted position. The timer
// must be an interval timer because one-shot timers are marked
// 'removed' above.
this.insertTimer(t);
}
}
}
EventLoop.run = function() {
var wait;
var POLLIN = Poll.POLLIN;
var POLLOUT = Poll.POLLOUT;
var poll_set;
var poll_count;
var fd;
var t, rev;
var rc;
var acc_res;
for (;;) {
/*
* Process expired timers.
*/
this.processTimers();
//this.dumpState();
/*
* Exit check (may be requested by a user callback)
*/
if (this.exitRequested) {
//print('exit requested, exit');
break;
}
/*
* Create poll socket list. This is a very naive approach.
* On Linux, one could use e.g. epoll() and manage socket lists
* incrementally.
*/
poll_set = {};
poll_count = 0;
for (fd in this.socketListening) {
poll_set[fd] = { events: POLLIN, revents: 0 };
poll_count++;
}
for (fd in this.socketReading) {
poll_set[fd] = { events: POLLIN, revents: 0 };
poll_count++;
}
for (fd in this.socketConnecting) {
poll_set[fd] = { events: POLLOUT, revents: 0 };
poll_count++;
}
//print(new Date(), 'poll_set IN:', Duktape.enc('jx', poll_set));
/*
* Wait timeout for timer closest to expiry. Since the poll
* timeout is relative, get this as close to poll() as possible.
*/
wait = this.getEarliestWait();
if (wait === null) {
if (poll_count === 0) {
print('no active timers and no sockets to poll, exit');
break;
} else {
wait = this.maximumWait;
}
} else {
wait = Math.min(this.maximumWait, Math.max(this.minimumWait, wait));
}
/*
* Do the actual poll.
*/
try {
Poll.poll(poll_set, wait);
} catch (e) {
// Eat errors silently. When resizing curses window an EINTR
// happens now.
}
/*
* Process all sockets so that nothing is left unhandled for the
* next round.
*/
//print(new Date(), 'poll_set OUT:', Duktape.enc('jx', poll_set));
for (fd in poll_set) {
t = poll_set[fd];
rev = t.revents;
if (rev & POLLIN) {
cb = this.socketReading[fd];
if (cb) {
data = Socket.read(fd); // no size control now
//print('READ', Duktape.enc('jx', data));
if (data.length === 0) {
//print('zero read for fd ' + fd + ', closing forcibly');
rc = Socket.close(fd); // ignore result
delete this.socketListening[fd];
delete this.socketReading[fd];
} else {
cb(fd, data);
}
} else {
cb = this.socketListening[fd];
if (cb) {
acc_res = Socket.accept(fd);
//print('ACCEPT:', Duktape.enc('jx', acc_res));
cb(acc_res.fd, acc_res.addr, acc_res.port);
} else {
//print('UNKNOWN');
}
}
}
if (rev & POLLOUT) {
cb = this.socketConnecting[fd];
if (cb) {
delete this.socketConnecting[fd];
cb(fd);
} else {
//print('UNKNOWN POLLOUT');
}
}
if ((rev & ~(POLLIN | POLLOUT)) !== 0) {
//print('revents ' + t.revents + ' for fd ' + fd + ', closing forcibly');
rc = Socket.close(fd); // ignore result
delete this.socketListening[fd];
delete this.socketReading[fd];
}
}
}
}
EventLoop.requestExit = function() {
this.exitRequested = true;
}
EventLoop.server = function(address, port, cb_accepted) {
var fd = Socket.createServerSocket(address, port);
this.socketListening[fd] = cb_accepted;
}
EventLoop.connect = function(address, port, cb_connected) {
var fd = Socket.connect(address, port);
this.socketConnecting[fd] = cb_connected;
}
EventLoop.close = function(fd) {
delete this.socketReading[fd];
delete this.socketListening[fd];
}
EventLoop.setReader = function(fd, cb_read) {
this.socketReading[fd] = cb_read;
}
EventLoop.write = function(fd, data) {
// This simple example doesn't have support for write blocking / draining
var rc = Socket.write(fd, Duktape.Buffer(data));
}
/*
* Timer API
*
* These interface with the singleton EventLoop.
*/
function setTimeout(func, delay) {
var cb_func;
var bind_args;
var timer_id;
var evloop = EventLoop;
if (typeof delay !== 'number') {
throw new TypeError('delay is not a number');
}
delay = Math.max(evloop.minimumDelay, delay);
if (typeof func === 'string') {
// Legacy case: callback is a string.
cb_func = eval.bind(this, func);
} else if (typeof func !== 'function') {
throw new TypeError('callback is not a function/string');
} else if (arguments.length > 2) {
// Special case: callback arguments are provided.
bind_args = Array.prototype.slice.call(arguments, 2); // [ arg1, arg2, ... ]
bind_args.unshift(this); // [ global(this), arg1, arg2, ... ]
cb_func = func.bind.apply(func, bind_args);
} else {
// Normal case: callback given as a function without arguments.
cb_func = func;
}
timer_id = evloop.nextTimerId++;
evloop.insertTimer({
id: timer_id,
oneshot: true,
cb: cb_func,
delay: delay,
target: Date.now() + delay
});
return timer_id;
}
function clearTimeout(timer_id) {
var evloop = EventLoop;
if (typeof timer_id !== 'number') {
throw new TypeError('timer ID is not a number');
}
evloop.removeTimerById(timer_id);
}
function setInterval(func, delay) {
var cb_func;
var bind_args;
var timer_id;
var evloop = EventLoop;
if (typeof delay !== 'number') {
throw new TypeError('delay is not a number');
}
delay = Math.max(evloop.minimumDelay, delay);
if (typeof func === 'string') {
// Legacy case: callback is a string.
cb_func = eval.bind(this, func);
} else if (typeof func !== 'function') {
throw new TypeError('callback is not a function/string');
} else if (arguments.length > 2) {
// Special case: callback arguments are provided.
bind_args = Array.prototype.slice.call(arguments, 2); // [ arg1, arg2, ... ]
bind_args.unshift(this); // [ global(this), arg1, arg2, ... ]
cb_func = func.bind.apply(func, bind_args);
} else {
// Normal case: callback given as a function without arguments.
cb_func = func;
}
timer_id = evloop.nextTimerId++;
evloop.insertTimer({
id: timer_id,
oneshot: false,
cb: cb_func,
delay: delay,
target: Date.now() + delay
});
return timer_id;
}
function clearInterval(timer_id) {
var evloop = EventLoop;
if (typeof timer_id !== 'number') {
throw new TypeError('timer ID is not a number');
}
evloop.removeTimerById(timer_id);
}
/* custom call */
function requestEventLoopExit() {
EventLoop.requestExit();
}

View File

@@ -1,69 +0,0 @@
/*
* File I/O binding example.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "duktape.h"
static int fileio_readfile(duk_context *ctx) {
const char *filename = duk_to_string(ctx, 0);
FILE *f = NULL;
long len;
void *buf;
size_t got;
if (!filename) {
goto error;
}
f = fopen(filename, "rb");
if (!f) {
goto error;
}
if (fseek(f, 0, SEEK_END) != 0) {
goto error;
}
len = ftell(f);
if (fseek(f, 0, SEEK_SET) != 0) {
goto error;
}
buf = duk_push_fixed_buffer(ctx, (size_t) len);
got = fread(buf, 1, len, f);
if (got != (size_t) len) {
goto error;
}
fclose(f);
f = NULL;
return 1;
error:
if (f) {
fclose(f);
}
return DUK_RET_ERROR;
}
static duk_function_list_entry fileio_funcs[] = {
{ "readfile", fileio_readfile, 1 },
{ NULL, NULL, 0 }
};
void fileio_register(duk_context *ctx) {
/* Set global 'FileIo'. */
duk_push_global_object(ctx);
duk_push_object(ctx);
duk_put_function_list(ctx, -1, fileio_funcs);
duk_put_prop_string(ctx, -2, "FileIo");
duk_pop(ctx);
}

View File

@@ -1,256 +0,0 @@
/*
* Main for evloop command line tool.
*
* Runs a given script from file or stdin inside an eventloop. The
* script can then access setTimeout() etc.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef NO_SIGNAL
#include <signal.h>
#endif
#include "duktape.h"
extern void poll_register(duk_context *ctx);
extern void ncurses_register(duk_context *ctx);
extern void socket_register(duk_context *ctx);
extern void fileio_register(duk_context *ctx);
extern void eventloop_register(duk_context *ctx);
extern int eventloop_run(duk_context *ctx); /* Duktape/C function, safe called */
static int c_evloop = 0;
#ifndef NO_SIGNAL
static void my_sighandler(int x) {
fprintf(stderr, "Got signal %d\n", x);
fflush(stderr);
}
static void set_sigint_handler(void) {
(void) signal(SIGINT, my_sighandler);
}
#endif /* NO_SIGNAL */
/* Print error to stderr and pop error. */
static void print_error(duk_context *ctx, FILE *f) {
if (duk_is_object(ctx, -1) && duk_has_prop_string(ctx, -1, "stack")) {
/* XXX: print error objects specially */
/* XXX: pcall the string coercion */
duk_get_prop_string(ctx, -1, "stack");
if (duk_is_string(ctx, -1)) {
fprintf(f, "%s\n", duk_get_string(ctx, -1));
fflush(f);
duk_pop_2(ctx);
return;
} else {
duk_pop(ctx);
}
}
duk_to_string(ctx, -1);
fprintf(f, "%s\n", duk_get_string(ctx, -1));
fflush(f);
duk_pop(ctx);
}
int wrapped_compile_execute(duk_context *ctx) {
int comp_flags = 0;
int rc;
/* Compile input and place it into global _USERCODE */
duk_compile(ctx, comp_flags);
duk_push_global_object(ctx);
duk_insert(ctx, -2); /* [ ... global func ] */
duk_put_prop_string(ctx, -2, "_USERCODE");
duk_pop(ctx);
#if 0
printf("compiled usercode\n");
#endif
/* Start a zero timer which will call _USERCODE from within
* the event loop.
*/
fprintf(stderr, "set _USERCODE timer\n");
fflush(stderr);
duk_eval_string(ctx, "setTimeout(function() { _USERCODE(); }, 0);");
duk_pop(ctx);
/* Finally, launch eventloop. This call only returns after the
* eventloop terminates.
*/
if (c_evloop) {
fprintf(stderr, "calling eventloop_run()\n");
fflush(stderr);
rc = duk_safe_call(ctx, eventloop_run, 0 /*nargs*/, 1 /*nrets*/);
if (rc != 0) {
fprintf(stderr, "eventloop_run() failed: %s\n", duk_to_string(ctx, -1));
fflush(stderr);
}
duk_pop(ctx);
} else {
fprintf(stderr, "calling EventLoop.run()\n");
fflush(stderr);
duk_eval_string(ctx, "EventLoop.run();");
duk_pop(ctx);
}
return 0;
}
int handle_fh(duk_context *ctx, FILE *f, const char *filename) {
char *buf = NULL;
int len;
int got;
int rc;
int retval = -1;
if (fseek(f, 0, SEEK_END) < 0) {
goto error;
}
len = (int) ftell(f);
if (fseek(f, 0, SEEK_SET) < 0) {
goto error;
}
buf = (char *) malloc(len);
if (!buf) {
goto error;
}
got = fread((void *) buf, (size_t) 1, (size_t) len, f);
duk_push_lstring(ctx, buf, got);
duk_push_string(ctx, filename);
free(buf);
buf = NULL;
rc = duk_safe_call(ctx, wrapped_compile_execute, 2 /*nargs*/, 1 /*nret*/);
if (rc != DUK_EXEC_SUCCESS) {
print_error(ctx, stderr);
goto error;
} else {
duk_pop(ctx);
retval = 0;
}
/* fall thru */
error:
if (buf) {
free(buf);
}
return retval;
}
int handle_file(duk_context *ctx, const char *filename) {
FILE *f = NULL;
int retval;
f = fopen(filename, "rb");
if (!f) {
fprintf(stderr, "failed to open source file: %s\n", filename);
fflush(stderr);
goto error;
}
retval = handle_fh(ctx, f, filename);
fclose(f);
return retval;
error:
return -1;
}
int handle_stdin(duk_context *ctx) {
int retval;
retval = handle_fh(ctx, stdin, "stdin");
return retval;
}
int main(int argc, char *argv[]) {
duk_context *ctx = NULL;
int retval = 0;
const char *filename = NULL;
int i;
#ifndef NO_SIGNAL
set_sigint_handler();
/* This is useful at the global level; libraries should avoid SIGPIPE though */
/*signal(SIGPIPE, SIG_IGN);*/
#endif
for (i = 1; i < argc; i++) {
char *arg = argv[i];
if (!arg) {
goto usage;
}
if (strcmp(arg, "-c") == 0) {
c_evloop = 1;
} else if (strlen(arg) > 1 && arg[0] == '-') {
goto usage;
} else {
if (filename) {
goto usage;
}
filename = arg;
}
}
if (!filename) {
goto usage;
}
ctx = duk_create_heap_default();
poll_register(ctx);
ncurses_register(ctx);
socket_register(ctx);
fileio_register(ctx);
if (c_evloop) {
fprintf(stderr, "Using C based eventloop (omit -c to use Ecmascript based eventloop)\n");
fflush(stderr);
eventloop_register(ctx);
duk_eval_file(ctx, "c_eventloop.js");
} else {
fprintf(stderr, "Using Ecmascript based eventloop (give -c to use C based eventloop)\n");
fflush(stderr);
duk_eval_file(ctx, "ecma_eventloop.js");
}
fprintf(stderr, "Executing code from: '%s'\n", filename);
fflush(stderr);
if (strcmp(filename, "-") == 0) {
if (handle_stdin(ctx) != 0) {
retval = 1;
goto cleanup;
}
} else {
if (handle_file(ctx, filename) != 0) {
retval = 1;
goto cleanup;
}
}
cleanup:
if (ctx) {
duk_destroy_heap(ctx);
}
return retval;
usage:
fprintf(stderr, "Usage: evloop [-c] <filename>\n");
fprintf(stderr, "\n");
fprintf(stderr, "Uses an Ecmascript based eventloop (ecma_eventloop.js) by default.\n");
fprintf(stderr, "If -c option given, uses a C based eventloop (c_eventloop.{c,js}).\n");
fprintf(stderr, "If <filename> is '-', the entire STDIN executed.\n");
fflush(stderr);
exit(1);
}

View File

@@ -1,105 +0,0 @@
/*
* Ncurses bindings example.
*
* VALGRIND NOTE: when you use ncurses, there seems to be no way to get a
* clean valgrind run. Even if ncurses state is properly shut down, there
* will still be some residual leaks.
*
* Debian: install libncurses5-dev
*/
#include <curses.h>
#include "duktape.h"
static int ncurses_initscr(duk_context *ctx) {
WINDOW *win;
win = initscr();
duk_push_pointer(ctx, (void *) win);
return 1;
}
static int ncurses_endwin(duk_context *ctx) {
int rc;
rc = endwin();
duk_push_int(ctx, rc);
return 1;
}
static int ncurses_delscreen(duk_context *ctx) {
/* XXX: no screen management now */
(void) ctx;
return 0;
}
static int ncurses_getmaxyx(duk_context *ctx) {
int row, col;
getmaxyx(stdscr, row, col);
duk_push_array(ctx);
duk_push_int(ctx, row);
duk_put_prop_index(ctx, -2, 0);
duk_push_int(ctx, col);
duk_put_prop_index(ctx, -2, 1);
return 1;
}
static int ncurses_printw(duk_context *ctx) {
int rc;
const char *str;
str = duk_to_string(ctx, 0);
rc = printw("%s", str);
duk_push_int(ctx, rc);
return 1;
}
static int ncurses_mvprintw(duk_context *ctx) {
int y = duk_to_int(ctx, 0);
int x = duk_to_int(ctx, 1);
const char *str = duk_to_string(ctx, 2);
int rc;
rc = mvprintw(y, x, "%s", str);
duk_push_int(ctx, rc);
return 1;
}
static int ncurses_refresh(duk_context *ctx) {
int rc;
rc = refresh();
duk_push_int(ctx, rc);
return 1;
}
static int ncurses_getch(duk_context *ctx) {
int rc;
rc = getch();
duk_push_int(ctx, rc);
return 1;
}
static duk_function_list_entry ncurses_funcs[] = {
{ "initscr", ncurses_initscr, 0 },
{ "endwin", ncurses_endwin, 0 },
{ "delscreen", ncurses_delscreen, 0 },
{ "getmaxyx", ncurses_getmaxyx, 0 },
{ "printw", ncurses_printw, 1 },
{ "mvprintw", ncurses_mvprintw, 3 },
{ "refresh", ncurses_refresh, 0 },
{ "getch", ncurses_getch, 0 },
{ NULL, NULL, 0 }
};
void ncurses_register(duk_context *ctx) {
/* Set global 'Ncurses'. */
duk_push_global_object(ctx);
duk_push_object(ctx);
duk_put_function_list(ctx, -1, ncurses_funcs);
duk_put_prop_string(ctx, -2, "Ncurses");
duk_pop(ctx);
}

View File

@@ -1,111 +0,0 @@
/*
* C wrapper for poll().
*/
#define _GNU_SOURCE
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <poll.h>
#include <time.h>
#include "duktape.h"
static int poll_poll(duk_context *ctx) {
int timeout = duk_to_int(ctx, 1);
int i, n, nchanged;
int fd, rc;
struct pollfd fds[20];
struct timespec ts;
memset(fds, 0, sizeof(fds));
n = 0;
duk_enum(ctx, 0, 0 /*enum_flags*/);
while (duk_next(ctx, -1, 0)) {
if ((size_t) n >= sizeof(fds) / sizeof(struct pollfd)) {
return -1;
}
/* [... enum key] */
duk_dup_top(ctx); /* -> [... enum key key] */
duk_get_prop(ctx, 0); /* -> [... enum key val] */
fd = duk_to_int(ctx, -2);
duk_push_string(ctx, "events");
duk_get_prop(ctx, -2); /* -> [... enum key val events] */
fds[n].fd = fd;
fds[n].events = duk_to_int(ctx, -1);
fds[n].revents = 0;
duk_pop_n(ctx, 3); /* -> [... enum] */
n++;
}
/* leave enum on stack */
memset(&ts, 0, sizeof(ts));
ts.tv_nsec = (timeout % 1000) * 1000000;
ts.tv_sec = timeout / 1000;
/*rc = ppoll(fds, n, &ts, NULL);*/
rc = poll(fds, n, timeout);
if (rc < 0) {
duk_error(ctx, DUK_ERR_ERROR, "%s (errno=%d)", strerror(errno), errno);
}
duk_push_array(ctx);
nchanged = 0;
for (i = 0; i < n; i++) {
/* update revents */
if (fds[i].revents) {
duk_push_int(ctx, fds[i].fd); /* -> [... retarr fd] */
duk_put_prop_index(ctx, -2, nchanged);
nchanged++;
}
duk_push_int(ctx, fds[i].fd); /* -> [... retarr key] */
duk_get_prop(ctx, 0); /* -> [... retarr val] */
duk_push_string(ctx, "revents");
duk_push_int(ctx, fds[i].revents); /* -> [... retarr val "revents" fds[i].revents] */
duk_put_prop(ctx, -3); /* -> [... retarr val] */
duk_pop(ctx);
}
/* [retarr] */
return 1;
}
static duk_function_list_entry poll_funcs[] = {
{ "poll", poll_poll, 2 },
{ NULL, NULL, 0 }
};
static duk_number_list_entry poll_consts[] = {
{ "POLLIN", (double) POLLIN },
{ "POLLPRI", (double) POLLPRI },
{ "POLLOUT", (double) POLLOUT },
#if 0
/* Linux 2.6.17 and upwards, requires _GNU_SOURCE etc, not added
* now because we don't use it.
*/
{ "POLLRDHUP", (double) POLLRDHUP },
#endif
{ "POLLERR", (double) POLLERR },
{ "POLLHUP", (double) POLLHUP },
{ "POLLNVAL", (double) POLLNVAL },
{ NULL, 0.0 }
};
void poll_register(duk_context *ctx) {
/* Set global 'Poll' with functions and constants. */
duk_push_global_object(ctx);
duk_push_object(ctx);
duk_put_function_list(ctx, -1, poll_funcs);
duk_put_number_list(ctx, -1, poll_consts);
duk_put_prop_string(ctx, -2, "Poll");
duk_pop(ctx);
}

View File

@@ -1,34 +0,0 @@
var HOST = 'localhost'
var PORT = 12345;
var EXIT_TIMEOUT = 300e3;
print('automatic exit after ' + (EXIT_TIMEOUT / 1e3) + ' seconds');
setTimeout(function () {
print('exit timer');
EventLoop.requestExit();
}, EXIT_TIMEOUT);
print('listen on ' + HOST + ':' + PORT);
EventLoop.server(HOST, PORT, function (fd, addr, port) {
print('new connection on fd ' + fd + ' from ' + addr + ':' + port);
EventLoop.setReader(fd, function (fd, data) {
var b, i, n, x;
// Handle socket data carefully: if you convert it to a string,
// it may not be valid UTF-8 etc. Here we operate on the data
// directly in the buffer.
b = data.valueOf(); // ensure we get a plain buffer
n = b.length;
for (i = 0; i < n; i++) {
x = b[i];
if (x >= 0x61 && x <= 0x7a) {
b[i] = x - 0x20; // uppercase
}
}
print('read data on fd ' + fd + ', length ' + data.length);
EventLoop.write(fd, data);
});
});

View File

@@ -1,286 +0,0 @@
/*
* TCP sockets binding example.
*/
#define _GNU_SOURCE
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <time.h>
#include "duktape.h"
#define ERROR_FROM_ERRNO(ctx) do { \
duk_error(ctx, DUK_ERR_ERROR, "%s (errno=%d)", strerror(errno), errno); \
} while (0)
static void set_nonblocking(duk_context *ctx, int fd) {
int rc;
int flags;
rc = fcntl(fd, F_GETFL);
if (rc < 0) {
ERROR_FROM_ERRNO(ctx);
}
flags = rc;
flags |= O_NONBLOCK;
rc = fcntl(fd, F_SETFL, flags);
if (rc < 0) {
ERROR_FROM_ERRNO(ctx);
}
}
static void set_reuseaddr(duk_context *ctx, int fd) {
int val;
int rc;
val = 1;
rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *) &val, sizeof(val));
if (rc != 0) {
ERROR_FROM_ERRNO(ctx);
}
}
#ifdef __APPLE__
static void set_nosigpipe(duk_context *ctx, int fd) {
int val;
int rc;
val = 1;
rc = setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (const void *) &val, sizeof(val));
if (rc != 0) {
ERROR_FROM_ERRNO(ctx);
}
}
#endif
static int socket_create_server_socket(duk_context *ctx) {
const char *addr = duk_to_string(ctx, 0);
int port = duk_to_int(ctx, 1);
int sock;
struct sockaddr_in sockaddr;
struct hostent *ent;
struct in_addr **addr_list;
struct in_addr *addr_inet;
int i;
int rc;
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
ERROR_FROM_ERRNO(ctx);
}
set_nonblocking(ctx, sock);
set_reuseaddr(ctx, sock);
#ifdef __APPLE__
set_nosigpipe(ctx, sock);
#endif
ent = gethostbyname(addr);
if (!ent) {
ERROR_FROM_ERRNO(ctx);
}
addr_list = (struct in_addr **) ent->h_addr_list;
addr_inet = NULL;
for (i = 0; addr_list[i]; i++) {
addr_inet = addr_list[i];
break;
}
if (!addr_inet) {
duk_error(ctx, DUK_ERR_ERROR, "cannot resolve %s", addr);
}
memset(&sockaddr, 0, sizeof(sockaddr));
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons(port);
sockaddr.sin_addr = *addr_inet;
rc = bind(sock, (const struct sockaddr *) &sockaddr, sizeof(sockaddr));
if (rc < 0) {
ERROR_FROM_ERRNO(ctx);
}
rc = listen(sock, 10 /*backlog*/);
if (rc < 0) {
(void) close(sock);
ERROR_FROM_ERRNO(ctx);
}
duk_push_int(ctx, sock);
return 1;
}
static int socket_close(duk_context *ctx) {
int sock = duk_to_int(ctx, 0);
int rc;
rc = close(sock);
if (rc < 0) {
ERROR_FROM_ERRNO(ctx);
}
return 0;
}
static int socket_accept(duk_context *ctx) {
int sock = duk_to_int(ctx, 0);
int rc;
struct sockaddr_in addr;
socklen_t addrlen;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addrlen = sizeof(addr);
rc = accept(sock, (struct sockaddr *) &addr, &addrlen);
if (rc < 0) {
ERROR_FROM_ERRNO(ctx);
}
set_nonblocking(ctx, sock);
#ifdef __APPLE__
set_nosigpipe(ctx, sock);
#endif
if (addrlen == sizeof(addr)) {
uint32_t tmp = ntohl(addr.sin_addr.s_addr);
duk_push_object(ctx);
duk_push_string(ctx, "fd");
duk_push_int(ctx, rc);
duk_put_prop(ctx, -3);
duk_push_string(ctx, "addr");
duk_push_sprintf(ctx, "%d.%d.%d.%d", ((tmp >> 24) & 0xff), ((tmp >> 16) & 0xff), ((tmp >> 8) & 0xff), (tmp & 0xff));
duk_put_prop(ctx, -3);
duk_push_string(ctx, "port");
duk_push_int(ctx, ntohs(addr.sin_port));
duk_put_prop(ctx, -3);
return 1;
}
return 0;
}
static int socket_connect(duk_context *ctx) {
const char *addr = duk_to_string(ctx, 0);
int port = duk_to_int(ctx, 1);
int sock;
struct sockaddr_in sockaddr;
struct hostent *ent;
struct in_addr **addr_list;
struct in_addr *addr_inet;
int i;
int rc;
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
ERROR_FROM_ERRNO(ctx);
}
set_nonblocking(ctx, sock);
#ifdef __APPLE__
set_nosigpipe(ctx, sock);
#endif
ent = gethostbyname(addr);
if (!ent) {
ERROR_FROM_ERRNO(ctx);
}
addr_list = (struct in_addr **) ent->h_addr_list;
addr_inet = NULL;
for (i = 0; addr_list[i]; i++) {
addr_inet = addr_list[i];
break;
}
if (!addr_inet) {
duk_error(ctx, DUK_ERR_ERROR, "cannot resolve %s", addr);
}
memset(&sockaddr, 0, sizeof(sockaddr));
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons(port);
sockaddr.sin_addr = *addr_inet;
rc = connect(sock, (const struct sockaddr *) &sockaddr, (socklen_t) sizeof(sockaddr));
if (rc < 0) {
if (errno == EINPROGRESS) {
#if 0
fprintf(stderr, "connect() returned EINPROGRESS as expected, need to poll writability\n");
fflush(stderr);
#endif
} else {
ERROR_FROM_ERRNO(ctx);
}
}
duk_push_int(ctx, sock);
return 1;
}
static int socket_read(duk_context *ctx) {
int sock = duk_to_int(ctx, 0);
char readbuf[1024];
int rc;
void *data;
rc = recvfrom(sock, (void *) readbuf, sizeof(readbuf), 0, NULL, NULL);
if (rc < 0) {
ERROR_FROM_ERRNO(ctx);
}
data = duk_push_fixed_buffer(ctx, rc);
memcpy(data, readbuf, rc);
return 1;
}
static int socket_write(duk_context *ctx) {
int sock = duk_to_int(ctx, 0);
const char *data;
size_t len;
ssize_t rc;
data = duk_to_buffer(ctx, 1, &len);
/* MSG_NOSIGNAL: avoid SIGPIPE */
#ifdef __APPLE__
rc = sendto(sock, (void *) data, len, 0, NULL, 0);
#else
rc = sendto(sock, (void *) data, len, MSG_NOSIGNAL, NULL, 0);
#endif
if (rc < 0) {
ERROR_FROM_ERRNO(ctx);
}
duk_push_int(ctx, rc);
return 1;
}
static duk_function_list_entry socket_funcs[] = {
{ "createServerSocket", socket_create_server_socket, 2 },
{ "close", socket_close, 1 },
{ "accept", socket_accept, 1 },
{ "connect", socket_connect, 2 },
{ "read", socket_read, 1 },
{ "write", socket_write, 2 },
{ NULL, NULL, 0 }
};
void socket_register(duk_context *ctx) {
/* Set global 'Socket'. */
duk_push_global_object(ctx);
duk_push_object(ctx);
duk_put_function_list(ctx, -1, socket_funcs);
duk_put_prop_string(ctx, -2, "Socket");
duk_pop(ctx);
}

View File

@@ -1,5 +0,0 @@
===========================
Duktape guide example files
===========================
Examples used in the Duktape guide.

View File

@@ -1,16 +0,0 @@
// fib.js
function fib(n) {
if (n == 0) { return 0; }
if (n == 1) { return 1; }
return fib(n-1) + fib(n-2);
}
function test() {
var res = [];
for (i = 0; i < 20; i++) {
res.push(fib(i));
}
print(res.join(' '));
}
test();

View File

@@ -1,32 +0,0 @@
// prime.js
// Pure Ecmascript version of low level helper
function primeCheckEcmascript(val, limit) {
for (var i = 2; i <= limit; i++) {
if ((val % i) == 0) { return false; }
}
return true;
}
// Select available helper at load time
var primeCheckHelper = (this.primeCheckNative || primeCheckEcmascript);
// Check 'val' for primality
function primeCheck(val) {
if (val == 1 || val == 2) { return true; }
var limit = Math.ceil(Math.sqrt(val));
while (limit * limit < val) { limit += 1; }
return primeCheckHelper(val, limit);
}
// Find primes below one million ending in '9999'.
function primeTest() {
var res = [];
print('Have native helper: ' + (primeCheckHelper !== primeCheckEcmascript));
for (var i = 1; i < 1000000; i++) {
if (primeCheck(i) && (i % 10000) == 9999) { res.push(i); }
}
print(res.join(' '));
}

View File

@@ -1,52 +0,0 @@
/* primecheck.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "duktape.h"
static duk_ret_t native_prime_check(duk_context *ctx) {
int val = duk_require_int(ctx, 0);
int lim = duk_require_int(ctx, 1);
int i;
for (i = 2; i <= lim; i++) {
if (val % i == 0) {
duk_push_false(ctx);
return 1;
}
}
duk_push_true(ctx);
return 1;
}
int main(int argc, const char *argv[]) {
duk_context *ctx = NULL;
ctx = duk_create_heap_default();
if (!ctx) {
printf("Failed to create a Duktape heap.\n");
exit(1);
}
duk_push_global_object(ctx);
duk_push_c_function(ctx, native_prime_check, 2 /*nargs*/);
duk_put_prop_string(ctx, -2, "primeCheckNative");
if (duk_peval_file(ctx, "prime.js") != 0) {
printf("Error: %s\n", duk_safe_to_string(ctx, -1));
goto finished;
}
duk_pop(ctx); /* ignore result */
duk_get_prop_string(ctx, -1, "primeTest");
if (duk_pcall(ctx, 0) != 0) {
printf("Error: %s\n", duk_safe_to_string(ctx, -1));
}
duk_pop(ctx); /* ignore result */
finished:
duk_destroy_heap(ctx);
exit(0);
}

View File

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

View File

@@ -1,59 +0,0 @@
/* processlines.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "duktape.h"
int main(int argc, const char *argv[]) {
duk_context *ctx = NULL;
char line[4096];
char idx;
int ch;
ctx = duk_create_heap_default();
if (!ctx) {
printf("Failed to create a Duktape heap.\n");
exit(1);
}
if (duk_peval_file(ctx, "process.js") != 0) {
printf("Error: %s\n", duk_safe_to_string(ctx, -1));
goto finished;
}
duk_pop(ctx); /* ignore result */
memset(line, 0, sizeof(line));
idx = 0;
for (;;) {
if (idx >= sizeof(line)) {
printf("Line too long\n");
exit(1);
}
ch = fgetc(stdin);
if (ch == 0x0a) {
line[idx++] = '\0';
duk_push_global_object(ctx);
duk_get_prop_string(ctx, -1 /*index*/, "processLine");
duk_push_string(ctx, line);
if (duk_pcall(ctx, 1 /*nargs*/) != 0) {
printf("Error: %s\n", duk_safe_to_string(ctx, -1));
} else {
printf("%s\n", duk_safe_to_string(ctx, -1));
}
duk_pop(ctx); /* pop result/error */
idx = 0;
} else if (ch == EOF) {
break;
} else {
line[idx++] = (char) ch;
}
}
finished:
duk_destroy_heap(ctx);
exit(0);
}

View File

@@ -1,42 +0,0 @@
/* uppercase.c */
#include <stdio.h>
#include <stdlib.h>
#include "duktape.h"
static int dummy_upper_case(duk_context *ctx) {
size_t sz;
const char *val = duk_require_lstring(ctx, 0, &sz);
size_t i;
/* We're going to need 'sz' additional entries on the stack. */
duk_require_stack(ctx, sz);
for (i = 0; i < sz; i++) {
char ch = val[i];
if (ch >= 'a' && ch <= 'z') {
ch = ch - 'a' + 'A';
}
duk_push_lstring(ctx, (const char *) &ch, 1);
}
duk_concat(ctx, sz);
return 1;
}
int main(int argc, char *argv[]) {
duk_context *ctx;
if (argc < 2) { exit(1); }
ctx = duk_create_heap_default();
if (!ctx) { exit(1); }
duk_push_c_function(ctx, dummy_upper_case, 1);
duk_push_string(ctx, argv[1]);
duk_call(ctx, 1);
printf("%s -> %s\n", argv[1], duk_to_string(ctx, -1));
duk_pop(ctx);
duk_destroy_heap(ctx);
return 0;
}

View File

@@ -1,5 +0,0 @@
===================
Hello world example
===================
Very simple example, most useful for compilation tests.

View File

@@ -1,38 +0,0 @@
/*
* Very simple example program
*/
#include "duktape.h"
int adder(duk_context *ctx) {
int i;
int n = duk_get_top(ctx); /* #args */
double res = 0.0;
for (i = 0; i < n; i++) {
res += duk_to_number(ctx, i);
}
duk_push_number(ctx, res);
return 1; /* one return value */
}
int main(int argc, char *argv[]) {
duk_context *ctx = duk_create_heap_default();
(void) argc; (void) argv; /* suppress warning */
duk_eval_string(ctx, "print('Hello world!');");
duk_push_global_object(ctx);
duk_push_c_function(ctx, adder, DUK_VARARGS);
duk_put_prop_string(ctx, -2, "adder");
duk_pop(ctx); /* pop global */
duk_eval_string(ctx, "print('2+3=' + adder(2, 3));");
duk_pop(ctx); /* pop eval result */
duk_destroy_heap(ctx);
return 0;
}

View File

@@ -1,5 +0,0 @@
================
Jxpretty example
================
Simple command line utility to pretty print JSON in the JX format.

View File

@@ -1,63 +0,0 @@
/*
* Pretty print JSON from stdin into indented JX.
*/
#include <stdio.h>
#include <stdlib.h>
#include "duktape.h"
static duk_ret_t do_jxpretty(duk_context *ctx) {
FILE *f = stdin;
char buf[4096];
size_t ret;
for (;;) {
if (ferror(f)) {
duk_error(ctx, DUK_ERR_ERROR, "ferror() on stdin");
}
if (feof(f)) {
break;
}
ret = fread(buf, 1, sizeof(buf), f);
#if 0
fprintf(stderr, "Read: %ld\n", (long) ret);
fflush(stderr);
#endif
if (ret == 0) {
break;
}
duk_require_stack(ctx, 1);
duk_push_lstring(ctx, (const char *) buf, ret);
}
duk_concat(ctx, duk_get_top(ctx));
duk_eval_string(ctx, "(function (v) { print(Duktape.enc('jx', JSON.parse(v), null, 4)); })");
duk_insert(ctx, -2);
duk_call(ctx, 1);
return 0;
}
int main(int argc, char *argv[]) {
duk_context *ctx;
duk_int_t rc;
/* suppress warnings */
(void) argc;
(void) argv;
ctx = duk_create_heap_default();
rc = duk_safe_call(ctx, do_jxpretty, 0 /*nargs*/, 1 /*nrets*/);
if (rc) {
fprintf(stderr, "ERROR: %s\n", duk_safe_to_string(ctx, -1));
fflush(stderr);
}
duk_destroy_heap(ctx);
return 0;
}

View File

@@ -1,5 +0,0 @@
===============
Sandbox example
===============
Very simple, minimal sandboxing example.

View File

@@ -1,252 +0,0 @@
/*
* Sandboxing example
*
* Uses custom memory allocation functions which keep track of total amount
* of memory allocated, imposing a maximum total allocation size.
*/
#include <stdio.h>
#include <stdlib.h>
#include "duktape.h"
/*
* Memory allocator which backs to standard library memory functions but
* keeps a small header to track current allocation size.
*
* Many other sandbox allocation models are useful, e.g. preallocated pools.
*/
typedef struct {
/* The double value in the union is there to ensure alignment is
* good for IEEE doubles too. In many 32-bit environments 4 bytes
* would be sufficiently aligned and the double value is unnecessary.
*/
union {
size_t sz;
double d;
} u;
} alloc_hdr;
static size_t total_allocated = 0;
static size_t max_allocated = 256 * 1024; /* 256kB sandbox */
static void sandbox_dump_memstate(void) {
#if 0
fprintf(stderr, "Total allocated: %ld\n", (long) total_allocated);
fflush(stderr);
#endif
}
static void *sandbox_alloc(void *udata, duk_size_t size) {
alloc_hdr *hdr;
(void) udata; /* Suppress warning. */
if (size == 0) {
return NULL;
}
if (total_allocated + size > max_allocated) {
fprintf(stderr, "Sandbox maximum allocation size reached, %ld requested in sandbox_alloc\n",
(long) size);
fflush(stderr);
return NULL;
}
hdr = (alloc_hdr *) malloc(size + sizeof(alloc_hdr));
if (!hdr) {
return NULL;
}
hdr->u.sz = size;
total_allocated += size;
sandbox_dump_memstate();
return (void *) (hdr + 1);
}
static void *sandbox_realloc(void *udata, void *ptr, duk_size_t size) {
alloc_hdr *hdr;
size_t old_size;
void *t;
(void) udata; /* Suppress warning. */
/* Handle the ptr-NULL vs. size-zero cases explicitly to minimize
* platform assumptions. You can get away with much less in specific
* well-behaving environments.
*/
if (ptr) {
hdr = (alloc_hdr *) (((char *) ptr) - sizeof(alloc_hdr));
old_size = hdr->u.sz;
if (size == 0) {
total_allocated -= old_size;
free((void *) hdr);
sandbox_dump_memstate();
return NULL;
} else {
if (total_allocated - old_size + size > max_allocated) {
fprintf(stderr, "Sandbox maximum allocation size reached, %ld requested in sandbox_realloc\n",
(long) size);
fflush(stderr);
return NULL;
}
t = realloc((void *) hdr, size + sizeof(alloc_hdr));
if (!t) {
return NULL;
}
hdr = (alloc_hdr *) t;
total_allocated -= old_size;
total_allocated += size;
hdr->u.sz = size;
sandbox_dump_memstate();
return (void *) (hdr + 1);
}
} else {
if (size == 0) {
return NULL;
} else {
if (total_allocated + size > max_allocated) {
fprintf(stderr, "Sandbox maximum allocation size reached, %ld requested in sandbox_realloc\n",
(long) size);
fflush(stderr);
return NULL;
}
hdr = (alloc_hdr *) malloc(size + sizeof(alloc_hdr));
if (!hdr) {
return NULL;
}
hdr->u.sz = size;
total_allocated += size;
sandbox_dump_memstate();
return (void *) (hdr + 1);
}
}
}
static void sandbox_free(void *udata, void *ptr) {
alloc_hdr *hdr;
(void) udata; /* Suppress warning. */
if (!ptr) {
return;
}
hdr = (alloc_hdr *) (((char *) ptr) - sizeof(alloc_hdr));
total_allocated -= hdr->u.sz;
free((void *) hdr);
sandbox_dump_memstate();
}
/*
* Sandbox setup and test
*/
static duk_ret_t do_sandbox_test(duk_context *ctx) {
FILE *f;
char buf[4096];
size_t ret;
const char *globobj;
/*
* Setup sandbox
*/
globobj =
"({\n"
" print: print,\n"
" Math: {\n"
" max: Math.max\n"
" }\n"
"})\n";
#if 1
fprintf(stderr, "Sandbox global object:\n----------------\n%s----------------\n", globobj);
fflush(stderr);
#endif
duk_eval_string(ctx, globobj);
duk_set_global_object(ctx);
/*
* Execute code from specified file
*/
f = fopen(duk_require_string(ctx, -1), "rb");
if (!f) {
duk_error(ctx, DUK_ERR_ERROR, "failed to open file");
}
for (;;) {
if (ferror(f)) {
fclose(f);
duk_error(ctx, DUK_ERR_ERROR, "ferror when reading file");
}
if (feof(f)) {
break;
}
ret = fread(buf, 1, sizeof(buf), f);
if (ret == 0) {
break;
}
duk_push_lstring(ctx, (const char *) buf, ret);
}
duk_concat(ctx, duk_get_top(ctx) - 1); /* -1 for filename */
/* -> [ ... filename source ] */
duk_insert(ctx, -2);
/* -> [ ... source filename ] */
duk_compile(ctx, 0 /*flags*/); /* Compile as program */
duk_call(ctx, 0 /*nargs*/);
return 0;
}
/*
* Main
*/
static void sandbox_fatal(duk_context *ctx, duk_errcode_t code, const char *msg) {
(void) ctx; /* Suppress warning. */
fprintf(stderr, "FATAL %ld: %s\n", (long) code, (msg ? msg : "no message"));
fflush(stderr);
exit(1); /* must not return */
}
int main(int argc, char *argv[]) {
duk_context *ctx;
duk_int_t rc;
if (argc < 2) {
fprintf(stderr, "Usage: sandbox <test.js>\n");
fflush(stderr);
exit(1);
}
ctx = duk_create_heap(sandbox_alloc,
sandbox_realloc,
sandbox_free,
NULL,
sandbox_fatal);
duk_push_string(ctx, argv[1]);
rc = duk_safe_call(ctx, do_sandbox_test, 1 /*nargs*/, 1 /*nrets*/);
if (rc) {
fprintf(stderr, "ERROR: %s\n", duk_safe_to_string(ctx, -1));
fflush(stderr);
}
duk_destroy_heap(ctx);
/* Should be zero. */
fprintf(stderr, "Final allocation: %ld\n", (long) total_allocated);
fflush(stderr);
return 1;
}

View File

@@ -1,13 +0,0 @@
==============
Duktape extras
==============
Extra modules and utilities. Extras provide functionality that doesn't
comfortably fit into the main Duktape library, perhaps for footprint or
portability reasons, but are still useful for most users.
Extras are maintained and will be bug fixed. However, they don't have the
same semantic versioning guarantees like the main Duktape library. Extras
may be dropped without warning as Duktape is versioned. For instance, if
an extra breaks due to Duktape changes and there is no time to fix it, the
missing extra won't block a release and will be dropped.

File diff suppressed because it is too large Load Diff

View File

@@ -1,2 +0,0 @@
CommonJS specification snapshots are included in the references/
directory. CommonJS is under the MIT license: http://www.commonjs.org/.

View File

@@ -1,21 +0,0 @@
The MIT License
Copyright (c) <year> <copyright holders>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -1,53 +0,0 @@
/*
* Mandelbrot example:
*
* $ ./duk mandel.js
* [...]
*/
function mandel() {
var w = 76, h = 28, iter = 100;
var i, j, k, c;
var x0, y0, xx, yy, xx2, yy2;
var line;
for (i = 0; i < h; i++) {
y0 = (i / h) * 2.5 - 1.25;
for (j = 0, line = []; j < w; j++) {
x0 = (j / w) * 3.0 - 2.0;
for (k = 0, xx = 0, yy = 0, c = '#'; k < iter; k++) {
/* z -> z^2 + c
* -> (xx+i*yy)^2 + (x0+i*y0)
* -> xx*xx+i*2*xx*yy-yy*yy + x0 + i*y0
* -> (xx*xx - yy*yy + x0) + i*(2*xx*yy + y0)
*/
xx2 = xx*xx; yy2 = yy*yy;
if (xx2 + yy2 < 4.0) {
yy = 2*xx*yy + y0;
xx = xx2 - yy2 + x0;
} else {
/* xx^2 + yy^2 >= 4.0 */
if (k < 3) { c = '.'; }
else if (k < 5) { c = ','; }
else if (k < 10) { c = '-'; }
else { c = '='; }
break;
}
}
line.push(c);
}
print(line.join(''));
}
}
try {
mandel();
} catch (e) {
print(e.stack || e);
}

View File

@@ -1,20 +0,0 @@
/*
* Minimal console.log() polyfill
*/
if (typeof console === 'undefined') {
Object.defineProperty(this, 'console', {
value: {}, writable: true, enumerable: false, configurable: true
});
}
if (typeof console.log === 'undefined') {
(function () {
var origPrint = print; // capture in closure in case changed later
Object.defineProperty(this.console, 'log', {
value: function () {
var strArgs = Array.prototype.map.call(arguments, function (v) { return String(v); });
origPrint(Array.prototype.join.call(strArgs, ' '));
}, writable: true, enumerable: false, configurable: true
});
})();
}

View File

@@ -1,38 +0,0 @@
/*
* Helper to check if a number is internally represented as a fastint:
*
* if (Duktape.isFastint(x)) {
* print('fastint');
* } else {
* print('not a fastint (or not a number)');
* }
*
* NOTE: This helper depends on the internal tag numbering (defined in
* duk_tval.h) which is both version specific and depends on whether
* duk_tval is packed or not.
*/
if (typeof Duktape === 'object') {
if (typeof Duktape.fastintTag === 'undefined') {
Object.defineProperty(Duktape, 'fastintTag', {
/* Tag number depends on duk_tval packing. */
value: (Duktape.info(true)[1] === 0xfff4) ?
0xfff1 /* tag for packed duk_tval */ :
1 /* tag for unpacked duk_tval */,
writable: false,
enumerable: false,
configurable: true
});
}
if (typeof Duktape.isFastint === 'undefined') {
Object.defineProperty(Duktape, 'isFastint', {
value: function (v) {
return Duktape.info(v)[0] === 4 && /* public type is DUK_TYPE_NUMBER */
Duktape.info(v)[1] === Duktape.fastintTag; /* internal tag is fastint */
},
writable: false,
enumerable: false,
configurable: true
});
}
}

View File

@@ -1,45 +0,0 @@
/*
* Object.assign(), described in E6 Section 19.1.2.1
*
* http://www.ecma-international.org/ecma-262/6.0/index.html#sec-object.assign
*/
if (typeof Object.assign === 'undefined') {
Object.defineProperty(Object, 'assign', {
value: function (target) {
var i, n, j, m, k;
var source, keys;
var gotError;
var pendingError;
if (target == null) {
throw new Exception('target null or undefined');
}
for (i = 1, n = arguments.length; i < n; i++) {
source = arguments[i];
if (source == null) {
continue; // null or undefined
}
source = Object(source);
keys = Object.keys(source); // enumerable own keys
for (j = 0, m = keys.length; j < m; j++) {
k = keys[j];
try {
target[k] = source[k];
} catch (e) {
if (!gotError) {
gotError = true;
pendingError = e;
}
}
}
}
if (gotError) {
throw pendingError;
}
}, writable: true, enumerable: false, configurable: true
});
}

Some files were not shown because too many files have changed in this diff Show More