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:
@@ -967,7 +967,6 @@ CIVETWEB_API int mg_get_response(struct mg_connection *conn,
|
||||
8 support IPv6 (USE_IPV6 set)
|
||||
16 support WebSocket (USE_WEBSOCKET 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)
|
||||
The result is undefined for all other feature values.
|
||||
|
||||
|
@@ -59,9 +59,6 @@ unsigned mg_check_feature( unsigned feature ) {
|
||||
#if defined(USE_LUA)
|
||||
| 0x0020u
|
||||
#endif
|
||||
#if defined(USE_DUKTAPE)
|
||||
| 0x0040u
|
||||
#endif
|
||||
#if !defined(NO_CACHING)
|
||||
| 0x0080u
|
||||
#endif
|
||||
|
@@ -479,9 +479,6 @@ enum {
|
||||
LUA_SCRIPT_EXTENSIONS,
|
||||
LUA_SERVER_PAGE_EXTENSIONS,
|
||||
#endif
|
||||
#if defined(USE_DUKTAPE)
|
||||
DUKTAPE_SCRIPT_EXTENSIONS,
|
||||
#endif
|
||||
|
||||
#if defined(USE_WEBSOCKET)
|
||||
WEBSOCKET_ROOT,
|
||||
|
@@ -640,12 +640,6 @@ struct mg_option XX_httplib_config_options[] = {
|
||||
{"lua_script_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lua$"},
|
||||
{"lua_server_page_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lp$|**.lsp$"},
|
||||
#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)
|
||||
{"websocket_root", CONFIG_TYPE_DIRECTORY, NULL},
|
||||
#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
|
||||
* is now stored in "filename" variable. */
|
||||
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. */
|
||||
if (0
|
||||
#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],
|
||||
strlen(conn->ctx->config[LUA_SCRIPT_EXTENSIONS]),
|
||||
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
|
||||
) {
|
||||
/* 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. */
|
||||
*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;
|
||||
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. */
|
||||
for (p = filename + strlen(filename); p > filename + 1; p--) {
|
||||
if (*p == '/') {
|
||||
@@ -3707,12 +3695,6 @@ interpret_uri(struct mg_connection *conn, /* in: request (must be valid) */
|
||||
strlen(
|
||||
conn->ctx->config[LUA_SCRIPT_EXTENSIONS]),
|
||||
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
|
||||
) && mg_stat(conn, filename, filep)) {
|
||||
/* 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) */
|
||||
return;
|
||||
|
||||
@@ -7200,10 +7182,6 @@ mg_unlock_context(struct mg_context *ctx)
|
||||
#include "mod_lua.inl"
|
||||
#endif /* USE_LUA */
|
||||
|
||||
#ifdef USE_DUKTAPE
|
||||
#include "mod_duktape.inl"
|
||||
#endif /* USE_DUKTAPE */
|
||||
|
||||
#if defined(USE_WEBSOCKET)
|
||||
|
||||
/* START OF SHA-1 code
|
||||
@@ -8803,14 +8781,6 @@ handle_file_based_request(struct mg_connection *conn,
|
||||
* entire reply. */
|
||||
mg_exec_lua_script(conn, path, NULL);
|
||||
#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)
|
||||
} else if (match_prefix(conn->ctx->config[CGI_EXTENSIONS],
|
||||
strlen(conn->ctx->config[CGI_EXTENSIONS]),
|
||||
|
47
src/main.c
47
src/main.c
@@ -786,38 +786,6 @@ run_lua(const char *file_name)
|
||||
#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__)
|
||||
/* For __MINGW32/64_MAJOR/MINOR_VERSION define */
|
||||
@@ -975,21 +943,6 @@ start_libhttp(int argc, char *argv[])
|
||||
#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 */
|
||||
if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "-H")
|
||||
|| !strcmp(argv[1], "--help"))) {
|
||||
|
@@ -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);
|
||||
}
|
67
src/third_party/duktape-1.3.0/AUTHORS.rst
vendored
67
src/third_party/duktape-1.3.0/AUTHORS.rst
vendored
@@ -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.
|
25
src/third_party/duktape-1.3.0/LICENSE.txt
vendored
25
src/third_party/duktape-1.3.0/LICENSE.txt
vendored
@@ -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.
|
30
src/third_party/duktape-1.3.0/Makefile.cmdline
vendored
30
src/third_party/duktape-1.3.0/Makefile.cmdline
vendored
@@ -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)
|
@@ -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
|
@@ -1,4 +0,0 @@
|
||||
dummy:
|
||||
coffee -c examples/coffee/globals.coffee
|
||||
coffee -c examples/coffee/hello.coffee
|
||||
coffee -c examples/coffee/mandel.coffee
|
23
src/third_party/duktape-1.3.0/Makefile.dukdebug
vendored
23
src/third_party/duktape-1.3.0/Makefile.dukdebug
vendored
@@ -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)
|
7
src/third_party/duktape-1.3.0/Makefile.eval
vendored
7
src/third_party/duktape-1.3.0/Makefile.eval
vendored
@@ -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
|
22
src/third_party/duktape-1.3.0/Makefile.eventloop
vendored
22
src/third_party/duktape-1.3.0/Makefile.eventloop
vendored
@@ -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"
|
35
src/third_party/duktape-1.3.0/Makefile.hello
vendored
35
src/third_party/duktape-1.3.0/Makefile.hello
vendored
@@ -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)
|
@@ -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
|
@@ -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
|
103
src/third_party/duktape-1.3.0/README.rst
vendored
103
src/third_party/duktape-1.3.0/README.rst
vendored
@@ -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)
|
39
src/third_party/duktape-1.3.0/config/README.rst
vendored
39
src/third_party/duktape-1.3.0/config/README.rst
vendored
@@ -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.
|
1872
src/third_party/duktape-1.3.0/config/genconfig.py
vendored
1872
src/third_party/duktape-1.3.0/config/genconfig.py
vendored
File diff suppressed because it is too large
Load Diff
Binary file not shown.
80
src/third_party/duktape-1.3.0/debugger/Makefile
vendored
80
src/third_party/duktape-1.3.0/debugger/Makefile
vendored
@@ -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/
|
268
src/third_party/duktape-1.3.0/debugger/README.rst
vendored
268
src/third_party/duktape-1.3.0/debugger/README.rst
vendored
@@ -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.
|
@@ -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
|
2448
src/third_party/duktape-1.3.0/debugger/duk_debug.js
vendored
2448
src/third_party/duktape-1.3.0/debugger/duk_debug.js
vendored
File diff suppressed because it is too large
Load Diff
@@ -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
|
@@ -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
|
@@ -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"
|
||||
}
|
@@ -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 into</button>
|
||||
<button id="stepover-button">Step over</button>
|
||||
<button id="stepout-button">Step 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 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" /> 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>
|
@@ -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;
|
||||
}
|
@@ -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();
|
||||
});
|
838
src/third_party/duktape-1.3.0/duk_build_meta.json
vendored
838
src/third_party/duktape-1.3.0/duk_build_meta.json
vendored
@@ -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"
|
||||
}
|
@@ -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.
|
@@ -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.
|
@@ -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
|
||||
}
|
@@ -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 */
|
@@ -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.
|
@@ -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);
|
||||
}
|
@@ -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 */
|
@@ -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()
|
@@ -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.
|
@@ -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);
|
||||
}
|
@@ -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 */
|
@@ -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.
|
@@ -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);
|
||||
}
|
@@ -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 */
|
@@ -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.
|
@@ -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);
|
||||
}
|
@@ -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 */
|
@@ -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;
|
||||
}
|
@@ -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
|
@@ -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
|
||||
|
@@ -1,2 +0,0 @@
|
||||
print 'Hello world!'
|
||||
print 'version: ' + Duktape.version
|
@@ -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)
|
||||
|
@@ -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
|
@@ -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
@@ -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 */
|
@@ -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;
|
||||
}
|
@@ -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.
|
@@ -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;
|
||||
}
|
@@ -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 */
|
@@ -1,5 +0,0 @@
|
||||
====================================
|
||||
Dummy external Date provider example
|
||||
====================================
|
||||
|
||||
This example implements a dummy, minimal external Date provider.
|
@@ -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;
|
||||
}
|
@@ -1,5 +0,0 @@
|
||||
============
|
||||
Eval example
|
||||
============
|
||||
|
||||
Evaluate expressions from command line.
|
@@ -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;
|
||||
}
|
@@ -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.
|
@@ -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');
|
||||
|
@@ -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);
|
||||
}
|
@@ -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));
|
||||
}
|
@@ -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");
|
||||
});
|
||||
|
@@ -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();
|
@@ -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();
|
||||
}
|
@@ -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);
|
||||
}
|
@@ -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);
|
||||
}
|
@@ -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);
|
||||
}
|
@@ -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);
|
||||
}
|
@@ -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);
|
||||
});
|
||||
});
|
@@ -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);
|
||||
}
|
@@ -1,5 +0,0 @@
|
||||
===========================
|
||||
Duktape guide example files
|
||||
===========================
|
||||
|
||||
Examples used in the Duktape guide.
|
@@ -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();
|
@@ -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(' '));
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
@@ -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>';
|
||||
});
|
||||
}
|
@@ -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);
|
||||
}
|
@@ -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;
|
||||
}
|
@@ -1,5 +0,0 @@
|
||||
===================
|
||||
Hello world example
|
||||
===================
|
||||
|
||||
Very simple example, most useful for compilation tests.
|
@@ -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;
|
||||
}
|
@@ -1,5 +0,0 @@
|
||||
================
|
||||
Jxpretty example
|
||||
================
|
||||
|
||||
Simple command line utility to pretty print JSON in the JX format.
|
@@ -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;
|
||||
}
|
@@ -1,5 +0,0 @@
|
||||
===============
|
||||
Sandbox example
|
||||
===============
|
||||
|
||||
Very simple, minimal sandboxing example.
|
@@ -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;
|
||||
}
|
13
src/third_party/duktape-1.3.0/extras/README.rst
vendored
13
src/third_party/duktape-1.3.0/extras/README.rst
vendored
@@ -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.
|
3629
src/third_party/duktape-1.3.0/license.spdx
vendored
3629
src/third_party/duktape-1.3.0/license.spdx
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,2 +0,0 @@
|
||||
CommonJS specification snapshots are included in the references/
|
||||
directory. CommonJS is under the MIT license: http://www.commonjs.org/.
|
@@ -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.
|
53
src/third_party/duktape-1.3.0/mandel.js
vendored
53
src/third_party/duktape-1.3.0/mandel.js
vendored
@@ -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);
|
||||
}
|
@@ -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
|
||||
});
|
||||
})();
|
||||
}
|
@@ -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
|
||||
});
|
||||
}
|
||||
}
|
@@ -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
Reference in New Issue
Block a user