From 39301c3bbbb3f8730dec51f075fcef595d3152b0 Mon Sep 17 00:00:00 2001 From: Hans Dedecker Date: Sun, 31 May 2020 20:28:06 +0200 Subject: [PATCH 01/55] ppp: update to version 2.4.8.git-2020-05-25 ddd57c2 pppd: Add lcp-echo-adaptive option c319558 pppd: Handle SIGINT and SIGTERM during interrupted syscalls (#148) 0bc11fb Added missing options to manual pages. (#149) b1fcf16 Merge branch 'monotonic-time' of https://github.com/themiron/ppp c78e312 pppd: linux: use monotonic time if possible Remove patch 121-debian_adaptive_lcp_echo as patch is upstream accepted Remove patch 206-compensate_time_change.patch as timewrap issues are solved by a patch making use of monotonic time Signed-off-by: Hans Dedecker --- package/network/services/ppp/Makefile | 6 +- .../ppp/patches/100-debian_ip-ip_option.patch | 2 +- .../ppp/patches/105-debian_demand.patch | 2 +- .../ppp/patches/110-debian_defaultroute.patch | 6 +- .../120-debian_ipv6_updown_option.patch | 2 +- .../121-debian_adaptive_lcp_echo.patch | 56 ----------- .../patches/206-compensate_time_change.patch | 94 ------------------- .../ppp/patches/208-fix_status_code.patch | 2 +- .../ppp/patches/401-no_record_file.patch | 2 +- .../404-remove_obsolete_protocol_names.patch | 16 ++-- ...openssl-for-the-DES-instead-of-the-l.patch | 2 +- 11 files changed, 20 insertions(+), 170 deletions(-) delete mode 100644 package/network/services/ppp/patches/121-debian_adaptive_lcp_echo.patch delete mode 100644 package/network/services/ppp/patches/206-compensate_time_change.patch diff --git a/package/network/services/ppp/Makefile b/package/network/services/ppp/Makefile index babc7a90bf..8cd2cbc1d9 100644 --- a/package/network/services/ppp/Makefile +++ b/package/network/services/ppp/Makefile @@ -13,9 +13,9 @@ PKG_RELEASE:=5 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL:=https://github.com/paulusmack/ppp -PKG_SOURCE_DATE:=2020-03-21 -PKG_SOURCE_VERSION:=41a73232c16e9a063cfe3fad461c94af3f450f6c -PKG_MIRROR_HASH:=7b966003b7c1bec95156159119b52583778405f0229dc7590911fa93f031552b +PKG_SOURCE_DATE:=2020-05-25 +PKG_SOURCE_VERSION:=ddd57c279f467e18c4fb3a661f21a069a5a37a1f +PKG_MIRROR_HASH:=aac65e65dd25eaf8978a7a0b3258c863d90e29e247dd33d95276c0c189bcd424 PKG_MAINTAINER:=Felix Fietkau PKG_LICENSE:=BSD-4-Clause PKG_CPE_ID:=cpe:/a:samba:ppp diff --git a/package/network/services/ppp/patches/100-debian_ip-ip_option.patch b/package/network/services/ppp/patches/100-debian_ip-ip_option.patch index 335ce72b0a..a996622922 100644 --- a/package/network/services/ppp/patches/100-debian_ip-ip_option.patch +++ b/package/network/services/ppp/patches/100-debian_ip-ip_option.patch @@ -85,7 +85,7 @@ Signed-off-by: Jo-Philipp Wich "Enable multilink operation", OPT_PRIO | 1 }, --- a/pppd/pppd.h +++ b/pppd/pppd.h -@@ -334,6 +334,8 @@ extern int connect_delay; /* Time to del +@@ -335,6 +335,8 @@ extern int connect_delay; /* Time to del extern int max_data_rate; /* max bytes/sec through charshunt */ extern int req_unit; /* interface unit number to use */ extern char req_ifname[MAXIFNAMELEN]; /* interface name to use */ diff --git a/package/network/services/ppp/patches/105-debian_demand.patch b/package/network/services/ppp/patches/105-debian_demand.patch index c68bafa8df..fb6f39f4b9 100644 --- a/package/network/services/ppp/patches/105-debian_demand.patch +++ b/package/network/services/ppp/patches/105-debian_demand.patch @@ -161,7 +161,7 @@ } else { --- a/pppd/pppd.h +++ b/pppd/pppd.h -@@ -601,7 +601,7 @@ void demand_conf __P((void)); /* config +@@ -602,7 +602,7 @@ void demand_conf __P((void)); /* config void demand_block __P((void)); /* set all NPs to queue up packets */ void demand_unblock __P((void)); /* set all NPs to pass packets */ void demand_discard __P((void)); /* set all NPs to discard packets */ diff --git a/package/network/services/ppp/patches/110-debian_defaultroute.patch b/package/network/services/ppp/patches/110-debian_defaultroute.patch index 38739a5243..e474571767 100644 --- a/package/network/services/ppp/patches/110-debian_defaultroute.patch +++ b/package/network/services/ppp/patches/110-debian_defaultroute.patch @@ -137,7 +137,7 @@ Signed-off-by: Jo-Philipp Wich .B disconnect \fIscript Execute the command specified by \fIscript\fR, by passing it to a shell, after -@@ -746,7 +751,12 @@ disable both forms of hardware flow cont +@@ -751,7 +756,12 @@ disable both forms of hardware flow cont .TP .B nodefaultroute Disable the \fIdefaultroute\fR option. The system administrator who @@ -153,7 +153,7 @@ Signed-off-by: Jo-Philipp Wich .B nodefaultroute6 --- a/pppd/pppd.h +++ b/pppd/pppd.h -@@ -683,7 +683,7 @@ int sif6addr __P((int, eui64_t, eui64_t +@@ -684,7 +684,7 @@ int sif6addr __P((int, eui64_t, eui64_t int cif6addr __P((int, eui64_t, eui64_t)); /* Remove an IPv6 address from i/f */ #endif @@ -292,7 +292,7 @@ Signed-off-by: Jo-Philipp Wich } --- a/pppd/sys-solaris.c +++ b/pppd/sys-solaris.c -@@ -2119,12 +2119,18 @@ cifaddr(u, o, h) +@@ -2120,12 +2120,18 @@ cifaddr(u, o, h) * sifdefaultroute - assign a default route through the address given. */ int diff --git a/package/network/services/ppp/patches/120-debian_ipv6_updown_option.patch b/package/network/services/ppp/patches/120-debian_ipv6_updown_option.patch index 4a9ca7d5e7..5ef09b3f3e 100644 --- a/package/network/services/ppp/patches/120-debian_ipv6_updown_option.patch +++ b/package/network/services/ppp/patches/120-debian_ipv6_updown_option.patch @@ -84,7 +84,7 @@ Signed-off-by: Jo-Philipp Wich } --- a/pppd/pppd.h +++ b/pppd/pppd.h -@@ -336,6 +336,8 @@ extern int req_unit; /* interface unit n +@@ -337,6 +337,8 @@ extern int req_unit; /* interface unit n extern char req_ifname[MAXIFNAMELEN]; /* interface name to use */ extern char path_ipup[MAXPATHLEN]; /* pathname of ip-up script */ extern char path_ipdown[MAXPATHLEN]; /* pathname of ip-down script */ diff --git a/package/network/services/ppp/patches/121-debian_adaptive_lcp_echo.patch b/package/network/services/ppp/patches/121-debian_adaptive_lcp_echo.patch deleted file mode 100644 index 510c96ba97..0000000000 --- a/package/network/services/ppp/patches/121-debian_adaptive_lcp_echo.patch +++ /dev/null @@ -1,56 +0,0 @@ ---- a/pppd/lcp.c -+++ b/pppd/lcp.c -@@ -72,6 +72,7 @@ static void lcp_delayed_up __P((void *)) - */ - int lcp_echo_interval = 0; /* Interval between LCP echo-requests */ - int lcp_echo_fails = 0; /* Tolerance to unanswered echo-requests */ -+bool lcp_echo_adaptive = 0; /* request echo only if the link was idle */ - bool lax_recv = 0; /* accept control chars in asyncmap */ - bool noendpoint = 0; /* don't send/accept endpoint discriminator */ - -@@ -150,6 +151,8 @@ static option_t lcp_option_list[] = { - OPT_PRIO }, - { "lcp-echo-interval", o_int, &lcp_echo_interval, - "Set time in seconds between LCP echo requests", OPT_PRIO }, -+ { "lcp-echo-adaptive", o_bool, &lcp_echo_adaptive, -+ "Suppress LCP echo requests if traffic was received", 1 }, - { "lcp-restart", o_int, &lcp_fsm[0].timeouttime, - "Set time in seconds between LCP retransmissions", OPT_PRIO }, - { "lcp-max-terminate", o_int, &lcp_fsm[0].maxtermtransmits, -@@ -2330,6 +2333,22 @@ LcpSendEchoRequest (f) - } - } - -+ /* -+ * If adaptive echos have been enabled, only send the echo request if -+ * no traffic was received since the last one. -+ */ -+ if (lcp_echo_adaptive) { -+ static unsigned int last_pkts_in = 0; -+ -+ update_link_stats(f->unit); -+ link_stats_valid = 0; -+ -+ if (link_stats.pkts_in != last_pkts_in) { -+ last_pkts_in = link_stats.pkts_in; -+ return; -+ } -+ } -+ - /* - * Make and send the echo request frame. - */ ---- a/pppd/pppd.8 -+++ b/pppd/pppd.8 -@@ -575,6 +575,11 @@ to 1) if the \fIproxyarp\fR option is us - dynamic IP address option (i.e. set /proc/sys/net/ipv4/ip_dynaddr to - 1) in demand mode if the local address changes. - .TP -+.B lcp\-echo\-adaptive -+If this option is used with the \fIlcp\-echo\-failure\fR option then -+pppd will send LCP echo\-request frames only if no traffic was received -+from the peer since the last echo\-request was sent. -+.TP - .B lcp\-echo\-failure \fIn - If this option is given, pppd will presume the peer to be dead - if \fIn\fR LCP echo\-requests are sent without receiving a valid LCP diff --git a/package/network/services/ppp/patches/206-compensate_time_change.patch b/package/network/services/ppp/patches/206-compensate_time_change.patch deleted file mode 100644 index a1eb6a716c..0000000000 --- a/package/network/services/ppp/patches/206-compensate_time_change.patch +++ /dev/null @@ -1,94 +0,0 @@ -pppd: Watch out for time warps - -On many embedded systems there is no battery backed RTC and a proper system -time only becomes available through NTP after establishing a connection. - -When the clock suddenly jumps forward, the internal accounting (connect time) -is confused resulting in unreliable data. - -This patch implements periodic clock checking to look for time warps, if one -is detected, the internal counters are adjusted accordingly. - -Signed-off-by: Jo-Philipp Wich - ---- a/pppd/main.c -+++ b/pppd/main.c -@@ -89,6 +89,7 @@ - #include - #include - #include -+#include - - #include "pppd.h" - #include "magic.h" -@@ -226,6 +227,7 @@ static struct subprocess *children; - - /* Prototypes for procedures local to this file. */ - -+static void check_time(void); - static void setup_signals __P((void)); - static void create_pidfile __P((int pid)); - static void create_linkpidfile __P((int pid)); -@@ -525,6 +527,7 @@ main(argc, argv) - info("Starting link"); - } - -+ check_time(); - gettimeofday(&start_time, NULL); - script_unsetenv("CONNECT_TIME"); - script_unsetenv("BYTES_SENT"); -@@ -1274,6 +1277,36 @@ struct callout { - - static struct callout *callout = NULL; /* Callout list */ - static struct timeval timenow; /* Current time */ -+static long uptime_diff = 0; -+static int uptime_diff_set = 0; -+ -+static void check_time(void) -+{ -+ long new_diff; -+ struct timeval t; -+ struct sysinfo i; -+ struct callout *p; -+ -+ gettimeofday(&t, NULL); -+ sysinfo(&i); -+ new_diff = t.tv_sec - i.uptime; -+ -+ if (!uptime_diff_set) { -+ uptime_diff = new_diff; -+ uptime_diff_set = 1; -+ return; -+ } -+ -+ if ((new_diff - 5 > uptime_diff) || (new_diff + 5 < uptime_diff)) { -+ /* system time has changed, update counters and timeouts */ -+ info("System time change detected."); -+ start_time.tv_sec += new_diff - uptime_diff; -+ -+ for (p = callout; p != NULL; p = p->c_next) -+ p->c_time.tv_sec += new_diff - uptime_diff; -+ } -+ uptime_diff = new_diff; -+} - - /* - * timeout - Schedule a timeout. -@@ -1344,6 +1377,8 @@ calltimeout() - { - struct callout *p; - -+ check_time(); -+ - while (callout != NULL) { - p = callout; - -@@ -1371,6 +1406,8 @@ timeleft(tvp) - { - if (callout == NULL) - return NULL; -+ -+ check_time(); - - gettimeofday(&timenow, NULL); - tvp->tv_sec = callout->c_time.tv_sec - timenow.tv_sec; diff --git a/package/network/services/ppp/patches/208-fix_status_code.patch b/package/network/services/ppp/patches/208-fix_status_code.patch index 31460148a4..f1a12006a5 100644 --- a/package/network/services/ppp/patches/208-fix_status_code.patch +++ b/package/network/services/ppp/patches/208-fix_status_code.patch @@ -12,7 +12,7 @@ Signed-off-by: Jo-Philipp Wich --- a/pppd/main.c +++ b/pppd/main.c -@@ -1055,7 +1055,8 @@ get_input() +@@ -1052,7 +1052,8 @@ get_input() } notice("Modem hangup"); hungup = 1; diff --git a/package/network/services/ppp/patches/401-no_record_file.patch b/package/network/services/ppp/patches/401-no_record_file.patch index 56a9f02eb8..f707fda8ab 100644 --- a/package/network/services/ppp/patches/401-no_record_file.patch +++ b/package/network/services/ppp/patches/401-no_record_file.patch @@ -7,7 +7,7 @@ Signed-off-by: Jo-Philipp Wich --- a/pppd/pppd.h +++ b/pppd/pppd.h -@@ -325,7 +325,6 @@ extern int holdoff; /* Dead time before +@@ -326,7 +326,6 @@ extern int holdoff; /* Dead time before extern bool holdoff_specified; /* true if user gave a holdoff value */ extern bool notty; /* Stdin/out is not a tty */ extern char *pty_socket; /* Socket to connect to pty */ diff --git a/package/network/services/ppp/patches/404-remove_obsolete_protocol_names.patch b/package/network/services/ppp/patches/404-remove_obsolete_protocol_names.patch index 1d94873f35..d37fc03539 100644 --- a/package/network/services/ppp/patches/404-remove_obsolete_protocol_names.patch +++ b/package/network/services/ppp/patches/404-remove_obsolete_protocol_names.patch @@ -7,7 +7,7 @@ Signed-off-by: Jo-Philipp Wich --- a/pppd/main.c +++ b/pppd/main.c -@@ -886,14 +886,17 @@ struct protocol_list { +@@ -883,14 +883,17 @@ struct protocol_list { const char *name; } protocol_list[] = { { 0x21, "IP" }, @@ -25,7 +25,7 @@ Signed-off-by: Jo-Philipp Wich { 0x33, "Stream Protocol ST-II" }, { 0x35, "Banyan Vines" }, { 0x39, "AppleTalk EDDP" }, -@@ -907,8 +910,11 @@ struct protocol_list { +@@ -904,8 +907,11 @@ struct protocol_list { { 0x49, "Serial Data Transport Protocol (PPP-SDTP)" }, { 0x4b, "SNA over 802.2" }, { 0x4d, "SNA" }, @@ -37,7 +37,7 @@ Signed-off-by: Jo-Philipp Wich { 0x53, "Encryption" }, { 0x55, "Individual Link Encryption" }, { 0x57, "IPv6" }, -@@ -919,12 +925,15 @@ struct protocol_list { +@@ -916,12 +922,15 @@ struct protocol_list { { 0x65, "RTP IPHC Compressed non-TCP" }, { 0x67, "RTP IPHC Compressed UDP 8" }, { 0x69, "RTP IPHC Compressed RTP 8" }, @@ -53,7 +53,7 @@ Signed-off-by: Jo-Philipp Wich { 0x0203, "IBM Source Routing BPDU" }, { 0x0205, "DEC LANBridge100 Spanning Tree" }, { 0x0207, "Cisco Discovery Protocol" }, -@@ -936,15 +945,19 @@ struct protocol_list { +@@ -933,15 +942,19 @@ struct protocol_list { { 0x0231, "Luxcom" }, { 0x0233, "Sigma Network Systems" }, { 0x0235, "Apple Client Server Protocol" }, @@ -73,7 +73,7 @@ Signed-off-by: Jo-Philipp Wich { 0x4001, "Cray Communications Control Protocol" }, { 0x4003, "CDPD Mobile Network Registration Protocol" }, { 0x4005, "Expand accelerator protocol" }, -@@ -955,8 +968,10 @@ struct protocol_list { +@@ -952,8 +965,10 @@ struct protocol_list { { 0x4023, "RefTek Protocol" }, { 0x4025, "Fibre Channel" }, { 0x4027, "EMIT Protocols" }, @@ -84,7 +84,7 @@ Signed-off-by: Jo-Philipp Wich { 0x8023, "OSI Network Layer Control Protocol" }, { 0x8025, "Xerox NS IDP Control Protocol" }, { 0x8027, "DECnet Phase IV Control Protocol" }, -@@ -965,7 +980,9 @@ struct protocol_list { +@@ -962,7 +977,9 @@ struct protocol_list { { 0x8031, "Bridging NCP" }, { 0x8033, "Stream Protocol Control Protocol" }, { 0x8035, "Banyan Vines Control Protocol" }, @@ -94,7 +94,7 @@ Signed-off-by: Jo-Philipp Wich { 0x803f, "NETBIOS Framing Control Protocol" }, { 0x8041, "Cisco Systems Control Protocol" }, { 0x8043, "Ascom Timeplex" }, -@@ -974,18 +991,24 @@ struct protocol_list { +@@ -971,18 +988,24 @@ struct protocol_list { { 0x8049, "Serial Data Control Protocol (PPP-SDCP)" }, { 0x804b, "SNA over 802.2 Control Protocol" }, { 0x804d, "SNA Control Protocol" }, @@ -119,7 +119,7 @@ Signed-off-by: Jo-Philipp Wich { 0x8207, "Cisco Discovery Protocol Control" }, { 0x8209, "Netcs Twin Routing" }, { 0x820b, "STP - Control Protocol" }, -@@ -994,24 +1017,29 @@ struct protocol_list { +@@ -991,24 +1014,29 @@ struct protocol_list { { 0x8281, "MPLSCP" }, { 0x8285, "IEEE p1284.4 standard - Protocol Control" }, { 0x8287, "ETSI TETRA TNP1 Control Protocol" }, diff --git a/package/network/services/ppp/patches/600-Revert-pppd-Use-openssl-for-the-DES-instead-of-the-l.patch b/package/network/services/ppp/patches/600-Revert-pppd-Use-openssl-for-the-DES-instead-of-the-l.patch index 331ec09ed2..5802d6158c 100644 --- a/package/network/services/ppp/patches/600-Revert-pppd-Use-openssl-for-the-DES-instead-of-the-l.patch +++ b/package/network/services/ppp/patches/600-Revert-pppd-Use-openssl-for-the-DES-instead-of-the-l.patch @@ -17,7 +17,7 @@ This reverts commit 3c7b86229f7bd2600d74db14b1fe5b3896be3875. +++ b/pppd/Makefile.linux @@ -35,10 +35,10 @@ endif COPTS = -O2 -pipe -Wall -g - LIBS = + LIBS = -lrt -# Uncomment the next line to include support for Microsoft's +# Uncomment the next 2 lines to include support for Microsoft's From dd46600b60f1bc8e96e9740fb8bf59135b2e32e0 Mon Sep 17 00:00:00 2001 From: AmadeusGhost <42570690+AmadeusGhost@users.noreply.github.com> Date: Mon, 1 Jun 2020 23:01:24 +0800 Subject: [PATCH 02/55] ppp: refresh patches --- .../services/ppp/patches/511-syncppp.patch | 51 +++++++++---------- ...openssl-for-the-DES-instead-of-the-l.patch | 2 +- 2 files changed, 25 insertions(+), 28 deletions(-) diff --git a/package/network/services/ppp/patches/511-syncppp.patch b/package/network/services/ppp/patches/511-syncppp.patch index 8b8b6cce6a..455ae1fd22 100644 --- a/package/network/services/ppp/patches/511-syncppp.patch +++ b/package/network/services/ppp/patches/511-syncppp.patch @@ -9,22 +9,21 @@ #ifdef CHAPMS #include "chap_ms.h" #define MDTYPE_ALL (MDTYPE_MICROSOFT_V2 | MDTYPE_MICROSOFT | MDTYPE_MD5) -@@ -492,6 +494,19 @@ chap_respond(struct chap_client_state *c +@@ -481,6 +483,18 @@ chap_respond(struct chap_client_state *cs, int id, p[2] = len >> 8; p[3] = len; -+ if (npppd > 1) { -+ if (syncppp(npppd) < 0) { -+ error("syncppp sync fail"); -+ sem_unlink(SEM_COUNT_NAME); -+ sem_unlink(SEM_BLOCK_NAME); -+ } else { -+ info("syncppp sync succeeded"); -+ } -+ } else { -+ info("syncppp not active"); -+ } -+ ++ if (npppd > 1) { ++ if (syncppp(npppd) < 0) { ++ error("syncppp sync fail"); ++ sem_unlink(SEM_COUNT_NAME); ++ sem_unlink(SEM_BLOCK_NAME); ++ } else { ++ info("syncppp sync succeeded"); ++ } ++ } else { ++ info("syncppp not active"); ++ } + output(0, response, PPP_HDRLEN + len); } @@ -55,39 +54,38 @@ # CC = gcc # COPTS = -O2 -pipe -Wall -g --LIBS = +-LIBS = -lrt +LIBS = -lpthread # Uncomment the next line to include support for Microsoft's # MS-CHAP authentication protocol. Also, edit plugins/radius/Makefile.linux. --- a/pppd/options.c +++ b/pppd/options.c -@@ -128,6 +128,7 @@ bool dump_options; /* print out option +@@ -123,6 +123,7 @@ bool dump_options; /* print out option values */ bool dryrun; /* print out option values and exit */ char *domain; /* domain name set by domain option */ int child_wait = 5; /* # seconds to wait for children at exit */ -+int npppd = 0; /* synchronize between multiple pppd */ ++int npppd = 0; /* synchronize between multiple pppd */ struct userenv *userenv_list; /* user environment variables */ int dfl_route_metric = -1; /* metric of the default route to set over the PPP link */ -@@ -342,6 +343,10 @@ option_t general_options[] = { - "Set pathname of ipv6-down script", - OPT_PRIV|OPT_STATIC, NULL, MAXPATHLEN }, +@@ -312,6 +313,9 @@ option_t general_options[] = { + "Unset user environment variable", + OPT_A2PRINTER | OPT_NOPRINT, (void *)user_unsetprint }, + { "syncppp", o_int, &npppd, + "sync among multiple pppd when sending chap/pap respond", OPT_PRIO }, + -+ - #ifdef HAVE_MULTILINK - { "multilink", o_bool, &multilink, - "Enable multilink operation", OPT_PRIO | 1 }, + { "defaultroute-metric", o_int, &dfl_route_metric, + "Metric to use for the default route (Linux only; -1 for default behavior)", + OPT_PRIV|OPT_LLIMIT|OPT_INITONLY, NULL, 0, -1 }, --- a/pppd/pppd.h +++ b/pppd/pppd.h -@@ -343,6 +343,7 @@ extern char *bundle_name; /* bundle name +@@ -341,6 +341,7 @@ extern char *bundle_name; /* bundle name for multilink */ extern bool dump_options; /* print out option values */ extern bool dryrun; /* check everything, print options, exit */ extern int child_wait; /* # seconds to wait for children at end */ -+extern int npppd; /* synchronize between multiple pppd */ ++extern int npppd; /* synchronize between multiple pppd */ #ifdef MAXOCTETS extern unsigned int maxoctets; /* Maximum octetes per session (in bytes) */ @@ -199,9 +197,8 @@ + } + } else { + info("syncppp not active"); -+ } ++ } + output(u->us_unit, outpacket_buf, outlen + PPP_HDRLEN); TIMEOUT(upap_timeout, u, u->us_timeouttime); - diff --git a/package/network/services/ppp/patches/600-Revert-pppd-Use-openssl-for-the-DES-instead-of-the-l.patch b/package/network/services/ppp/patches/600-Revert-pppd-Use-openssl-for-the-DES-instead-of-the-l.patch index 5802d6158c..49bdb28fe2 100644 --- a/package/network/services/ppp/patches/600-Revert-pppd-Use-openssl-for-the-DES-instead-of-the-l.patch +++ b/package/network/services/ppp/patches/600-Revert-pppd-Use-openssl-for-the-DES-instead-of-the-l.patch @@ -17,7 +17,7 @@ This reverts commit 3c7b86229f7bd2600d74db14b1fe5b3896be3875. +++ b/pppd/Makefile.linux @@ -35,10 +35,10 @@ endif COPTS = -O2 -pipe -Wall -g - LIBS = -lrt + LIBS = -lpthread -# Uncomment the next line to include support for Microsoft's +# Uncomment the next 2 lines to include support for Microsoft's From ed8476472bf50a30abe98773dd44e0775fac0a93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibaut=20VAR=C3=88NE?= Date: Fri, 5 Jun 2020 13:59:41 +0200 Subject: [PATCH 03/55] generic: platform/mikrotik: fix routerboot_tag_show_u32s() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The routine would only accurately print out the first word. Signed-off-by: Thibaut VARÈNE Fixes: 5ecf7d96 ("generic: routerboot sysfs platform driver") --- .../files/drivers/platform/mikrotik/routerboot.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/target/linux/generic/files/drivers/platform/mikrotik/routerboot.c b/target/linux/generic/files/drivers/platform/mikrotik/routerboot.c index 47e4471f8e..4c8c0bfac5 100644 --- a/target/linux/generic/files/drivers/platform/mikrotik/routerboot.c +++ b/target/linux/generic/files/drivers/platform/mikrotik/routerboot.c @@ -194,18 +194,18 @@ ssize_t routerboot_tag_show_string(const u8 *pld, u16 pld_len, char *buf) ssize_t routerboot_tag_show_u32s(const u8 *pld, u16 pld_len, char *buf) { char *out = buf; - u32 data; // cpu-endian + u32 *data; // cpu-endian /* Caller ensures pld_len > 0 */ - if (pld_len % sizeof(data)) + if (pld_len % sizeof(*data)) return -EINVAL; - data = *(u32 *)pld; + data = (u32 *)pld; do { - out += sprintf(out, "0x%08x\n", data); + out += sprintf(out, "0x%08x\n", *data); data++; - } while ((pld_len -= sizeof(data))); + } while ((pld_len -= sizeof(*data))); return out - buf; } From e61ff86eeb146b7e86bc917333bd5d7b6849b325 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibaut=20VAR=C3=88NE?= Date: Fri, 5 Jun 2020 14:06:02 +0200 Subject: [PATCH 04/55] generic: platform/mikrotik: match RouterBOOT nomenclature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RouterBOOT cpu frequency settings are letter-indexed. Follow the same logic for the sysfs interface. Signed-off-by: Thibaut VARÈNE --- .../drivers/platform/mikrotik/rb_softconfig.c | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/target/linux/generic/files/drivers/platform/mikrotik/rb_softconfig.c b/target/linux/generic/files/drivers/platform/mikrotik/rb_softconfig.c index 7bef497c9b..be526e9a6d 100644 --- a/target/linux/generic/files/drivers/platform/mikrotik/rb_softconfig.c +++ b/target/linux/generic/files/drivers/platform/mikrotik/rb_softconfig.c @@ -56,7 +56,7 @@ #include "routerboot.h" -#define RB_SOFTCONFIG_VER "0.02" +#define RB_SOFTCONFIG_VER "0.03" #define RB_SC_PR_PFX "[rb_softconfig] " /* @@ -123,20 +123,20 @@ /* * ATH79 CPU frequency indices. * It is unknown if they apply to all ATH79 RBs, and some do not seem to feature - * the up levels (QCA955x), while U3 is presumably AR9344-only. + * the upper levels (QCA955x), while F is presumably AR9344-only. */ -#define RB_CPU_FREQ_IDX_ATH79_D2 (0 << 3) -#define RB_CPU_FREQ_IDX_ATH79_D1 (1 << 3) // 0x8 -#define RB_CPU_FREQ_IDX_ATH79_N0 (2 << 3) // 0x10 - factory freq for many devices -#define RB_CPU_FREQ_IDX_ATH79_U1 (3 << 3) // 0x18 -#define RB_CPU_FREQ_IDX_ATH79_U2 (4 << 3) // 0x20 -#define RB_CPU_FREQ_IDX_ATH79_U3 (5 << 3) // 0x28 +#define RB_CPU_FREQ_IDX_ATH79_A (0 << 3) +#define RB_CPU_FREQ_IDX_ATH79_B (1 << 3) // 0x8 +#define RB_CPU_FREQ_IDX_ATH79_C (2 << 3) // 0x10 - factory freq for many devices +#define RB_CPU_FREQ_IDX_ATH79_D (3 << 3) // 0x18 +#define RB_CPU_FREQ_IDX_ATH79_E (4 << 3) // 0x20 +#define RB_CPU_FREQ_IDX_ATH79_F (5 << 3) // 0x28 #define RB_CPU_FREQ_IDX_ATH79_MIN 0 // all devices support lowest setting -#define RB_CPU_FREQ_IDX_ATH79_AR9334_MAX 5 // stops at U3 -#define RB_CPU_FREQ_IDX_ATH79_QCA953X_MAX 4 // stops at U2 -#define RB_CPU_FREQ_IDX_ATH79_QCA9556_MAX 2 // stops at N0 -#define RB_CPU_FREQ_IDX_ATH79_QCA9558_MAX 3 // stops at U1 +#define RB_CPU_FREQ_IDX_ATH79_AR9334_MAX 5 // stops at F +#define RB_CPU_FREQ_IDX_ATH79_QCA953X_MAX 4 // stops at E +#define RB_CPU_FREQ_IDX_ATH79_QCA9556_MAX 2 // stops at C +#define RB_CPU_FREQ_IDX_ATH79_QCA9558_MAX 3 // stops at D #define RB_SC_CRC32_OFFSET 4 // located right after magic @@ -387,13 +387,14 @@ static ssize_t sc_tag_store_bootdelays(const u8 *pld, u16 pld_len, const char *b /* Support CPU frequency accessors only when the tag format has been asserted */ #if defined(CONFIG_ATH79) +/* Use the same letter-based nomenclature as RouterBOOT */ static struct sc_u32tvs const sc_cpufreq_indexes_ath79[] = { - RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_D2, "-2"), - RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_D1, "-1"), - RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_N0, "0"), - RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_U1, "+1"), - RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_U2, "+2"), - RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_U3, "+3"), + RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_A, "a"), + RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_B, "b"), + RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_C, "c"), + RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_D, "d"), + RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_E, "e"), + RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_F, "f"), }; static int sc_tag_cpufreq_ath79_idxmax(void) From 332cdea2de83dbe8dcb4b2aede34bb0f5e6117ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibaut=20VAR=C3=88NE?= Date: Fri, 5 Jun 2020 15:56:15 +0200 Subject: [PATCH 05/55] generic: platform/mikrotik: graceful fallback for cpufreq_index MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current code would return an error on ath79 when the SoC isn't known to the driver. Return the raw hex value instead, as happens with non supported targets. Also return the correctly incremented value from sc_tag_cpufreq_ath79_arraysize() Signed-off-by: Thibaut VARÈNE --- .../drivers/platform/mikrotik/rb_softconfig.c | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/target/linux/generic/files/drivers/platform/mikrotik/rb_softconfig.c b/target/linux/generic/files/drivers/platform/mikrotik/rb_softconfig.c index be526e9a6d..5cfb08f8ac 100644 --- a/target/linux/generic/files/drivers/platform/mikrotik/rb_softconfig.c +++ b/target/linux/generic/files/drivers/platform/mikrotik/rb_softconfig.c @@ -168,8 +168,9 @@ static ssize_t sc_tag_show_u32tvs(const u8 *pld, u16 pld_len, char *buf, u32 data; // cpu-endian int i; + // fallback to raw hex output if we can't handle the input if (tvselmts < 0) - return tvselmts; + return routerboot_tag_show_u32s(pld, pld_len, buf); if (sizeof(data) != pld_len) return -EINVAL; @@ -397,30 +398,32 @@ static struct sc_u32tvs const sc_cpufreq_indexes_ath79[] = { RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_F, "f"), }; -static int sc_tag_cpufreq_ath79_idxmax(void) +static int sc_tag_cpufreq_ath79_arraysize(void) { - int idx_max = -EOPNOTSUPP; + int idx_max; if (soc_is_ar9344()) - idx_max = RB_CPU_FREQ_IDX_ATH79_AR9334_MAX; + idx_max = RB_CPU_FREQ_IDX_ATH79_AR9334_MAX+1; else if (soc_is_qca953x()) - idx_max = RB_CPU_FREQ_IDX_ATH79_QCA953X_MAX; + idx_max = RB_CPU_FREQ_IDX_ATH79_QCA953X_MAX+1; else if (soc_is_qca9556()) - idx_max = RB_CPU_FREQ_IDX_ATH79_QCA9556_MAX; + idx_max = RB_CPU_FREQ_IDX_ATH79_QCA9556_MAX+1; else if (soc_is_qca9558()) - idx_max = RB_CPU_FREQ_IDX_ATH79_QCA9558_MAX; + idx_max = RB_CPU_FREQ_IDX_ATH79_QCA9558_MAX+1; + else + idx_max = -EOPNOTSUPP; return idx_max; } static ssize_t sc_tag_show_cpufreq_indexes(const u8 *pld, u16 pld_len, char * buf) { - return sc_tag_show_u32tvs(pld, pld_len, buf, sc_cpufreq_indexes_ath79, sc_tag_cpufreq_ath79_idxmax()+1); + return sc_tag_show_u32tvs(pld, pld_len, buf, sc_cpufreq_indexes_ath79, sc_tag_cpufreq_ath79_arraysize()); } static ssize_t sc_tag_store_cpufreq_indexes(const u8 *pld, u16 pld_len, const char *buf, size_t count) { - return sc_tag_store_u32tvs(pld, pld_len, buf, count, sc_cpufreq_indexes_ath79, sc_tag_cpufreq_ath79_idxmax()+1); + return sc_tag_store_u32tvs(pld, pld_len, buf, count, sc_cpufreq_indexes_ath79, sc_tag_cpufreq_ath79_arraysize()); } #else /* By default we only show the raw value to help with reverse-engineering */ From 1affa1b4320a03c61bbab9123130160b57ee15aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibaut=20VAR=C3=88NE?= Date: Sat, 6 Jun 2020 15:46:08 +0200 Subject: [PATCH 06/55] generic: platform/mikrotik: add board_revision tag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tag was discovered exploring the hard_config of a "r2" board. Signed-off-by: Thibaut VARÈNE --- .../files/drivers/platform/mikrotik/rb_hardconfig.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.c b/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.c index 992689605d..4559418de7 100644 --- a/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.c +++ b/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.c @@ -39,7 +39,7 @@ #include "routerboot.h" -#define RB_HARDCONFIG_VER "0.04" +#define RB_HARDCONFIG_VER "0.05" #define RB_HC_PR_PFX "[rb_hardconfig] " /* ID values for hardware settings */ @@ -58,6 +58,7 @@ #define RB_ID_BOARD_IDENTIFIER 0x17 #define RB_ID_PRODUCT_NAME 0x21 #define RB_ID_DEFCONF 0x26 +#define RB_ID_BOARD_REVISION 0x27 /* Bit definitions for hardware options */ #define RB_HW_OPT_NO_UART BIT(0) @@ -415,6 +416,10 @@ static struct hc_attr { .tag_id = RB_ID_DEFCONF, .tshow = routerboot_tag_show_string, .kattr = __ATTR(defconf, S_IRUSR, hc_attr_show, NULL), + }, { + .tag_id = RB_ID_BOARD_REVISION, + .tshow = routerboot_tag_show_string, + .kattr = __ATTR(board_revision, S_IRUSR, hc_attr_show, NULL), } }; From 96f44a4f9ad4a89c86e1698ff6c3810923239d81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibaut=20VAR=C3=88NE?= Date: Sun, 21 Jun 2020 12:59:56 +0200 Subject: [PATCH 07/55] generic: platform/mikrotik: add partial AR71xx cpufreq support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds cpu frequency setting for AR724x and AR7161. It is unknown if all the AR71xx devices support the same value range, so support is only enabled for AR7161 at this stage. Signed-off-by: Thibaut VARÈNE --- .../drivers/platform/mikrotik/rb_softconfig.c | 97 +++++++++++++------ 1 file changed, 70 insertions(+), 27 deletions(-) diff --git a/target/linux/generic/files/drivers/platform/mikrotik/rb_softconfig.c b/target/linux/generic/files/drivers/platform/mikrotik/rb_softconfig.c index 5cfb08f8ac..51a178ec7c 100644 --- a/target/linux/generic/files/drivers/platform/mikrotik/rb_softconfig.c +++ b/target/linux/generic/files/drivers/platform/mikrotik/rb_softconfig.c @@ -121,22 +121,36 @@ #define RB_BOOT_DEVICE_FLSHONCE 8 // "boot in flash configuration mode once, then NAND" /* - * ATH79 CPU frequency indices. + * ATH79 9xxx CPU frequency indices. * It is unknown if they apply to all ATH79 RBs, and some do not seem to feature * the upper levels (QCA955x), while F is presumably AR9344-only. */ -#define RB_CPU_FREQ_IDX_ATH79_A (0 << 3) -#define RB_CPU_FREQ_IDX_ATH79_B (1 << 3) // 0x8 -#define RB_CPU_FREQ_IDX_ATH79_C (2 << 3) // 0x10 - factory freq for many devices -#define RB_CPU_FREQ_IDX_ATH79_D (3 << 3) // 0x18 -#define RB_CPU_FREQ_IDX_ATH79_E (4 << 3) // 0x20 -#define RB_CPU_FREQ_IDX_ATH79_F (5 << 3) // 0x28 +#define RB_CPU_FREQ_IDX_ATH79_9X_A (0 << 3) +#define RB_CPU_FREQ_IDX_ATH79_9X_B (1 << 3) // 0x8 +#define RB_CPU_FREQ_IDX_ATH79_9X_C (2 << 3) // 0x10 - factory freq for many devices +#define RB_CPU_FREQ_IDX_ATH79_9X_D (3 << 3) // 0x18 +#define RB_CPU_FREQ_IDX_ATH79_9X_E (4 << 3) // 0x20 +#define RB_CPU_FREQ_IDX_ATH79_9X_F (5 << 3) // 0x28 -#define RB_CPU_FREQ_IDX_ATH79_MIN 0 // all devices support lowest setting -#define RB_CPU_FREQ_IDX_ATH79_AR9334_MAX 5 // stops at F -#define RB_CPU_FREQ_IDX_ATH79_QCA953X_MAX 4 // stops at E -#define RB_CPU_FREQ_IDX_ATH79_QCA9556_MAX 2 // stops at C -#define RB_CPU_FREQ_IDX_ATH79_QCA9558_MAX 3 // stops at D +#define RB_CPU_FREQ_IDX_ATH79_9X_MIN 0 // all devices support lowest setting +#define RB_CPU_FREQ_IDX_ATH79_9X_AR9334_MAX 5 // stops at F +#define RB_CPU_FREQ_IDX_ATH79_9X_QCA953X_MAX 4 // stops at E +#define RB_CPU_FREQ_IDX_ATH79_9X_QCA9556_MAX 2 // stops at C +#define RB_CPU_FREQ_IDX_ATH79_9X_QCA9558_MAX 3 // stops at D + +/* ATH79 7xxx CPU frequency indices. */ +#define RB_CPU_FREQ_IDX_ATH79_7X_A ((0 * 9) << 4) +#define RB_CPU_FREQ_IDX_ATH79_7X_B ((1 * 9) << 4) +#define RB_CPU_FREQ_IDX_ATH79_7X_C ((2 * 9) << 4) +#define RB_CPU_FREQ_IDX_ATH79_7X_D ((3 * 9) << 4) +#define RB_CPU_FREQ_IDX_ATH79_7X_E ((4 * 9) << 4) +#define RB_CPU_FREQ_IDX_ATH79_7X_F ((5 * 9) << 4) +#define RB_CPU_FREQ_IDX_ATH79_7X_G ((6 * 9) << 4) +#define RB_CPU_FREQ_IDX_ATH79_7X_H ((7 * 9) << 4) + +#define RB_CPU_FREQ_IDX_ATH79_7X_MIN 0 // all devices support lowest setting +#define RB_CPU_FREQ_IDX_ATH79_7X_AR724X_MAX 3 // stops at D +#define RB_CPU_FREQ_IDX_ATH79_7X_AR7161_MAX 7 // stops at H - check if applies to all AR71xx devices #define RB_SC_CRC32_OFFSET 4 // located right after magic @@ -389,41 +403,70 @@ static ssize_t sc_tag_store_bootdelays(const u8 *pld, u16 pld_len, const char *b /* Support CPU frequency accessors only when the tag format has been asserted */ #if defined(CONFIG_ATH79) /* Use the same letter-based nomenclature as RouterBOOT */ -static struct sc_u32tvs const sc_cpufreq_indexes_ath79[] = { - RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_A, "a"), - RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_B, "b"), - RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_C, "c"), - RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_D, "d"), - RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_E, "e"), - RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_F, "f"), +static struct sc_u32tvs const sc_cpufreq_indexes_ath79_9x[] = { + RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_9X_A, "a"), + RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_9X_B, "b"), + RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_9X_C, "c"), + RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_9X_D, "d"), + RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_9X_E, "e"), + RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_9X_F, "f"), +}; + +static struct sc_u32tvs const sc_cpufreq_indexes_ath79_7x[] = { + RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_7X_A, "a"), + RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_7X_B, "b"), + RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_7X_C, "c"), + RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_7X_D, "d"), + RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_7X_E, "e"), + RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_7X_F, "f"), + RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_7X_G, "g"), + RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_7X_H, "h"), }; static int sc_tag_cpufreq_ath79_arraysize(void) { int idx_max; - if (soc_is_ar9344()) - idx_max = RB_CPU_FREQ_IDX_ATH79_AR9334_MAX+1; + if (ATH79_SOC_AR7161 == ath79_soc) + idx_max = RB_CPU_FREQ_IDX_ATH79_7X_AR7161_MAX+1; + else if (soc_is_ar724x()) + idx_max = RB_CPU_FREQ_IDX_ATH79_7X_AR724X_MAX+1; + else if (soc_is_ar9344()) + idx_max = RB_CPU_FREQ_IDX_ATH79_9X_AR9334_MAX+1; else if (soc_is_qca953x()) - idx_max = RB_CPU_FREQ_IDX_ATH79_QCA953X_MAX+1; + idx_max = RB_CPU_FREQ_IDX_ATH79_9X_QCA953X_MAX+1; else if (soc_is_qca9556()) - idx_max = RB_CPU_FREQ_IDX_ATH79_QCA9556_MAX+1; + idx_max = RB_CPU_FREQ_IDX_ATH79_9X_QCA9556_MAX+1; else if (soc_is_qca9558()) - idx_max = RB_CPU_FREQ_IDX_ATH79_QCA9558_MAX+1; + idx_max = RB_CPU_FREQ_IDX_ATH79_9X_QCA9558_MAX+1; else idx_max = -EOPNOTSUPP; return idx_max; } -static ssize_t sc_tag_show_cpufreq_indexes(const u8 *pld, u16 pld_len, char * buf) +static ssize_t sc_tag_show_cpufreq_indexes(const u8 *pld, u16 pld_len, char *buf) { - return sc_tag_show_u32tvs(pld, pld_len, buf, sc_cpufreq_indexes_ath79, sc_tag_cpufreq_ath79_arraysize()); + const struct sc_u32tvs *tvs; + + if (soc_is_ar71xx() || soc_is_ar724x()) + tvs = sc_cpufreq_indexes_ath79_7x; + else + tvs = sc_cpufreq_indexes_ath79_9x; + + return sc_tag_show_u32tvs(pld, pld_len, buf, tvs, sc_tag_cpufreq_ath79_arraysize()); } static ssize_t sc_tag_store_cpufreq_indexes(const u8 *pld, u16 pld_len, const char *buf, size_t count) { - return sc_tag_store_u32tvs(pld, pld_len, buf, count, sc_cpufreq_indexes_ath79, sc_tag_cpufreq_ath79_arraysize()); + const struct sc_u32tvs *tvs; + + if (soc_is_ar71xx() || soc_is_ar724x()) + tvs = sc_cpufreq_indexes_ath79_7x; + else + tvs = sc_cpufreq_indexes_ath79_9x; + + return sc_tag_store_u32tvs(pld, pld_len, buf, count, tvs, sc_tag_cpufreq_ath79_arraysize()); } #else /* By default we only show the raw value to help with reverse-engineering */ From 79479c607d577f1f485ea731125fca662c8cdd48 Mon Sep 17 00:00:00 2001 From: Wren Turkal Date: Mon, 25 May 2020 14:05:51 -0700 Subject: [PATCH 08/55] uclibc++: make verbosity affect uClibc++ build Before this change, setting the verbosity to anything with V=blah would cause uclibc++ build to print errors to the screen. Now, it the clibc++ build verbosity will be altered in the following manners: * V=s will set V=1 in the uclibc++ build * V=sc will set V=2 in the uclibc++ build Signed-off-by: Wren Turkal --- package/libs/uclibc++/Makefile | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/package/libs/uclibc++/Makefile b/package/libs/uclibc++/Makefile index 7a0d9094ff..0e3210a5b9 100644 --- a/package/libs/uclibc++/Makefile +++ b/package/libs/uclibc++/Makefile @@ -55,6 +55,14 @@ ifeq ($(CONFIG_USE_MUSL),y) SSP_LIB=-lssp_nonshared endif +ifeq (${V}, s) +MAKE_VARS+= \ + V=1 +else ifeq (${V}, sc) +MAKE_VARS+= \ + V=2 +endif + MAKE_FLAGS:= \ $(TARGET_CONFIGURE_OPTS) \ CPU_CFLAGS="$(TARGET_CFLAGS)" \ From 01123cbf81a7579882f1f8cffff846d1f5659e5b Mon Sep 17 00:00:00 2001 From: Lucian Cristian Date: Sun, 12 Apr 2020 17:45:41 +0300 Subject: [PATCH 09/55] kernel: add iscsi-initator support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Module is needed for using iscsi-initiator userspace applications Signed-off-by: Lucian Cristian [added missing newline between kernel modules] Signed-off-by: Petr Štetiar --- package/kernel/linux/modules/block.mk | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/package/kernel/linux/modules/block.mk b/package/kernel/linux/modules/block.mk index c486a433c2..8faaa08b36 100644 --- a/package/kernel/linux/modules/block.mk +++ b/package/kernel/linux/modules/block.mk @@ -272,6 +272,32 @@ endef $(eval $(call KernelPackage,dm-raid)) +define KernelPackage/iscsi-initiator + SUBMENU:=$(BLOCK_MENU) + TITLE:=iSCSI Initiator over TCP/IP + DEPENDS:=+kmod-scsi-core + KCONFIG:= \ + CONFIG_INET \ + CONFIG_SCSI_LOWLEVEL \ + CONFIG_ISCSI_TCP \ + CONFIG_SCSI_ISCSI_ATTRS + FILES:= \ + $(LINUX_DIR)/drivers/scsi/iscsi_tcp.ko \ + $(LINUX_DIR)/drivers/scsi/libiscsi.ko \ + $(LINUX_DIR)/drivers/scsi/libiscsi_tcp.ko \ + $(LINUX_DIR)/drivers/scsi/scsi_transport_iscsi.ko + AUTOLOAD:=$(call AutoProbe,libiscsi libiscsi_tcp scsi_transport_iscsi iscsi_tcp) +endef + +define KernelPackage/iscsi-initiator/description +The iSCSI Driver provides a host with the ability to access storage through an +IP network. The driver uses the iSCSI protocol to transport SCSI requests and +responses over a TCP/IP network between the host (the "initiator") and "targets". +endef + +$(eval $(call KernelPackage,iscsi-initiator)) + + define KernelPackage/md-mod SUBMENU:=$(BLOCK_MENU) TITLE:=MD RAID From bba8068011f05badc939c86d15903220c4a984b8 Mon Sep 17 00:00:00 2001 From: "Sergio E. Nemirowski" Date: Thu, 2 Apr 2020 02:21:37 +0300 Subject: [PATCH 10/55] build: add 'make kernel_xconfig' command This adds the kernel_xconfig make target. Signed-off-by: Sergio E. Nemirowski --- include/kernel-build.mk | 2 +- include/toplevel.mk | 4 ++++ target/linux/Makefile | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/include/kernel-build.mk b/include/kernel-build.mk index 7108ee10f1..f1b3448f6d 100644 --- a/include/kernel-build.mk +++ b/include/kernel-build.mk @@ -157,7 +157,7 @@ define BuildKernel compile: $(LINUX_DIR)/.modules $(MAKE) -C image compile TARGET_BUILD= - oldconfig menuconfig nconfig: $(STAMP_PREPARED) $(STAMP_CHECKED) FORCE + oldconfig menuconfig nconfig xconfig: $(STAMP_PREPARED) $(STAMP_CHECKED) FORCE rm -f $(LINUX_DIR)/.config.prev rm -f $(STAMP_CONFIGURED) $(LINUX_RECONF_CMD) > $(LINUX_DIR)/.config diff --git a/include/toplevel.mk b/include/toplevel.mk index ba59595764..8564a4de73 100644 --- a/include/toplevel.mk +++ b/include/toplevel.mk @@ -169,6 +169,7 @@ kernel_oldconfig: prepare_kernel_conf ifneq ($(DISTRO_PKG_CONFIG),) kernel_menuconfig: export PATH:=$(dir $(DISTRO_PKG_CONFIG)):$(PATH) kernel_nconfig: export PATH:=$(dir $(DISTRO_PKG_CONFIG)):$(PATH) +kernel_xconfig: export PATH:=$(dir $(DISTRO_PKG_CONFIG)):$(PATH) endif kernel_menuconfig: prepare_kernel_conf $(_SINGLE)$(NO_TRACE_MAKE) -C target/linux menuconfig @@ -176,6 +177,9 @@ kernel_menuconfig: prepare_kernel_conf kernel_nconfig: prepare_kernel_conf $(_SINGLE)$(NO_TRACE_MAKE) -C target/linux nconfig +kernel_xconfig: prepare_kernel_conf + $(_SINGLE)$(NO_TRACE_MAKE) -C target/linux xconfig + staging_dir/host/.prereq-build: include/prereq-build.mk mkdir -p tmp @$(_SINGLE)$(NO_TRACE_MAKE) -j1 -r -s -f $(TOPDIR)/include/prereq-build.mk prereq 2>/dev/null || { \ diff --git a/target/linux/Makefile b/target/linux/Makefile index f7bbdffbf8..3a70b80bb3 100644 --- a/target/linux/Makefile +++ b/target/linux/Makefile @@ -9,5 +9,5 @@ include $(INCLUDE_DIR)/target.mk export TARGET_BUILD=1 -prereq clean download prepare compile install menuconfig nconfig oldconfig update refresh: FORCE +prereq clean download prepare compile install oldconfig menuconfig nconfig xconfig update refresh: FORCE @+$(NO_TRACE_MAKE) -C $(BOARD) $@ From fa881e1c30caf30e5be54e0d390ef282a3725de6 Mon Sep 17 00:00:00 2001 From: Andrea Dalla Costa Date: Sat, 11 Jan 2020 22:57:58 +0100 Subject: [PATCH 11/55] firmware-utils/hcsmakeimage: fix possible memory leak and resource leaks Add missing calls to `free` for variable `filebuffer`. Add missing calls to `fclose` for variables `fd` and `fd_out`. Signed-off-by: Andrea Dalla Costa --- tools/firmware-utils/src/hcsmakeimage.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/firmware-utils/src/hcsmakeimage.c b/tools/firmware-utils/src/hcsmakeimage.c index 7baa7b5845..2888810cc7 100644 --- a/tools/firmware-utils/src/hcsmakeimage.c +++ b/tools/firmware-utils/src/hcsmakeimage.c @@ -183,6 +183,7 @@ int main ( int argc, char** argv ) char* filebuffer = malloc ( buf.st_size+10 ); FILE* fd = fopen ( input,"r" ); fread ( filebuffer, 1, buf.st_size,fd ); + fclose (fd); if (!output) { output = malloc(strlen(input+5)); @@ -194,10 +195,13 @@ int main ( int argc, char** argv ) if (!fd_out) { fprintf(stderr, "Failed to open output file: %s\n", output); + free(filebuffer); exit(1); } fwrite ( head,1,sizeof ( ldr_header_t ),fd_out ); fwrite ( filebuffer,1,buf.st_size,fd_out ); printf("Firmware image %s is ready\n", output); + free(filebuffer); + fclose(fd_out); return 0; } From 8f09dbbb24fcadc25f6d706c56ad15c1935b4e3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20=C5=A0tetiar?= Date: Wed, 8 Jul 2020 21:11:40 +0200 Subject: [PATCH 12/55] kernel: iscsi-initator: fix missing dependency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes following issue: Package kmod-iscsi-initiator is missing dependencies for the following libraries: crypto_hash.ko Fixes: b88f8202c4ce ("kernel: add iscsi-initator support") Signed-off-by: Petr Štetiar --- package/kernel/linux/modules/block.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/kernel/linux/modules/block.mk b/package/kernel/linux/modules/block.mk index 8faaa08b36..83b42eeb66 100644 --- a/package/kernel/linux/modules/block.mk +++ b/package/kernel/linux/modules/block.mk @@ -275,7 +275,7 @@ $(eval $(call KernelPackage,dm-raid)) define KernelPackage/iscsi-initiator SUBMENU:=$(BLOCK_MENU) TITLE:=iSCSI Initiator over TCP/IP - DEPENDS:=+kmod-scsi-core + DEPENDS:=+kmod-scsi-core +kmod-crypto-hash KCONFIG:= \ CONFIG_INET \ CONFIG_SCSI_LOWLEVEL \ From f86f9b1dc578b4a0ea9023e0e9bcebee09701ef6 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sat, 11 Jan 2020 20:44:34 -0800 Subject: [PATCH 13/55] target.mk: change i386 CPU type to pentium-mmx f4f8f4a180366ee90fd8e153213db2cb746ca361 broke ffmpeg compilation with x86 The reason is that ffmpeg's x86 assembly requires at least MMX, which the pentium CPU_TYPE was preventing. Fixes ffmpeg compilation on x86_legacy and x86_geode. Ref: https://github.com/openwrt/openwrt/pull/3061 Signed-off-by: Rosen Penev --- include/target.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/target.mk b/include/target.mk index 2557e74967..07749962eb 100644 --- a/include/target.mk +++ b/include/target.mk @@ -179,8 +179,8 @@ ifeq ($(DUMP),1) CPU_CFLAGS_octeonplus = -march=octeon+ -mabi=64 endif ifeq ($(ARCH),i386) - CPU_TYPE ?= pentium - CPU_CFLAGS_pentium = -march=pentium-mmx + CPU_TYPE ?= pentium-mmx + CPU_CFLAGS_pentium-mmx = -march=pentium-mmx CPU_CFLAGS_pentium4 = -march=pentium4 endif ifneq ($(findstring arm,$(ARCH)),) From 044863689f71145d1dfa6d84a2b9734d3caf7846 Mon Sep 17 00:00:00 2001 From: CN_SZTL Date: Thu, 9 Jul 2020 22:13:51 +0800 Subject: [PATCH 14/55] kernel: bump 4.9 to 4.9.230 Signed-off-by: CN_SZTL --- include/kernel-version.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/kernel-version.mk b/include/kernel-version.mk index f5139682c1..bd48585c84 100644 --- a/include/kernel-version.mk +++ b/include/kernel-version.mk @@ -6,11 +6,11 @@ ifdef CONFIG_TESTING_KERNEL KERNEL_PATCHVER:=$(KERNEL_TESTING_PATCHVER) endif -LINUX_VERSION-4.9 = .229 +LINUX_VERSION-4.9 = .230 LINUX_VERSION-4.14 = .187 LINUX_VERSION-4.19 = .131 -LINUX_KERNEL_HASH-4.9.229 = 3256c2835fd95a1a739603e78b02d363eac2ce73a39fa19b13b32da4fc370fdc +LINUX_KERNEL_HASH-4.9.230 = d5ba9256e3ebf1cead127f323acc8124965d89cc88e75eca377fcca3bd6c037c LINUX_KERNEL_HASH-4.14.187 = 5b223475eaeea196aa7e127d3f253bca5c35d8afdc72ca75230ce1ecdd1454bd LINUX_KERNEL_HASH-4.19.131 = 19dfb9f6cc4ba30104b65dcce7d78240a4ae188cb366747d5f8eae35e98964ba From b243788b28941e1e4ea2a9d362938d3b601cd7ba Mon Sep 17 00:00:00 2001 From: Lucian Cristian Date: Wed, 8 Jul 2020 22:49:57 +0300 Subject: [PATCH 15/55] kernel: iscsi-initator: fix kernel config symbols MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable SCSI low-level drivers on targets that don't have it already in order to fix following build failures on few platforms: .config:4739:warning: symbol value 'm' invalid for SCSI_LOWLEVEL * Restart config... * SCSI low-level drivers SCSI low-level drivers (SCSI_LOWLEVEL) [Y/n] (NEW) aborted! Fixes: b88f8202c4ce ("kernel: add iscsi-initator support") Signed-off-by: Lucian Cristian [commit subject and description facelift] Signed-off-by: Petr Štetiar --- package/kernel/linux/modules/block.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package/kernel/linux/modules/block.mk b/package/kernel/linux/modules/block.mk index 83b42eeb66..dea7e46a39 100644 --- a/package/kernel/linux/modules/block.mk +++ b/package/kernel/linux/modules/block.mk @@ -278,9 +278,9 @@ define KernelPackage/iscsi-initiator DEPENDS:=+kmod-scsi-core +kmod-crypto-hash KCONFIG:= \ CONFIG_INET \ - CONFIG_SCSI_LOWLEVEL \ + CONFIG_SCSI_LOWLEVEL=y \ CONFIG_ISCSI_TCP \ - CONFIG_SCSI_ISCSI_ATTRS + CONFIG_SCSI_ISCSI_ATTRS=y FILES:= \ $(LINUX_DIR)/drivers/scsi/iscsi_tcp.ko \ $(LINUX_DIR)/drivers/scsi/libiscsi.ko \ From a4910eb11460d894a1912aa11af8fb81138a8fb5 Mon Sep 17 00:00:00 2001 From: AmadeusGhost <42570690+AmadeusGhost@users.noreply.github.com> Date: Fri, 10 Jul 2020 12:15:12 +0800 Subject: [PATCH 16/55] generic/4.19: add missing config --- target/linux/generic/config-4.19 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/linux/generic/config-4.19 b/target/linux/generic/config-4.19 index e07a2bd722..aec1b2e78f 100644 --- a/target/linux/generic/config-4.19 +++ b/target/linux/generic/config-4.19 @@ -2927,6 +2927,7 @@ CONFIG_MODULE_UNLOAD=y # CONFIG_MPL115_SPI is not set # CONFIG_MPL3115 is not set # CONFIG_MPLS is not set +# CONFIG_MPLS_ROUTING is not set # CONFIG_MPU3050_I2C is not set # CONFIG_MQ_IOSCHED_DEADLINE is not set # CONFIG_MQ_IOSCHED_KYBER is not set @@ -4067,7 +4068,6 @@ CONFIG_RFKILL=y # CONFIG_ROMFS_FS is not set # CONFIG_ROSE is not set # CONFIG_RPCSEC_GSS_KRB5 is not set -# CONFIG_RPI_AXIPERF is not set # CONFIG_RPMSG_QCOM_GLINK_RPM is not set # CONFIG_RPMSG_VIRTIO is not set # CONFIG_RPR0521 is not set From f2139cc98eb29dde14951febc132437f30b0af87 Mon Sep 17 00:00:00 2001 From: CN_SZTL Date: Fri, 10 Jul 2020 12:54:11 +0800 Subject: [PATCH 17/55] kernel: bump 4.14 to 4.14.188 Refreshed all patches. Signed-off-by: CN_SZTL --- include/kernel-version.mk | 4 ++-- .../910-unaligned_access_hacks.patch | 2 +- .../910-unaligned_access_hacks.patch | 2 +- .../202-core-linux-support-layerscape.patch | 18 +++++++++--------- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/include/kernel-version.mk b/include/kernel-version.mk index bd48585c84..471ac7d1b8 100644 --- a/include/kernel-version.mk +++ b/include/kernel-version.mk @@ -7,11 +7,11 @@ ifdef CONFIG_TESTING_KERNEL endif LINUX_VERSION-4.9 = .230 -LINUX_VERSION-4.14 = .187 +LINUX_VERSION-4.14 = .188 LINUX_VERSION-4.19 = .131 LINUX_KERNEL_HASH-4.9.230 = d5ba9256e3ebf1cead127f323acc8124965d89cc88e75eca377fcca3bd6c037c -LINUX_KERNEL_HASH-4.14.187 = 5b223475eaeea196aa7e127d3f253bca5c35d8afdc72ca75230ce1ecdd1454bd +LINUX_KERNEL_HASH-4.14.188 = 766fe01387b1d1b7bd37ca45a5048cdad13e3a51c6c43746dbb657a0ba67064b LINUX_KERNEL_HASH-4.19.131 = 19dfb9f6cc4ba30104b65dcce7d78240a4ae188cb366747d5f8eae35e98964ba remove_uri_prefix=$(subst git://,,$(subst http://,,$(subst https://,,$(1)))) diff --git a/target/linux/ar71xx/patches-4.14/910-unaligned_access_hacks.patch b/target/linux/ar71xx/patches-4.14/910-unaligned_access_hacks.patch index 52f48d0d0e..94eab1138f 100644 --- a/target/linux/ar71xx/patches-4.14/910-unaligned_access_hacks.patch +++ b/target/linux/ar71xx/patches-4.14/910-unaligned_access_hacks.patch @@ -733,7 +733,7 @@ static struct fib6_node *node_alloc(void) --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c -@@ -447,7 +447,7 @@ static void tcp_sack(const struct sk_buf +@@ -453,7 +453,7 @@ static void tcp_sack(const struct sk_buf /* Fast path for timestamp-only option */ if (length == TCPOLEN_TSTAMP_ALIGNED diff --git a/target/linux/ath79/patches-4.14/910-unaligned_access_hacks.patch b/target/linux/ath79/patches-4.14/910-unaligned_access_hacks.patch index 5eaa14d327..9af21ceaad 100644 --- a/target/linux/ath79/patches-4.14/910-unaligned_access_hacks.patch +++ b/target/linux/ath79/patches-4.14/910-unaligned_access_hacks.patch @@ -713,7 +713,7 @@ static struct fib6_node *node_alloc(void) --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c -@@ -447,7 +447,7 @@ static void tcp_sack(const struct sk_buf +@@ -453,7 +453,7 @@ static void tcp_sack(const struct sk_buf /* Fast path for timestamp-only option */ if (length == TCPOLEN_TSTAMP_ALIGNED diff --git a/target/linux/layerscape/patches-4.14/202-core-linux-support-layerscape.patch b/target/linux/layerscape/patches-4.14/202-core-linux-support-layerscape.patch index a774329d52..e0e2029554 100644 --- a/target/linux/layerscape/patches-4.14/202-core-linux-support-layerscape.patch +++ b/target/linux/layerscape/patches-4.14/202-core-linux-support-layerscape.patch @@ -572,7 +572,7 @@ Signed-off-by: Yangbo Lu --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h -@@ -977,6 +977,7 @@ void kfree_skb_list(struct sk_buff *segs +@@ -980,6 +980,7 @@ void kfree_skb_list(struct sk_buff *segs void skb_tx_error(struct sk_buff *skb); void consume_skb(struct sk_buff *skb); void __consume_stateless_skb(struct sk_buff *skb); @@ -580,7 +580,7 @@ Signed-off-by: Yangbo Lu void __kfree_skb(struct sk_buff *skb); #if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) -@@ -3334,6 +3335,7 @@ static inline void skb_free_datagram_loc +@@ -3337,6 +3338,7 @@ static inline void skb_free_datagram_loc } int skb_kill_datagram(struct sock *sk, struct sk_buff *skb, unsigned int flags); int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len); @@ -713,7 +713,7 @@ Signed-off-by: Yangbo Lu } #ifdef CONFIG_NET_INGRESS -@@ -6348,7 +6349,15 @@ static int __netdev_upper_dev_link(struc +@@ -6375,7 +6376,15 @@ static int __netdev_upper_dev_link(struc struct net_device *upper_dev, bool master, void *upper_priv, void *upper_info) { @@ -730,7 +730,7 @@ Signed-off-by: Yangbo Lu int ret = 0; ASSERT_RTNL(); -@@ -6366,12 +6375,7 @@ static int __netdev_upper_dev_link(struc +@@ -6393,12 +6402,7 @@ static int __netdev_upper_dev_link(struc if (master && netdev_master_upper_dev_get(dev)) return -EBUSY; @@ -744,7 +744,7 @@ Signed-off-by: Yangbo Lu &changeupper_info.info); ret = notifier_to_errno(ret); if (ret) -@@ -6383,7 +6387,7 @@ static int __netdev_upper_dev_link(struc +@@ -6410,7 +6414,7 @@ static int __netdev_upper_dev_link(struc return ret; netdev_update_addr_mask(dev); @@ -753,7 +753,7 @@ Signed-off-by: Yangbo Lu &changeupper_info.info); ret = notifier_to_errno(ret); if (ret) -@@ -6447,21 +6451,25 @@ EXPORT_SYMBOL(netdev_master_upper_dev_li +@@ -6474,21 +6478,25 @@ EXPORT_SYMBOL(netdev_master_upper_dev_li void netdev_upper_dev_unlink(struct net_device *dev, struct net_device *upper_dev) { @@ -784,7 +784,7 @@ Signed-off-by: Yangbo Lu &changeupper_info.info); } EXPORT_SYMBOL(netdev_upper_dev_unlink); -@@ -6477,11 +6485,13 @@ EXPORT_SYMBOL(netdev_upper_dev_unlink); +@@ -6504,11 +6512,13 @@ EXPORT_SYMBOL(netdev_upper_dev_unlink); void netdev_bonding_info_change(struct net_device *dev, struct netdev_bonding_info *bonding_info) { @@ -800,7 +800,7 @@ Signed-off-by: Yangbo Lu &info.info); } EXPORT_SYMBOL(netdev_bonding_info_change); -@@ -6607,11 +6617,13 @@ EXPORT_SYMBOL(dev_get_nest_level); +@@ -6634,11 +6644,13 @@ EXPORT_SYMBOL(dev_get_nest_level); void netdev_lower_state_changed(struct net_device *lower_dev, void *lower_state_info) { @@ -816,7 +816,7 @@ Signed-off-by: Yangbo Lu &changelowerstate_info.info); } EXPORT_SYMBOL(netdev_lower_state_changed); -@@ -6902,11 +6914,14 @@ void __dev_notify_flags(struct net_devic +@@ -6929,11 +6941,14 @@ void __dev_notify_flags(struct net_devic if (dev->flags & IFF_UP && (changes & ~(IFF_UP | IFF_PROMISC | IFF_ALLMULTI | IFF_VOLATILE))) { From bcbd0854cd850e2a11fdf86fb959326e0fd8d29a Mon Sep 17 00:00:00 2001 From: CN_SZTL Date: Fri, 10 Jul 2020 18:03:01 +0800 Subject: [PATCH 18/55] kernel: bump 4.19 to 4.19.132 Refreshed all patches. Signed-off-by: CN_SZTL --- include/kernel-version.mk | 4 ++-- .../patches-4.19/910-unaligned_access_hacks.patch | 2 +- ...ld-Ignore-dtco-targets-when-filtering-symbols.patch | 2 +- ...bcmgenet-Better-coalescing-parameter-defaults.patch | 4 ++-- ...plement-xhci_fixup_endpoint-for-interval-adju.patch | 4 ++-- ...t-bcmgenet-Workaround-2-for-Pi4-Ethernet-fail.patch | 2 +- ...-hacky-CPU-port-fixes-for-devices-not-using-p.patch | 2 +- .../generic/hack-4.19/250-netfilter_depends.patch | 2 +- .../hack-4.19/661-use_fq_codel_by_default.patch | 2 +- .../generic/hack-4.19/662-remove_pfifo_fast.patch | 2 +- target/linux/generic/hack-4.19/721-phy_packets.patch | 8 ++++---- .../pending-4.19/601-add-kernel-imq-support.patch | 2 +- .../generic/pending-4.19/655-increase_skb_pad.patch | 2 +- .../680-NET-skip-GRO-for-foreign-MAC-addresses.patch | 10 +++++----- 14 files changed, 24 insertions(+), 24 deletions(-) diff --git a/include/kernel-version.mk b/include/kernel-version.mk index 471ac7d1b8..7473626193 100644 --- a/include/kernel-version.mk +++ b/include/kernel-version.mk @@ -8,11 +8,11 @@ endif LINUX_VERSION-4.9 = .230 LINUX_VERSION-4.14 = .188 -LINUX_VERSION-4.19 = .131 +LINUX_VERSION-4.19 = .132 LINUX_KERNEL_HASH-4.9.230 = d5ba9256e3ebf1cead127f323acc8124965d89cc88e75eca377fcca3bd6c037c LINUX_KERNEL_HASH-4.14.188 = 766fe01387b1d1b7bd37ca45a5048cdad13e3a51c6c43746dbb657a0ba67064b -LINUX_KERNEL_HASH-4.19.131 = 19dfb9f6cc4ba30104b65dcce7d78240a4ae188cb366747d5f8eae35e98964ba +LINUX_KERNEL_HASH-4.19.132 = d7d7ee94962ce4cc070e8db7f7f3755cbef122a3fd77c436d5381a61819a7d44 remove_uri_prefix=$(subst git://,,$(subst http://,,$(subst https://,,$(1)))) sanitize_uri=$(call qstrip,$(subst @,_,$(subst :,_,$(subst .,_,$(subst -,_,$(subst /,_,$(1))))))) diff --git a/target/linux/ath79/patches-4.19/910-unaligned_access_hacks.patch b/target/linux/ath79/patches-4.19/910-unaligned_access_hacks.patch index 61695ae55f..7c066c4f7d 100644 --- a/target/linux/ath79/patches-4.19/910-unaligned_access_hacks.patch +++ b/target/linux/ath79/patches-4.19/910-unaligned_access_hacks.patch @@ -704,7 +704,7 @@ struct fib6_info *fib6_info_alloc(gfp_t gfp_flags) --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c -@@ -423,7 +423,7 @@ static void tcp_sack(const struct sk_buf +@@ -429,7 +429,7 @@ static void tcp_sack(const struct sk_buf /* Fast path for timestamp-only option */ if (length == TCPOLEN_TSTAMP_ALIGNED diff --git a/target/linux/bcm27xx/patches-4.19/950-0021-kbuild-Ignore-dtco-targets-when-filtering-symbols.patch b/target/linux/bcm27xx/patches-4.19/950-0021-kbuild-Ignore-dtco-targets-when-filtering-symbols.patch index 6d5d7d4d8b..d15f3adbc4 100644 --- a/target/linux/bcm27xx/patches-4.19/950-0021-kbuild-Ignore-dtco-targets-when-filtering-symbols.patch +++ b/target/linux/bcm27xx/patches-4.19/950-0021-kbuild-Ignore-dtco-targets-when-filtering-symbols.patch @@ -9,7 +9,7 @@ Subject: [PATCH] kbuild: Ignore dtco targets when filtering symbols --- a/scripts/Kbuild.include +++ b/scripts/Kbuild.include -@@ -287,7 +287,7 @@ ksym_dep_filter = +@@ -288,7 +288,7 @@ ksym_dep_filter = $(CPP) $(call flags_nodeps,c_flags) -D__KSYM_DEPS__ $< ;; \ as_*_S|cpp_s_S) \ $(CPP) $(call flags_nodeps,a_flags) -D__KSYM_DEPS__ $< ;; \ diff --git a/target/linux/bcm27xx/patches-4.19/950-0470-bcmgenet-Better-coalescing-parameter-defaults.patch b/target/linux/bcm27xx/patches-4.19/950-0470-bcmgenet-Better-coalescing-parameter-defaults.patch index 15f8d2a9fa..5d3403c96c 100644 --- a/target/linux/bcm27xx/patches-4.19/950-0470-bcmgenet-Better-coalescing-parameter-defaults.patch +++ b/target/linux/bcm27xx/patches-4.19/950-0470-bcmgenet-Better-coalescing-parameter-defaults.patch @@ -18,7 +18,7 @@ Signed-off-by: Phil Elwell --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c -@@ -2153,7 +2153,7 @@ static void bcmgenet_init_tx_ring(struct +@@ -2151,7 +2151,7 @@ static void bcmgenet_init_tx_ring(struct bcmgenet_tdma_ring_writel(priv, index, 0, TDMA_PROD_INDEX); bcmgenet_tdma_ring_writel(priv, index, 0, TDMA_CONS_INDEX); @@ -27,7 +27,7 @@ Signed-off-by: Phil Elwell /* Disable rate control for now */ bcmgenet_tdma_ring_writel(priv, index, flow_period_val, TDMA_FLOW_PERIOD); -@@ -3584,9 +3584,12 @@ static int bcmgenet_probe(struct platfor +@@ -3582,9 +3582,12 @@ static int bcmgenet_probe(struct platfor netif_set_real_num_rx_queues(priv->dev, priv->hw_params->rx_queues + 1); /* Set default coalescing parameters */ diff --git a/target/linux/bcm27xx/patches-4.19/950-0573-xhci-implement-xhci_fixup_endpoint-for-interval-adju.patch b/target/linux/bcm27xx/patches-4.19/950-0573-xhci-implement-xhci_fixup_endpoint-for-interval-adju.patch index 5df9ceca9c..f098ff8d22 100644 --- a/target/linux/bcm27xx/patches-4.19/950-0573-xhci-implement-xhci_fixup_endpoint-for-interval-adju.patch +++ b/target/linux/bcm27xx/patches-4.19/950-0573-xhci-implement-xhci_fixup_endpoint-for-interval-adju.patch @@ -15,7 +15,7 @@ Signed-off-by: Jonathan Bell --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c -@@ -1427,6 +1427,103 @@ command_cleanup: +@@ -1428,6 +1428,103 @@ command_cleanup: } /* @@ -119,7 +119,7 @@ Signed-off-by: Jonathan Bell * non-error returns are a promise to giveback() the urb later * we drop ownership so next owner (or urb unlink) can get it */ -@@ -5219,6 +5316,7 @@ static const struct hc_driver xhci_hc_dr +@@ -5226,6 +5323,7 @@ static const struct hc_driver xhci_hc_dr .endpoint_reset = xhci_endpoint_reset, .check_bandwidth = xhci_check_bandwidth, .reset_bandwidth = xhci_reset_bandwidth, diff --git a/target/linux/bcm27xx/patches-4.19/950-0658-net-bcmgenet-Workaround-2-for-Pi4-Ethernet-fail.patch b/target/linux/bcm27xx/patches-4.19/950-0658-net-bcmgenet-Workaround-2-for-Pi4-Ethernet-fail.patch index 44061a11ce..2eb92c616b 100644 --- a/target/linux/bcm27xx/patches-4.19/950-0658-net-bcmgenet-Workaround-2-for-Pi4-Ethernet-fail.patch +++ b/target/linux/bcm27xx/patches-4.19/950-0658-net-bcmgenet-Workaround-2-for-Pi4-Ethernet-fail.patch @@ -38,7 +38,7 @@ Signed-off-by: Phil Elwell static inline void bcmgenet_writel(u32 value, void __iomem *offset) { /* MIPS chips strapped for BE will automagically configure the -@@ -1999,6 +2003,11 @@ static void reset_umac(struct bcmgenet_p +@@ -1997,6 +2001,11 @@ static void reset_umac(struct bcmgenet_p bcmgenet_rbuf_ctrl_set(priv, 0); udelay(10); diff --git a/target/linux/bcm53xx/patches-4.19/700-b53-add-hacky-CPU-port-fixes-for-devices-not-using-p.patch b/target/linux/bcm53xx/patches-4.19/700-b53-add-hacky-CPU-port-fixes-for-devices-not-using-p.patch index a5f13afe02..91bb4fae11 100644 --- a/target/linux/bcm53xx/patches-4.19/700-b53-add-hacky-CPU-port-fixes-for-devices-not-using-p.patch +++ b/target/linux/bcm53xx/patches-4.19/700-b53-add-hacky-CPU-port-fixes-for-devices-not-using-p.patch @@ -21,7 +21,7 @@ Signed-off-by: Rafał Miłecki #include "b53_regs.h" #include "b53_priv.h" -@@ -1587,6 +1588,28 @@ static int b53_switch_init(struct b53_de +@@ -1579,6 +1580,28 @@ static int b53_switch_init(struct b53_de return ret; } diff --git a/target/linux/generic/hack-4.19/250-netfilter_depends.patch b/target/linux/generic/hack-4.19/250-netfilter_depends.patch index cb744bd8ab..e18d201c1e 100644 --- a/target/linux/generic/hack-4.19/250-netfilter_depends.patch +++ b/target/linux/generic/hack-4.19/250-netfilter_depends.patch @@ -17,7 +17,7 @@ Signed-off-by: Felix Fietkau depends on NETFILTER_ADVANCED help H.323 is a VoIP signalling protocol from ITU-T. As one of the most -@@ -1077,7 +1076,6 @@ config NETFILTER_XT_TARGET_SECMARK +@@ -1089,7 +1088,6 @@ config NETFILTER_XT_TARGET_SECMARK config NETFILTER_XT_TARGET_TCPMSS tristate '"TCPMSS" target support' diff --git a/target/linux/generic/hack-4.19/661-use_fq_codel_by_default.patch b/target/linux/generic/hack-4.19/661-use_fq_codel_by_default.patch index 1710c13b15..398c143108 100644 --- a/target/linux/generic/hack-4.19/661-use_fq_codel_by_default.patch +++ b/target/linux/generic/hack-4.19/661-use_fq_codel_by_default.patch @@ -83,7 +83,7 @@ Signed-off-by: Felix Fietkau EXPORT_SYMBOL(default_qdisc_ops); /* Main transmission queue. */ -@@ -1026,7 +1026,7 @@ static void attach_one_default_qdisc(str +@@ -1034,7 +1034,7 @@ static void attach_one_default_qdisc(str void *_unused) { struct Qdisc *qdisc; diff --git a/target/linux/generic/hack-4.19/662-remove_pfifo_fast.patch b/target/linux/generic/hack-4.19/662-remove_pfifo_fast.patch index c89329c1cc..4164955edb 100644 --- a/target/linux/generic/hack-4.19/662-remove_pfifo_fast.patch +++ b/target/linux/generic/hack-4.19/662-remove_pfifo_fast.patch @@ -10,7 +10,7 @@ Signed-off-by: Felix Fietkau --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c -@@ -613,207 +613,6 @@ struct Qdisc_ops noqueue_qdisc_ops __rea +@@ -621,207 +621,6 @@ struct Qdisc_ops noqueue_qdisc_ops __rea .owner = THIS_MODULE, }; diff --git a/target/linux/generic/hack-4.19/721-phy_packets.patch b/target/linux/generic/hack-4.19/721-phy_packets.patch index 0a3a0664d3..1f1e5bf43d 100644 --- a/target/linux/generic/hack-4.19/721-phy_packets.patch +++ b/target/linux/generic/hack-4.19/721-phy_packets.patch @@ -56,7 +56,7 @@ Signed-off-by: Felix Fietkau */ --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h -@@ -2547,6 +2547,10 @@ static inline int pskb_trim(struct sk_bu +@@ -2566,6 +2566,10 @@ static inline int pskb_trim(struct sk_bu return (len < skb->len) ? __pskb_trim(skb, len) : 0; } @@ -67,7 +67,7 @@ Signed-off-by: Felix Fietkau /** * pskb_trim_unique - remove end from a paged unique (not cloned) buffer * @skb: buffer to alter -@@ -2678,16 +2682,6 @@ static inline struct sk_buff *dev_alloc_ +@@ -2697,16 +2701,6 @@ static inline struct sk_buff *dev_alloc_ } @@ -101,7 +101,7 @@ Signed-off-by: Felix Fietkau help --- a/net/core/dev.c +++ b/net/core/dev.c -@@ -3251,10 +3251,20 @@ static int xmit_one(struct sk_buff *skb, +@@ -3259,10 +3259,20 @@ static int xmit_one(struct sk_buff *skb, #endif dev_queue_xmit_nit(skb, dev); @@ -136,7 +136,7 @@ Signed-off-by: Felix Fietkau #include #include -@@ -503,6 +504,22 @@ skb_fail: +@@ -555,6 +556,22 @@ skb_fail: } EXPORT_SYMBOL(__napi_alloc_skb); diff --git a/target/linux/generic/pending-4.19/601-add-kernel-imq-support.patch b/target/linux/generic/pending-4.19/601-add-kernel-imq-support.patch index 92ec940669..6ba37b6042 100644 --- a/target/linux/generic/pending-4.19/601-add-kernel-imq-support.patch +++ b/target/linux/generic/pending-4.19/601-add-kernel-imq-support.patch @@ -1132,7 +1132,7 @@ /* Interface address info used in eth_type_trans() */ unsigned char *dev_addr; -@@ -3940,6 +3945,19 @@ static inline void netif_tx_unlock_bh(st +@@ -3959,6 +3964,19 @@ static inline void netif_tx_unlock_bh(st } \ } diff --git a/target/linux/generic/pending-4.19/655-increase_skb_pad.patch b/target/linux/generic/pending-4.19/655-increase_skb_pad.patch index 94e325b0cc..4fce3c3025 100644 --- a/target/linux/generic/pending-4.19/655-increase_skb_pad.patch +++ b/target/linux/generic/pending-4.19/655-increase_skb_pad.patch @@ -9,7 +9,7 @@ Signed-off-by: Felix Fietkau --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h -@@ -2511,7 +2511,7 @@ static inline int pskb_network_may_pull( +@@ -2530,7 +2530,7 @@ static inline int pskb_network_may_pull( * NET_IP_ALIGN(2) + ethernet_header(14) + IP_header(20/40) + ports(8) */ #ifndef NET_SKB_PAD diff --git a/target/linux/generic/pending-4.19/680-NET-skip-GRO-for-foreign-MAC-addresses.patch b/target/linux/generic/pending-4.19/680-NET-skip-GRO-for-foreign-MAC-addresses.patch index 1a5b9c2a14..03e310a695 100644 --- a/target/linux/generic/pending-4.19/680-NET-skip-GRO-for-foreign-MAC-addresses.patch +++ b/target/linux/generic/pending-4.19/680-NET-skip-GRO-for-foreign-MAC-addresses.patch @@ -32,7 +32,7 @@ Signed-off-by: Felix Fietkau __u16 tc_index; /* traffic control index */ --- a/net/core/dev.c +++ b/net/core/dev.c -@@ -5473,6 +5473,9 @@ static enum gro_result dev_gro_receive(s +@@ -5471,6 +5471,9 @@ static enum gro_result dev_gro_receive(s int same_flow; int grow; @@ -42,7 +42,7 @@ Signed-off-by: Felix Fietkau if (netif_elide_gro(skb->dev)) goto normal; -@@ -7131,6 +7134,48 @@ static void __netdev_adjacent_dev_unlink +@@ -7129,6 +7132,48 @@ static void __netdev_adjacent_dev_unlink &upper_dev->adj_list.lower); } @@ -91,7 +91,7 @@ Signed-off-by: Felix Fietkau static int __netdev_upper_dev_link(struct net_device *dev, struct net_device *upper_dev, bool master, void *upper_priv, void *upper_info, -@@ -7181,6 +7226,7 @@ static int __netdev_upper_dev_link(struc +@@ -7179,6 +7224,7 @@ static int __netdev_upper_dev_link(struc if (ret) return ret; @@ -99,7 +99,7 @@ Signed-off-by: Felix Fietkau ret = call_netdevice_notifiers_info(NETDEV_CHANGEUPPER, &changeupper_info.info); ret = notifier_to_errno(ret); -@@ -7273,6 +7319,7 @@ void netdev_upper_dev_unlink(struct net_ +@@ -7271,6 +7317,7 @@ void netdev_upper_dev_unlink(struct net_ __netdev_adjacent_dev_unlink_neighbour(dev, upper_dev); @@ -107,7 +107,7 @@ Signed-off-by: Felix Fietkau call_netdevice_notifiers_info(NETDEV_CHANGEUPPER, &changeupper_info.info); -@@ -7912,6 +7959,7 @@ int dev_set_mac_address(struct net_devic +@@ -7910,6 +7957,7 @@ int dev_set_mac_address(struct net_devic if (err) return err; dev->addr_assign_type = NET_ADDR_SET; From 38919ba8ed2276d0acafdce204f664b18d935971 Mon Sep 17 00:00:00 2001 From: CN_SZTL Date: Fri, 10 Jul 2020 22:57:19 +0800 Subject: [PATCH 19/55] subconverter: bump to 0.6.1 --- package/ctcgfw/subconverter/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package/ctcgfw/subconverter/Makefile b/package/ctcgfw/subconverter/Makefile index 430758b64c..38e62f6a8b 100644 --- a/package/ctcgfw/subconverter/Makefile +++ b/package/ctcgfw/subconverter/Makefile @@ -7,12 +7,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=subconverter -PKG_VERSION:=0.6.0-6629f86 +PKG_VERSION:=0.6.1-081b5ad PKG_RELEASE:=1 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL:=https://github.com/tindy2013/subconverter.git -PKG_SOURCE_VERSION:=6629f861da73fc673e5b9a89e1d9168fd1815d38 +PKG_SOURCE_VERSION:=081b5adb71f9dee918dc0a026d350efe6ff392b5 PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION) PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz From 35a7e12385b952945bf0edfcba511cb26a02008c Mon Sep 17 00:00:00 2001 From: CN_SZTL Date: Fri, 10 Jul 2020 23:22:39 +0800 Subject: [PATCH 20/55] duktape: drop dependency of python --- package/ctcgfw/duktape/Makefile | 5 - .../patches/0001-add-prepared-files.patch | 396802 +++++++++++++++ 2 files changed, 396802 insertions(+), 5 deletions(-) create mode 100644 package/ctcgfw/duktape/patches/0001-add-prepared-files.patch diff --git a/package/ctcgfw/duktape/Makefile b/package/ctcgfw/duktape/Makefile index 4492afa732..5564bd55db 100644 --- a/package/ctcgfw/duktape/Makefile +++ b/package/ctcgfw/duktape/Makefile @@ -22,8 +22,6 @@ PKG_MAINTAINER:=CN_SZTL PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION) -PKG_BUILD_DEPENDS:=python - PKG_FIXUP:=autoreconf PKG_USE_MIPS16:=0 PKG_BUILD_PARALLEL:=1 @@ -58,9 +56,6 @@ endef define Build/Compile ( \ cd $(PKG_BUILD_DIR); \ - mkdir dist; \ - $(STAGING_DIR_HOSTPKG)/bin/pip2 install PyYAML; \ - $(STAGING_DIR_HOSTPKG)/bin/python2 util/dist.py; \ cd dist/source/src; \ $(TARGET_CC) -c -O3 -o duktape.o duktape.c; \ $(TARGET_CC) -c -O3 -o duk_module_node.o -I. ../extras/module-node/duk_module_node.c; \ diff --git a/package/ctcgfw/duktape/patches/0001-add-prepared-files.patch b/package/ctcgfw/duktape/patches/0001-add-prepared-files.patch new file mode 100644 index 0000000000..753a147a29 --- /dev/null +++ b/package/ctcgfw/duktape/patches/0001-add-prepared-files.patch @@ -0,0 +1,396802 @@ +From 33aa594b6882bbe740f1878507b583b9f65e11f1 Mon Sep 17 00:00:00 2001 +From: CN_SZTL +Date: Wed, 8 Jul 2020 03:09:48 +0000 +Subject: [PATCH] add prepared files + +--- + dist/source/AUTHORS.rst | 115 + + dist/source/LICENSE.txt | 25 + + dist/source/Makefile.cmdline | 47 + + dist/source/Makefile.codepage | 6 + + dist/source/Makefile.coffee | 4 + + dist/source/Makefile.dukdebug | 41 + + dist/source/Makefile.eval | 9 + + dist/source/Makefile.eventloop | 23 + + dist/source/Makefile.hello | 32 + + dist/source/Makefile.jsoncbor | 10 + + dist/source/Makefile.jxpretty | 10 + + dist/source/Makefile.sandbox | 9 + + dist/source/Makefile.sharedlibrary | 88 + + dist/source/README.rst | 129 + + dist/source/config/README.rst | 19 + + dist/source/config/architectures.yaml | 71 + + .../architectures/architecture_arm32.h.in | 3 + + .../architectures/architecture_arm64.h.in | 3 + + .../architecture_emscripten.h.in | 5 + + .../architectures/architecture_generic.h.in | 3 + + .../architectures/architecture_m68k.h.in | 5 + + .../architectures/architecture_mips32.h.in | 3 + + .../architectures/architecture_mips64.h.in | 3 + + .../architectures/architecture_powerpc32.h.in | 5 + + .../architectures/architecture_powerpc64.h.in | 3 + + .../architectures/architecture_riscv32.h.in | 3 + + .../architectures/architecture_riscv64.h.in | 3 + + .../architectures/architecture_sparc32.h.in | 3 + + .../architectures/architecture_sparc64.h.in | 3 + + .../architectures/architecture_superh.h.in | 3 + + .../architectures/architecture_x32.h.in | 5 + + .../architectures/architecture_x64.h.in | 5 + + .../architectures/architecture_x86.h.in | 14 + + dist/source/config/compilers.yaml | 35 + + .../source/config/compilers/compiler_bcc.h.in | 24 + + .../config/compilers/compiler_clang.h.in | 76 + + .../config/compilers/compiler_emscripten.h.in | 45 + + .../source/config/compilers/compiler_gcc.h.in | 99 + + .../config/compilers/compiler_generic.h.in | 24 + + .../config/compilers/compiler_msvc.h.in | 84 + + .../config/compilers/compiler_tinyc.h.in | 18 + + .../config/compilers/compiler_vbcc.h.in | 23 + + .../config-options/DUK_USE_32BIT_PTRS.yaml | 8 + + .../config-options/DUK_USE_64BIT_OPS.yaml | 9 + + .../config-options/DUK_USE_ALIGN_4.yaml | 11 + + .../config-options/DUK_USE_ALIGN_8.yaml | 11 + + .../config-options/DUK_USE_ALIGN_BY.yaml | 8 + + .../DUK_USE_ALLOW_UNDEFINED_BEHAVIOR.yaml | 10 + + .../config-options/DUK_USE_ARCH_STRING.yaml | 9 + + .../config-options/DUK_USE_ARRAY_BUILTIN.yaml | 7 + + .../DUK_USE_ARRAY_FASTPATH.yaml | 15 + + .../DUK_USE_ARRAY_PROP_FASTPATH.yaml | 14 + + .../config-options/DUK_USE_ASSERTIONS.yaml | 9 + + .../DUK_USE_ATAN2_WORKAROUNDS.yaml | 8 + + .../DUK_USE_AUGMENT_ERROR_CREATE.yaml | 8 + + .../DUK_USE_AUGMENT_ERROR_THROW.yaml | 8 + + .../DUK_USE_AVOID_PLATFORM_FUNCPTRS.yaml | 13 + + .../DUK_USE_BASE64_FASTPATH.yaml | 10 + + .../DUK_USE_BASE64_SUPPORT.yaml | 7 + + .../DUK_USE_BOOLEAN_BUILTIN.yaml | 7 + + .../config-options/DUK_USE_BRANCH_HINTS.yaml | 9 + + .../config-options/DUK_USE_BROWSER_LIKE.yaml | 8 + + .../DUK_USE_BUFFEROBJECT_SUPPORT.yaml | 15 + + .../config-options/DUK_USE_BUFLEN16.yaml | 8 + + .../DUK_USE_BUILTIN_INITJS.yaml | 12 + + .../DUK_USE_BYTECODE_DUMP_SUPPORT.yaml | 7 + + .../config-options/DUK_USE_BYTEORDER.yaml | 16 + + .../DUK_USE_BYTEORDER_FORCED.yaml | 9 + + .../DUK_USE_CACHE_ACTIVATION.yaml | 9 + + .../config-options/DUK_USE_CACHE_CATCHER.yaml | 9 + + .../DUK_USE_CALLSTACK_LIMIT.yaml | 10 + + .../config-options/DUK_USE_CBOR_BUILTIN.yaml | 9 + + .../DUK_USE_CBOR_DEC_RECLIMIT.yaml | 8 + + .../DUK_USE_CBOR_ENC_RECLIMIT.yaml | 8 + + .../config-options/DUK_USE_CBOR_SUPPORT.yaml | 10 + + .../config-options/DUK_USE_CLANG_PRAGMAS.yaml | 8 + + .../DUK_USE_COMMONJS_MODULES.yaml | 11 + + .../DUK_USE_COMPILER_RECLIMIT.yaml | 8 + + .../DUK_USE_COMPILER_STRING.yaml | 9 + + .../DUK_USE_COMPUTED_INFINITY.yaml | 15 + + .../config-options/DUK_USE_COMPUTED_NAN.yaml | 15 + + .../DUK_USE_COROUTINE_SUPPORT.yaml | 7 + + .../DUK_USE_CPP_EXCEPTIONS.yaml | 9 + + .../config-options/DUK_USE_DATAPTR16.yaml | 21 + + .../config-options/DUK_USE_DATAPTR_DEC16.yaml | 13 + + .../config-options/DUK_USE_DATAPTR_ENC16.yaml | 15 + + .../config-options/DUK_USE_DATE_BUILTIN.yaml | 7 + + .../DUK_USE_DATE_FMT_STRFTIME.yaml | 10 + + .../DUK_USE_DATE_FORMAT_STRING.yaml | 9 + + .../DUK_USE_DATE_GET_LOCAL_TZOFFSET.yaml | 9 + + .../config-options/DUK_USE_DATE_GET_NOW.yaml | 15 + + .../DUK_USE_DATE_NOW_GETTIMEOFDAY.yaml | 9 + + .../config-options/DUK_USE_DATE_NOW_TIME.yaml | 10 + + .../DUK_USE_DATE_NOW_WINDOWS.yaml | 8 + + .../DUK_USE_DATE_NOW_WINDOWS_SUBMS.yaml | 9 + + .../DUK_USE_DATE_PARSE_STRING.yaml | 9 + + .../DUK_USE_DATE_PRS_GETDATE.yaml | 11 + + .../DUK_USE_DATE_PRS_STRPTIME.yaml | 9 + + .../DUK_USE_DATE_TZO_GMTIME.yaml | 11 + + .../DUK_USE_DATE_TZO_GMTIME_R.yaml | 9 + + .../DUK_USE_DATE_TZO_GMTIME_S.yaml | 8 + + .../DUK_USE_DATE_TZO_WINDOWS.yaml | 8 + + .../DUK_USE_DATE_TZO_WINDOWS_NO_DST.yaml | 9 + + .../config-options/DUK_USE_DDDPRINT.yaml | 9 + + .../config-options/DUK_USE_DDPRINT.yaml | 8 + + .../config/config-options/DUK_USE_DEBUG.yaml | 9 + + .../DUK_USE_DEBUGGER_DUMPHEAP.yaml | 12 + + .../DUK_USE_DEBUGGER_FWD_LOGGING.yaml | 14 + + .../DUK_USE_DEBUGGER_FWD_PRINTALERT.yaml | 12 + + .../DUK_USE_DEBUGGER_INSPECT.yaml | 13 + + .../DUK_USE_DEBUGGER_PAUSE_UNCAUGHT.yaml | 17 + + .../DUK_USE_DEBUGGER_SUPPORT.yaml | 13 + + .../DUK_USE_DEBUGGER_THROW_NOTIFY.yaml | 14 + + .../DUK_USE_DEBUGGER_TRANSPORT_TORTURE.yaml | 14 + + .../config-options/DUK_USE_DEBUG_BUFSIZE.yaml | 12 + + .../config-options/DUK_USE_DEBUG_LEVEL.yaml | 8 + + .../config-options/DUK_USE_DEBUG_WRITE.yaml | 16 + + .../config-options/DUK_USE_DEEP_C_STACK.yaml | 12 + + .../config-options/DUK_USE_DOUBLE_BE.yaml | 11 + + .../config-options/DUK_USE_DOUBLE_LE.yaml | 11 + + .../DUK_USE_DOUBLE_LINKED_HEAP.yaml | 11 + + .../config-options/DUK_USE_DOUBLE_ME.yaml | 13 + + .../config/config-options/DUK_USE_DPRINT.yaml | 10 + + .../config-options/DUK_USE_DPRINT_COLORS.yaml | 10 + + .../config-options/DUK_USE_DPRINT_RDTSC.yaml | 8 + + .../DUK_USE_DUKTAPE_BUILTIN.yaml | 7 + + .../DUK_USE_ENCODING_BUILTINS.yaml | 13 + + .../config-options/DUK_USE_ERRCREATE.yaml | 8 + + .../config-options/DUK_USE_ERRTHROW.yaml | 8 + + .../config/config-options/DUK_USE_ES6.yaml | 7 + + .../DUK_USE_ES6_OBJECT_PROTO_PROPERTY.yaml | 7 + + .../DUK_USE_ES6_OBJECT_SETPROTOTYPEOF.yaml | 7 + + .../config-options/DUK_USE_ES6_PROXY.yaml | 7 + + .../DUK_USE_ES6_REGEXP_BRACES.yaml | 8 + + .../DUK_USE_ES6_REGEXP_SYNTAX.yaml | 14 + + .../DUK_USE_ES6_UNICODE_ESCAPE.yaml | 8 + + .../config/config-options/DUK_USE_ES7.yaml | 7 + + .../DUK_USE_ES7_EXP_OPERATOR.yaml | 9 + + .../config/config-options/DUK_USE_ES8.yaml | 7 + + .../config/config-options/DUK_USE_ES9.yaml | 7 + + .../config-options/DUK_USE_ESBC_LIMITS.yaml | 7 + + .../DUK_USE_ESBC_MAX_BYTES.yaml | 9 + + .../DUK_USE_ESBC_MAX_LINENUMBER.yaml | 9 + + .../config-options/DUK_USE_EXAMPLE.yaml | 74 + + .../DUK_USE_EXEC_FUN_LOCAL.yaml | 14 + + .../DUK_USE_EXEC_INDIRECT_BOUND_CHECK.yaml | 11 + + .../DUK_USE_EXEC_PREFER_SIZE.yaml | 7 + + .../DUK_USE_EXEC_REGCONST_OPTIMIZE.yaml | 11 + + .../DUK_USE_EXEC_TIMEOUT_CHECK.yaml | 31 + + .../DUK_USE_EXPLICIT_NULL_INIT.yaml | 12 + + .../config-options/DUK_USE_EXTSTR_FREE.yaml | 23 + + .../DUK_USE_EXTSTR_INTERN_CHECK.yaml | 39 + + .../config-options/DUK_USE_FASTINT.yaml | 14 + + .../DUK_USE_FAST_REFCOUNT_DEFAULT.yaml | 10 + + .../config-options/DUK_USE_FATAL_HANDLER.yaml | 23 + + .../config-options/DUK_USE_FATAL_MAXLEN.yaml | 8 + + .../config-options/DUK_USE_FILE_IO.yaml | 10 + + .../DUK_USE_FINALIZER_SUPPORT.yaml | 7 + + .../DUK_USE_FINALIZER_TORTURE.yaml | 11 + + .../config-options/DUK_USE_FLEX_C99.yaml | 9 + + .../config-options/DUK_USE_FLEX_ONESIZE.yaml | 11 + + .../config-options/DUK_USE_FLEX_ZEROSIZE.yaml | 11 + + .../config-options/DUK_USE_FULL_TVAL.yaml | 16 + + .../config-options/DUK_USE_FUNCPTR16.yaml | 21 + + .../config-options/DUK_USE_FUNCPTR_DEC16.yaml | 15 + + .../config-options/DUK_USE_FUNCPTR_ENC16.yaml | 13 + + .../DUK_USE_FUNCTION_BUILTIN.yaml | 7 + + .../DUK_USE_FUNC_FILENAME_PROPERTY.yaml | 9 + + .../DUK_USE_FUNC_NAME_PROPERTY.yaml | 10 + + .../config-options/DUK_USE_FUZZILLI.yaml | 8 + + .../config-options/DUK_USE_GCC_PRAGMAS.yaml | 8 + + .../config-options/DUK_USE_GC_TORTURE.yaml | 14 + + .../DUK_USE_GET_MONOTONIC_TIME.yaml | 22 + + ..._USE_GET_MONOTONIC_TIME_CLOCK_GETTIME.yaml | 8 + + ...UK_USE_GET_MONOTONIC_TIME_WINDOWS_QPC.yaml | 7 + + .../DUK_USE_GET_RANDOM_DOUBLE.yaml | 16 + + .../DUK_USE_GLOBAL_BINDING.yaml | 8 + + .../DUK_USE_GLOBAL_BUILTIN.yaml | 9 + + ...UK_USE_HASHBYTES_UNALIGNED_U32_ACCESS.yaml | 8 + + .../config-options/DUK_USE_HEAPPTR16.yaml | 35 + + .../config-options/DUK_USE_HEAPPTR_DEC16.yaml | 13 + + .../config-options/DUK_USE_HEAPPTR_ENC16.yaml | 13 + + .../config-options/DUK_USE_HEX_FASTPATH.yaml | 10 + + .../config-options/DUK_USE_HEX_SUPPORT.yaml | 7 + + .../DUK_USE_HOBJECT_ARRAY_ABANDON_LIMIT.yaml | 15 + + ...DUK_USE_HOBJECT_ARRAY_ABANDON_MINSIZE.yaml | 11 + + ...K_USE_HOBJECT_ARRAY_FAST_RESIZE_LIMIT.yaml | 13 + + .../DUK_USE_HOBJECT_ARRAY_MINGROW_ADD.yaml | 8 + + ...DUK_USE_HOBJECT_ARRAY_MINGROW_DIVISOR.yaml | 8 + + .../DUK_USE_HOBJECT_ENTRY_MINGROW_ADD.yaml | 8 + + ...DUK_USE_HOBJECT_ENTRY_MINGROW_DIVISOR.yaml | 8 + + .../DUK_USE_HOBJECT_HASH_PART.yaml | 11 + + .../DUK_USE_HOBJECT_HASH_PROP_LIMIT.yaml | 20 + + .../DUK_USE_HOBJECT_LAYOUT_1.yaml | 10 + + .../DUK_USE_HOBJECT_LAYOUT_2.yaml | 8 + + .../DUK_USE_HOBJECT_LAYOUT_3.yaml | 9 + + .../DUK_USE_HSTRING_ARRIDX.yaml | 12 + + .../config-options/DUK_USE_HSTRING_CLEN.yaml | 13 + + .../DUK_USE_HSTRING_EXTDATA.yaml | 12 + + .../DUK_USE_HSTRING_LAZY_CLEN.yaml | 11 + + .../config-options/DUK_USE_HTML_COMMENTS.yaml | 7 + + .../DUK_USE_IDCHAR_FASTPATH.yaml | 10 + + .../DUK_USE_INJECT_HEAP_ALLOC_ERROR.yaml | 7 + + .../config-options/DUK_USE_INTEGER_BE.yaml | 11 + + .../config-options/DUK_USE_INTEGER_LE.yaml | 11 + + .../config-options/DUK_USE_INTEGER_ME.yaml | 14 + + .../DUK_USE_INTERRUPT_COUNTER.yaml | 13 + + .../DUK_USE_INTERRUPT_DEBUG_FIXUP.yaml | 10 + + .../config/config-options/DUK_USE_JC.yaml | 7 + + .../config-options/DUK_USE_JSON_BUILTIN.yaml | 7 + + .../DUK_USE_JSON_DECNUMBER_FASTPATH.yaml | 10 + + .../DUK_USE_JSON_DECSTRING_FASTPATH.yaml | 10 + + .../DUK_USE_JSON_DEC_RECLIMIT.yaml | 8 + + .../DUK_USE_JSON_EATWHITE_FASTPATH.yaml | 10 + + .../DUK_USE_JSON_ENC_RECLIMIT.yaml | 11 + + .../DUK_USE_JSON_QUOTESTRING_FASTPATH.yaml | 10 + + .../DUK_USE_JSON_STRINGIFY_FASTPATH.yaml | 15 + + .../config-options/DUK_USE_JSON_SUPPORT.yaml | 10 + + .../config/config-options/DUK_USE_JX.yaml | 7 + + .../DUK_USE_LEXER_SLIDING_WINDOW.yaml | 9 + + .../DUK_USE_LIGHTFUNC_BUILTINS.yaml | 10 + + .../config-options/DUK_USE_LITCACHE_SIZE.yaml | 27 + + ...UK_USE_MARKANDSWEEP_FINALIZER_TORTURE.yaml | 17 + + .../DUK_USE_MARK_AND_SWEEP.yaml | 19 + + .../DUK_USE_MARK_AND_SWEEP_RECLIMIT.yaml | 10 + + .../config-options/DUK_USE_MATH_BUILTIN.yaml | 7 + + .../config-options/DUK_USE_MATH_FMAX.yaml | 14 + + .../config-options/DUK_USE_MATH_FMIN.yaml | 14 + + .../config-options/DUK_USE_MATH_ROUND.yaml | 15 + + .../DUK_USE_MS_STRINGTABLE_RESIZE.yaml | 13 + + .../DUK_USE_NATIVE_CALL_RECLIMIT.yaml | 12 + + .../DUK_USE_NATIVE_STACK_CHECK.yaml | 22 + + .../DUK_USE_NONSTD_ARRAY_CONCAT_TRAILER.yaml | 11 + + .../DUK_USE_NONSTD_ARRAY_MAP_TRAILER.yaml | 13 + + .../DUK_USE_NONSTD_ARRAY_SPLICE_DELCOUNT.yaml | 15 + + .../DUK_USE_NONSTD_FUNC_CALLER_PROPERTY.yaml | 16 + + .../DUK_USE_NONSTD_FUNC_SOURCE_PROPERTY.yaml | 12 + + .../DUK_USE_NONSTD_FUNC_STMT.yaml | 13 + + .../DUK_USE_NONSTD_GETTER_KEY_ARGUMENT.yaml | 11 + + .../DUK_USE_NONSTD_JSON_ESC_U2028_U2029.yaml | 13 + + .../DUK_USE_NONSTD_REGEXP_DOLLAR_ESCAPE.yaml | 9 + + .../DUK_USE_NONSTD_SETTER_KEY_ARGUMENT.yaml | 11 + + ..._USE_NONSTD_STRING_FROMCHARCODE_32BIT.yaml | 12 + + .../DUK_USE_NO_DOUBLE_ALIASING_SELFTEST.yaml | 12 + + .../DUK_USE_NUMBER_BUILTIN.yaml | 7 + + .../DUK_USE_OBJECT_BUILTIN.yaml | 7 + + .../config-options/DUK_USE_OBJSIZES16.yaml | 12 + + .../config-options/DUK_USE_OCTAL_SUPPORT.yaml | 10 + + .../config-options/DUK_USE_OS_STRING.yaml | 9 + + .../config-options/DUK_USE_PACKED_TVAL.yaml | 11 + + .../DUK_USE_PACKED_TVAL_POSSIBLE.yaml | 8 + + .../DUK_USE_PACK_CLANG_ATTR.yaml | 7 + + .../DUK_USE_PACK_DUMMY_MEMBER.yaml | 7 + + .../config-options/DUK_USE_PACK_GCC_ATTR.yaml | 7 + + .../DUK_USE_PACK_MSVC_PRAGMA.yaml | 7 + + .../config-options/DUK_USE_PANIC_ABORT.yaml | 8 + + .../config-options/DUK_USE_PANIC_EXIT.yaml | 8 + + .../config-options/DUK_USE_PANIC_HANDLER.yaml | 11 + + .../DUK_USE_PANIC_SEGFAULT.yaml | 10 + + .../DUK_USE_PARANOID_DATE_COMPUTATION.yaml | 15 + + .../DUK_USE_PARANOID_ERRORS.yaml | 14 + + .../config-options/DUK_USE_PARANOID_MATH.yaml | 8 + + .../config-options/DUK_USE_PC2LINE.yaml | 11 + + .../DUK_USE_PERFORMANCE_BUILTIN.yaml | 7 + + .../DUK_USE_POW_NETBSD_WORKAROUND.yaml | 11 + + .../DUK_USE_POW_WORKAROUNDS.yaml | 9 + + .../config-options/DUK_USE_PREFER_SIZE.yaml | 9 + + .../DUK_USE_PROMISE_BUILTIN.yaml | 9 + + ...K_USE_PROVIDE_DEFAULT_ALLOC_FUNCTIONS.yaml | 9 + + .../config/config-options/DUK_USE_RDTSC.yaml | 8 + + .../config-options/DUK_USE_REFCOUNT16.yaml | 9 + + .../config-options/DUK_USE_REFCOUNT32.yaml | 12 + + .../DUK_USE_REFERENCE_COUNTING.yaml | 7 + + .../DUK_USE_REFLECT_BUILTIN.yaml | 8 + + .../DUK_USE_REFZERO_FINALIZER_TORTURE.yaml | 15 + + .../DUK_USE_REGEXP_CANON_BITMAP.yaml | 11 + + .../DUK_USE_REGEXP_CANON_WORKAROUND.yaml | 13 + + .../DUK_USE_REGEXP_COMPILER_RECLIMIT.yaml | 8 + + .../DUK_USE_REGEXP_EXECUTOR_RECLIMIT.yaml | 8 + + .../DUK_USE_REGEXP_SUPPORT.yaml | 15 + + .../DUK_USE_REPL_FPCLASSIFY.yaml | 12 + + .../config-options/DUK_USE_REPL_ISFINITE.yaml | 12 + + .../config-options/DUK_USE_REPL_ISINF.yaml | 12 + + .../config-options/DUK_USE_REPL_ISNAN.yaml | 12 + + .../config-options/DUK_USE_REPL_SIGNBIT.yaml | 12 + + .../DUK_USE_ROM_GLOBAL_CLONE.yaml | 18 + + .../DUK_USE_ROM_GLOBAL_INHERIT.yaml | 23 + + .../config-options/DUK_USE_ROM_OBJECTS.yaml | 22 + + .../DUK_USE_ROM_PTRCOMP_FIRST.yaml | 16 + + .../config-options/DUK_USE_ROM_STRINGS.yaml | 13 + + .../config-options/DUK_USE_SECTION_B.yaml | 11 + + .../config-options/DUK_USE_SELF_TESTS.yaml | 9 + + .../config/config-options/DUK_USE_SETJMP.yaml | 17 + + .../DUK_USE_SHEBANG_COMMENTS.yaml | 8 + + .../DUK_USE_SHUFFLE_TORTURE.yaml | 12 + + .../config-options/DUK_USE_SIGSETJMP.yaml | 12 + + .../config-options/DUK_USE_SOURCE_NONBMP.yaml | 11 + + .../config-options/DUK_USE_STRHASH16.yaml | 8 + + .../config-options/DUK_USE_STRHASH_DENSE.yaml | 10 + + .../DUK_USE_STRHASH_SKIP_SHIFT.yaml | 12 + + .../config-options/DUK_USE_STRICT_DECL.yaml | 13 + + .../DUK_USE_STRICT_UTF8_SOURCE.yaml | 11 + + .../DUK_USE_STRING_BUILTIN.yaml | 7 + + .../config-options/DUK_USE_STRLEN16.yaml | 8 + + .../config-options/DUK_USE_STRTAB_CHAIN.yaml | 27 + + .../DUK_USE_STRTAB_CHAIN_SIZE.yaml | 10 + + .../DUK_USE_STRTAB_GROW_LIMIT.yaml | 10 + + .../DUK_USE_STRTAB_MAXSIZE.yaml | 13 + + .../DUK_USE_STRTAB_MINSIZE.yaml | 9 + + .../config-options/DUK_USE_STRTAB_PROBE.yaml | 8 + + .../DUK_USE_STRTAB_PTRCOMP.yaml | 14 + + .../DUK_USE_STRTAB_RESIZE_CHECK_MASK.yaml | 16 + + .../DUK_USE_STRTAB_SHRINK_LIMIT.yaml | 10 + + .../DUK_USE_STRTAB_TORTURE.yaml | 9 + + .../DUK_USE_SYMBOL_BUILTIN.yaml | 10 + + .../config-options/DUK_USE_TAILCALL.yaml | 13 + + .../config-options/DUK_USE_TARGET_INFO.yaml | 10 + + .../config-options/DUK_USE_TRACEBACKS.yaml | 12 + + .../DUK_USE_TRACEBACK_DEPTH.yaml | 13 + + .../DUK_USE_UNALIGNED_ACCESSES_POSSIBLE.yaml | 9 + + .../DUK_USE_UNDERSCORE_SETJMP.yaml | 13 + + .../DUK_USE_UNION_INITIALIZERS.yaml | 11 + + .../config-options/DUK_USE_USER_DECLARE.yaml | 17 + + .../config-options/DUK_USE_USER_INITJS.yaml | 13 + + .../DUK_USE_VALSTACK_GROW_SHIFT.yaml | 13 + + .../DUK_USE_VALSTACK_LIMIT.yaml | 11 + + .../DUK_USE_VALSTACK_SHRINK_CHECK_SHIFT.yaml | 13 + + .../DUK_USE_VALSTACK_SHRINK_SLACK_SHIFT.yaml | 10 + + .../DUK_USE_VALSTACK_UNSAFE.yaml | 11 + + .../DUK_USE_VARIADIC_MACROS.yaml | 11 + + .../DUK_USE_VERBOSE_ERRORS.yaml | 11 + + .../DUK_USE_VERBOSE_EXECUTOR_ERRORS.yaml | 10 + + .../config-options/DUK_USE_VOLUNTARY_GC.yaml | 16 + + .../DUK_USE_ZERO_BUFFER_DATA.yaml | 11 + + dist/source/config/examples/compliance.yaml | 18 + + .../config/examples/debugger_support.yaml | 24 + + .../examples/disable_bufferobjects.yaml | 3 + + dist/source/config/examples/disable_es6.yaml | 5 + + .../config/examples/enable_debug_print0.yaml | 3 + + .../config/examples/enable_debug_print1.yaml | 3 + + .../config/examples/enable_debug_print2.yaml | 3 + + .../config/examples/enable_fastint.yaml | 3 + + dist/source/config/examples/low_memory.yaml | 158 + + .../config/examples/low_memory_strip.yaml | 49 + + .../examples/performance_sensitive.yaml | 38 + + dist/source/config/examples/rom_builtins.yaml | 19 + + .../config/examples/security_sensitive.yaml | 17 + + .../config/examples/shallow_c_stack.yaml | 10 + + .../config/examples/timing_sensitive.yaml | 6 + + .../feature-options/DUK_OPT_ASSERTIONS.yaml | 8 + + .../DUK_OPT_BUFFEROBJECT_SUPPORT.yaml | 8 + + .../feature-options/DUK_OPT_BUFLEN16.yaml | 7 + + .../feature-options/DUK_OPT_DATAPTR16.yaml | 20 + + .../DUK_OPT_DATAPTR_DEC16.yaml | 12 + + .../DUK_OPT_DATAPTR_ENC16.yaml | 14 + + .../feature-options/DUK_OPT_DDDPRINT.yaml | 9 + + .../feature-options/DUK_OPT_DDPRINT.yaml | 8 + + .../config/feature-options/DUK_OPT_DEBUG.yaml | 7 + + .../DUK_OPT_DEBUGGER_DUMPHEAP.yaml | 10 + + .../DUK_OPT_DEBUGGER_FWD_LOGGING.yaml | 11 + + .../DUK_OPT_DEBUGGER_FWD_PRINTALERT.yaml | 9 + + .../DUK_OPT_DEBUGGER_SUPPORT.yaml | 12 + + .../DUK_OPT_DEBUGGER_TRANSPORT_TORTURE.yaml | 11 + + .../DUK_OPT_DEBUG_BUFSIZE.yaml | 11 + + .../feature-options/DUK_OPT_DECLARE.yaml | 10 + + .../feature-options/DUK_OPT_DEEP_C_STACK.yaml | 15 + + .../feature-options/DUK_OPT_DLL_BUILD.yaml | 12 + + .../feature-options/DUK_OPT_DPRINT.yaml | 8 + + .../DUK_OPT_DPRINT_COLORS.yaml | 8 + + .../feature-options/DUK_OPT_DPRINT_RDTSC.yaml | 7 + + .../feature-options/DUK_OPT_EXAMPLE.yaml | 52 + + .../DUK_OPT_EXEC_TIMEOUT_CHECK.yaml | 30 + + .../DUK_OPT_EXTERNAL_STRINGS.yaml | 11 + + .../feature-options/DUK_OPT_EXTSTR_FREE.yaml | 22 + + .../DUK_OPT_EXTSTR_INTERN_CHECK.yaml | 38 + + .../feature-options/DUK_OPT_FASTINT.yaml | 10 + + .../feature-options/DUK_OPT_FORCE_ALIGN.yaml | 8 + + .../DUK_OPT_FORCE_BYTEORDER.yaml | 10 + + .../feature-options/DUK_OPT_FUNCPTR16.yaml | 21 + + .../DUK_OPT_FUNCPTR_DEC16.yaml | 12 + + .../DUK_OPT_FUNCPTR_ENC16.yaml | 12 + + .../DUK_OPT_FUNC_NONSTD_CALLER_PROPERTY.yaml | 9 + + .../DUK_OPT_FUNC_NONSTD_SOURCE_PROPERTY.yaml | 9 + + .../feature-options/DUK_OPT_GC_TORTURE.yaml | 9 + + .../DUK_OPT_HAVE_CUSTOM_H.yaml | 26 + + .../feature-options/DUK_OPT_HEAPPTR16.yaml | 34 + + .../DUK_OPT_HEAPPTR_DEC16.yaml | 12 + + .../DUK_OPT_HEAPPTR_ENC16.yaml | 12 + + .../DUK_OPT_INTERRUPT_COUNTER.yaml | 12 + + .../DUK_OPT_JSON_STRINGIFY_FASTPATH.yaml | 9 + + .../DUK_OPT_LIGHTFUNC_BUILTINS.yaml | 9 + + .../DUK_OPT_NONSTD_FUNC_CALLER_PROPERTY.yaml | 14 + + .../DUK_OPT_NONSTD_FUNC_SOURCE_PROPERTY.yaml | 10 + + ...K_OPT_NO_ARRAY_SPLICE_NONSTD_DELCOUNT.yaml | 9 + + .../DUK_OPT_NO_AUGMENT_ERRORS.yaml | 10 + + .../DUK_OPT_NO_BROWSER_LIKE.yaml | 9 + + .../DUK_OPT_NO_BUFFEROBJECT_SUPPORT.yaml | 8 + + .../DUK_OPT_NO_BYTECODE_DUMP_SUPPORT.yaml | 7 + + .../DUK_OPT_NO_COMMONJS_MODULES.yaml | 6 + + .../DUK_OPT_NO_ES6_OBJECT_PROTO_PROPERTY.yaml | 8 + + .../DUK_OPT_NO_ES6_OBJECT_SETPROTOTYPEOF.yaml | 8 + + .../feature-options/DUK_OPT_NO_ES6_PROXY.yaml | 8 + + .../feature-options/DUK_OPT_NO_FILE_IO.yaml | 10 + + .../feature-options/DUK_OPT_NO_FUNC_STMT.yaml | 9 + + .../config/feature-options/DUK_OPT_NO_JC.yaml | 7 + + .../feature-options/DUK_OPT_NO_JSONC.yaml | 9 + + .../feature-options/DUK_OPT_NO_JSONX.yaml | 9 + + .../config/feature-options/DUK_OPT_NO_JX.yaml | 7 + + .../DUK_OPT_NO_MARK_AND_SWEEP.yaml | 14 + + .../DUK_OPT_NO_MS_STRINGTABLE_RESIZE.yaml | 9 + + ...K_OPT_NO_NONSTD_ACCESSOR_KEY_ARGUMENT.yaml | 8 + + ...UK_OPT_NO_NONSTD_ARRAY_CONCAT_TRAILER.yaml | 12 + + .../DUK_OPT_NO_NONSTD_ARRAY_MAP_TRAILER.yaml | 12 + + ...K_OPT_NO_NONSTD_ARRAY_SPLICE_DELCOUNT.yaml | 13 + + .../DUK_OPT_NO_NONSTD_FUNC_STMT.yaml | 10 + + ...UK_OPT_NO_NONSTD_JSON_ESC_U2028_U2029.yaml | 10 + + ...T_NO_NONSTD_STRING_FROMCHARCODE_32BIT.yaml | 9 + + .../DUK_OPT_NO_OBJECT_ES6_PROTO_PROPERTY.yaml | 10 + + .../DUK_OPT_NO_OBJECT_ES6_SETPROTOTYPEOF.yaml | 10 + + .../DUK_OPT_NO_OCTAL_SUPPORT.yaml | 7 + + .../DUK_OPT_NO_PACKED_TVAL.yaml | 9 + + .../feature-options/DUK_OPT_NO_PC2LINE.yaml | 9 + + .../DUK_OPT_NO_REFERENCE_COUNTING.yaml | 9 + + .../DUK_OPT_NO_REGEXP_SUPPORT.yaml | 11 + + .../feature-options/DUK_OPT_NO_SECTION_B.yaml | 9 + + .../DUK_OPT_NO_SOURCE_NONBMP.yaml | 7 + + .../DUK_OPT_NO_STRICT_DECL.yaml | 8 + + .../DUK_OPT_NO_TRACEBACKS.yaml | 8 + + .../DUK_OPT_NO_VERBOSE_ERRORS.yaml | 8 + + .../DUK_OPT_NO_VOLUNTARY_GC.yaml | 13 + + .../DUK_OPT_NO_ZERO_BUFFER_DATA.yaml | 8 + + .../feature-options/DUK_OPT_OBJSIZES16.yaml | 11 + + .../DUK_OPT_PANIC_HANDLER.yaml | 6 + + .../feature-options/DUK_OPT_REFCOUNT16.yaml | 7 + + .../DUK_OPT_SEGFAULT_ON_PANIC.yaml | 8 + + .../feature-options/DUK_OPT_SELF_TESTS.yaml | 8 + + .../feature-options/DUK_OPT_SETJMP.yaml | 18 + + .../DUK_OPT_SHUFFLE_TORTURE.yaml | 10 + + .../feature-options/DUK_OPT_SIGSETJMP.yaml | 9 + + .../feature-options/DUK_OPT_STRHASH16.yaml | 7 + + .../DUK_OPT_STRICT_UTF8_SOURCE.yaml | 9 + + .../feature-options/DUK_OPT_STRLEN16.yaml | 7 + + .../feature-options/DUK_OPT_STRTAB_CHAIN.yaml | 25 + + .../DUK_OPT_STRTAB_CHAIN_SIZE.yaml | 8 + + .../feature-options/DUK_OPT_TARGET_INFO.yaml | 8 + + .../DUK_OPT_TRACEBACK_DEPTH.yaml | 6 + + .../DUK_OPT_UNDERSCORE_SETJMP.yaml | 10 + + .../feature-options/DUK_OPT_USER_INITJS.yaml | 11 + + .../config/header-snippets/64bitops.h.in | 11 + + .../header-snippets/alignment_fillin.h.in | 26 + + .../header-snippets/architecture_fillins.h.in | 0 + .../header-snippets/byteorder_derived.h.in | 21 + + .../header-snippets/byteorder_fillin.h.in | 109 + + .../header-snippets/compiler_fillins.h.in | 205 + + .../header-snippets/cpp_exception_sanity.h.in | 3 + + .../config/header-snippets/date_provider.h.in | 66 + + .../header-snippets/gcc_clang_visibility.h.in | 26 + + .../header-snippets/inline_workaround.h.in | 11 + + .../header-snippets/msvc_visibility.h.in | 19 + + .../config/header-snippets/object_layout.h.in | 21 + + .../header-snippets/packed_tval_fillin.h.in | 40 + + .../platform_conditionalincludes.h.in | 4 + + .../header-snippets/platform_cppextras.h.in | 9 + + .../header-snippets/platform_fillins.h.in | 327 + + .../platform_sharedincludes.h.in | 24 + + .../header-snippets/reject_fast_math.h.in | 6 + + .../source/config/header-snippets/types1.h.in | 78 + + .../source/config/header-snippets/types2.h.in | 132 + + .../config/header-snippets/types_c99.h.in | 95 + + .../config/header-snippets/types_legacy.h.in | 242 + + .../config/helper-snippets/DUK_F_AIX.h.in | 5 + + .../config/helper-snippets/DUK_F_AMIGAOS.h.in | 6 + + .../config/helper-snippets/DUK_F_ANDROID.h.in | 3 + + .../config/helper-snippets/DUK_F_APPLE.h.in | 4 + + .../config/helper-snippets/DUK_F_ARM.h.in | 9 + + .../config/helper-snippets/DUK_F_BCC.h.in | 4 + + .../config/helper-snippets/DUK_F_BSD.h.in | 5 + + .../config/helper-snippets/DUK_F_C99.h.in | 5 + + .../config/helper-snippets/DUK_F_CLANG.h.in | 4 + + .../config/helper-snippets/DUK_F_CPP.h.in | 5 + + .../config/helper-snippets/DUK_F_CPP11.h.in | 5 + + .../config/helper-snippets/DUK_F_CYGWIN.h.in | 4 + + .../config/helper-snippets/DUK_F_DURANGO.h.in | 4 + + .../helper-snippets/DUK_F_EMSCRIPTEN.h.in | 4 + + .../helper-snippets/DUK_F_FLASHPLAYER.h.in | 4 + + .../config/helper-snippets/DUK_F_FREEBSD.h.in | 4 + + .../config/helper-snippets/DUK_F_GCC.h.in | 10 + + .../config/helper-snippets/DUK_F_HPUX.h.in | 7 + + .../config/helper-snippets/DUK_F_LINUX.h.in | 4 + + .../config/helper-snippets/DUK_F_M68K.h.in | 6 + + .../config/helper-snippets/DUK_F_MINGW.h.in | 4 + + .../config/helper-snippets/DUK_F_MINT.h.in | 4 + + .../config/helper-snippets/DUK_F_MIPS.h.in | 14 + + .../config/helper-snippets/DUK_F_MSVC.h.in | 15 + + .../config/helper-snippets/DUK_F_NETBSD.h.in | 4 + + .../helper-snippets/DUK_F_NO_STDINT_H.h.in | 10 + + .../config/helper-snippets/DUK_F_OPENBSD.h.in | 4 + + .../config/helper-snippets/DUK_F_ORBIS.h.in | 4 + + .../config/helper-snippets/DUK_F_POSIX.h.in | 4 + + .../config/helper-snippets/DUK_F_PPC.h.in | 9 + + .../config/helper-snippets/DUK_F_QNX.h.in | 4 + + .../config/helper-snippets/DUK_F_RISCV.h.in | 15 + + .../config/helper-snippets/DUK_F_SPARC.h.in | 9 + + .../config/helper-snippets/DUK_F_SUN.h.in | 12 + + .../config/helper-snippets/DUK_F_SUPERH.h.in | 9 + + .../helper-snippets/DUK_F_TINSPIRE.h.in | 4 + + .../config/helper-snippets/DUK_F_TINYC.h.in | 5 + + .../config/helper-snippets/DUK_F_TOS.h.in | 6 + + .../config/helper-snippets/DUK_F_UCLIBC.h.in | 4 + + .../helper-snippets/DUK_F_ULL_CONSTS.h.in | 9 + + .../config/helper-snippets/DUK_F_UNIX.h.in | 5 + + .../config/helper-snippets/DUK_F_VBCC.h.in | 4 + + .../config/helper-snippets/DUK_F_WINDOWS.h.in | 10 + + .../config/helper-snippets/DUK_F_X86.h.in | 26 + + dist/source/config/platforms.yaml | 89 + + .../source/config/platforms/platform_aix.h.in | 12 + + .../config/platforms/platform_amigaos.h.in | 26 + + .../config/platforms/platform_android.h.in | 33 + + .../config/platforms/platform_apple.h.in | 49 + + .../config/platforms/platform_cygwin.h.in | 15 + + .../config/platforms/platform_durango.h.in | 32 + + .../config/platforms/platform_emscripten.h.in | 29 + + .../platforms/platform_flashplayer.h.in | 14 + + .../config/platforms/platform_generic.h.in | 18 + + .../config/platforms/platform_genericbsd.h.in | 11 + + .../platforms/platform_genericunix.h.in | 7 + + .../config/platforms/platform_hpux.h.in | 13 + + .../config/platforms/platform_linux.h.in | 33 + + .../config/platforms/platform_openbsd.h.in | 12 + + .../config/platforms/platform_orbis.h.in | 12 + + .../config/platforms/platform_posix.h.in | 11 + + .../source/config/platforms/platform_qnx.h.in | 16 + + .../config/platforms/platform_solaris.h.in | 21 + + .../config/platforms/platform_tinspire.h.in | 14 + + .../source/config/platforms/platform_tos.h.in | 12 + + .../config/platforms/platform_windows.h.in | 59 + + dist/source/config/tags.yaml | 105 + + dist/source/debugger/Makefile | 101 + + dist/source/debugger/README.rst | 385 + + dist/source/debugger/duk_classnames.yaml | 32 + + dist/source/debugger/duk_debug.js | 2491 + + dist/source/debugger/duk_debug_meta.json | 2189 + + dist/source/debugger/duk_debug_proxy.js | 1044 + + dist/source/debugger/duk_debugcommands.yaml | 52 + + dist/source/debugger/duk_debugerrors.yaml | 6 + + dist/source/debugger/duk_opcodes.yaml | 1193 + + dist/source/debugger/package.json | 27 + + dist/source/debugger/static/index.html | 96 + + dist/source/debugger/static/style.css | 517 + + dist/source/debugger/static/webui.js | 808 + + dist/source/duk_dist_meta.json | 9 + + dist/source/examples/README.rst | 10 + + dist/source/examples/alloc-hybrid/README.rst | 10 + + .../examples/alloc-hybrid/duk_alloc_hybrid.c | 294 + + .../examples/alloc-hybrid/duk_alloc_hybrid.h | 11 + + dist/source/examples/alloc-logging/README.rst | 7 + + .../alloc-logging/duk_alloc_logging.c | 139 + + .../alloc-logging/duk_alloc_logging.h | 10 + + .../examples/alloc-logging/log2gnuplot.py | 41 + + dist/source/examples/alloc-torture/README.rst | 10 + + .../alloc-torture/duk_alloc_torture.c | 183 + + .../alloc-torture/duk_alloc_torture.h | 10 + + dist/source/examples/cmdline/README.rst | 6 + + dist/source/examples/cmdline/duk_cmdline.c | 1867 + + dist/source/examples/cmdline/duk_cmdline.h | 16 + + .../examples/cmdline/duk_cmdline_lowmem.c | 1016 + + dist/source/examples/codepage-conv/README.rst | 8 + + .../codepage-conv/duk_codepage_conv.c | 54 + + .../codepage-conv/duk_codepage_conv.h | 8 + + dist/source/examples/codepage-conv/test.c | 298 + + dist/source/examples/coffee/README.rst | 10 + + dist/source/examples/coffee/globals.coffee | 7 + + dist/source/examples/coffee/hello.coffee | 2 + + dist/source/examples/coffee/mandel.coffee | 28 + + .../source/examples/cpp-exceptions/README.rst | 29 + + .../cpp-exceptions/cpp_exceptions.cpp | 311 + + .../examples/debug-trans-dvalue/Makefile | 16 + + .../examples/debug-trans-dvalue/README.rst | 8 + + .../debug-trans-dvalue/duk_trans_dvalue.c | 1241 + + .../debug-trans-dvalue/duk_trans_dvalue.h | 113 + + .../source/examples/debug-trans-dvalue/test.c | 248 + + .../examples/debug-trans-socket/README.rst | 17 + + .../debug-trans-socket/duk_trans_socket.h | 23 + + .../duk_trans_socket_unix.c | 364 + + .../duk_trans_socket_windows.c | 419 + + .../examples/dummy-date-provider/README.rst | 5 + + .../dummy-date-provider/dummy_date_provider.c | 27 + + dist/source/examples/eval/README.rst | 5 + + dist/source/examples/eval/eval.c | 55 + + dist/source/examples/eventloop/README.rst | 72 + + dist/source/examples/eventloop/basic-test.js | 16 + + dist/source/examples/eventloop/c_eventloop.c | 626 + + dist/source/examples/eventloop/c_eventloop.h | 7 + + dist/source/examples/eventloop/c_eventloop.js | 192 + + .../examples/eventloop/client-socket-test.js | 16 + + .../examples/eventloop/ecma_eventloop.js | 478 + + dist/source/examples/eventloop/fileio.c | 84 + + dist/source/examples/eventloop/main.c | 286 + + dist/source/examples/eventloop/poll.c | 119 + + .../examples/eventloop/server-socket-test.js | 33 + + dist/source/examples/eventloop/socket.c | 286 + + dist/source/examples/eventloop/timer-test.js | 40 + + dist/source/examples/guide/README.rst | 5 + + dist/source/examples/guide/fib.js | 16 + + dist/source/examples/guide/prime.js | 32 + + dist/source/examples/guide/primecheck.c | 81 + + dist/source/examples/guide/process.js | 12 + + dist/source/examples/guide/processlines.c | 78 + + dist/source/examples/guide/uppercase.c | 42 + + dist/source/examples/hello/README.rst | 5 + + dist/source/examples/hello/hello.c | 46 + + dist/source/examples/jxpretty/README.rst | 5 + + dist/source/examples/jxpretty/jxpretty.c | 76 + + dist/source/examples/sandbox/README.rst | 5 + + dist/source/examples/sandbox/sandbox.c | 266 + + dist/source/extras/README.rst | 13 + + dist/source/extras/alloc-pool/Makefile | 42 + + dist/source/extras/alloc-pool/README.rst | 38 + + .../source/extras/alloc-pool/duk_alloc_pool.c | 612 + + .../source/extras/alloc-pool/duk_alloc_pool.h | 231 + + dist/source/extras/alloc-pool/ptrcomp.yaml | 16 + + dist/source/extras/alloc-pool/ptrcomp_fixup.h | 2 + + dist/source/extras/alloc-pool/test.c | 114 + + dist/source/extras/cbor/Makefile | 261 + + dist/source/extras/cbor/README.rst | 150 + + dist/source/extras/cbor/cbordecode.py | 24 + + dist/source/extras/cbor/duk_cbor.c | 2075 + + dist/source/extras/cbor/duk_cbor.h | 20 + + dist/source/extras/cbor/jsoncbor.c | 207 + + dist/source/extras/cbor/run_testvectors.js | 30 + + dist/source/extras/console/Makefile | 20 + + dist/source/extras/console/README.rst | 35 + + dist/source/extras/console/duk_console.c | 185 + + dist/source/extras/console/duk_console.h | 29 + + dist/source/extras/console/test.c | 30 + + dist/source/extras/duk-v1-compat/Makefile | 15 + + dist/source/extras/duk-v1-compat/README.rst | 25 + + .../extras/duk-v1-compat/duk_v1_compat.c | 131 + + .../extras/duk-v1-compat/duk_v1_compat.h | 36 + + dist/source/extras/duk-v1-compat/test.c | 104 + + .../extras/duk-v1-compat/test_compile1.js | 3 + + .../extras/duk-v1-compat/test_compile2.js | 3 + + .../source/extras/duk-v1-compat/test_eval1.js | 4 + + .../source/extras/duk-v1-compat/test_eval2.js | 4 + + dist/source/extras/logging/Makefile | 22 + + dist/source/extras/logging/README.rst | 33 + + dist/source/extras/logging/duk_logging.c | 380 + + dist/source/extras/logging/duk_logging.h | 28 + + dist/source/extras/logging/test.c | 56 + + dist/source/extras/minimal-printf/Makefile | 7 + + dist/source/extras/minimal-printf/README.rst | 116 + + .../minimal-printf/duk_minimal_printf.c | 312 + + .../minimal-printf/duk_minimal_printf.h | 20 + + dist/source/extras/minimal-printf/test.c | 190 + + dist/source/extras/module-duktape/Makefile | 23 + + dist/source/extras/module-duktape/README.rst | 31 + + .../module-duktape/duk_module_duktape.c | 471 + + .../module-duktape/duk_module_duktape.h | 22 + + dist/source/extras/module-duktape/test.c | 55 + + dist/source/extras/module-node/Makefile | 28 + + dist/source/extras/module-node/README.rst | 89 + + .../extras/module-node/duk_module_node.c | 333 + + .../extras/module-node/duk_module_node.h | 17 + + dist/source/extras/module-node/test.c | 103 + + dist/source/extras/print-alert/Makefile | 16 + + dist/source/extras/print-alert/README.rst | 27 + + .../extras/print-alert/duk_print_alert.c | 127 + + .../extras/print-alert/duk_print_alert.h | 18 + + dist/source/extras/print-alert/test.c | 30 + + dist/source/licenses/commonjs.txt | 2 + + dist/source/licenses/lua.txt | 1 + + dist/source/licenses/murmurhash2.txt | 21 + + dist/source/licenses/splitmix64.txt | 7 + + dist/source/licenses/xoroshiro128plus.txt | 7 + + dist/source/mandel.js | 53 + + dist/source/polyfills/console-minimal.js | 20 + + dist/source/polyfills/duktape-buffer.js | 53 + + .../duktape-error-setter-nonwritable.js | 20 + + .../duktape-error-setter-writable.js | 19 + + dist/source/polyfills/duktape-isfastint.js | 38 + + dist/source/polyfills/global.js | 15 + + dist/source/polyfills/object-assign.js | 45 + + .../object-prototype-definegetter.js | 14 + + .../object-prototype-definesetter.js | 14 + + dist/source/polyfills/performance-now.js | 25 + + dist/source/polyfills/promise.js | 607 + + dist/source/src-input/SpecialCasing-8bit.txt | 1 + + dist/source/src-input/SpecialCasing.txt | 281 + + dist/source/src-input/UnicodeData-8bit.txt | 256 + + dist/source/src-input/UnicodeData.txt | 32841 +++++ + dist/source/src-input/builtins.yaml | 5539 + + dist/source/src-input/duk_alloc_default.c | 34 + + dist/source/src-input/duk_api_buffer.c | 73 + + dist/source/src-input/duk_api_bytecode.c | 760 + + dist/source/src-input/duk_api_call.c | 516 + + dist/source/src-input/duk_api_codec.c | 926 + + dist/source/src-input/duk_api_compile.c | 172 + + dist/source/src-input/duk_api_debug.c | 261 + + dist/source/src-input/duk_api_heap.c | 211 + + dist/source/src-input/duk_api_inspect.c | 229 + + dist/source/src-input/duk_api_internal.h | 386 + + dist/source/src-input/duk_api_memory.c | 80 + + dist/source/src-input/duk_api_object.c | 1050 + + dist/source/src-input/duk_api_random.c | 9 + + dist/source/src-input/duk_api_stack.c | 6867 ++ + dist/source/src-input/duk_api_string.c | 378 + + dist/source/src-input/duk_api_time.c | 110 + + dist/source/src-input/duk_bi_array.c | 1647 + + dist/source/src-input/duk_bi_boolean.c | 69 + + dist/source/src-input/duk_bi_buffer.c | 2923 + + dist/source/src-input/duk_bi_cbor.c | 1737 + + dist/source/src-input/duk_bi_date.c | 1769 + + dist/source/src-input/duk_bi_date_unix.c | 325 + + dist/source/src-input/duk_bi_date_windows.c | 193 + + dist/source/src-input/duk_bi_duktape.c | 158 + + dist/source/src-input/duk_bi_encoding.c | 533 + + dist/source/src-input/duk_bi_error.c | 387 + + dist/source/src-input/duk_bi_function.c | 453 + + dist/source/src-input/duk_bi_global.c | 727 + + dist/source/src-input/duk_bi_json.c | 3266 + + dist/source/src-input/duk_bi_math.c | 519 + + dist/source/src-input/duk_bi_number.c | 280 + + dist/source/src-input/duk_bi_object.c | 803 + + dist/source/src-input/duk_bi_performance.c | 31 + + dist/source/src-input/duk_bi_pointer.c | 75 + + dist/source/src-input/duk_bi_promise.c | 44 + + dist/source/src-input/duk_bi_protos.h | 81 + + dist/source/src-input/duk_bi_proxy.c | 98 + + dist/source/src-input/duk_bi_reflect.c | 99 + + dist/source/src-input/duk_bi_regexp.c | 226 + + dist/source/src-input/duk_bi_string.c | 1603 + + dist/source/src-input/duk_bi_symbol.c | 170 + + dist/source/src-input/duk_bi_thread.c | 326 + + dist/source/src-input/duk_bi_thrower.c | 9 + + dist/source/src-input/duk_dblunion.h | 424 + + dist/source/src-input/duk_debug.h | 184 + + dist/source/src-input/duk_debug_fixedbuffer.c | 69 + + dist/source/src-input/duk_debug_macros.c | 88 + + dist/source/src-input/duk_debug_vsnprintf.c | 1093 + + dist/source/src-input/duk_debugger.c | 2909 + + dist/source/src-input/duk_debugger.h | 151 + + dist/source/src-input/duk_error.h | 525 + + dist/source/src-input/duk_error_augment.c | 588 + + dist/source/src-input/duk_error_longjmp.c | 103 + + dist/source/src-input/duk_error_macros.c | 152 + + dist/source/src-input/duk_error_misc.c | 181 + + dist/source/src-input/duk_error_throw.c | 162 + + dist/source/src-input/duk_exception.h | 30 + + dist/source/src-input/duk_fltunion.h | 39 + + dist/source/src-input/duk_forwdecl.h | 134 + + dist/source/src-input/duk_harray.h | 48 + + dist/source/src-input/duk_hboundfunc.h | 37 + + dist/source/src-input/duk_hbuffer.h | 336 + + dist/source/src-input/duk_hbuffer_alloc.c | 132 + + dist/source/src-input/duk_hbuffer_assert.c | 13 + + dist/source/src-input/duk_hbuffer_ops.c | 78 + + dist/source/src-input/duk_hbufobj.h | 127 + + dist/source/src-input/duk_hbufobj_misc.c | 20 + + dist/source/src-input/duk_hcompfunc.h | 273 + + dist/source/src-input/duk_heap.h | 723 + + dist/source/src-input/duk_heap_alloc.c | 1217 + + dist/source/src-input/duk_heap_finalize.c | 445 + + dist/source/src-input/duk_heap_hashstring.c | 116 + + dist/source/src-input/duk_heap_markandsweep.c | 1486 + + dist/source/src-input/duk_heap_memory.c | 438 + + dist/source/src-input/duk_heap_misc.c | 187 + + dist/source/src-input/duk_heap_refcount.c | 833 + + dist/source/src-input/duk_heap_stringcache.c | 309 + + dist/source/src-input/duk_heap_stringtable.c | 1043 + + dist/source/src-input/duk_heaphdr.h | 297 + + dist/source/src-input/duk_heaphdr_assert.c | 78 + + dist/source/src-input/duk_henv.h | 45 + + dist/source/src-input/duk_hnatfunc.h | 39 + + dist/source/src-input/duk_hobject.h | 981 + + dist/source/src-input/duk_hobject_alloc.c | 271 + + dist/source/src-input/duk_hobject_assert.c | 127 + + dist/source/src-input/duk_hobject_class.c | 129 + + dist/source/src-input/duk_hobject_enum.c | 713 + + dist/source/src-input/duk_hobject_misc.c | 53 + + dist/source/src-input/duk_hobject_pc2line.c | 244 + + dist/source/src-input/duk_hobject_props.c | 6207 + + dist/source/src-input/duk_hproxy.h | 26 + + dist/source/src-input/duk_hstring.h | 253 + + dist/source/src-input/duk_hstring_assert.c | 13 + + dist/source/src-input/duk_hstring_misc.c | 196 + + dist/source/src-input/duk_hthread.h | 408 + + dist/source/src-input/duk_hthread_alloc.c | 59 + + dist/source/src-input/duk_hthread_builtins.c | 871 + + dist/source/src-input/duk_hthread_misc.c | 97 + + dist/source/src-input/duk_hthread_stacks.c | 407 + + dist/source/src-input/duk_internal.h | 77 + + dist/source/src-input/duk_jmpbuf.h | 24 + + dist/source/src-input/duk_js.h | 116 + + dist/source/src-input/duk_js_arith.c | 137 + + dist/source/src-input/duk_js_bytecode.h | 482 + + dist/source/src-input/duk_js_call.c | 2933 + + dist/source/src-input/duk_js_compiler.c | 8025 ++ + dist/source/src-input/duk_js_compiler.h | 227 + + dist/source/src-input/duk_js_executor.c | 5202 + + dist/source/src-input/duk_js_ops.c | 1478 + + dist/source/src-input/duk_js_var.c | 1797 + + dist/source/src-input/duk_json.h | 68 + + dist/source/src-input/duk_lexer.c | 2440 + + dist/source/src-input/duk_lexer.h | 438 + + dist/source/src-input/duk_numconv.c | 2280 + + dist/source/src-input/duk_numconv.h | 104 + + dist/source/src-input/duk_refcount.h | 725 + + dist/source/src-input/duk_regexp.h | 84 + + dist/source/src-input/duk_regexp_compiler.c | 1287 + + dist/source/src-input/duk_regexp_executor.c | 1029 + + dist/source/src-input/duk_replacements.c | 82 + + dist/source/src-input/duk_replacements.h | 29 + + dist/source/src-input/duk_selftest.c | 678 + + dist/source/src-input/duk_selftest.h | 15 + + dist/source/src-input/duk_strings.h | 169 + + dist/source/src-input/duk_tval.c | 152 + + dist/source/src-input/duk_tval.h | 641 + + dist/source/src-input/duk_unicode.h | 250 + + dist/source/src-input/duk_unicode_support.c | 1259 + + dist/source/src-input/duk_unicode_tables.c | 64 + + dist/source/src-input/duk_util.h | 731 + + dist/source/src-input/duk_util_bitdecoder.c | 157 + + dist/source/src-input/duk_util_bitencoder.c | 43 + + dist/source/src-input/duk_util_bufwriter.c | 286 + + dist/source/src-input/duk_util_cast.c | 160 + + dist/source/src-input/duk_util_double.c | 343 + + dist/source/src-input/duk_util_hashbytes.c | 57 + + dist/source/src-input/duk_util_memory.c | 36 + + dist/source/src-input/duk_util_memrw.c | 148 + + dist/source/src-input/duk_util_misc.c | 183 + + dist/source/src-input/duk_util_tinyrandom.c | 125 + + dist/source/src-input/duktape.h.in | 1320 + + dist/source/src-input/strings.yaml | 1253 + + dist/source/src/duk_config.h | 3805 + + dist/source/src/duk_source_meta.json | 1911 + + dist/source/src/duktape.c | 99784 +++++++++++++++ + dist/source/src/duktape.h | 1462 + + dist/source/tools/combine_src.py | 271 + + dist/source/tools/configure.py | 1005 + + dist/source/tools/create_spdx_license.py | 246 + + dist/source/tools/duk_meta_to_strarray.py | 49 + + dist/source/tools/dukutil.py | 300 + + dist/source/tools/dump_bytecode.py | 276 + + dist/source/tools/extract_caseconv.py | 733 + + dist/source/tools/extract_chars.py | 385 + + dist/source/tools/extract_unique_options.py | 41 + + dist/source/tools/genbuiltins.py | 3221 + + dist/source/tools/genconfig.py | 1542 + + dist/source/tools/json2yaml.py | 5 + + dist/source/tools/merge_debug_meta.py | 48 + + dist/source/tools/prepare_unicode_data.py | 51 + + dist/source/tools/resolve_combined_lineno.py | 26 + + dist/source/tools/scan_strings.py | 135 + + dist/source/tools/scan_used_stridx_bidx.py | 58 + + dist/source/tools/yaml2json.py | 4 + + prep/nondebug/duk_config.h | 3229 + + prep/nondebug/duk_source_meta.json | 1925 + + prep/nondebug/duktape.c | 99924 ++++++++++++++++ + prep/nondebug/duktape.h | 1462 + + 859 files changed, 389919 insertions(+) + create mode 100644 dist/source/AUTHORS.rst + create mode 100644 dist/source/LICENSE.txt + create mode 100644 dist/source/Makefile.cmdline + create mode 100644 dist/source/Makefile.codepage + create mode 100644 dist/source/Makefile.coffee + create mode 100644 dist/source/Makefile.dukdebug + create mode 100644 dist/source/Makefile.eval + create mode 100644 dist/source/Makefile.eventloop + create mode 100644 dist/source/Makefile.hello + create mode 100644 dist/source/Makefile.jsoncbor + create mode 100644 dist/source/Makefile.jxpretty + create mode 100644 dist/source/Makefile.sandbox + create mode 100644 dist/source/Makefile.sharedlibrary + create mode 100644 dist/source/README.rst + create mode 100644 dist/source/config/README.rst + create mode 100644 dist/source/config/architectures.yaml + create mode 100644 dist/source/config/architectures/architecture_arm32.h.in + create mode 100644 dist/source/config/architectures/architecture_arm64.h.in + create mode 100644 dist/source/config/architectures/architecture_emscripten.h.in + create mode 100644 dist/source/config/architectures/architecture_generic.h.in + create mode 100644 dist/source/config/architectures/architecture_m68k.h.in + create mode 100644 dist/source/config/architectures/architecture_mips32.h.in + create mode 100644 dist/source/config/architectures/architecture_mips64.h.in + create mode 100644 dist/source/config/architectures/architecture_powerpc32.h.in + create mode 100644 dist/source/config/architectures/architecture_powerpc64.h.in + create mode 100644 dist/source/config/architectures/architecture_riscv32.h.in + create mode 100644 dist/source/config/architectures/architecture_riscv64.h.in + create mode 100644 dist/source/config/architectures/architecture_sparc32.h.in + create mode 100644 dist/source/config/architectures/architecture_sparc64.h.in + create mode 100644 dist/source/config/architectures/architecture_superh.h.in + create mode 100644 dist/source/config/architectures/architecture_x32.h.in + create mode 100644 dist/source/config/architectures/architecture_x64.h.in + create mode 100644 dist/source/config/architectures/architecture_x86.h.in + create mode 100644 dist/source/config/compilers.yaml + create mode 100644 dist/source/config/compilers/compiler_bcc.h.in + create mode 100644 dist/source/config/compilers/compiler_clang.h.in + create mode 100644 dist/source/config/compilers/compiler_emscripten.h.in + create mode 100644 dist/source/config/compilers/compiler_gcc.h.in + create mode 100644 dist/source/config/compilers/compiler_generic.h.in + create mode 100644 dist/source/config/compilers/compiler_msvc.h.in + create mode 100644 dist/source/config/compilers/compiler_tinyc.h.in + create mode 100644 dist/source/config/compilers/compiler_vbcc.h.in + create mode 100644 dist/source/config/config-options/DUK_USE_32BIT_PTRS.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_64BIT_OPS.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_ALIGN_4.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_ALIGN_8.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_ALIGN_BY.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_ALLOW_UNDEFINED_BEHAVIOR.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_ARCH_STRING.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_ARRAY_BUILTIN.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_ARRAY_FASTPATH.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_ARRAY_PROP_FASTPATH.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_ASSERTIONS.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_ATAN2_WORKAROUNDS.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_AUGMENT_ERROR_CREATE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_AUGMENT_ERROR_THROW.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_AVOID_PLATFORM_FUNCPTRS.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_BASE64_FASTPATH.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_BASE64_SUPPORT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_BOOLEAN_BUILTIN.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_BRANCH_HINTS.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_BROWSER_LIKE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_BUFFEROBJECT_SUPPORT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_BUFLEN16.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_BUILTIN_INITJS.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_BYTECODE_DUMP_SUPPORT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_BYTEORDER.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_BYTEORDER_FORCED.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_CACHE_ACTIVATION.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_CACHE_CATCHER.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_CALLSTACK_LIMIT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_CBOR_BUILTIN.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_CBOR_DEC_RECLIMIT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_CBOR_ENC_RECLIMIT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_CBOR_SUPPORT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_CLANG_PRAGMAS.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_COMMONJS_MODULES.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_COMPILER_RECLIMIT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_COMPILER_STRING.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_COMPUTED_INFINITY.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_COMPUTED_NAN.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_COROUTINE_SUPPORT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_CPP_EXCEPTIONS.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DATAPTR16.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DATAPTR_DEC16.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DATAPTR_ENC16.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DATE_BUILTIN.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DATE_FMT_STRFTIME.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DATE_FORMAT_STRING.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DATE_GET_LOCAL_TZOFFSET.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DATE_GET_NOW.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DATE_NOW_GETTIMEOFDAY.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DATE_NOW_TIME.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DATE_NOW_WINDOWS.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DATE_NOW_WINDOWS_SUBMS.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DATE_PARSE_STRING.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DATE_PRS_GETDATE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DATE_PRS_STRPTIME.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DATE_TZO_GMTIME.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DATE_TZO_GMTIME_R.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DATE_TZO_GMTIME_S.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DATE_TZO_WINDOWS.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DATE_TZO_WINDOWS_NO_DST.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DDDPRINT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DDPRINT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DEBUG.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DEBUGGER_DUMPHEAP.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DEBUGGER_FWD_LOGGING.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DEBUGGER_FWD_PRINTALERT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DEBUGGER_INSPECT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DEBUGGER_PAUSE_UNCAUGHT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DEBUGGER_SUPPORT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DEBUGGER_THROW_NOTIFY.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DEBUGGER_TRANSPORT_TORTURE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DEBUG_BUFSIZE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DEBUG_LEVEL.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DEBUG_WRITE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DEEP_C_STACK.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DOUBLE_BE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DOUBLE_LE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DOUBLE_LINKED_HEAP.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DOUBLE_ME.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DPRINT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DPRINT_COLORS.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DPRINT_RDTSC.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_DUKTAPE_BUILTIN.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_ENCODING_BUILTINS.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_ERRCREATE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_ERRTHROW.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_ES6.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_ES6_OBJECT_PROTO_PROPERTY.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_ES6_OBJECT_SETPROTOTYPEOF.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_ES6_PROXY.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_ES6_REGEXP_BRACES.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_ES6_REGEXP_SYNTAX.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_ES6_UNICODE_ESCAPE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_ES7.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_ES7_EXP_OPERATOR.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_ES8.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_ES9.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_ESBC_LIMITS.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_ESBC_MAX_BYTES.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_ESBC_MAX_LINENUMBER.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_EXAMPLE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_EXEC_FUN_LOCAL.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_EXEC_INDIRECT_BOUND_CHECK.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_EXEC_PREFER_SIZE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_EXEC_REGCONST_OPTIMIZE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_EXEC_TIMEOUT_CHECK.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_EXPLICIT_NULL_INIT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_EXTSTR_FREE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_EXTSTR_INTERN_CHECK.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_FASTINT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_FAST_REFCOUNT_DEFAULT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_FATAL_HANDLER.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_FATAL_MAXLEN.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_FILE_IO.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_FINALIZER_SUPPORT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_FINALIZER_TORTURE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_FLEX_C99.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_FLEX_ONESIZE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_FLEX_ZEROSIZE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_FULL_TVAL.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_FUNCPTR16.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_FUNCPTR_DEC16.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_FUNCPTR_ENC16.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_FUNCTION_BUILTIN.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_FUNC_FILENAME_PROPERTY.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_FUNC_NAME_PROPERTY.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_FUZZILLI.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_GCC_PRAGMAS.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_GC_TORTURE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_GET_MONOTONIC_TIME.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_GET_MONOTONIC_TIME_CLOCK_GETTIME.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_GET_MONOTONIC_TIME_WINDOWS_QPC.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_GET_RANDOM_DOUBLE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_GLOBAL_BINDING.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_GLOBAL_BUILTIN.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_HASHBYTES_UNALIGNED_U32_ACCESS.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_HEAPPTR16.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_HEAPPTR_DEC16.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_HEAPPTR_ENC16.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_HEX_FASTPATH.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_HEX_SUPPORT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_HOBJECT_ARRAY_ABANDON_LIMIT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_HOBJECT_ARRAY_ABANDON_MINSIZE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_HOBJECT_ARRAY_FAST_RESIZE_LIMIT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_HOBJECT_ARRAY_MINGROW_ADD.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_HOBJECT_ARRAY_MINGROW_DIVISOR.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_HOBJECT_ENTRY_MINGROW_ADD.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_HOBJECT_ENTRY_MINGROW_DIVISOR.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_HOBJECT_HASH_PART.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_HOBJECT_HASH_PROP_LIMIT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_HOBJECT_LAYOUT_1.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_HOBJECT_LAYOUT_2.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_HOBJECT_LAYOUT_3.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_HSTRING_ARRIDX.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_HSTRING_CLEN.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_HSTRING_EXTDATA.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_HSTRING_LAZY_CLEN.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_HTML_COMMENTS.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_IDCHAR_FASTPATH.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_INJECT_HEAP_ALLOC_ERROR.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_INTEGER_BE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_INTEGER_LE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_INTEGER_ME.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_INTERRUPT_COUNTER.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_INTERRUPT_DEBUG_FIXUP.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_JC.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_JSON_BUILTIN.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_JSON_DECNUMBER_FASTPATH.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_JSON_DECSTRING_FASTPATH.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_JSON_DEC_RECLIMIT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_JSON_EATWHITE_FASTPATH.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_JSON_ENC_RECLIMIT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_JSON_QUOTESTRING_FASTPATH.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_JSON_STRINGIFY_FASTPATH.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_JSON_SUPPORT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_JX.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_LEXER_SLIDING_WINDOW.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_LIGHTFUNC_BUILTINS.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_LITCACHE_SIZE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_MARKANDSWEEP_FINALIZER_TORTURE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_MARK_AND_SWEEP.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_MARK_AND_SWEEP_RECLIMIT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_MATH_BUILTIN.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_MATH_FMAX.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_MATH_FMIN.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_MATH_ROUND.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_MS_STRINGTABLE_RESIZE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_NATIVE_CALL_RECLIMIT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_NATIVE_STACK_CHECK.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_NONSTD_ARRAY_CONCAT_TRAILER.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_NONSTD_ARRAY_MAP_TRAILER.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_NONSTD_ARRAY_SPLICE_DELCOUNT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_NONSTD_FUNC_CALLER_PROPERTY.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_NONSTD_FUNC_SOURCE_PROPERTY.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_NONSTD_FUNC_STMT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_NONSTD_GETTER_KEY_ARGUMENT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_NONSTD_JSON_ESC_U2028_U2029.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_NONSTD_REGEXP_DOLLAR_ESCAPE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_NONSTD_SETTER_KEY_ARGUMENT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_NONSTD_STRING_FROMCHARCODE_32BIT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_NO_DOUBLE_ALIASING_SELFTEST.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_NUMBER_BUILTIN.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_OBJECT_BUILTIN.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_OBJSIZES16.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_OCTAL_SUPPORT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_OS_STRING.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_PACKED_TVAL.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_PACKED_TVAL_POSSIBLE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_PACK_CLANG_ATTR.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_PACK_DUMMY_MEMBER.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_PACK_GCC_ATTR.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_PACK_MSVC_PRAGMA.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_PANIC_ABORT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_PANIC_EXIT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_PANIC_HANDLER.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_PANIC_SEGFAULT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_PARANOID_DATE_COMPUTATION.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_PARANOID_ERRORS.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_PARANOID_MATH.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_PC2LINE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_PERFORMANCE_BUILTIN.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_POW_NETBSD_WORKAROUND.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_POW_WORKAROUNDS.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_PREFER_SIZE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_PROMISE_BUILTIN.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_PROVIDE_DEFAULT_ALLOC_FUNCTIONS.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_RDTSC.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_REFCOUNT16.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_REFCOUNT32.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_REFERENCE_COUNTING.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_REFLECT_BUILTIN.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_REFZERO_FINALIZER_TORTURE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_REGEXP_CANON_BITMAP.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_REGEXP_CANON_WORKAROUND.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_REGEXP_COMPILER_RECLIMIT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_REGEXP_EXECUTOR_RECLIMIT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_REGEXP_SUPPORT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_REPL_FPCLASSIFY.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_REPL_ISFINITE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_REPL_ISINF.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_REPL_ISNAN.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_REPL_SIGNBIT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_ROM_GLOBAL_CLONE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_ROM_GLOBAL_INHERIT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_ROM_OBJECTS.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_ROM_PTRCOMP_FIRST.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_ROM_STRINGS.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_SECTION_B.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_SELF_TESTS.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_SETJMP.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_SHEBANG_COMMENTS.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_SHUFFLE_TORTURE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_SIGSETJMP.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_SOURCE_NONBMP.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_STRHASH16.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_STRHASH_DENSE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_STRHASH_SKIP_SHIFT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_STRICT_DECL.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_STRICT_UTF8_SOURCE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_STRING_BUILTIN.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_STRLEN16.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_STRTAB_CHAIN.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_STRTAB_CHAIN_SIZE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_STRTAB_GROW_LIMIT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_STRTAB_MAXSIZE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_STRTAB_MINSIZE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_STRTAB_PROBE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_STRTAB_PTRCOMP.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_STRTAB_RESIZE_CHECK_MASK.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_STRTAB_SHRINK_LIMIT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_STRTAB_TORTURE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_SYMBOL_BUILTIN.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_TAILCALL.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_TARGET_INFO.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_TRACEBACKS.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_TRACEBACK_DEPTH.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_UNALIGNED_ACCESSES_POSSIBLE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_UNDERSCORE_SETJMP.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_UNION_INITIALIZERS.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_USER_DECLARE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_USER_INITJS.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_VALSTACK_GROW_SHIFT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_VALSTACK_LIMIT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_VALSTACK_SHRINK_CHECK_SHIFT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_VALSTACK_SHRINK_SLACK_SHIFT.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_VALSTACK_UNSAFE.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_VARIADIC_MACROS.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_VERBOSE_ERRORS.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_VERBOSE_EXECUTOR_ERRORS.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_VOLUNTARY_GC.yaml + create mode 100644 dist/source/config/config-options/DUK_USE_ZERO_BUFFER_DATA.yaml + create mode 100644 dist/source/config/examples/compliance.yaml + create mode 100644 dist/source/config/examples/debugger_support.yaml + create mode 100644 dist/source/config/examples/disable_bufferobjects.yaml + create mode 100644 dist/source/config/examples/disable_es6.yaml + create mode 100644 dist/source/config/examples/enable_debug_print0.yaml + create mode 100644 dist/source/config/examples/enable_debug_print1.yaml + create mode 100644 dist/source/config/examples/enable_debug_print2.yaml + create mode 100644 dist/source/config/examples/enable_fastint.yaml + create mode 100644 dist/source/config/examples/low_memory.yaml + create mode 100644 dist/source/config/examples/low_memory_strip.yaml + create mode 100644 dist/source/config/examples/performance_sensitive.yaml + create mode 100644 dist/source/config/examples/rom_builtins.yaml + create mode 100644 dist/source/config/examples/security_sensitive.yaml + create mode 100644 dist/source/config/examples/shallow_c_stack.yaml + create mode 100644 dist/source/config/examples/timing_sensitive.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_ASSERTIONS.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_BUFFEROBJECT_SUPPORT.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_BUFLEN16.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_DATAPTR16.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_DATAPTR_DEC16.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_DATAPTR_ENC16.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_DDDPRINT.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_DDPRINT.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_DEBUG.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_DEBUGGER_DUMPHEAP.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_DEBUGGER_FWD_LOGGING.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_DEBUGGER_FWD_PRINTALERT.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_DEBUGGER_SUPPORT.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_DEBUGGER_TRANSPORT_TORTURE.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_DEBUG_BUFSIZE.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_DECLARE.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_DEEP_C_STACK.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_DLL_BUILD.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_DPRINT.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_DPRINT_COLORS.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_DPRINT_RDTSC.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_EXAMPLE.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_EXEC_TIMEOUT_CHECK.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_EXTERNAL_STRINGS.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_EXTSTR_FREE.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_EXTSTR_INTERN_CHECK.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_FASTINT.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_FORCE_ALIGN.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_FORCE_BYTEORDER.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_FUNCPTR16.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_FUNCPTR_DEC16.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_FUNCPTR_ENC16.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_FUNC_NONSTD_CALLER_PROPERTY.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_FUNC_NONSTD_SOURCE_PROPERTY.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_GC_TORTURE.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_HAVE_CUSTOM_H.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_HEAPPTR16.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_HEAPPTR_DEC16.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_HEAPPTR_ENC16.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_INTERRUPT_COUNTER.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_JSON_STRINGIFY_FASTPATH.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_LIGHTFUNC_BUILTINS.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NONSTD_FUNC_CALLER_PROPERTY.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NONSTD_FUNC_SOURCE_PROPERTY.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_ARRAY_SPLICE_NONSTD_DELCOUNT.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_AUGMENT_ERRORS.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_BROWSER_LIKE.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_BUFFEROBJECT_SUPPORT.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_BYTECODE_DUMP_SUPPORT.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_COMMONJS_MODULES.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_ES6_OBJECT_PROTO_PROPERTY.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_ES6_OBJECT_SETPROTOTYPEOF.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_ES6_PROXY.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_FILE_IO.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_FUNC_STMT.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_JC.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_JSONC.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_JSONX.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_JX.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_MARK_AND_SWEEP.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_MS_STRINGTABLE_RESIZE.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_NONSTD_ACCESSOR_KEY_ARGUMENT.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_NONSTD_ARRAY_CONCAT_TRAILER.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_NONSTD_ARRAY_MAP_TRAILER.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_NONSTD_ARRAY_SPLICE_DELCOUNT.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_NONSTD_FUNC_STMT.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_NONSTD_JSON_ESC_U2028_U2029.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_NONSTD_STRING_FROMCHARCODE_32BIT.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_OBJECT_ES6_PROTO_PROPERTY.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_OBJECT_ES6_SETPROTOTYPEOF.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_OCTAL_SUPPORT.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_PACKED_TVAL.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_PC2LINE.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_REFERENCE_COUNTING.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_REGEXP_SUPPORT.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_SECTION_B.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_SOURCE_NONBMP.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_STRICT_DECL.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_TRACEBACKS.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_VERBOSE_ERRORS.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_VOLUNTARY_GC.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_NO_ZERO_BUFFER_DATA.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_OBJSIZES16.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_PANIC_HANDLER.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_REFCOUNT16.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_SEGFAULT_ON_PANIC.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_SELF_TESTS.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_SETJMP.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_SHUFFLE_TORTURE.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_SIGSETJMP.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_STRHASH16.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_STRICT_UTF8_SOURCE.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_STRLEN16.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_STRTAB_CHAIN.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_STRTAB_CHAIN_SIZE.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_TARGET_INFO.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_TRACEBACK_DEPTH.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_UNDERSCORE_SETJMP.yaml + create mode 100644 dist/source/config/feature-options/DUK_OPT_USER_INITJS.yaml + create mode 100644 dist/source/config/header-snippets/64bitops.h.in + create mode 100644 dist/source/config/header-snippets/alignment_fillin.h.in + create mode 100644 dist/source/config/header-snippets/architecture_fillins.h.in + create mode 100644 dist/source/config/header-snippets/byteorder_derived.h.in + create mode 100644 dist/source/config/header-snippets/byteorder_fillin.h.in + create mode 100644 dist/source/config/header-snippets/compiler_fillins.h.in + create mode 100644 dist/source/config/header-snippets/cpp_exception_sanity.h.in + create mode 100644 dist/source/config/header-snippets/date_provider.h.in + create mode 100644 dist/source/config/header-snippets/gcc_clang_visibility.h.in + create mode 100644 dist/source/config/header-snippets/inline_workaround.h.in + create mode 100644 dist/source/config/header-snippets/msvc_visibility.h.in + create mode 100644 dist/source/config/header-snippets/object_layout.h.in + create mode 100644 dist/source/config/header-snippets/packed_tval_fillin.h.in + create mode 100644 dist/source/config/header-snippets/platform_conditionalincludes.h.in + create mode 100644 dist/source/config/header-snippets/platform_cppextras.h.in + create mode 100644 dist/source/config/header-snippets/platform_fillins.h.in + create mode 100644 dist/source/config/header-snippets/platform_sharedincludes.h.in + create mode 100644 dist/source/config/header-snippets/reject_fast_math.h.in + create mode 100644 dist/source/config/header-snippets/types1.h.in + create mode 100644 dist/source/config/header-snippets/types2.h.in + create mode 100644 dist/source/config/header-snippets/types_c99.h.in + create mode 100644 dist/source/config/header-snippets/types_legacy.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_AIX.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_AMIGAOS.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_ANDROID.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_APPLE.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_ARM.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_BCC.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_BSD.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_C99.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_CLANG.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_CPP.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_CPP11.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_CYGWIN.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_DURANGO.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_EMSCRIPTEN.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_FLASHPLAYER.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_FREEBSD.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_GCC.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_HPUX.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_LINUX.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_M68K.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_MINGW.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_MINT.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_MIPS.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_MSVC.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_NETBSD.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_NO_STDINT_H.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_OPENBSD.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_ORBIS.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_POSIX.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_PPC.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_QNX.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_RISCV.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_SPARC.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_SUN.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_SUPERH.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_TINSPIRE.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_TINYC.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_TOS.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_UCLIBC.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_ULL_CONSTS.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_UNIX.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_VBCC.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_WINDOWS.h.in + create mode 100644 dist/source/config/helper-snippets/DUK_F_X86.h.in + create mode 100644 dist/source/config/platforms.yaml + create mode 100644 dist/source/config/platforms/platform_aix.h.in + create mode 100644 dist/source/config/platforms/platform_amigaos.h.in + create mode 100644 dist/source/config/platforms/platform_android.h.in + create mode 100644 dist/source/config/platforms/platform_apple.h.in + create mode 100644 dist/source/config/platforms/platform_cygwin.h.in + create mode 100644 dist/source/config/platforms/platform_durango.h.in + create mode 100644 dist/source/config/platforms/platform_emscripten.h.in + create mode 100644 dist/source/config/platforms/platform_flashplayer.h.in + create mode 100644 dist/source/config/platforms/platform_generic.h.in + create mode 100644 dist/source/config/platforms/platform_genericbsd.h.in + create mode 100644 dist/source/config/platforms/platform_genericunix.h.in + create mode 100644 dist/source/config/platforms/platform_hpux.h.in + create mode 100644 dist/source/config/platforms/platform_linux.h.in + create mode 100644 dist/source/config/platforms/platform_openbsd.h.in + create mode 100644 dist/source/config/platforms/platform_orbis.h.in + create mode 100644 dist/source/config/platforms/platform_posix.h.in + create mode 100644 dist/source/config/platforms/platform_qnx.h.in + create mode 100644 dist/source/config/platforms/platform_solaris.h.in + create mode 100644 dist/source/config/platforms/platform_tinspire.h.in + create mode 100644 dist/source/config/platforms/platform_tos.h.in + create mode 100644 dist/source/config/platforms/platform_windows.h.in + create mode 100644 dist/source/config/tags.yaml + create mode 100644 dist/source/debugger/Makefile + create mode 100644 dist/source/debugger/README.rst + create mode 100644 dist/source/debugger/duk_classnames.yaml + create mode 100644 dist/source/debugger/duk_debug.js + create mode 100644 dist/source/debugger/duk_debug_meta.json + create mode 100644 dist/source/debugger/duk_debug_proxy.js + create mode 100644 dist/source/debugger/duk_debugcommands.yaml + create mode 100644 dist/source/debugger/duk_debugerrors.yaml + create mode 100644 dist/source/debugger/duk_opcodes.yaml + create mode 100644 dist/source/debugger/package.json + create mode 100644 dist/source/debugger/static/index.html + create mode 100644 dist/source/debugger/static/style.css + create mode 100644 dist/source/debugger/static/webui.js + create mode 100644 dist/source/duk_dist_meta.json + create mode 100644 dist/source/examples/README.rst + create mode 100644 dist/source/examples/alloc-hybrid/README.rst + create mode 100644 dist/source/examples/alloc-hybrid/duk_alloc_hybrid.c + create mode 100644 dist/source/examples/alloc-hybrid/duk_alloc_hybrid.h + create mode 100644 dist/source/examples/alloc-logging/README.rst + create mode 100644 dist/source/examples/alloc-logging/duk_alloc_logging.c + create mode 100644 dist/source/examples/alloc-logging/duk_alloc_logging.h + create mode 100644 dist/source/examples/alloc-logging/log2gnuplot.py + create mode 100644 dist/source/examples/alloc-torture/README.rst + create mode 100644 dist/source/examples/alloc-torture/duk_alloc_torture.c + create mode 100644 dist/source/examples/alloc-torture/duk_alloc_torture.h + create mode 100644 dist/source/examples/cmdline/README.rst + create mode 100644 dist/source/examples/cmdline/duk_cmdline.c + create mode 100644 dist/source/examples/cmdline/duk_cmdline.h + create mode 100644 dist/source/examples/cmdline/duk_cmdline_lowmem.c + create mode 100644 dist/source/examples/codepage-conv/README.rst + create mode 100644 dist/source/examples/codepage-conv/duk_codepage_conv.c + create mode 100644 dist/source/examples/codepage-conv/duk_codepage_conv.h + create mode 100644 dist/source/examples/codepage-conv/test.c + create mode 100644 dist/source/examples/coffee/README.rst + create mode 100644 dist/source/examples/coffee/globals.coffee + create mode 100644 dist/source/examples/coffee/hello.coffee + create mode 100644 dist/source/examples/coffee/mandel.coffee + create mode 100644 dist/source/examples/cpp-exceptions/README.rst + create mode 100644 dist/source/examples/cpp-exceptions/cpp_exceptions.cpp + create mode 100644 dist/source/examples/debug-trans-dvalue/Makefile + create mode 100644 dist/source/examples/debug-trans-dvalue/README.rst + create mode 100644 dist/source/examples/debug-trans-dvalue/duk_trans_dvalue.c + create mode 100644 dist/source/examples/debug-trans-dvalue/duk_trans_dvalue.h + create mode 100644 dist/source/examples/debug-trans-dvalue/test.c + create mode 100644 dist/source/examples/debug-trans-socket/README.rst + create mode 100644 dist/source/examples/debug-trans-socket/duk_trans_socket.h + create mode 100644 dist/source/examples/debug-trans-socket/duk_trans_socket_unix.c + create mode 100644 dist/source/examples/debug-trans-socket/duk_trans_socket_windows.c + create mode 100644 dist/source/examples/dummy-date-provider/README.rst + create mode 100644 dist/source/examples/dummy-date-provider/dummy_date_provider.c + create mode 100644 dist/source/examples/eval/README.rst + create mode 100644 dist/source/examples/eval/eval.c + create mode 100644 dist/source/examples/eventloop/README.rst + create mode 100644 dist/source/examples/eventloop/basic-test.js + create mode 100644 dist/source/examples/eventloop/c_eventloop.c + create mode 100644 dist/source/examples/eventloop/c_eventloop.h + create mode 100644 dist/source/examples/eventloop/c_eventloop.js + create mode 100644 dist/source/examples/eventloop/client-socket-test.js + create mode 100644 dist/source/examples/eventloop/ecma_eventloop.js + create mode 100644 dist/source/examples/eventloop/fileio.c + create mode 100644 dist/source/examples/eventloop/main.c + create mode 100644 dist/source/examples/eventloop/poll.c + create mode 100644 dist/source/examples/eventloop/server-socket-test.js + create mode 100644 dist/source/examples/eventloop/socket.c + create mode 100644 dist/source/examples/eventloop/timer-test.js + create mode 100644 dist/source/examples/guide/README.rst + create mode 100644 dist/source/examples/guide/fib.js + create mode 100644 dist/source/examples/guide/prime.js + create mode 100644 dist/source/examples/guide/primecheck.c + create mode 100644 dist/source/examples/guide/process.js + create mode 100644 dist/source/examples/guide/processlines.c + create mode 100644 dist/source/examples/guide/uppercase.c + create mode 100644 dist/source/examples/hello/README.rst + create mode 100644 dist/source/examples/hello/hello.c + create mode 100644 dist/source/examples/jxpretty/README.rst + create mode 100644 dist/source/examples/jxpretty/jxpretty.c + create mode 100644 dist/source/examples/sandbox/README.rst + create mode 100644 dist/source/examples/sandbox/sandbox.c + create mode 100644 dist/source/extras/README.rst + create mode 100644 dist/source/extras/alloc-pool/Makefile + create mode 100644 dist/source/extras/alloc-pool/README.rst + create mode 100644 dist/source/extras/alloc-pool/duk_alloc_pool.c + create mode 100644 dist/source/extras/alloc-pool/duk_alloc_pool.h + create mode 100644 dist/source/extras/alloc-pool/ptrcomp.yaml + create mode 100644 dist/source/extras/alloc-pool/ptrcomp_fixup.h + create mode 100644 dist/source/extras/alloc-pool/test.c + create mode 100644 dist/source/extras/cbor/Makefile + create mode 100644 dist/source/extras/cbor/README.rst + create mode 100644 dist/source/extras/cbor/cbordecode.py + create mode 100644 dist/source/extras/cbor/duk_cbor.c + create mode 100644 dist/source/extras/cbor/duk_cbor.h + create mode 100644 dist/source/extras/cbor/jsoncbor.c + create mode 100644 dist/source/extras/cbor/run_testvectors.js + create mode 100644 dist/source/extras/console/Makefile + create mode 100644 dist/source/extras/console/README.rst + create mode 100644 dist/source/extras/console/duk_console.c + create mode 100644 dist/source/extras/console/duk_console.h + create mode 100644 dist/source/extras/console/test.c + create mode 100644 dist/source/extras/duk-v1-compat/Makefile + create mode 100644 dist/source/extras/duk-v1-compat/README.rst + create mode 100644 dist/source/extras/duk-v1-compat/duk_v1_compat.c + create mode 100644 dist/source/extras/duk-v1-compat/duk_v1_compat.h + create mode 100644 dist/source/extras/duk-v1-compat/test.c + create mode 100644 dist/source/extras/duk-v1-compat/test_compile1.js + create mode 100644 dist/source/extras/duk-v1-compat/test_compile2.js + create mode 100644 dist/source/extras/duk-v1-compat/test_eval1.js + create mode 100644 dist/source/extras/duk-v1-compat/test_eval2.js + create mode 100644 dist/source/extras/logging/Makefile + create mode 100644 dist/source/extras/logging/README.rst + create mode 100644 dist/source/extras/logging/duk_logging.c + create mode 100644 dist/source/extras/logging/duk_logging.h + create mode 100644 dist/source/extras/logging/test.c + create mode 100644 dist/source/extras/minimal-printf/Makefile + create mode 100644 dist/source/extras/minimal-printf/README.rst + create mode 100644 dist/source/extras/minimal-printf/duk_minimal_printf.c + create mode 100644 dist/source/extras/minimal-printf/duk_minimal_printf.h + create mode 100644 dist/source/extras/minimal-printf/test.c + create mode 100644 dist/source/extras/module-duktape/Makefile + create mode 100644 dist/source/extras/module-duktape/README.rst + create mode 100644 dist/source/extras/module-duktape/duk_module_duktape.c + create mode 100644 dist/source/extras/module-duktape/duk_module_duktape.h + create mode 100644 dist/source/extras/module-duktape/test.c + create mode 100644 dist/source/extras/module-node/Makefile + create mode 100644 dist/source/extras/module-node/README.rst + create mode 100644 dist/source/extras/module-node/duk_module_node.c + create mode 100644 dist/source/extras/module-node/duk_module_node.h + create mode 100644 dist/source/extras/module-node/test.c + create mode 100644 dist/source/extras/print-alert/Makefile + create mode 100644 dist/source/extras/print-alert/README.rst + create mode 100644 dist/source/extras/print-alert/duk_print_alert.c + create mode 100644 dist/source/extras/print-alert/duk_print_alert.h + create mode 100644 dist/source/extras/print-alert/test.c + create mode 100644 dist/source/licenses/commonjs.txt + create mode 100644 dist/source/licenses/lua.txt + create mode 100644 dist/source/licenses/murmurhash2.txt + create mode 100644 dist/source/licenses/splitmix64.txt + create mode 100644 dist/source/licenses/xoroshiro128plus.txt + create mode 100644 dist/source/mandel.js + create mode 100644 dist/source/polyfills/console-minimal.js + create mode 100644 dist/source/polyfills/duktape-buffer.js + create mode 100644 dist/source/polyfills/duktape-error-setter-nonwritable.js + create mode 100644 dist/source/polyfills/duktape-error-setter-writable.js + create mode 100644 dist/source/polyfills/duktape-isfastint.js + create mode 100644 dist/source/polyfills/global.js + create mode 100644 dist/source/polyfills/object-assign.js + create mode 100644 dist/source/polyfills/object-prototype-definegetter.js + create mode 100644 dist/source/polyfills/object-prototype-definesetter.js + create mode 100644 dist/source/polyfills/performance-now.js + create mode 100644 dist/source/polyfills/promise.js + create mode 100644 dist/source/src-input/SpecialCasing-8bit.txt + create mode 100644 dist/source/src-input/SpecialCasing.txt + create mode 100644 dist/source/src-input/UnicodeData-8bit.txt + create mode 100644 dist/source/src-input/UnicodeData.txt + create mode 100644 dist/source/src-input/builtins.yaml + create mode 100644 dist/source/src-input/duk_alloc_default.c + create mode 100644 dist/source/src-input/duk_api_buffer.c + create mode 100644 dist/source/src-input/duk_api_bytecode.c + create mode 100644 dist/source/src-input/duk_api_call.c + create mode 100644 dist/source/src-input/duk_api_codec.c + create mode 100644 dist/source/src-input/duk_api_compile.c + create mode 100644 dist/source/src-input/duk_api_debug.c + create mode 100644 dist/source/src-input/duk_api_heap.c + create mode 100644 dist/source/src-input/duk_api_inspect.c + create mode 100644 dist/source/src-input/duk_api_internal.h + create mode 100644 dist/source/src-input/duk_api_memory.c + create mode 100644 dist/source/src-input/duk_api_object.c + create mode 100644 dist/source/src-input/duk_api_random.c + create mode 100644 dist/source/src-input/duk_api_stack.c + create mode 100644 dist/source/src-input/duk_api_string.c + create mode 100644 dist/source/src-input/duk_api_time.c + create mode 100644 dist/source/src-input/duk_bi_array.c + create mode 100644 dist/source/src-input/duk_bi_boolean.c + create mode 100644 dist/source/src-input/duk_bi_buffer.c + create mode 100644 dist/source/src-input/duk_bi_cbor.c + create mode 100644 dist/source/src-input/duk_bi_date.c + create mode 100644 dist/source/src-input/duk_bi_date_unix.c + create mode 100644 dist/source/src-input/duk_bi_date_windows.c + create mode 100644 dist/source/src-input/duk_bi_duktape.c + create mode 100644 dist/source/src-input/duk_bi_encoding.c + create mode 100644 dist/source/src-input/duk_bi_error.c + create mode 100644 dist/source/src-input/duk_bi_function.c + create mode 100644 dist/source/src-input/duk_bi_global.c + create mode 100644 dist/source/src-input/duk_bi_json.c + create mode 100644 dist/source/src-input/duk_bi_math.c + create mode 100644 dist/source/src-input/duk_bi_number.c + create mode 100644 dist/source/src-input/duk_bi_object.c + create mode 100644 dist/source/src-input/duk_bi_performance.c + create mode 100644 dist/source/src-input/duk_bi_pointer.c + create mode 100644 dist/source/src-input/duk_bi_promise.c + create mode 100644 dist/source/src-input/duk_bi_protos.h + create mode 100644 dist/source/src-input/duk_bi_proxy.c + create mode 100644 dist/source/src-input/duk_bi_reflect.c + create mode 100644 dist/source/src-input/duk_bi_regexp.c + create mode 100644 dist/source/src-input/duk_bi_string.c + create mode 100644 dist/source/src-input/duk_bi_symbol.c + create mode 100644 dist/source/src-input/duk_bi_thread.c + create mode 100644 dist/source/src-input/duk_bi_thrower.c + create mode 100644 dist/source/src-input/duk_dblunion.h + create mode 100644 dist/source/src-input/duk_debug.h + create mode 100644 dist/source/src-input/duk_debug_fixedbuffer.c + create mode 100644 dist/source/src-input/duk_debug_macros.c + create mode 100644 dist/source/src-input/duk_debug_vsnprintf.c + create mode 100644 dist/source/src-input/duk_debugger.c + create mode 100644 dist/source/src-input/duk_debugger.h + create mode 100644 dist/source/src-input/duk_error.h + create mode 100644 dist/source/src-input/duk_error_augment.c + create mode 100644 dist/source/src-input/duk_error_longjmp.c + create mode 100644 dist/source/src-input/duk_error_macros.c + create mode 100644 dist/source/src-input/duk_error_misc.c + create mode 100644 dist/source/src-input/duk_error_throw.c + create mode 100644 dist/source/src-input/duk_exception.h + create mode 100644 dist/source/src-input/duk_fltunion.h + create mode 100644 dist/source/src-input/duk_forwdecl.h + create mode 100644 dist/source/src-input/duk_harray.h + create mode 100644 dist/source/src-input/duk_hboundfunc.h + create mode 100644 dist/source/src-input/duk_hbuffer.h + create mode 100644 dist/source/src-input/duk_hbuffer_alloc.c + create mode 100644 dist/source/src-input/duk_hbuffer_assert.c + create mode 100644 dist/source/src-input/duk_hbuffer_ops.c + create mode 100644 dist/source/src-input/duk_hbufobj.h + create mode 100644 dist/source/src-input/duk_hbufobj_misc.c + create mode 100644 dist/source/src-input/duk_hcompfunc.h + create mode 100644 dist/source/src-input/duk_heap.h + create mode 100644 dist/source/src-input/duk_heap_alloc.c + create mode 100644 dist/source/src-input/duk_heap_finalize.c + create mode 100644 dist/source/src-input/duk_heap_hashstring.c + create mode 100644 dist/source/src-input/duk_heap_markandsweep.c + create mode 100644 dist/source/src-input/duk_heap_memory.c + create mode 100644 dist/source/src-input/duk_heap_misc.c + create mode 100644 dist/source/src-input/duk_heap_refcount.c + create mode 100644 dist/source/src-input/duk_heap_stringcache.c + create mode 100644 dist/source/src-input/duk_heap_stringtable.c + create mode 100644 dist/source/src-input/duk_heaphdr.h + create mode 100644 dist/source/src-input/duk_heaphdr_assert.c + create mode 100644 dist/source/src-input/duk_henv.h + create mode 100644 dist/source/src-input/duk_hnatfunc.h + create mode 100644 dist/source/src-input/duk_hobject.h + create mode 100644 dist/source/src-input/duk_hobject_alloc.c + create mode 100644 dist/source/src-input/duk_hobject_assert.c + create mode 100644 dist/source/src-input/duk_hobject_class.c + create mode 100644 dist/source/src-input/duk_hobject_enum.c + create mode 100644 dist/source/src-input/duk_hobject_misc.c + create mode 100644 dist/source/src-input/duk_hobject_pc2line.c + create mode 100644 dist/source/src-input/duk_hobject_props.c + create mode 100644 dist/source/src-input/duk_hproxy.h + create mode 100644 dist/source/src-input/duk_hstring.h + create mode 100644 dist/source/src-input/duk_hstring_assert.c + create mode 100644 dist/source/src-input/duk_hstring_misc.c + create mode 100644 dist/source/src-input/duk_hthread.h + create mode 100644 dist/source/src-input/duk_hthread_alloc.c + create mode 100644 dist/source/src-input/duk_hthread_builtins.c + create mode 100644 dist/source/src-input/duk_hthread_misc.c + create mode 100644 dist/source/src-input/duk_hthread_stacks.c + create mode 100644 dist/source/src-input/duk_internal.h + create mode 100644 dist/source/src-input/duk_jmpbuf.h + create mode 100644 dist/source/src-input/duk_js.h + create mode 100644 dist/source/src-input/duk_js_arith.c + create mode 100644 dist/source/src-input/duk_js_bytecode.h + create mode 100644 dist/source/src-input/duk_js_call.c + create mode 100644 dist/source/src-input/duk_js_compiler.c + create mode 100644 dist/source/src-input/duk_js_compiler.h + create mode 100644 dist/source/src-input/duk_js_executor.c + create mode 100644 dist/source/src-input/duk_js_ops.c + create mode 100644 dist/source/src-input/duk_js_var.c + create mode 100644 dist/source/src-input/duk_json.h + create mode 100644 dist/source/src-input/duk_lexer.c + create mode 100644 dist/source/src-input/duk_lexer.h + create mode 100644 dist/source/src-input/duk_numconv.c + create mode 100644 dist/source/src-input/duk_numconv.h + create mode 100644 dist/source/src-input/duk_refcount.h + create mode 100644 dist/source/src-input/duk_regexp.h + create mode 100644 dist/source/src-input/duk_regexp_compiler.c + create mode 100644 dist/source/src-input/duk_regexp_executor.c + create mode 100644 dist/source/src-input/duk_replacements.c + create mode 100644 dist/source/src-input/duk_replacements.h + create mode 100644 dist/source/src-input/duk_selftest.c + create mode 100644 dist/source/src-input/duk_selftest.h + create mode 100644 dist/source/src-input/duk_strings.h + create mode 100644 dist/source/src-input/duk_tval.c + create mode 100644 dist/source/src-input/duk_tval.h + create mode 100644 dist/source/src-input/duk_unicode.h + create mode 100644 dist/source/src-input/duk_unicode_support.c + create mode 100644 dist/source/src-input/duk_unicode_tables.c + create mode 100644 dist/source/src-input/duk_util.h + create mode 100644 dist/source/src-input/duk_util_bitdecoder.c + create mode 100644 dist/source/src-input/duk_util_bitencoder.c + create mode 100644 dist/source/src-input/duk_util_bufwriter.c + create mode 100644 dist/source/src-input/duk_util_cast.c + create mode 100644 dist/source/src-input/duk_util_double.c + create mode 100644 dist/source/src-input/duk_util_hashbytes.c + create mode 100644 dist/source/src-input/duk_util_memory.c + create mode 100644 dist/source/src-input/duk_util_memrw.c + create mode 100644 dist/source/src-input/duk_util_misc.c + create mode 100644 dist/source/src-input/duk_util_tinyrandom.c + create mode 100644 dist/source/src-input/duktape.h.in + create mode 100644 dist/source/src-input/strings.yaml + create mode 100644 dist/source/src/duk_config.h + create mode 100644 dist/source/src/duk_source_meta.json + create mode 100644 dist/source/src/duktape.c + create mode 100644 dist/source/src/duktape.h + create mode 100644 dist/source/tools/combine_src.py + create mode 100644 dist/source/tools/configure.py + create mode 100644 dist/source/tools/create_spdx_license.py + create mode 100644 dist/source/tools/duk_meta_to_strarray.py + create mode 100644 dist/source/tools/dukutil.py + create mode 100644 dist/source/tools/dump_bytecode.py + create mode 100644 dist/source/tools/extract_caseconv.py + create mode 100644 dist/source/tools/extract_chars.py + create mode 100644 dist/source/tools/extract_unique_options.py + create mode 100644 dist/source/tools/genbuiltins.py + create mode 100644 dist/source/tools/genconfig.py + create mode 100644 dist/source/tools/json2yaml.py + create mode 100644 dist/source/tools/merge_debug_meta.py + create mode 100644 dist/source/tools/prepare_unicode_data.py + create mode 100644 dist/source/tools/resolve_combined_lineno.py + create mode 100644 dist/source/tools/scan_strings.py + create mode 100644 dist/source/tools/scan_used_stridx_bidx.py + create mode 100644 dist/source/tools/yaml2json.py + create mode 100644 prep/nondebug/duk_config.h + create mode 100644 prep/nondebug/duk_source_meta.json + create mode 100644 prep/nondebug/duktape.c + create mode 100644 prep/nondebug/duktape.h + +diff --git a/dist/source/AUTHORS.rst b/dist/source/AUTHORS.rst +new file mode 100644 +index 0000000..4bfebfc +--- /dev/null ++++ b/dist/source/AUTHORS.rst +@@ -0,0 +1,115 @@ ++=============== ++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 ++* Niki Dobrev ++* Andreas Öman ++* László Langó ++* Legimet ++* Karl Skomski ++* Bruce Pascoe ++* René Hollander ++* Julien Hamaide (https://github.com/crazyjul) ++* Sebastian Götte (https://github.com/jaseg) ++* Tomasz Magulski (https://github.com/magul) ++* \D. Bohdan (https://github.com/dbohdan) ++* Ondřej Jirman (https://github.com/megous) ++* Saúl Ibarra Corretgé ++* Jeremy HU ++* Ole André Vadla Ravnås (https://github.com/oleavr) ++* Harold Brenes (https://github.com/harold-b) ++* Oliver Crow (https://github.com/ocrow) ++* Jakub Chłapiński (https://github.com/jchlapinski) ++* Brett Vickers (https://github.com/beevik) ++* Dominik Okwieka (https://github.com/okitec) ++* Remko Tronçon (https://el-tramo.be) ++* Romero Malaquias (rbsm@ic.ufal.br) ++* Michael Drake ++* Steven Don (https://github.com/shdon) ++* Simon Stone (https://github.com/sstone1) ++* \J. McC. (https://github.com/jmhmccr) ++* Jakub Nowakowski (https://github.com/jimvonmoon) ++* Tommy Nguyen (https://github.com/tn0502) ++* Fabrice Fontaine (https://github.com/ffontaine) ++* Christopher Hiller (https://github.com/boneskull) ++* Gonzalo Diethelm (https://github.com/gonzus) ++* Michal Kasperek (https://github.com/michalkas) ++* Andrew Janke (https://github.com/apjanke) ++* Steve Fan (https://github.com/stevefan1999) ++* Edward Betts (https://github.com/edwardbetts) ++* Ozhan Duz (https://github.com/webfolderio) ++* Akos Kiss (https://github.com/akosthekiss) ++* TheBrokenRail (https://github.com/TheBrokenRail) ++* Jesse Doyle (https://github.com/jessedoyle) ++* Gero Kuehn (https://github.com/dc6jgk) ++* James Swift (https://github.com/phraemer) ++* Luis de Bethencourt (https://github.com/luisbg) ++* Ian Whyman (https://github.com/v00d00) ++* Rick Sayre (https://github.com/whorfin) ++* Craig Leres (https://github.com/leres) ++* Maurici Abad (https://github.com/mauriciabad) ++* Nancy Li (https://github.com/NancyLi1013) ++* William Parks (https://github.com/WilliamParks) ++ ++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 ++* Laurent Zubiaur (https://github.com/lzubiaur) ++* Neil Kolban (https://github.com/nkolban) ++* Wilhelm Wanecek (https://github.com/wanecek) ++* Andrew Janke (https://github.com/apjanke) ++* Unamer (https://github.com/unamer) ++* Karl Dahlke (eklhad@gmail.com) ++ ++If you are accidentally missing from this list, send me an e-mail ++(``sami.vaarala@iki.fi``) and I'll fix the omission. +diff --git a/dist/source/LICENSE.txt b/dist/source/LICENSE.txt +new file mode 100644 +index 0000000..783fd1b +--- /dev/null ++++ b/dist/source/LICENSE.txt +@@ -0,0 +1,25 @@ ++=============== ++Duktape license ++=============== ++ ++(http://opensource.org/licenses/MIT) ++ ++Copyright (c) 2013-2020 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. +diff --git a/dist/source/Makefile.cmdline b/dist/source/Makefile.cmdline +new file mode 100644 +index 0000000..235566b +--- /dev/null ++++ b/dist/source/Makefile.cmdline +@@ -0,0 +1,47 @@ ++# ++# Example Makefile for building a program with embedded Duktape. ++# The example program here is the Duktape command line tool. ++# ++ ++DUKTAPE_SOURCES = src/duktape.c ++ ++CMDLINE_SOURCES = \ ++ examples/cmdline/duk_cmdline.c ++ ++CC = gcc ++CCOPTS = -Os -pedantic -std=c99 -Wall -fstrict-aliasing -fomit-frame-pointer ++CCOPTS += -I./examples/cmdline -I./src # duktape.h and duk_config.h must be in include path ++CCLIBS = -lm ++ ++# Enable print() and alert() for command line using an optional extra module. ++CCOPTS += -DDUK_CMDLINE_PRINTALERT_SUPPORT -I./extras/print-alert ++CMDLINE_SOURCES += extras/print-alert/duk_print_alert.c ++ ++# Enable console object (console.log() etc) for command line. ++CCOPTS += -DDUK_CMDLINE_CONSOLE_SUPPORT -I./extras/console ++CMDLINE_SOURCES += extras/console/duk_console.c ++ ++# Enable Duktape.Logger for command line. ++CCOPTS += -DDUK_CMDLINE_LOGGING_SUPPORT -I./extras/logging ++CMDLINE_SOURCES += extras/logging/duk_logging.c ++ ++# Enable Duktape 1.x module loading for command line. ++CCOPTS += -DDUK_CMDLINE_MODULE_SUPPORT -I./extras/module-duktape ++CMDLINE_SOURCES += extras/module-duktape/duk_module_duktape.c ++ ++# If you want linenoise, you can enable these. At the moment linenoise ++# will cause some harmless compilation warnings. ++#CCOPTS += -DDUK_CMDLINE_FANCY -I./linenoise ++#CMDLINE_SOURCES += linenoise/linenoise.c ++#duk: linenoise ++ ++# Use the tools/configure.py utility to modify Duktape default configuration: ++# http://duktape.org/guide.html#compiling ++# http://wiki.duktape.org/Configuring.html ++ ++duk: $(DUKTAPE_SOURCES) $(CMDLINE_SOURCES) ++ $(CC) -o $@ $(DEFINES) $(CCOPTS) $(DUKTAPE_SOURCES) $(CMDLINE_SOURCES) $(CCLIBS) ++ ++linenoise/linenoise.c: linenoise ++linenoise: ++ git clone https://github.com/antirez/linenoise.git +diff --git a/dist/source/Makefile.codepage b/dist/source/Makefile.codepage +new file mode 100644 +index 0000000..f6ef158 +--- /dev/null ++++ b/dist/source/Makefile.codepage +@@ -0,0 +1,6 @@ ++CC = gcc ++ ++codepage: ++ $(CC) -o $@ -std=c99 -O2 -Wall -Wextra -Isrc/ \ ++ src/duktape.c examples/codepage-conv/duk_codepage_conv.c \ ++ examples/codepage-conv/test.c -lm +diff --git a/dist/source/Makefile.coffee b/dist/source/Makefile.coffee +new file mode 100644 +index 0000000..b99eea2 +--- /dev/null ++++ b/dist/source/Makefile.coffee +@@ -0,0 +1,4 @@ ++dummy: ++ coffee -c examples/coffee/globals.coffee ++ coffee -c examples/coffee/hello.coffee ++ coffee -c examples/coffee/mandel.coffee +diff --git a/dist/source/Makefile.dukdebug b/dist/source/Makefile.dukdebug +new file mode 100644 +index 0000000..9fafcc3 +--- /dev/null ++++ b/dist/source/Makefile.dukdebug +@@ -0,0 +1,41 @@ ++# ++# Duktape command line tool with debugger support. ++# ++ ++DUKTAPE_SOURCES = prep/duktape.c ++ ++# Windows (MinGW): use examples/debug-trans-socket/duk_trans_socket_windows.c ++# and link with -lws2_32. ++CMDLINE_SOURCES = \ ++ examples/cmdline/duk_cmdline.c \ ++ examples/debug-trans-socket/duk_trans_socket_unix.c ++ ++CC = gcc ++CCOPTS = -Os -pedantic -std=c99 -Wall -fstrict-aliasing -fomit-frame-pointer ++CCOPTS += -I./prep -I./examples/cmdline -I./examples/debug-trans-socket ++CCOPTS += -DDUK_CMDLINE_DEBUGGER_SUPPORT # enable --debugger in ./duk ++CCLIBS = -lm ++ ++# Enable a few optional modules. ++CCOPTS += -DDUK_CMDLINE_PRINTALERT_SUPPORT -I./extras/print-alert ++CMDLINE_SOURCES += extras/print-alert/duk_print_alert.c ++CCOPTS += -DDUK_CMDLINE_CONSOLE_SUPPORT -I./extras/console ++CMDLINE_SOURCES += extras/console/duk_console.c ++CCOPTS += -DDUK_CMDLINE_LOGGING_SUPPORT -I./extras/logging ++CMDLINE_SOURCES += extras/logging/duk_logging.c ++CCOPTS += -DDUK_CMDLINE_MODULE_SUPPORT -I./extras/module-duktape ++CMDLINE_SOURCES += extras/module-duktape/duk_module_duktape.c ++ ++# Use tools/configure.py to prepare Duktape config header and sources with ++# custom configuration. ++duk: $(CMDLINE_SOURCES) ++ @rm -rf prep ++ python2 tools/configure.py \ ++ --source-directory src-input \ ++ --output-directory prep \ ++ --config-metadata config \ ++ -DDUK_USE_DEBUGGER_SUPPORT \ ++ -DDUK_USE_INTERRUPT_COUNTER \ ++ -DDUK_USE_DEBUGGER_DUMPHEAP \ ++ -DDUK_USE_DEBUGGER_INSPECT ++ $(CC) -o $@ $(DEFINES) $(CCOPTS) $(DUKTAPE_SOURCES) $(CMDLINE_SOURCES) $(CCLIBS) +diff --git a/dist/source/Makefile.eval b/dist/source/Makefile.eval +new file mode 100644 +index 0000000..530f74a +--- /dev/null ++++ b/dist/source/Makefile.eval +@@ -0,0 +1,9 @@ ++# ++# Example Makefile for building the eval example ++# ++ ++CC = gcc ++ ++eval: ++ $(CC) -o $@ -std=c99 -O2 -Wall -Wextra -Isrc/ \ ++ src/duktape.c examples/eval/eval.c -lm +diff --git a/dist/source/Makefile.eventloop b/dist/source/Makefile.eventloop +new file mode 100644 +index 0000000..1a75587 +--- /dev/null ++++ b/dist/source/Makefile.eventloop +@@ -0,0 +1,23 @@ ++# ++# Example Makefile for building the eventloop example ++# ++ ++CC = gcc ++ ++evloop: ++ @echo "NOTE: The eventloop is an example intended to be used on Linux" ++ @echo " or other common UNIX variants. It is not fully portable." ++ @echo "" ++ ++ $(CC) -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 \ ++ src/duktape.c \ ++ -lm ++ ++ @echo "" ++ @echo "NOTE: You must 'cd examples/eventloop' before you execute the" ++ @echo " eventloop binary: it relies on finding .js files in CWD" +diff --git a/dist/source/Makefile.hello b/dist/source/Makefile.hello +new file mode 100644 +index 0000000..c12b8e0 +--- /dev/null ++++ b/dist/source/Makefile.hello +@@ -0,0 +1,32 @@ ++# ++# 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 ++ ++# Use the tools/configure.py utility to modify Duktape default configuration: ++# http://duktape.org/guide.html#compiling ++# http://wiki.duktape.org/Configuring.html ++ ++# 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) +diff --git a/dist/source/Makefile.jsoncbor b/dist/source/Makefile.jsoncbor +new file mode 100644 +index 0000000..30387a2 +--- /dev/null ++++ b/dist/source/Makefile.jsoncbor +@@ -0,0 +1,10 @@ ++# ++# Example Makefile for building the jsoncbor example ++# ++ ++CC = gcc ++ ++jsoncbor: ++ $(CC) -o $@ -std=c99 -Wall -Wextra -O2 -Isrc -Iextras/cbor \ ++ src/duktape.c extras/cbor/duk_cbor.c extras/cbor/jsoncbor.c \ ++ -lm +diff --git a/dist/source/Makefile.jxpretty b/dist/source/Makefile.jxpretty +new file mode 100644 +index 0000000..7ab00ce +--- /dev/null ++++ b/dist/source/Makefile.jxpretty +@@ -0,0 +1,10 @@ ++# ++# Example Makefile for building the jxpretty example ++# ++ ++CC = gcc ++ ++jxpretty: ++ $(CC) -o $@ -std=c99 -Wall -Wextra -O2 -Isrc \ ++ src/duktape.c examples/jxpretty/jxpretty.c \ ++ -lm +diff --git a/dist/source/Makefile.sandbox b/dist/source/Makefile.sandbox +new file mode 100644 +index 0000000..26bb5ff +--- /dev/null ++++ b/dist/source/Makefile.sandbox +@@ -0,0 +1,9 @@ ++# ++# Example Makefile for building the sandbox example ++# ++ ++CC = gcc ++ ++sandbox: ++ $(CC) -o $@ -std=c99 -O2 -Wall -Wextra -Isrc/ \ ++ src/duktape.c examples/sandbox/sandbox.c -lm +diff --git a/dist/source/Makefile.sharedlibrary b/dist/source/Makefile.sharedlibrary +new file mode 100644 +index 0000000..e37e088 +--- /dev/null ++++ b/dist/source/Makefile.sharedlibrary +@@ -0,0 +1,88 @@ ++# ++# Example of how to build and install locally as a shared library ++# ++# Usage: ++# ++# $ make -f Makefile.sharedlibrary ++# $ sudo make -f Makefile.sharedlibrary install ++# $ make -f Makefile.sharedlibrary duk # --> example 'duk' linked to shared libduktape ++# ++# $ ls -l duk ++# -rwxrwxr-x 1 duktape duktape 19407 Nov 30 15:48 duk ++# ++# $ ldd ./duk ++# linux-vdso.so.1 => (0x00007ffd5ed3c000) ++# libduktape.so.104 => /usr/local/lib/libduktape.so.104 (0x00007fb2f9753000) ++# libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fb2f944d000) ++# libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb2f9088000) ++# /lib64/ld-linux-x86-64.so.2 (0x00007fb2f9991000) ++# ++# Based on: http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html ++ ++# Soname version must be bumped whenever a binary compatibility change occurs ++# (and should not be bumped when the library is compatible). A simple Duktape ++# convention is to set soname version to (100*MAJOR + MINOR), e.g. 104 for ++# Duktape 1.4.x, so that it gets automatically bumped for major and minor ++# releases (potentially binary incompatible), but not for patch releases. ++DUK_VERSION = 29999 ++SONAME_VERSION = 299 ++REAL_VERSION = $(SONAME_VERSION).$(DUK_VERSION) ++ ++# Mac has an unusual .so naming convention ++ifeq ($(OS),Windows_NT) ++ DETECTED_OS := Windows ++else ++ DETECTED_OS := $(shell uname -s) ++endif ++ifeq ($(DETECTED_OS),Darwin) ++ LD_SONAME_ARG=-install_name ++ SO_SONAME_SUFFIX=$(SONAME_VERSION).so ++ SO_REALNAME_SUFFIX=$(REAL_VERSION).so ++else ++ LD_SONAME_ARG=-soname ++ SO_SONAME_SUFFIX=so.$(SONAME_VERSION) ++ SO_REALNAME_SUFFIX=so.$(REAL_VERSION) ++endif ++ ++# Change to actual path for actual distribution packaging. ++INSTALL_PREFIX = /usr/local ++ ++# The 'noline' variant may be more appropriate for some distributions; it ++# doesn't have #line directives in the combined source. ++DUKTAPE_SRCDIR = ./src ++#DUKTAPE_SRCDIR = ./src-noline ++ ++CC = gcc ++ ++.PHONY: all ++all: libduktape.$(SO_REALNAME_SUFFIX) libduktaped.$(SO_REALNAME_SUFFIX) ++ ++# If the default duk_config.h is not suitable for the distribution, modify it ++# before compiling the shared library and copy the same, edited duk_config.h ++# to $INSTALL_PREFIX/include on installation. ++ ++libduktape.$(SO_REALNAME_SUFFIX): ++ $(CC) -shared -fPIC -Wall -Wextra -Os -Wl,$(LD_SONAME_ARG),libduktape.$(SO_SONAME_SUFFIX) \ ++ -o $@ $(DUKTAPE_SRCDIR)/duktape.c ++ ++libduktaped.$(SO_REALNAME_SUFFIX): ++ $(CC) -shared -fPIC -g -Wall -Wextra -Os -Wl,$(LD_SONAME_ARG),libduktaped.$(SO_SONAME_SUFFIX) \ ++ -o $@ $(DUKTAPE_SRCDIR)/duktape.c ++ ++# Symlinks depend on platform conventions. ++.PHONY: install ++install: libduktape.$(SO_REALNAME_SUFFIX) libduktaped.$(SO_REALNAME_SUFFIX) ++ mkdir -p $(INSTALL_PREFIX)/lib/ ++ cp $+ $(INSTALL_PREFIX)/lib/ ++ rm -f $(INSTALL_PREFIX)/lib/libduktape.so $(INSTALL_PREFIX)/lib/libduktape.$(SO_SONAME_SUFFIX) ++ ln -s libduktape.$(SO_REALNAME_SUFFIX) $(INSTALL_PREFIX)/lib/libduktape.so ++ ln -s libduktape.$(SO_REALNAME_SUFFIX) $(INSTALL_PREFIX)/lib/libduktape.$(SO_SONAME_SUFFIX) ++ rm -f $(INSTALL_PREFIX)/lib/libduktaped.so $(INSTALL_PREFIX)/lib/libduktaped.$(SO_SONAME_SUFFIX) ++ ln -s libduktaped.$(SO_REALNAME_SUFFIX) $(INSTALL_PREFIX)/lib/libduktaped.so ++ ln -s libduktaped.$(SO_REALNAME_SUFFIX) $(INSTALL_PREFIX)/lib/libduktaped.$(SO_SONAME_SUFFIX) ++ mkdir -p $(INSTALL_PREFIX)/include/ ++ cp $(DUKTAPE_SRCDIR)/duktape.h $(DUKTAPE_SRCDIR)/duk_config.h $(INSTALL_PREFIX)/include/ ++ ++CCOPTS = -I./examples/cmdline ++duk: ++ $(CC) $(CCOPTS) -I$(INSTALL_PREFIX)/include -L$(INSTALL_PREFIX)/lib -Wall -Wextra -Os -o $@ ./examples/cmdline/duk_cmdline.c -lduktape -lm +diff --git a/dist/source/README.rst b/dist/source/README.rst +new file mode 100644 +index 0000000..d3b6130 +--- /dev/null ++++ b/dist/source/README.rst +@@ -0,0 +1,129 @@ ++======= ++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 (with some semantics updated ++from ES2015+) including errors, Unicode strings, and regular expressions, ++a subset of ECMAScript 2015 (E6) and ECMAScript 2016 (E7) features (e.g. ++computed property names, Proxy objects, exponentiation operator, Reflect), ++ES2015 ArrayBuffer/TypedView, Node.js Buffer, performance.now(), CBOR, and ++WHATWG Encoding API living standard. ++ ++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, a built-in debugger protocol, function ++bytecode dump/load, and so on. Bundled extra modules provide functionality ++such as CommonJS module loading and a logging framework. ++ ++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/ ++ ++To build an example command line tool with interactive evaluation (REPL) and ++the ability to run script files:: ++ ++ $ cd ++ $ make -f Makefile.cmdline ++ [...] ++ ++ $ ./duk ++ ((o) Duktape ++ duk> print('Hello world!'); ++ Hello world! ++ = undefined ++ ++ $ ./duk mandel.js ++ [...] ++ ++To integrate Duktape into your program: ++ ++* Use ``tools/configure.py`` to prepare Duktape source and header files ++ for build:: ++ ++ # Duktape options can be customized via command line options. ++ # In this example, enable "fastint" support and disable ES2015 ++ # Proxy support ++ ++ $ python2 tools/configure.py --output-directory duktape-src \ ++ -DDUK_USE_FASTINT -UDUK_USE_ES6_PROXY ++ ++* The output directory (duktape-src) will then contain ``duktape.c``, ++ ``duktape.h``, and ``duk_config.h`` which you include in your build. ++ ++For more details, see: ++ ++* http://duktape.org/guide.html#compiling ++ ++* http://wiki.duktape.org/Configuring.html ++ ++This distributable contains: ++ ++* ``src/``: main Duktape library in a "single source file" format (duktape.c, ++ duktape.h, and duk_config.h; no ``#line`` directives), using Duktape default ++ configuration. ++ ++* ``src-input/``: raw input source files used by ``configure.py`` for creating ++ custom configurations. ++ ++* ``tools/``: various Python tools, such as ``configure.py`` for preparing ++ a ``duk_config.h`` header and Duktape source files for compilation, see ++ http://wiki.duktape.org/Configuring.html. ++ ++* ``config/``: configuration metadata for ``configure.py``. ++ ++* ``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. Also contains a JSON debug proxy ++ (one written in Node.js and another in DukLuv) to make talking to the ++ debug target easier. ++ ++* ``licenses/``: licensing information. ++ ++You can find release notes at: ++ ++* https://github.com/svaarala/duktape/blob/master/RELEASES.rst ++ (summary of all versions) ++ ++* https://github.com/svaarala/duktape/blob/master/doc/release-notes-v2-99.rst ++ (more detailed notes for this version) ++ ++This distributable contains Duktape version 2.99.99, created from git ++commit 65f1e02e580796929e92e91ba4776d4b0131beb5 (65f1e02). ++ ++Duktape is copyrighted by its authors (see ``AUTHORS.rst``) and licensed ++under the MIT license (see ``LICENSE.txt``). Duktape contains independent ++implementations, with no included code, of the following algorithms: Lua ++string hash (MIT license), djb2 hash, Murmurhash2 (MIT license), Adi ++Shamir's three-op hash algorithm, xoroshiro128+ PRNG (public domain), ++SplitMix64 PRNG (public domain), and CommonJS module loading specification ++(MIT license). ++ ++Have fun! ++ ++Sami Vaarala (sami.vaarala@iki.fi) +diff --git a/dist/source/config/README.rst b/dist/source/config/README.rst +new file mode 100644 +index 0000000..7be861f +--- /dev/null ++++ b/dist/source/config/README.rst +@@ -0,0 +1,19 @@ ++================= ++Duktape genconfig ++================= ++ ++``genconfig`` is a helper script for coming up with a ``duk_config.h`` for ++compiling Duktape for your platform. ++ ++To support this: ++ ++* 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 based on option metadata ++ files written in YAML. ++ ++NOTE: ``tools/configure.py`` is now the preferred tool for preparing a config ++header (using genconfig.py) and Duktape source files (using other tools) for ++build. +diff --git a/dist/source/config/architectures.yaml b/dist/source/config/architectures.yaml +new file mode 100644 +index 0000000..94f044a +--- /dev/null ++++ b/dist/source/config/architectures.yaml +@@ -0,0 +1,71 @@ ++# Architecture metadata. ++ ++autodetect: ++ - ++ name: x86 ++ check: DUK_F_X86 ++ include: architecture_x86.h.in ++ - ++ name: x64 ++ check: DUK_F_X64 ++ include: architecture_x64.h.in ++ - ++ name: x32 ++ check: DUK_F_X32 ++ include: architecture_x32.h.in ++ - ++ name: ARM 32-bit ++ check: DUK_F_ARM32 ++ include: architecture_arm32.h.in ++ - ++ name: ARM 64-bit ++ check: DUK_F_ARM64 ++ include: architecture_arm64.h.in ++ - ++ name: MIPS 32-bit ++ check: DUK_F_MIPS32 ++ include: architecture_mips32.h.in ++ - ++ name: MIPS 64-bit ++ check: DUK_F_MIPS64 ++ include: architecture_mips64.h.in ++ - ++ name: PowerPC 32-bit ++ check: DUK_F_PPC32 ++ include: architecture_powerpc32.h.in ++ - ++ name: PowerPC 64-bit ++ check: DUK_F_PPC64 ++ include: architecture_powerpc64.h.in ++ - ++ name: SPARC 32-bit ++ check: DUK_F_SPARC32 ++ include: architecture_sparc32.h.in ++ - ++ name: SPARC 64-bit ++ check: DUK_F_SPARC64 ++ include: architecture_sparc64.h.in ++ - ++ name: RISC-V 32-bit ++ check: DUK_F_RISCV32 ++ include: architecture_riscv32.h.in ++ - ++ name: RISC-V 64-bit ++ check: DUK_F_RISCV64 ++ include: architecture_riscv64.h.in ++ - ++ name: SuperH ++ check: DUK_F_SUPERH ++ include: architecture_superh.h.in ++ - ++ name: Motorola 68k ++ check: DUK_F_M68K ++ include: architecture_m68k.h.in ++ - ++ name: Emscripten ++ check: DUK_F_EMSCRIPTEN ++ include: architecture_emscripten.h.in ++ - ++ name: Generic ++ check: null ++ include: architecture_generic.h.in +diff --git a/dist/source/config/architectures/architecture_arm32.h.in b/dist/source/config/architectures/architecture_arm32.h.in +new file mode 100644 +index 0000000..1eb6462 +--- /dev/null ++++ b/dist/source/config/architectures/architecture_arm32.h.in +@@ -0,0 +1,3 @@ ++#define DUK_USE_ARCH_STRING "arm32" ++/* Byte order varies, so rely on autodetect. */ ++#define DUK_USE_PACKED_TVAL +diff --git a/dist/source/config/architectures/architecture_arm64.h.in b/dist/source/config/architectures/architecture_arm64.h.in +new file mode 100644 +index 0000000..f732ce4 +--- /dev/null ++++ b/dist/source/config/architectures/architecture_arm64.h.in +@@ -0,0 +1,3 @@ ++#define DUK_USE_ARCH_STRING "arm64" ++/* Byte order varies, so rely on autodetect. */ ++#undef DUK_USE_PACKED_TVAL +diff --git a/dist/source/config/architectures/architecture_emscripten.h.in b/dist/source/config/architectures/architecture_emscripten.h.in +new file mode 100644 +index 0000000..ea54e71 +--- /dev/null ++++ b/dist/source/config/architectures/architecture_emscripten.h.in +@@ -0,0 +1,5 @@ ++#define DUK_USE_ARCH_STRING "emscripten" ++#if !defined(DUK_USE_BYTEORDER) ++#define DUK_USE_BYTEORDER 1 ++#endif ++#undef DUK_USE_PACKED_TVAL +diff --git a/dist/source/config/architectures/architecture_generic.h.in b/dist/source/config/architectures/architecture_generic.h.in +new file mode 100644 +index 0000000..783f58b +--- /dev/null ++++ b/dist/source/config/architectures/architecture_generic.h.in +@@ -0,0 +1,3 @@ ++/* These are necessary wild guesses. */ ++#define DUK_USE_ARCH_STRING "generic" ++/* Rely on autodetection for byte order, alignment, and packed tval. */ +diff --git a/dist/source/config/architectures/architecture_m68k.h.in b/dist/source/config/architectures/architecture_m68k.h.in +new file mode 100644 +index 0000000..26260ea +--- /dev/null ++++ b/dist/source/config/architectures/architecture_m68k.h.in +@@ -0,0 +1,5 @@ ++#define DUK_USE_ARCH_STRING "m68k" ++#if !defined(DUK_USE_BYTEORDER) ++#define DUK_USE_BYTEORDER 3 ++#endif ++#define DUK_USE_PACKED_TVAL +diff --git a/dist/source/config/architectures/architecture_mips32.h.in b/dist/source/config/architectures/architecture_mips32.h.in +new file mode 100644 +index 0000000..772f8a2 +--- /dev/null ++++ b/dist/source/config/architectures/architecture_mips32.h.in +@@ -0,0 +1,3 @@ ++#define DUK_USE_ARCH_STRING "mips32" ++/* MIPS byte order varies so rely on autodetection. */ ++#define DUK_USE_PACKED_TVAL +diff --git a/dist/source/config/architectures/architecture_mips64.h.in b/dist/source/config/architectures/architecture_mips64.h.in +new file mode 100644 +index 0000000..90ddcb3 +--- /dev/null ++++ b/dist/source/config/architectures/architecture_mips64.h.in +@@ -0,0 +1,3 @@ ++#define DUK_USE_ARCH_STRING "mips64" ++/* MIPS byte order varies so rely on autodetection. */ ++#undef DUK_USE_PACKED_TVAL +diff --git a/dist/source/config/architectures/architecture_powerpc32.h.in b/dist/source/config/architectures/architecture_powerpc32.h.in +new file mode 100644 +index 0000000..111bc03 +--- /dev/null ++++ b/dist/source/config/architectures/architecture_powerpc32.h.in +@@ -0,0 +1,5 @@ ++#define DUK_USE_ARCH_STRING "ppc32" ++#if !defined(DUK_USE_BYTEORDER) ++#define DUK_USE_BYTEORDER 3 ++#endif ++#define DUK_USE_PACKED_TVAL +diff --git a/dist/source/config/architectures/architecture_powerpc64.h.in b/dist/source/config/architectures/architecture_powerpc64.h.in +new file mode 100644 +index 0000000..d9cb0aa +--- /dev/null ++++ b/dist/source/config/architectures/architecture_powerpc64.h.in +@@ -0,0 +1,3 @@ ++#define DUK_USE_ARCH_STRING "ppc64" ++/* No forced byteorder (both little and big endian are possible). */ ++#undef DUK_USE_PACKED_TVAL +diff --git a/dist/source/config/architectures/architecture_riscv32.h.in b/dist/source/config/architectures/architecture_riscv32.h.in +new file mode 100644 +index 0000000..ba42086 +--- /dev/null ++++ b/dist/source/config/architectures/architecture_riscv32.h.in +@@ -0,0 +1,3 @@ ++#define DUK_USE_ARCH_STRING "riscv32" ++#define DUK_USE_BYTEORDER 1 ++#define DUK_USE_PACKED_TVAL +diff --git a/dist/source/config/architectures/architecture_riscv64.h.in b/dist/source/config/architectures/architecture_riscv64.h.in +new file mode 100644 +index 0000000..dd7eb1f +--- /dev/null ++++ b/dist/source/config/architectures/architecture_riscv64.h.in +@@ -0,0 +1,3 @@ ++#define DUK_USE_ARCH_STRING "riscv64" ++#define DUK_USE_BYTEORDER 1 ++#undef DUK_USE_PACKED_TVAL +diff --git a/dist/source/config/architectures/architecture_sparc32.h.in b/dist/source/config/architectures/architecture_sparc32.h.in +new file mode 100644 +index 0000000..ba43b72 +--- /dev/null ++++ b/dist/source/config/architectures/architecture_sparc32.h.in +@@ -0,0 +1,3 @@ ++#define DUK_USE_ARCH_STRING "sparc32" ++/* SPARC byte order varies so rely on autodetection. */ ++#define DUK_USE_PACKED_TVAL +diff --git a/dist/source/config/architectures/architecture_sparc64.h.in b/dist/source/config/architectures/architecture_sparc64.h.in +new file mode 100644 +index 0000000..37b0dc0 +--- /dev/null ++++ b/dist/source/config/architectures/architecture_sparc64.h.in +@@ -0,0 +1,3 @@ ++#define DUK_USE_ARCH_STRING "sparc64" ++/* SPARC byte order varies so rely on autodetection. */ ++#undef DUK_USE_PACKED_TVAL +diff --git a/dist/source/config/architectures/architecture_superh.h.in b/dist/source/config/architectures/architecture_superh.h.in +new file mode 100644 +index 0000000..0aa4d55 +--- /dev/null ++++ b/dist/source/config/architectures/architecture_superh.h.in +@@ -0,0 +1,3 @@ ++#define DUK_USE_ARCH_STRING "sh" ++/* Byte order varies, rely on autodetection. */ ++#define DUK_USE_PACKED_TVAL +diff --git a/dist/source/config/architectures/architecture_x32.h.in b/dist/source/config/architectures/architecture_x32.h.in +new file mode 100644 +index 0000000..2100c76 +--- /dev/null ++++ b/dist/source/config/architectures/architecture_x32.h.in +@@ -0,0 +1,5 @@ ++#define DUK_USE_ARCH_STRING "x32" ++#if !defined(DUK_USE_BYTEORDER) ++#define DUK_USE_BYTEORDER 1 ++#endif ++#define DUK_USE_PACKED_TVAL +diff --git a/dist/source/config/architectures/architecture_x64.h.in b/dist/source/config/architectures/architecture_x64.h.in +new file mode 100644 +index 0000000..7925b1e +--- /dev/null ++++ b/dist/source/config/architectures/architecture_x64.h.in +@@ -0,0 +1,5 @@ ++#define DUK_USE_ARCH_STRING "x64" ++#if !defined(DUK_USE_BYTEORDER) ++#define DUK_USE_BYTEORDER 1 ++#endif ++#undef DUK_USE_PACKED_TVAL +diff --git a/dist/source/config/architectures/architecture_x86.h.in b/dist/source/config/architectures/architecture_x86.h.in +new file mode 100644 +index 0000000..ae572bd +--- /dev/null ++++ b/dist/source/config/architectures/architecture_x86.h.in +@@ -0,0 +1,14 @@ ++#define DUK_USE_ARCH_STRING "x86" ++#if !defined(DUK_USE_BYTEORDER) ++#define DUK_USE_BYTEORDER 1 ++#endif ++ ++#define DUK_USE_PACKED_TVAL ++ ++/* FreeBSD, -m32, and clang prior to 5.0 has union aliasing issues which ++ * break duk_tval copying. Disable packed duk_tval automatically. ++ */ ++#if defined(DUK_F_FREEBSD) && defined(DUK_F_X86) && \ ++ defined(__clang__) && defined(__clang_major__) && (__clang_major__ < 5) ++#undef DUK_USE_PACKED_TVAL ++#endif +diff --git a/dist/source/config/compilers.yaml b/dist/source/config/compilers.yaml +new file mode 100644 +index 0000000..4399cc2 +--- /dev/null ++++ b/dist/source/config/compilers.yaml +@@ -0,0 +1,35 @@ ++# Compiler metadata. ++ ++autodetect: ++ - ++ name: Clang ++ check: DUK_F_CLANG ++ include: compiler_clang.h.in ++ - ++ name: GCC ++ check: DUK_F_GCC ++ include: compiler_gcc.h.in ++ - ++ name: MSVC ++ check: DUK_F_MSVC ++ include: compiler_msvc.h.in ++ - ++ name: Emscripten ++ check: DUK_F_EMSCRIPTEN ++ include: compiler_emscripten.h.in ++ - ++ name: TinyC ++ check: DUK_F_TINYC ++ include: compiler_tinyc.h.in ++ - ++ name: VBCC ++ check: DUK_F_VBCC ++ include: compiler_vbcc.h.in ++ - ++ name: Bruce's C compiler ++ check: DUK_F_BCC ++ include: compiler_bcc.h.in ++ - ++ name: Generic ++ check: null ++ include: compiler_generic.h.in +diff --git a/dist/source/config/compilers/compiler_bcc.h.in b/dist/source/config/compilers/compiler_bcc.h.in +new file mode 100644 +index 0000000..a12bf62 +--- /dev/null ++++ b/dist/source/config/compilers/compiler_bcc.h.in +@@ -0,0 +1,24 @@ ++#undef DUK_USE_BRANCH_HINTS ++ ++#if defined(DUK_F_CPP) ++#define DUK_USE_COMPILER_STRING "bcc++" ++#else ++#define DUK_USE_COMPILER_STRING "bcc" ++#endif ++ ++/* Most portable */ ++#undef DUK_USE_VARIADIC_MACROS ++ ++/* Most portable, wastes space */ ++#undef DUK_USE_UNION_INITIALIZERS ++ ++/* Most portable, wastes space */ ++#define DUK_USE_FLEX_ONESIZE ++ ++/* Most portable, potentially wastes space */ ++#define DUK_USE_PACK_DUMMY_MEMBER ++ ++/* BCC, assume we're on x86. */ ++#if !defined(DUK_USE_BYTEORDER) ++#define DUK_USE_BYTEORDER 1 ++#endif +diff --git a/dist/source/config/compilers/compiler_clang.h.in b/dist/source/config/compilers/compiler_clang.h.in +new file mode 100644 +index 0000000..5d38ae4 +--- /dev/null ++++ b/dist/source/config/compilers/compiler_clang.h.in +@@ -0,0 +1,76 @@ ++#if defined(DUK_F_C99) || defined(DUK_F_CPP11) ++/* C99 / C++11 and above: rely on va_copy() which is required. */ ++#define DUK_VA_COPY(dest,src) va_copy(dest,src) ++#else ++/* Clang: assume we have __va_copy() in non-C99 mode. */ ++#define DUK_VA_COPY(dest,src) __va_copy(dest,src) ++#endif ++ ++#define DUK_NORETURN(decl) decl __attribute__((noreturn)) ++ ++#if defined(__clang__) && defined(__has_builtin) ++#if __has_builtin(__builtin_unreachable) ++#define DUK_UNREACHABLE() do { __builtin_unreachable(); } while (0) ++#endif ++#endif ++ ++#define DUK_USE_BRANCH_HINTS ++#define DUK_LIKELY(x) __builtin_expect((x), 1) ++#define DUK_UNLIKELY(x) __builtin_expect((x), 0) ++#if defined(__clang__) && defined(__has_builtin) ++#if __has_builtin(__builtin_unpredictable) ++#define DUK_UNPREDICTABLE(x) __builtin_unpredictable((x)) ++#endif ++#endif ++ ++#if defined(DUK_F_C99) || defined(DUK_F_CPP11) ++#define DUK_NOINLINE __attribute__((noinline)) ++#define DUK_INLINE inline ++#define DUK_ALWAYS_INLINE inline __attribute__((always_inline)) ++#endif ++ ++/* DUK_HOT */ ++/* DUK_COLD */ ++ ++#if defined(DUK_F_DLL_BUILD) && defined(DUK_F_WINDOWS) ++#snippet "msvc_visibility.h.in" ++#else ++#snippet "gcc_clang_visibility.h.in" ++#endif ++ ++#if defined(DUK_F_CPP) ++#define DUK_USE_COMPILER_STRING "clang" ++#else ++#define DUK_USE_COMPILER_STRING "clang" ++#endif ++ ++#undef DUK_USE_VARIADIC_MACROS ++#if defined(DUK_F_C99) || defined(DUK_F_CPP11) ++#define DUK_USE_VARIADIC_MACROS ++#endif ++ ++#define DUK_USE_UNION_INITIALIZERS ++ ++#undef DUK_USE_FLEX_C99 ++#undef DUK_USE_FLEX_ZEROSIZE ++#undef DUK_USE_FLEX_ONESIZE ++#if defined(DUK_F_C99) ++#define DUK_USE_FLEX_C99 ++#else ++#define DUK_USE_FLEX_ZEROSIZE ++#endif ++ ++#define DUK_USE_CLANG_PRAGMAS ++#define DUK_USE_PACK_CLANG_ATTR ++ ++#if defined(__clang__) && defined(__has_builtin) ++#if __has_builtin(__builtin_bswap64) ++#define DUK_BSWAP64(x) ((duk_uint64_t) __builtin_bswap64((duk_uint64_t) (x))) ++#endif ++#if __has_builtin(__builtin_bswap32) ++#define DUK_BSWAP32(x) ((duk_uint32_t) __builtin_bswap32((duk_uint32_t) (x))) ++#endif ++#if __has_builtin(__builtin_bswap16) ++#define DUK_BSWAP16(x) ((duk_uint16_t) __builtin_bswap16((duk_uint16_t) (x))) ++#endif ++#endif +diff --git a/dist/source/config/compilers/compiler_emscripten.h.in b/dist/source/config/compilers/compiler_emscripten.h.in +new file mode 100644 +index 0000000..dd18700 +--- /dev/null ++++ b/dist/source/config/compilers/compiler_emscripten.h.in +@@ -0,0 +1,45 @@ ++#define DUK_NORETURN(decl) decl __attribute__((noreturn)) ++ ++#if defined(__clang__) && defined(__has_builtin) ++#if __has_builtin(__builtin_unreachable) ++#define DUK_UNREACHABLE() do { __builtin_unreachable(); } while (0) ++#endif ++#endif ++ ++#define DUK_USE_BRANCH_HINTS ++#define DUK_LIKELY(x) __builtin_expect((x), 1) ++#define DUK_UNLIKELY(x) __builtin_expect((x), 0) ++#if defined(__clang__) && defined(__has_builtin) ++#if __has_builtin(__builtin_unpredictable) ++#define DUK_UNPREDICTABLE(x) __builtin_unpredictable((x)) ++#endif ++#endif ++ ++#if defined(DUK_F_C99) || defined(DUK_F_CPP11) ++#define DUK_NOINLINE __attribute__((noinline)) ++#define DUK_INLINE inline ++#define DUK_ALWAYS_INLINE inline __attribute__((always_inline)) ++#endif ++ ++#snippet "gcc_clang_visibility.h.in" ++ ++#define DUK_USE_COMPILER_STRING "emscripten" ++ ++#undef DUK_USE_VARIADIC_MACROS ++#if defined(DUK_F_C99) || defined(DUK_F_CPP11) ++#define DUK_USE_VARIADIC_MACROS ++#endif ++ ++#define DUK_USE_UNION_INITIALIZERS ++ ++#undef DUK_USE_FLEX_C99 ++#undef DUK_USE_FLEX_ZEROSIZE ++#undef DUK_USE_FLEX_ONESIZE ++#if defined(DUK_F_C99) ++#define DUK_USE_FLEX_C99 ++#else ++#define DUK_USE_FLEX_ZEROSIZE ++#endif ++ ++#undef DUK_USE_GCC_PRAGMAS ++#define DUK_USE_PACK_CLANG_ATTR +diff --git a/dist/source/config/compilers/compiler_gcc.h.in b/dist/source/config/compilers/compiler_gcc.h.in +new file mode 100644 +index 0000000..7750f46 +--- /dev/null ++++ b/dist/source/config/compilers/compiler_gcc.h.in +@@ -0,0 +1,99 @@ ++#if defined(DUK_F_C99) || defined(DUK_F_CPP11) ++/* C99 / C++11 and above: rely on va_copy() which is required. */ ++#define DUK_VA_COPY(dest,src) va_copy(dest,src) ++#else ++/* GCC: assume we have __va_copy() in non-C99 mode. */ ++#define DUK_VA_COPY(dest,src) __va_copy(dest,src) ++#endif ++ ++#if defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION >= 20500L) && (DUK_F_GCC_VERSION < 50000L) ++/* Since gcc-2.5. ++ * ++ * Disabled temporarily in GCC 5+ because of an unresolved noreturn-related ++ * issue: https://github.com/svaarala/duktape/issues/2155. ++ */ ++#define DUK_NORETURN(decl) decl __attribute__((noreturn)) ++#endif ++ ++#if defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION >= 40500L) ++/* Since gcc-4.5. */ ++#define DUK_UNREACHABLE() do { __builtin_unreachable(); } while (0) ++#endif ++ ++#define DUK_USE_BRANCH_HINTS ++#if defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION >= 40500L) ++/* GCC: test not very accurate; enable only in relatively recent builds ++ * because of bugs in gcc-4.4 (http://lists.debian.org/debian-gcc/2010/04/msg00000.html) ++ */ ++#define DUK_LIKELY(x) __builtin_expect((x), 1) ++#define DUK_UNLIKELY(x) __builtin_expect((x), 0) ++#endif ++/* XXX: equivalent of clang __builtin_unpredictable? */ ++ ++#if (defined(DUK_F_C99) || defined(DUK_F_CPP11)) && \ ++ defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION >= 30101) ++#define DUK_NOINLINE __attribute__((noinline)) ++#define DUK_INLINE inline ++#define DUK_ALWAYS_INLINE inline __attribute__((always_inline)) ++#endif ++ ++#if (defined(DUK_F_C99) || defined(DUK_F_CPP11)) && \ ++ defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION >= 40300) ++#define DUK_HOT __attribute__((hot)) ++#define DUK_COLD __attribute__((cold)) ++#endif ++ ++#if defined(DUK_F_DLL_BUILD) && defined(DUK_F_WINDOWS) ++#snippet "msvc_visibility.h.in" ++#elif defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION >= 40000) ++#snippet "gcc_clang_visibility.h.in" ++#endif ++ ++#if defined(DUK_F_MINGW) ++#if defined(DUK_F_CPP) ++#define DUK_USE_COMPILER_STRING "mingw++" ++#else ++#define DUK_USE_COMPILER_STRING "mingw" ++#endif ++#else ++#if defined(DUK_F_CPP) ++#define DUK_USE_COMPILER_STRING "g++" ++#else ++#define DUK_USE_COMPILER_STRING "gcc" ++#endif ++#endif ++ ++#undef DUK_USE_VARIADIC_MACROS ++#if defined(DUK_F_C99) || (defined(DUK_F_CPP11) && defined(__GNUC__)) ++#define DUK_USE_VARIADIC_MACROS ++#endif ++ ++#define DUK_USE_UNION_INITIALIZERS ++ ++#undef DUK_USE_FLEX_C99 ++#undef DUK_USE_FLEX_ZEROSIZE ++#undef DUK_USE_FLEX_ONESIZE ++#if defined(DUK_F_C99) ++#define DUK_USE_FLEX_C99 ++#else ++#define DUK_USE_FLEX_ZEROSIZE ++#endif ++ ++/* Since 4.6 one can '#pragma GCC diagnostic push/pop'. */ ++#if defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION >= 40600) ++#define DUK_USE_GCC_PRAGMAS ++#else ++#undef DUK_USE_GCC_PRAGMAS ++#endif ++ ++#define DUK_USE_PACK_GCC_ATTR ++ ++/* Availability varies based on platform (between GCC 4.4 and 4.8), and there ++ * are apparently some bugs in GCC 4.x. Check for GCC 5.0 before enabling ++ * these to be safe. ++ */ ++#if defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION >= 50000L) ++#define DUK_BSWAP64(x) ((duk_uint64_t) __builtin_bswap64((duk_uint64_t) (x))) ++#define DUK_BSWAP32(x) ((duk_uint32_t) __builtin_bswap32((duk_uint32_t) (x))) ++#define DUK_BSWAP16(x) ((duk_uint16_t) __builtin_bswap16((duk_uint16_t) (x))) ++#endif +diff --git a/dist/source/config/compilers/compiler_generic.h.in b/dist/source/config/compilers/compiler_generic.h.in +new file mode 100644 +index 0000000..011199c +--- /dev/null ++++ b/dist/source/config/compilers/compiler_generic.h.in +@@ -0,0 +1,24 @@ ++#undef DUK_USE_BRANCH_HINTS ++ ++#if defined(DUK_F_CPP) ++#define DUK_USE_COMPILER_STRING "generic-c++" ++#else ++#define DUK_USE_COMPILER_STRING "generic" ++#endif ++ ++#undef DUK_USE_VARIADIC_MACROS ++#if defined(DUK_F_C99) || defined(DUK_F_CPP11) ++#define DUK_USE_VARIADIC_MACROS ++#endif ++ ++/* C++ doesn't have standard designated union initializers ({ .foo = 1 }). */ ++#undef DUK_USE_UNION_INITIALIZERS ++#if defined(DUK_F_C99) ++#define DUK_USE_UNION_INITIALIZERS ++#endif ++ ++/* Most portable, wastes space */ ++#define DUK_USE_FLEX_ONESIZE ++ ++/* Most portable, potentially wastes space */ ++#define DUK_USE_PACK_DUMMY_MEMBER +diff --git a/dist/source/config/compilers/compiler_msvc.h.in b/dist/source/config/compilers/compiler_msvc.h.in +new file mode 100644 +index 0000000..f1666c9 +--- /dev/null ++++ b/dist/source/config/compilers/compiler_msvc.h.in +@@ -0,0 +1,84 @@ ++/* http://msdn.microsoft.com/en-us/library/aa235362(VS.60).aspx */ ++#define DUK_NORETURN(decl) __declspec(noreturn) decl ++ ++/* XXX: DUK_UNREACHABLE for msvc? */ ++ ++#undef DUK_USE_BRANCH_HINTS ++ ++/* XXX: DUK_LIKELY, DUK_UNLIKELY for msvc? */ ++/* XXX: DUK_NOINLINE, DUK_INLINE, DUK_ALWAYS_INLINE for msvc? */ ++ ++#if defined(DUK_F_DLL_BUILD) && defined(DUK_F_WINDOWS) ++#snippet "msvc_visibility.h.in" ++#endif ++ ++#if defined(DUK_F_CPP) ++#define DUK_USE_COMPILER_STRING "msvc++" ++#else ++#define DUK_USE_COMPILER_STRING "msvc" ++#endif ++ ++#undef DUK_USE_VARIADIC_MACROS ++#if defined(DUK_F_C99) ++#define DUK_USE_VARIADIC_MACROS ++#elif defined(_MSC_VER) && (_MSC_VER >= 1400) ++/* VS2005+ should have variadic macros even when they're not C99. */ ++#define DUK_USE_VARIADIC_MACROS ++#endif ++ ++#undef DUK_USE_UNION_INITIALIZERS ++#if defined(_MSC_VER) && (_MSC_VER >= 1800) ++/* VS2013+ supports union initializers but there's a bug involving union-inside-struct: ++ * https://connect.microsoft.com/VisualStudio/feedback/details/805981 ++ * The bug was fixed (at least) in VS2015 so check for VS2015 for now: ++ * https://blogs.msdn.microsoft.com/vcblog/2015/07/01/c-compiler-front-end-fixes-in-vs2015/ ++ * Manually tested using VS2013, CL reports 18.00.31101, so enable for VS2013 too. ++ */ ++#define DUK_USE_UNION_INITIALIZERS ++#endif ++ ++#undef DUK_USE_FLEX_C99 ++#undef DUK_USE_FLEX_ZEROSIZE ++#undef DUK_USE_FLEX_ONESIZE ++#if defined(DUK_F_C99) ++#define DUK_USE_FLEX_C99 ++#else ++#define DUK_USE_FLEX_ZEROSIZE ++#endif ++ ++#undef DUK_USE_GCC_PRAGMAS ++ ++#define DUK_USE_PACK_MSVC_PRAGMA ++ ++/* These have been tested from VS2008 onwards; may work in older VS versions ++ * too but not enabled by default. ++ */ ++#if defined(_MSC_VER) && (_MSC_VER >= 1500) ++#define DUK_NOINLINE __declspec(noinline) ++#define DUK_INLINE __inline ++#define DUK_ALWAYS_INLINE __forceinline ++#endif ++ ++#if defined(_MSC_VER) && (_MSC_VER >= 1900) ++#define DUK_SNPRINTF snprintf ++#define DUK_VSNPRINTF vsnprintf ++#else ++/* (v)snprintf() is missing before MSVC 2015. Note that _(v)snprintf() does ++ * NOT NUL terminate on truncation, but Duktape code never assumes that. ++ * http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010 ++ */ ++#define DUK_SNPRINTF _snprintf ++#define DUK_VSNPRINTF _vsnprintf ++#endif ++ ++/* Avoid warning when doing DUK_UNREF(some_function). */ ++#if defined(_MSC_VER) && (_MSC_VER < 1500) ++#pragma warning(disable: 4100 4101 4550 4551) ++#define DUK_UNREF(x) ++#else ++#define DUK_UNREF(x) do { __pragma(warning(suppress:4100 4101 4550 4551)) (x); } while (0) ++#endif ++ ++/* Older versions of MSVC don't support the LL/ULL suffix. */ ++#define DUK_U64_CONSTANT(x) x##ui64 ++#define DUK_I64_CONSTANT(x) x##i64 +diff --git a/dist/source/config/compilers/compiler_tinyc.h.in b/dist/source/config/compilers/compiler_tinyc.h.in +new file mode 100644 +index 0000000..2a33010 +--- /dev/null ++++ b/dist/source/config/compilers/compiler_tinyc.h.in +@@ -0,0 +1,18 @@ ++#undef DUK_USE_BRANCH_HINTS ++ ++#if defined(DUK_F_CPP) ++#define DUK_USE_COMPILER_STRING "tinyc++" ++#else ++#define DUK_USE_COMPILER_STRING "tinyc" ++#endif ++ ++/* http://bellard.org/tcc/tcc-doc.html#SEC7 */ ++#define DUK_USE_VARIADIC_MACROS ++ ++#define DUK_USE_UNION_INITIALIZERS ++ ++/* Most portable, wastes space */ ++#define DUK_USE_FLEX_ONESIZE ++ ++/* Most portable, potentially wastes space */ ++#define DUK_USE_PACK_DUMMY_MEMBER +diff --git a/dist/source/config/compilers/compiler_vbcc.h.in b/dist/source/config/compilers/compiler_vbcc.h.in +new file mode 100644 +index 0000000..d0eae43 +--- /dev/null ++++ b/dist/source/config/compilers/compiler_vbcc.h.in +@@ -0,0 +1,23 @@ ++#undef DUK_USE_BRANCH_HINTS ++ ++#if defined(DUK_F_CPP) ++#define DUK_USE_COMPILER_STRING "vbcc-c++" ++#else ++#define DUK_USE_COMPILER_STRING "vbcc" ++#endif ++ ++#undef DUK_USE_VARIADIC_MACROS ++#if defined(DUK_F_C99) || defined(DUK_F_CPP11) ++#define DUK_USE_VARIADIC_MACROS ++#endif ++ ++/* VBCC supports C99 so check only for C99 for union initializer support. ++ * Designated union initializers would possibly work even without a C99 check. ++ */ ++#undef DUK_USE_UNION_INITIALIZERS ++#if defined(DUK_F_C99) ++#define DUK_USE_UNION_INITIALIZERS ++#endif ++ ++#define DUK_USE_FLEX_ZEROSIZE ++#define DUK_USE_PACK_DUMMY_MEMBER +diff --git a/dist/source/config/config-options/DUK_USE_32BIT_PTRS.yaml b/dist/source/config/config-options/DUK_USE_32BIT_PTRS.yaml +new file mode 100644 +index 0000000..d2214e0 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_32BIT_PTRS.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_32BIT_PTRS ++introduced: 1.0.0 ++removed: 1.4.0 ++default: false ++tags: ++ - portability ++description: > ++ Pointers are 32-bit integer compatible. +diff --git a/dist/source/config/config-options/DUK_USE_64BIT_OPS.yaml b/dist/source/config/config-options/DUK_USE_64BIT_OPS.yaml +new file mode 100644 +index 0000000..31fa1d4 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_64BIT_OPS.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_64BIT_OPS ++introduced: 1.0.0 ++default: true ++tags: ++ - portability ++description: > ++ Use 64-bit integer operations. On some platforms 64-bit types may be ++ available but 64-bit operations don't work correctly e.g. in integer/float ++ casts. +diff --git a/dist/source/config/config-options/DUK_USE_ALIGN_4.yaml b/dist/source/config/config-options/DUK_USE_ALIGN_4.yaml +new file mode 100644 +index 0000000..1cce360 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_ALIGN_4.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_ALIGN_4 ++introduced: 1.0.0 ++removed: 1.3.0 ++related: ++ - DUK_USE_ALIGN_BY ++default: false ++tags: ++ - portability ++description: > ++ Use 4-byte alignment for 64-bit integers and IEEE doubles. ++ Replaced by DUK_USE_ALIGN_BY. +diff --git a/dist/source/config/config-options/DUK_USE_ALIGN_8.yaml b/dist/source/config/config-options/DUK_USE_ALIGN_8.yaml +new file mode 100644 +index 0000000..3b3c955 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_ALIGN_8.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_ALIGN_8 ++introduced: 1.0.0 ++removed: 1.3.0 ++related: ++ - DUK_USE_ALIGN_BY ++default: false ++tags: ++ - portability ++description: > ++ Use 8-byte alignment for 64-bit integers and IEEE doubles. ++ Replaced by DUK_USE_ALIGN_BY. +diff --git a/dist/source/config/config-options/DUK_USE_ALIGN_BY.yaml b/dist/source/config/config-options/DUK_USE_ALIGN_BY.yaml +new file mode 100644 +index 0000000..bed2f50 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_ALIGN_BY.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_ALIGN_BY ++introduced: 1.3.0 ++default: 8 ++tags: ++ - portability ++description: > ++ Use N-byte alignment for 64-bit integers and IEEE doubles ++ (supported values are 1, 4, and 8). +diff --git a/dist/source/config/config-options/DUK_USE_ALLOW_UNDEFINED_BEHAVIOR.yaml b/dist/source/config/config-options/DUK_USE_ALLOW_UNDEFINED_BEHAVIOR.yaml +new file mode 100644 +index 0000000..892aa84 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_ALLOW_UNDEFINED_BEHAVIOR.yaml +@@ -0,0 +1,10 @@ ++define: DUK_USE_ALLOW_UNDEFINED_BEHAVIOR ++introduced: 2.3.0 ++default: false ++tags: ++ - portability ++description: > ++ Allow technically undefined behavior such as out-of-range double-to-integer ++ casts, floating point division by zero, etc. which are likely to work on the ++ majority of platforms. Default is to avoid such behaviors, at the cost of ++ some footprint and performance. +diff --git a/dist/source/config/config-options/DUK_USE_ARCH_STRING.yaml b/dist/source/config/config-options/DUK_USE_ARCH_STRING.yaml +new file mode 100644 +index 0000000..65b22c5 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_ARCH_STRING.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_ARCH_STRING ++introduced: 1.0.0 ++default: ++ string: "unknown" ++tags: ++ - portability ++description: > ++ Human-readable architecture string used in e.g. Duktape.env and debugger ++ protocol (example: x64). +diff --git a/dist/source/config/config-options/DUK_USE_ARRAY_BUILTIN.yaml b/dist/source/config/config-options/DUK_USE_ARRAY_BUILTIN.yaml +new file mode 100644 +index 0000000..0ca01f1 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_ARRAY_BUILTIN.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_ARRAY_BUILTIN ++introduced: 2.0.0 ++default: true ++tags: ++ - ecmascript ++description: > ++ Provide an Array built-in. +diff --git a/dist/source/config/config-options/DUK_USE_ARRAY_FASTPATH.yaml b/dist/source/config/config-options/DUK_USE_ARRAY_FASTPATH.yaml +new file mode 100644 +index 0000000..d223ab7 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_ARRAY_FASTPATH.yaml +@@ -0,0 +1,15 @@ ++define: DUK_USE_ARRAY_FASTPATH ++introduced: 2.0.0 ++default: true ++tags: ++ - performance ++ - fastpath ++ - lowmemory ++ - compliance ++description: > ++ Enable fast path for Array.prototype operations like push(), pop(), etc. ++ The fast path handles dense Array instances which are more common than ++ sparse arrays or non-array objects (which Array.prototype operations must ++ also support). The fast path assumes that Array.prototype doesn't contain ++ inherited index properties; such properties are very rarely used in ++ practical code. If compliance is critical, disable the fast path. +diff --git a/dist/source/config/config-options/DUK_USE_ARRAY_PROP_FASTPATH.yaml b/dist/source/config/config-options/DUK_USE_ARRAY_PROP_FASTPATH.yaml +new file mode 100644 +index 0000000..3b74c8a +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_ARRAY_PROP_FASTPATH.yaml +@@ -0,0 +1,14 @@ ++define: DUK_USE_ARRAY_PROP_FASTPATH ++introduced: 2.0.0 ++default: true ++tags: ++ - performance ++ - fastpath ++ - lowmemory ++ - compliance ++description: > ++ Enable a shallow fast path check for Array index property reads and writes. ++ The fast path assumes Array.prototype doesn't have numeric index properties ++ which would be inherited and affect read/write behavior. This behavior is ++ non-compliant (but practical because such inherited properties are very ++ rare). Disable this option to ensure strict compliance. +diff --git a/dist/source/config/config-options/DUK_USE_ASSERTIONS.yaml b/dist/source/config/config-options/DUK_USE_ASSERTIONS.yaml +new file mode 100644 +index 0000000..1f0cfe8 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_ASSERTIONS.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_ASSERTIONS ++introduced: 1.0.0 ++default: false ++tags: ++ - development ++ - debug ++description: > ++ Enable internal assert checks. These slow down execution considerably ++ so only use when debugging. +diff --git a/dist/source/config/config-options/DUK_USE_ATAN2_WORKAROUNDS.yaml b/dist/source/config/config-options/DUK_USE_ATAN2_WORKAROUNDS.yaml +new file mode 100644 +index 0000000..8ce15ea +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_ATAN2_WORKAROUNDS.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_ATAN2_WORKAROUNDS ++introduced: 2.0.0 ++default: false ++tags: ++ - portability ++description: > ++ Enable workarounds to common atan2() semantics issues. At least Cygwin/MinGW ++ has such issues, see test-bug-mingw-math-issues.js. +diff --git a/dist/source/config/config-options/DUK_USE_AUGMENT_ERROR_CREATE.yaml b/dist/source/config/config-options/DUK_USE_AUGMENT_ERROR_CREATE.yaml +new file mode 100644 +index 0000000..7d1f45b +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_AUGMENT_ERROR_CREATE.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_AUGMENT_ERROR_CREATE ++introduced: 1.0.0 ++default: true ++tags: ++ - ecmascript ++description: > ++ Augment an ECMAScript error object at creation with tracedata or ++ fileName/lineNumber, or Duktape.errCreate (if enabled). +diff --git a/dist/source/config/config-options/DUK_USE_AUGMENT_ERROR_THROW.yaml b/dist/source/config/config-options/DUK_USE_AUGMENT_ERROR_THROW.yaml +new file mode 100644 +index 0000000..8d16b39 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_AUGMENT_ERROR_THROW.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_AUGMENT_ERROR_THROW ++introduced: 1.0.0 ++default: true ++tags: ++ - ecmascript ++description: > ++ Augment an ECMAScript error object at throw time with Duktape.errThrow ++ (if enabled). +diff --git a/dist/source/config/config-options/DUK_USE_AVOID_PLATFORM_FUNCPTRS.yaml b/dist/source/config/config-options/DUK_USE_AVOID_PLATFORM_FUNCPTRS.yaml +new file mode 100644 +index 0000000..cbde8c6 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_AVOID_PLATFORM_FUNCPTRS.yaml +@@ -0,0 +1,13 @@ ++define: DUK_USE_AVOID_PLATFORM_FUNCPTRS ++introduced: 1.0.0 ++default: true ++tags: ++ - portability ++description: > ++ Don't assume that platform functions (e.g. math functions) are actual ++ functions. This option is needed if platform functions may be defined ++ as macros. This is certainly the case with some platform "polyfills" ++ which provide missing C99/C++11 functions through macros, and may be ++ the case with VS2013 (see GH-17). ++ ++ This is now the default: the cost in footprint is negligible. +diff --git a/dist/source/config/config-options/DUK_USE_BASE64_FASTPATH.yaml b/dist/source/config/config-options/DUK_USE_BASE64_FASTPATH.yaml +new file mode 100644 +index 0000000..7c6f48d +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_BASE64_FASTPATH.yaml +@@ -0,0 +1,10 @@ ++define: DUK_USE_BASE64_FASTPATH ++introduced: 1.4.0 ++default: true ++tags: ++ - performance ++ - fastpath ++ - lowmemory ++description: > ++ Enable fast path for base64 encode/decode. The fast path uses a lookup ++ table at a small cost in footprint. +diff --git a/dist/source/config/config-options/DUK_USE_BASE64_SUPPORT.yaml b/dist/source/config/config-options/DUK_USE_BASE64_SUPPORT.yaml +new file mode 100644 +index 0000000..f895e71 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_BASE64_SUPPORT.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_BASE64_SUPPORT ++introduced: 2.3.0 ++default: true ++tags: ++ - codec ++description: > ++ Enable base64 encoding/decoding support. +diff --git a/dist/source/config/config-options/DUK_USE_BOOLEAN_BUILTIN.yaml b/dist/source/config/config-options/DUK_USE_BOOLEAN_BUILTIN.yaml +new file mode 100644 +index 0000000..0ac5176 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_BOOLEAN_BUILTIN.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_BOOLEAN_BUILTIN ++introduced: 2.0.0 ++default: true ++tags: ++ - ecmascript ++description: > ++ Provide a Boolean built-in. +diff --git a/dist/source/config/config-options/DUK_USE_BRANCH_HINTS.yaml b/dist/source/config/config-options/DUK_USE_BRANCH_HINTS.yaml +new file mode 100644 +index 0000000..41ab42e +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_BRANCH_HINTS.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_BRANCH_HINTS ++introduced: 1.0.0 ++default: true ++tags: ++ - portability ++description: > ++ Use branch hints if the compiler supports them. ++ ++# XXX: Unused now, DUK_LIKELY and DUK_UNLIKELY are used instead. +diff --git a/dist/source/config/config-options/DUK_USE_BROWSER_LIKE.yaml b/dist/source/config/config-options/DUK_USE_BROWSER_LIKE.yaml +new file mode 100644 +index 0000000..79d37ef +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_BROWSER_LIKE.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_BROWSER_LIKE ++introduced: 1.0.0 ++removed: 2.0.0 ++default: true ++tags: ++ - ecmascript ++description: > ++ Provide browser-like bindings: currently print() and alert(). +diff --git a/dist/source/config/config-options/DUK_USE_BUFFEROBJECT_SUPPORT.yaml b/dist/source/config/config-options/DUK_USE_BUFFEROBJECT_SUPPORT.yaml +new file mode 100644 +index 0000000..9d2a7b9 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_BUFFEROBJECT_SUPPORT.yaml +@@ -0,0 +1,15 @@ ++define: DUK_USE_BUFFEROBJECT_SUPPORT ++introduced: 1.3.0 ++default: true ++tags: ++ - ecmascript2015 ++description: > ++ Enable support for Khronos/ES6 typed arrays and Node.js Buffer objects. ++ ++ When disabled, Duktape custom plain buffer type is present and functional ++ in the C API. Plain buffers have virtual properties and you can read/write ++ buffer contents in ECMAScript code. Plain buffers will still inherit from ++ ArrayBuffer.prototype, but none of the ECMAScript buffer related bindings ++ will work. This includes all ArrayBuffer, typed array, and Node.js Buffer ++ methods. Native bindings which produce plain buffer results (like ++ Duktape.dec()) will still work. +diff --git a/dist/source/config/config-options/DUK_USE_BUFLEN16.yaml b/dist/source/config/config-options/DUK_USE_BUFLEN16.yaml +new file mode 100644 +index 0000000..1e40f4b +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_BUFLEN16.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_BUFLEN16 ++introduced: 1.1.0 ++default: false ++tags: ++ - lowmemory ++ - experimental ++description: > ++ Use a 16-bit buffer length field (for low memory environments). +diff --git a/dist/source/config/config-options/DUK_USE_BUILTIN_INITJS.yaml b/dist/source/config/config-options/DUK_USE_BUILTIN_INITJS.yaml +new file mode 100644 +index 0000000..c6c840d +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_BUILTIN_INITJS.yaml +@@ -0,0 +1,12 @@ ++define: DUK_USE_BUILTIN_INITJS ++introduced: 1.0.0 ++removed: 2.0.0 ++default: true ++tags: ++ - ecmascript ++description: > ++ Use built-in .js init code when creating a new global context. ++ The .js init code (duk_initjs.js) provides some initialization ++ code that's nicer to implement in ECMAScript, and is also used ++ to provide some backwards compatibility bindings which are easy ++ to remove later. +diff --git a/dist/source/config/config-options/DUK_USE_BYTECODE_DUMP_SUPPORT.yaml b/dist/source/config/config-options/DUK_USE_BYTECODE_DUMP_SUPPORT.yaml +new file mode 100644 +index 0000000..0662fa3 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_BYTECODE_DUMP_SUPPORT.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_BYTECODE_DUMP_SUPPORT ++introduced: 1.3.0 ++default: true ++tags: ++ - api ++description: > ++ Enable support for API calls to dump/load function bytecode. +diff --git a/dist/source/config/config-options/DUK_USE_BYTEORDER.yaml b/dist/source/config/config-options/DUK_USE_BYTEORDER.yaml +new file mode 100644 +index 0000000..afab111 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_BYTEORDER.yaml +@@ -0,0 +1,16 @@ ++define: DUK_USE_BYTEORDER ++introduced: 1.4.0 ++default: 0 # no reasonable automatic default ++tags: ++ - portability ++description: > ++ Byte order for platform: 1 = little endian, 2 = mixed (arm hybrid) endian, ++ 3 = big endian. ++ ++ ARM mixed endian means integers are little endian but IEEE doubles have ++ mixed endianness: big endian bytes 12345678 are ordered in memory as ++ 43218765. See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0056d/Bcfhgcgd.html. ++ ++ (This define should be produced by duk_config.h; currently Duktape internals ++ use automatically derived defines DUK_USE_{INTEGER,DOUBLE}_{LE,BE_ME} ++ instead of using this define directly.) +diff --git a/dist/source/config/config-options/DUK_USE_BYTEORDER_FORCED.yaml b/dist/source/config/config-options/DUK_USE_BYTEORDER_FORCED.yaml +new file mode 100644 +index 0000000..c31fe66 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_BYTEORDER_FORCED.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_BYTEORDER_FORCED ++introduced: 1.0.0 ++removed: 1.4.0 ++default: false ++tags: ++ - portability ++description: > ++ Byte order was forced (instead of being autodetected). This has no ++ functional impact but the forced status shows up in Duktape.env. +diff --git a/dist/source/config/config-options/DUK_USE_CACHE_ACTIVATION.yaml b/dist/source/config/config-options/DUK_USE_CACHE_ACTIVATION.yaml +new file mode 100644 +index 0000000..6c22b56 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_CACHE_ACTIVATION.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_CACHE_ACTIVATION ++introduced: 2.2.0 ++default: true ++tags: ++ - performance ++description: > ++ Cache duk_activation records. When releasing a duk_activation, place it in ++ a free list for later reuse. Mark-and-sweep frees the free list to keep ++ memory usage in check. +diff --git a/dist/source/config/config-options/DUK_USE_CACHE_CATCHER.yaml b/dist/source/config/config-options/DUK_USE_CACHE_CATCHER.yaml +new file mode 100644 +index 0000000..d16627d +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_CACHE_CATCHER.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_CACHE_CATCHER ++introduced: 2.2.0 ++default: true ++tags: ++ - performance ++description: > ++ Cache duk_catcher records. When releasing a duk_catcher, place it in ++ a free list for later reuse. Mark-and-sweep frees the free list to keep ++ memory usage in check. +diff --git a/dist/source/config/config-options/DUK_USE_CALLSTACK_LIMIT.yaml b/dist/source/config/config-options/DUK_USE_CALLSTACK_LIMIT.yaml +new file mode 100644 +index 0000000..a2c154c +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_CALLSTACK_LIMIT.yaml +@@ -0,0 +1,10 @@ ++define: DUK_USE_CALLSTACK_LIMIT ++introduced: 2.2.0 ++default: 10000 ++tags: ++ - misc ++description: > ++ Maximum call stack size. If call stack would grow beyond this value, reject ++ the call in progress. This mainly protects against runaway recursion. Note ++ in particular that this limit is unrelated to the native C stack size which ++ has a separate limit define. +diff --git a/dist/source/config/config-options/DUK_USE_CBOR_BUILTIN.yaml b/dist/source/config/config-options/DUK_USE_CBOR_BUILTIN.yaml +new file mode 100644 +index 0000000..e983a34 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_CBOR_BUILTIN.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_CBOR_BUILTIN ++introduced: 2.5.0 ++default: true ++tags: ++ - codec ++ - cbor ++ - experimental ++description: > ++ Provide a CBOR built-in with CBOR.encode() and CBOR.decode() functions. +diff --git a/dist/source/config/config-options/DUK_USE_CBOR_DEC_RECLIMIT.yaml b/dist/source/config/config-options/DUK_USE_CBOR_DEC_RECLIMIT.yaml +new file mode 100644 +index 0000000..96e8f7d +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_CBOR_DEC_RECLIMIT.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_CBOR_DEC_RECLIMIT ++introduced: 2.6.0 ++default: 1000 ++tags: ++ - portability ++ - cstackdepth ++description: > ++ Maximum native stack recursion for CBOR decoding. +diff --git a/dist/source/config/config-options/DUK_USE_CBOR_ENC_RECLIMIT.yaml b/dist/source/config/config-options/DUK_USE_CBOR_ENC_RECLIMIT.yaml +new file mode 100644 +index 0000000..9d8b052 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_CBOR_ENC_RECLIMIT.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_CBOR_ENC_RECLIMIT ++introduced: 2.6.0 ++default: 1000 ++tags: ++ - portability ++ - cstackdepth ++description: > ++ Maximum native stack recursion for CBOR encoding. +diff --git a/dist/source/config/config-options/DUK_USE_CBOR_SUPPORT.yaml b/dist/source/config/config-options/DUK_USE_CBOR_SUPPORT.yaml +new file mode 100644 +index 0000000..8a9d859 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_CBOR_SUPPORT.yaml +@@ -0,0 +1,10 @@ ++define: DUK_USE_CBOR_SUPPORT ++introduced: 2.5.0 ++default: true ++tags: ++ - codec ++ - cbor ++ - experimental ++description: > ++ Include CBOR support. When disabled, CBOR functions in the C API (and the ++ CBOR built-in, if enabled) will throw an error. +diff --git a/dist/source/config/config-options/DUK_USE_CLANG_PRAGMAS.yaml b/dist/source/config/config-options/DUK_USE_CLANG_PRAGMAS.yaml +new file mode 100644 +index 0000000..5a8e143 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_CLANG_PRAGMAS.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_CLANG_PRAGMAS ++introduced: 2.5.0 ++default: false ++tags: ++ - portability ++description: > ++ Use Clang-specific pragmas, e.g. "#pragma clang diagnostic" to suppress ++ unnecessary warnings. +diff --git a/dist/source/config/config-options/DUK_USE_COMMONJS_MODULES.yaml b/dist/source/config/config-options/DUK_USE_COMMONJS_MODULES.yaml +new file mode 100644 +index 0000000..80d37c8 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_COMMONJS_MODULES.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_COMMONJS_MODULES ++introduced: 1.0.0 ++removed: 2.0.0 ++default: true ++tags: ++ - ecmascript ++description: > ++ Enable support for CommonJS modules. When enabled, the global require() ++ function provides a simple module loader facility which depends on the user ++ providing low level module searching functionality. When disabled, the ++ global require() function is present but throws an error. +diff --git a/dist/source/config/config-options/DUK_USE_COMPILER_RECLIMIT.yaml b/dist/source/config/config-options/DUK_USE_COMPILER_RECLIMIT.yaml +new file mode 100644 +index 0000000..eed337c +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_COMPILER_RECLIMIT.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_COMPILER_RECLIMIT ++introduced: 1.3.0 ++default: 2500 ++tags: ++ - portability ++ - cstackdepth ++description: > ++ ECMAScript compiler native call stack recursion limit. +diff --git a/dist/source/config/config-options/DUK_USE_COMPILER_STRING.yaml b/dist/source/config/config-options/DUK_USE_COMPILER_STRING.yaml +new file mode 100644 +index 0000000..2f47b01 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_COMPILER_STRING.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_COMPILER_STRING ++introduced: 1.0.0 ++default: ++ string: "unknown" ++tags: ++ - portability ++description: > ++ Human-readable compiler string used in e.g. Duktape.env and debugger ++ protocol (example: gcc). +diff --git a/dist/source/config/config-options/DUK_USE_COMPUTED_INFINITY.yaml b/dist/source/config/config-options/DUK_USE_COMPUTED_INFINITY.yaml +new file mode 100644 +index 0000000..3d3f605 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_COMPUTED_INFINITY.yaml +@@ -0,0 +1,15 @@ ++define: DUK_USE_COMPUTED_INFINITY ++introduced: 1.0.0 ++default: false ++tags: ++ - portability ++description: > ++ The DUK_DOUBLE_INFINITY is not a constant but refers to a global variable ++ with an IEEE double infinity value computed at run-time. Some compilers ++ don't provide a constant for infinity, and may incorrectly evaluate ++ (1 / 0) when doing constant folding. ++ ++ When enabled, define DUK_DOUBLE_INFINITY as duk_computed_infinity. ++ ++# XXX: Remove computed infinity from Duktape itself and let user ++# provide it instead? +diff --git a/dist/source/config/config-options/DUK_USE_COMPUTED_NAN.yaml b/dist/source/config/config-options/DUK_USE_COMPUTED_NAN.yaml +new file mode 100644 +index 0000000..8379feb +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_COMPUTED_NAN.yaml +@@ -0,0 +1,15 @@ ++define: DUK_USE_COMPUTED_NAN ++introduced: 1.0.0 ++default: false ++tags: ++ - portability ++description: > ++ The DUK_DOUBLE_NAN is not a constant but refers to a global variable with ++ an IEEE NaN value computed at run-time. Some compilers don't provide a ++ constant for NaN, and may incorrectly evaluate (0 / 0) when doing ++ constant folding. ++ ++ When enabled, define DUK_DOUBLE_NAN as duk_computed_nan. ++ ++# XXX: Remove computed NaN from Duktape itself and let user provide ++# it instead? +diff --git a/dist/source/config/config-options/DUK_USE_COROUTINE_SUPPORT.yaml b/dist/source/config/config-options/DUK_USE_COROUTINE_SUPPORT.yaml +new file mode 100644 +index 0000000..e608bdf +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_COROUTINE_SUPPORT.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_COROUTINE_SUPPORT ++introduced: 2.0.0 ++default: true ++tags: ++ - execution ++description: > ++ Enable support for Duktape coroutines, i.e. yield/resume. +diff --git a/dist/source/config/config-options/DUK_USE_CPP_EXCEPTIONS.yaml b/dist/source/config/config-options/DUK_USE_CPP_EXCEPTIONS.yaml +new file mode 100644 +index 0000000..c735666 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_CPP_EXCEPTIONS.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_CPP_EXCEPTIONS ++introduced: 1.4.0 ++default: false ++tags: ++ - portability ++description: > ++ Use C++ exceptions instead of setjmp/longjmp for long control transfers. ++ This allows Duktape/C functions written in C++ to use automatic destructors ++ (RAII or scope-based resource management). +diff --git a/dist/source/config/config-options/DUK_USE_DATAPTR16.yaml b/dist/source/config/config-options/DUK_USE_DATAPTR16.yaml +new file mode 100644 +index 0000000..5c424ce +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DATAPTR16.yaml +@@ -0,0 +1,21 @@ ++define: DUK_USE_DATAPTR16 ++introduced: 1.1.0 ++related: ++ - DUK_USE_DATAPTR_ENC16 ++ - DUK_USE_DATAPTR_DEC16 ++default: false ++tags: ++ - lowmemory ++ - experimental ++description: > ++ Enable "compression" of arbitrary data pointers into an unsigned 16-bit ++ value. Use together with DUK_USE_DATAPTR_ENC16 and DUK_USE_DATAPTR_DEC16. ++ ++ Pointers compressed are any void pointers in C code, not just the Duktape ++ heap. Also NULL pointer must encode and decode correctly. ++ ++ Currently it is required that NULL encodes to integer 0, and integer ++ 0 decodes to NULL. No other pointer can be encoded to 0. ++ ++ NOTE: This feature option is currently unimplemented, i.e. Duktape won't ++ compress any data pointers at the moment. +diff --git a/dist/source/config/config-options/DUK_USE_DATAPTR_DEC16.yaml b/dist/source/config/config-options/DUK_USE_DATAPTR_DEC16.yaml +new file mode 100644 +index 0000000..1cdf2ec +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DATAPTR_DEC16.yaml +@@ -0,0 +1,13 @@ ++define: DUK_USE_DATAPTR_DEC16 ++introduced: 1.1.0 ++requires: ++ - DUK_USE_DATAPTR16 ++default: false ++tags: ++ - lowmemory ++ - experimental ++description: > ++ Use together with DUK_USE_DATAPTR16 for arbitrary data pointer compression. ++ DUK_USE_DATAPTR_DEC16(udata,x) is a macro with a userdata and duk_uint16_t ++ argument, and a void ptr return value. The userdata argument is the heap ++ userdata value given at heap creation. +diff --git a/dist/source/config/config-options/DUK_USE_DATAPTR_ENC16.yaml b/dist/source/config/config-options/DUK_USE_DATAPTR_ENC16.yaml +new file mode 100644 +index 0000000..988e6b7 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DATAPTR_ENC16.yaml +@@ -0,0 +1,15 @@ ++define: DUK_USE_DATAPTR_ENC16 ++introduced: 1.1.0 ++requires: ++ - DUK_USE_DATAPTR16 ++default: false ++tags: ++ - lowmemory ++ - experimental ++description: > ++ Use together with DUK_USE_DATAPTR16 for arbitrary data pointer compression. ++ DUK_USE_DATAPTR_ENC16(udata,p) is a macro with a userdata and void ptr ++ argument, and a duk_uint16_t return value. The userdata argument is the ++ heap userdata value given at heap creation. Currently it is required that ++ NULL encodes to integer 0, and integer 0 decodes to NULL. No other pointer ++ can be encoded to 0. +diff --git a/dist/source/config/config-options/DUK_USE_DATE_BUILTIN.yaml b/dist/source/config/config-options/DUK_USE_DATE_BUILTIN.yaml +new file mode 100644 +index 0000000..f233b4a +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DATE_BUILTIN.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_DATE_BUILTIN ++introduced: 2.0.0 ++default: true ++tags: ++ - ecmascript ++description: > ++ Provide a Date built-in. +diff --git a/dist/source/config/config-options/DUK_USE_DATE_FMT_STRFTIME.yaml b/dist/source/config/config-options/DUK_USE_DATE_FMT_STRFTIME.yaml +new file mode 100644 +index 0000000..9477a4b +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DATE_FMT_STRFTIME.yaml +@@ -0,0 +1,10 @@ ++define: DUK_USE_DATE_FMT_STRFTIME ++introduced: 1.0.0 ++default: false ++tags: ++ - date ++ - portability ++description: > ++ Use strftime() to format Date values in native, platform specific format ++ before falling back into ISO 8601. When enabled, appropriate date/time ++ headers must be included. +diff --git a/dist/source/config/config-options/DUK_USE_DATE_FORMAT_STRING.yaml b/dist/source/config/config-options/DUK_USE_DATE_FORMAT_STRING.yaml +new file mode 100644 +index 0000000..6146b5e +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DATE_FORMAT_STRING.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_DATE_FORMAT_STRING ++introduced: 1.3.0 ++default: false ++tags: ++ - date ++ - portability ++description: > ++ Optional macro for formatting a date in a platform dependent manner, ++ see datetime.rst. +diff --git a/dist/source/config/config-options/DUK_USE_DATE_GET_LOCAL_TZOFFSET.yaml b/dist/source/config/config-options/DUK_USE_DATE_GET_LOCAL_TZOFFSET.yaml +new file mode 100644 +index 0000000..572f3c4 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DATE_GET_LOCAL_TZOFFSET.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_DATE_GET_LOCAL_TZOFFSET ++introduced: 1.3.0 ++default: false ++tags: ++ - date ++ - portability ++description: > ++ Mandatory macro for getting the local time offset for a given datetime, ++ see datetime.rst. +diff --git a/dist/source/config/config-options/DUK_USE_DATE_GET_NOW.yaml b/dist/source/config/config-options/DUK_USE_DATE_GET_NOW.yaml +new file mode 100644 +index 0000000..b89d9b0 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DATE_GET_NOW.yaml +@@ -0,0 +1,15 @@ ++define: DUK_USE_DATE_GET_NOW ++introduced: 1.3.0 ++default: false ++tags: ++ - date ++ - portability ++description: > ++ Mandatory macro for getting the current time, see datetime.rst. The macro ++ is allowed (and recommended) to return millisecond fractions. The fractions ++ are truncated by the Date built-in, but are available via duk_get_now() C ++ API call. ++ ++ If the time provided experiences time jumps or doesn't advance in realtime ++ (which is useful in some time virtualization scenarios), consider defining ++ DUK_USE_GET_MONOTONIC_TIME. +diff --git a/dist/source/config/config-options/DUK_USE_DATE_NOW_GETTIMEOFDAY.yaml b/dist/source/config/config-options/DUK_USE_DATE_NOW_GETTIMEOFDAY.yaml +new file mode 100644 +index 0000000..a642177 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DATE_NOW_GETTIMEOFDAY.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_DATE_NOW_GETTIMEOFDAY ++introduced: 1.0.0 ++default: false ++tags: ++ - date ++ - portability ++description: > ++ Use gettimeofday() to get current datetime. When enabled, appropriate ++ date/time headers must be included. +diff --git a/dist/source/config/config-options/DUK_USE_DATE_NOW_TIME.yaml b/dist/source/config/config-options/DUK_USE_DATE_NOW_TIME.yaml +new file mode 100644 +index 0000000..675fab8 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DATE_NOW_TIME.yaml +@@ -0,0 +1,10 @@ ++define: DUK_USE_DATE_NOW_TIME ++introduced: 1.0.0 ++default: false ++tags: ++ - date ++ - portability ++description: > ++ Use time() to get current datetime, with the limitation that datetime is ++ limited to one second resolution. When enabled, appropriate date/time ++ headers must be included. +diff --git a/dist/source/config/config-options/DUK_USE_DATE_NOW_WINDOWS.yaml b/dist/source/config/config-options/DUK_USE_DATE_NOW_WINDOWS.yaml +new file mode 100644 +index 0000000..de24e39 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DATE_NOW_WINDOWS.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_DATE_NOW_WINDOWS ++introduced: 1.0.0 ++default: false ++tags: ++ - date ++ - portability ++description: > ++ Use Win32 API calls to get current datetime. +diff --git a/dist/source/config/config-options/DUK_USE_DATE_NOW_WINDOWS_SUBMS.yaml b/dist/source/config/config-options/DUK_USE_DATE_NOW_WINDOWS_SUBMS.yaml +new file mode 100644 +index 0000000..bb5b77f +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DATE_NOW_WINDOWS_SUBMS.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_DATE_NOW_WINDOWS_SUBMS ++introduced: 2.2.0 ++default: false ++tags: ++ - date ++ - portability ++description: > ++ Like DUK_USE_DATE_NOW_WINDOWS but use GetSystemTimePreciseAsFileTime(), ++ available since Windows 8, for sub-millisecond resolution. +diff --git a/dist/source/config/config-options/DUK_USE_DATE_PARSE_STRING.yaml b/dist/source/config/config-options/DUK_USE_DATE_PARSE_STRING.yaml +new file mode 100644 +index 0000000..7fe0ac2 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DATE_PARSE_STRING.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_DATE_PARSE_STRING ++introduced: 1.3.0 ++default: false ++tags: ++ - date ++ - portability ++description: > ++ Optional macro for parsing a date in a platform dependent manner, ++ see datetime.rst. +diff --git a/dist/source/config/config-options/DUK_USE_DATE_PRS_GETDATE.yaml b/dist/source/config/config-options/DUK_USE_DATE_PRS_GETDATE.yaml +new file mode 100644 +index 0000000..c224d16 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DATE_PRS_GETDATE.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_DATE_PRS_GETDATE ++introduced: 1.3.0 ++default: false ++tags: ++ - date ++ - portability ++description: > ++ Use getdate_r() to parse a platform specific datetime string into ECMAScript ++ time. getdate_r() depends on DATEMSK being set so this is not always very ++ convenient for an embedded interpreter. When enabled, appropriate date/time ++ headers must be included. +diff --git a/dist/source/config/config-options/DUK_USE_DATE_PRS_STRPTIME.yaml b/dist/source/config/config-options/DUK_USE_DATE_PRS_STRPTIME.yaml +new file mode 100644 +index 0000000..2d7eb51 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DATE_PRS_STRPTIME.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_DATE_PRS_STRPTIME ++introduced: 1.0.0 ++default: false ++tags: ++ - date ++ - portability ++description: > ++ Use strptime() to parse a platform specific datetime string into ECMAScript ++ time. +diff --git a/dist/source/config/config-options/DUK_USE_DATE_TZO_GMTIME.yaml b/dist/source/config/config-options/DUK_USE_DATE_TZO_GMTIME.yaml +new file mode 100644 +index 0000000..53c8fa0 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DATE_TZO_GMTIME.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_DATE_TZO_GMTIME ++introduced: 1.0.0 ++default: false ++tags: ++ - date ++ - portability ++description: > ++ Use gmtime() to get local time offset at a certain time. ++ When enabled, appropriate date/time headers must be included. ++ ++ Since gmtime() is not re-entrant, this is not thread safe. +diff --git a/dist/source/config/config-options/DUK_USE_DATE_TZO_GMTIME_R.yaml b/dist/source/config/config-options/DUK_USE_DATE_TZO_GMTIME_R.yaml +new file mode 100644 +index 0000000..5ac25c3 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DATE_TZO_GMTIME_R.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_DATE_TZO_GMTIME_R ++introduced: 1.0.0 ++default: false ++tags: ++ - date ++ - portability ++description: > ++ Use gmtime_r() to get local time offset at a certain time. ++ When enabled, appropriate date/time headers must be included. +diff --git a/dist/source/config/config-options/DUK_USE_DATE_TZO_GMTIME_S.yaml b/dist/source/config/config-options/DUK_USE_DATE_TZO_GMTIME_S.yaml +new file mode 100644 +index 0000000..13779e7 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DATE_TZO_GMTIME_S.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_DATE_TZO_GMTIME_S ++introduced: 2.0.0 ++default: false ++tags: ++ - date ++ - portability ++description: > ++ Use gmtime_s() to get local time offset at a certain time. +diff --git a/dist/source/config/config-options/DUK_USE_DATE_TZO_WINDOWS.yaml b/dist/source/config/config-options/DUK_USE_DATE_TZO_WINDOWS.yaml +new file mode 100644 +index 0000000..dbfa9a8 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DATE_TZO_WINDOWS.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_DATE_TZO_WINDOWS ++introduced: 1.0.0 ++default: false ++tags: ++ - date ++ - portability ++description: > ++ Use Win32 API calls to get local time offset at a certain time. +diff --git a/dist/source/config/config-options/DUK_USE_DATE_TZO_WINDOWS_NO_DST.yaml b/dist/source/config/config-options/DUK_USE_DATE_TZO_WINDOWS_NO_DST.yaml +new file mode 100644 +index 0000000..f0639da +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DATE_TZO_WINDOWS_NO_DST.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_DATE_TZO_WINDOWS_NO_DST ++introduced: 2.0.1 ++default: false ++tags: ++ - date ++ - portability ++description: > ++ Use Win32 API calls to get local time offset at a certain time. ++ Does not take into account daylight savings time. +diff --git a/dist/source/config/config-options/DUK_USE_DDDPRINT.yaml b/dist/source/config/config-options/DUK_USE_DDDPRINT.yaml +new file mode 100644 +index 0000000..5dc055d +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DDDPRINT.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_DDDPRINT ++introduced: 1.0.0 ++removed: 2.0.0 ++default: false ++tags: ++ - debug ++description: > ++ Enable even more debug printouts. Not recommended unless you have ++ grep handy. Replaced by DUK_USE_DEBUG_LEVEL. +diff --git a/dist/source/config/config-options/DUK_USE_DDPRINT.yaml b/dist/source/config/config-options/DUK_USE_DDPRINT.yaml +new file mode 100644 +index 0000000..6406f0f +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DDPRINT.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_DDPRINT ++introduced: 1.0.0 ++removed: 2.0.0 ++default: false ++tags: ++ - debug ++description: > ++ Enable more debug printouts. Replaced by DUK_USE_DEBUG_LEVEL. +diff --git a/dist/source/config/config-options/DUK_USE_DEBUG.yaml b/dist/source/config/config-options/DUK_USE_DEBUG.yaml +new file mode 100644 +index 0000000..09b9785 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DEBUG.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_DEBUG ++introduced: 1.0.0 ++default: false ++tags: ++ - debug ++description: > ++ Enable debug code in Duktape internals. Without this option other ++ debugging options (such as DUK_USE_DEBUG_LEVEL and DUK_USE_DEBUG_WRITE) ++ have no effect. +diff --git a/dist/source/config/config-options/DUK_USE_DEBUGGER_DUMPHEAP.yaml b/dist/source/config/config-options/DUK_USE_DEBUGGER_DUMPHEAP.yaml +new file mode 100644 +index 0000000..ef9c8d8 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DEBUGGER_DUMPHEAP.yaml +@@ -0,0 +1,12 @@ ++define: DUK_USE_DEBUGGER_DUMPHEAP ++introduced: 1.2.0 ++# Option is ignored unless debugger support is enabled. ++#requires: ++# - DUK_USE_DEBUGGER_SUPPORT ++default: false ++tags: ++ - debugger ++description: > ++ Support the DumpHeap command. This is optional because the command is not ++ always needed. The command also has a relatively large footprint (about ++ 10% of debugger code); in absolute terms it's about 1kB of code footprint. +diff --git a/dist/source/config/config-options/DUK_USE_DEBUGGER_FWD_LOGGING.yaml b/dist/source/config/config-options/DUK_USE_DEBUGGER_FWD_LOGGING.yaml +new file mode 100644 +index 0000000..66fc0d9 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DEBUGGER_FWD_LOGGING.yaml +@@ -0,0 +1,14 @@ ++define: DUK_USE_DEBUGGER_FWD_LOGGING ++introduced: 1.2.0 ++removed: 2.0.0 ++# Option is ignored unless debugger support is enabled. ++#requires: ++# - DUK_USE_DEBUGGER_SUPPORT ++default: false ++tags: ++ - debugger ++description: > ++ Forward log writes using the built-in logging framework to the debug client. ++ Forwarding happens from the Duktape.Logger.prototype.info() etc calls before ++ the raw() function is called, so that logging is forwarded even if you ++ replace the backend. +diff --git a/dist/source/config/config-options/DUK_USE_DEBUGGER_FWD_PRINTALERT.yaml b/dist/source/config/config-options/DUK_USE_DEBUGGER_FWD_PRINTALERT.yaml +new file mode 100644 +index 0000000..842e541 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DEBUGGER_FWD_PRINTALERT.yaml +@@ -0,0 +1,12 @@ ++define: DUK_USE_DEBUGGER_FWD_PRINTALERT ++introduced: 1.2.0 ++removed: 2.0.0 ++# Option is ignored unless debugger support is enabled. ++#requires: ++# - DUK_USE_DEBUGGER_SUPPORT ++default: false ++tags: ++ - debugger ++description: > ++ Forward calls to the built-in print() and alert() function to the debug ++ client. +diff --git a/dist/source/config/config-options/DUK_USE_DEBUGGER_INSPECT.yaml b/dist/source/config/config-options/DUK_USE_DEBUGGER_INSPECT.yaml +new file mode 100644 +index 0000000..299e14d +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DEBUGGER_INSPECT.yaml +@@ -0,0 +1,13 @@ ++define: DUK_USE_DEBUGGER_INSPECT ++introduced: 1.5.0 ++# Option is ignored unless debugger support is enabled. ++#requires: ++# - DUK_USE_DEBUGGER_SUPPORT ++default: false ++tags: ++ - debugger ++description: > ++ Support debugger heap object inspection commands GetHeapObjInfo, ++ GetObjPropDesc, GetObjPropDescRange. These are optional because the ++ commands have a relatively high code footprint (about 3kB) and are not ++ always needed. +diff --git a/dist/source/config/config-options/DUK_USE_DEBUGGER_PAUSE_UNCAUGHT.yaml b/dist/source/config/config-options/DUK_USE_DEBUGGER_PAUSE_UNCAUGHT.yaml +new file mode 100644 +index 0000000..250d0d1 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DEBUGGER_PAUSE_UNCAUGHT.yaml +@@ -0,0 +1,17 @@ ++define: DUK_USE_DEBUGGER_PAUSE_UNCAUGHT ++introduced: 1.4.0 ++# Option is ignored unless debugger support is enabled. ++#requires: ++# - DUK_USE_DEBUGGER_SUPPORT ++related: ++ - DUK_USE_DEBUGGER_THROW_NOTIFY ++default: false ++tags: ++ - debugger ++description: > ++ Pause automatically when an error is about to be thrown and that error is ++ (most likely) not going to be caught. An error is considered uncaught if ++ there is no active catch clause in the current thread or in the current ++ thread's resumer chain. This is not 100% accurate because there may be a ++ finally clause which neutralizes the throw (e.g. converts it to a "return" ++ or a "continue"). +diff --git a/dist/source/config/config-options/DUK_USE_DEBUGGER_SUPPORT.yaml b/dist/source/config/config-options/DUK_USE_DEBUGGER_SUPPORT.yaml +new file mode 100644 +index 0000000..a67ac73fd +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DEBUGGER_SUPPORT.yaml +@@ -0,0 +1,13 @@ ++define: DUK_USE_DEBUGGER_SUPPORT ++introduced: 1.2.0 ++requires: ++ - DUK_USE_INTERRUPT_COUNTER ++default: false ++tags: ++ - debugger ++description: > ++ Enable support for Duktape debug protocol (see doc/debugger.rst) and the ++ debug API calls (duk_debugger_attach(), duk_debugger_detach(), etc). ++ This adds about 10kB of code footprint at the moment. ++ ++ This option requires DUK_USE_INTERRUPT_COUNTER. +diff --git a/dist/source/config/config-options/DUK_USE_DEBUGGER_THROW_NOTIFY.yaml b/dist/source/config/config-options/DUK_USE_DEBUGGER_THROW_NOTIFY.yaml +new file mode 100644 +index 0000000..b323e60 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DEBUGGER_THROW_NOTIFY.yaml +@@ -0,0 +1,14 @@ ++define: DUK_USE_DEBUGGER_THROW_NOTIFY ++introduced: 1.4.0 ++# Option is ignored unless debugger support is enabled. ++#requires: ++# - DUK_USE_DEBUGGER_SUPPORT ++related: ++ - DUK_USE_DEBUGGER_PAUSE_UNCAUGHT ++default: true ++tags: ++ - debugger ++description: > ++ Send a Throw notify when an error is about to be thrown. The Throw notify ++ also indicates if an error is fatal (most likely not caught) which is very ++ useful in debugging. +diff --git a/dist/source/config/config-options/DUK_USE_DEBUGGER_TRANSPORT_TORTURE.yaml b/dist/source/config/config-options/DUK_USE_DEBUGGER_TRANSPORT_TORTURE.yaml +new file mode 100644 +index 0000000..70a0f35 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DEBUGGER_TRANSPORT_TORTURE.yaml +@@ -0,0 +1,14 @@ ++define: DUK_USE_DEBUGGER_TRANSPORT_TORTURE ++introduced: 1.2.0 ++# Option is ignored unless debugger support is enabled. ++#requires: ++# - DUK_USE_DEBUGGER_SUPPORT ++default: false ++tags: ++ - debugger ++ - development ++ - torture ++description: > ++ Development time option: force debugger transport torture. Concretely this ++ now causes Duktape to read/write debug protocol data in 1-byte increments, ++ which stresses message parsing and transport code. +diff --git a/dist/source/config/config-options/DUK_USE_DEBUG_BUFSIZE.yaml b/dist/source/config/config-options/DUK_USE_DEBUG_BUFSIZE.yaml +new file mode 100644 +index 0000000..1d93415 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DEBUG_BUFSIZE.yaml +@@ -0,0 +1,12 @@ ++define: DUK_USE_DEBUG_BUFSIZE ++introduced: 1.0.0 ++related: # not strictly required, value is defined even when DUK_USE_DEBUG not defined ++ - DUK_USE_DEBUG ++default: 65536 ++tags: ++ - debug ++description: > ++ Debug code uses a static buffer as a formatting temporary to avoid side ++ effects in debug prints. The static buffer is large by default, which ++ may be an issue in constrained environments. You can set the buffer size ++ manually with this option, e.g. set DUK_USE_DEBUG_BUFSIZE to 2048. +diff --git a/dist/source/config/config-options/DUK_USE_DEBUG_LEVEL.yaml b/dist/source/config/config-options/DUK_USE_DEBUG_LEVEL.yaml +new file mode 100644 +index 0000000..aa0265e +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DEBUG_LEVEL.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_DEBUG_LEVEL ++introduced: 2.0.0 ++default: 0 ++tags: ++ - debug ++description: > ++ Set debug print level when DUK_USE_DEBUG is enabled. The level can be ++ 0 (minimal), 1 (verbose), 2 (very verbose). +diff --git a/dist/source/config/config-options/DUK_USE_DEBUG_WRITE.yaml b/dist/source/config/config-options/DUK_USE_DEBUG_WRITE.yaml +new file mode 100644 +index 0000000..46599be +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DEBUG_WRITE.yaml +@@ -0,0 +1,16 @@ ++define: DUK_USE_DEBUG_WRITE ++introduced: 2.0.0 ++default: false ++tags: ++ - debug ++description: > ++ Macro used for Duktape debug log writes (when DUK_USE_DEBUG is enabled). ++ There's no default provider to avoid a dependency on platform I/O calls. ++ The macro is called like a function with the following prototype: ++ "void DUK_USE_DEBUG_WRITE(long level, const char *file, long line, const char *func, const char *msg)". ++ The "file", "func", and "msg" arguments are non-NULL strings, though NULLs ++ should be handled as good practice (it's ultimately up to duk_config.h ++ whether NULL values are possible). ++ ++ See http://wiki.duktape.org/HowtoDebugPrints.html for more information ++ and examples. +diff --git a/dist/source/config/config-options/DUK_USE_DEEP_C_STACK.yaml b/dist/source/config/config-options/DUK_USE_DEEP_C_STACK.yaml +new file mode 100644 +index 0000000..82de994 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DEEP_C_STACK.yaml +@@ -0,0 +1,12 @@ ++define: DUK_USE_DEEP_C_STACK ++introduced: 1.0.0 ++removed: 1.3.0 ++default: true ++tags: ++ - portability ++description: > ++ Assume deep C stacks are not an issue on the target platform; on some ++ embedded platforms the native C stack is very limited (e.g. 32-64 kB) ++ and overrunning the stack leads to difficult-to-diagnose problems. ++ ++ Removed in Duktape 1.3.0, replaced by explicit recursion limits. +diff --git a/dist/source/config/config-options/DUK_USE_DOUBLE_BE.yaml b/dist/source/config/config-options/DUK_USE_DOUBLE_BE.yaml +new file mode 100644 +index 0000000..a63d9d9 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DOUBLE_BE.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_DOUBLE_BE ++introduced: 1.0.0 ++removed: 1.4.0 ++default: false ++conflicts: ++ - DUK_USE_DOUBLE_LE ++ - DUK_USE_DOUBLE_ME ++tags: ++ - portability ++description: > ++ IEEE double memory representation is big endian on the target platform. +diff --git a/dist/source/config/config-options/DUK_USE_DOUBLE_LE.yaml b/dist/source/config/config-options/DUK_USE_DOUBLE_LE.yaml +new file mode 100644 +index 0000000..fcbcbc0 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DOUBLE_LE.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_DOUBLE_LE ++introduced: 1.0.0 ++removed: 1.4.0 ++default: true ++conflicts: ++ - DUK_USE_DOUBLE_BE ++ - DUK_USE_DOUBLE_ME ++tags: ++ - portability ++description: > ++ IEEE double memory representation is little endian on the target platform. +diff --git a/dist/source/config/config-options/DUK_USE_DOUBLE_LINKED_HEAP.yaml b/dist/source/config/config-options/DUK_USE_DOUBLE_LINKED_HEAP.yaml +new file mode 100644 +index 0000000..8e55973 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DOUBLE_LINKED_HEAP.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_DOUBLE_LINKED_HEAP ++introduced: 1.0.0 ++default: true ++tags: ++ - ecmascript ++description: > ++ Use a double-linked duk_heaphdr structure. Required when reference ++ counting is enabled. ++ ++# XXX: This must currently always match DUK_USE_REFERENCE_COUNTING ++# so remove as a separate option? +diff --git a/dist/source/config/config-options/DUK_USE_DOUBLE_ME.yaml b/dist/source/config/config-options/DUK_USE_DOUBLE_ME.yaml +new file mode 100644 +index 0000000..61a4cda +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DOUBLE_ME.yaml +@@ -0,0 +1,13 @@ ++define: DUK_USE_DOUBLE_ME ++introduced: 1.0.0 ++removed: 1.4.0 ++default: false ++conflicts: ++ - DUK_USE_DOUBLE_LE ++ - DUK_USE_DOUBLE_BE ++tags: ++ - portability ++description: > ++ IEEE double memory representation is mixed endian on the target platform. ++ In other words the logical bytes ABCDEFGH are represented in memory as ++ DCBAHGFE. This endianness is used by some ARM platforms. +diff --git a/dist/source/config/config-options/DUK_USE_DPRINT.yaml b/dist/source/config/config-options/DUK_USE_DPRINT.yaml +new file mode 100644 +index 0000000..0895eb6 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DPRINT.yaml +@@ -0,0 +1,10 @@ ++define: DUK_USE_DPRINT ++introduced: 1.0.0 ++removed: 2.0.0 ++requires: ++ - DUK_USE_DEBUG ++default: false ++tags: ++ - debug ++description: > ++ Enable debug printouts. Replaced by DUK_USE_DEBUG_LEVEL. +diff --git a/dist/source/config/config-options/DUK_USE_DPRINT_COLORS.yaml b/dist/source/config/config-options/DUK_USE_DPRINT_COLORS.yaml +new file mode 100644 +index 0000000..e1b65f1 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DPRINT_COLORS.yaml +@@ -0,0 +1,10 @@ ++define: DUK_USE_DPRINT_COLORS ++introduced: 1.0.0 ++removed: 2.0.0 ++default: false ++tags: ++ - debug ++description: > ++ Enable coloring of debug prints with ANSI escape codes ++ (http://en.wikipedia.org/wiki/ANSI_escape_code). The behavior is not ++ sensitive to terminal settings. +diff --git a/dist/source/config/config-options/DUK_USE_DPRINT_RDTSC.yaml b/dist/source/config/config-options/DUK_USE_DPRINT_RDTSC.yaml +new file mode 100644 +index 0000000..e154585 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DPRINT_RDTSC.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_DPRINT_RDTSC ++introduced: 1.0.0 ++removed: 2.0.0 ++default: false ++tags: ++ - debug ++description: > ++ Print RDTSC cycle count in debug prints if available. +diff --git a/dist/source/config/config-options/DUK_USE_DUKTAPE_BUILTIN.yaml b/dist/source/config/config-options/DUK_USE_DUKTAPE_BUILTIN.yaml +new file mode 100644 +index 0000000..c36aebf +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_DUKTAPE_BUILTIN.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_DUKTAPE_BUILTIN ++introduced: 2.0.0 ++default: true ++tags: ++ - duktape ++description: > ++ Provide a Duktape built-in. +diff --git a/dist/source/config/config-options/DUK_USE_ENCODING_BUILTINS.yaml b/dist/source/config/config-options/DUK_USE_ENCODING_BUILTINS.yaml +new file mode 100644 +index 0000000..c8615e3 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_ENCODING_BUILTINS.yaml +@@ -0,0 +1,13 @@ ++define: DUK_USE_ENCODING_BUILTINS ++introduced: 2.0.0 ++default: true ++tags: ++ - encoding-api ++description: > ++ Provide TextEncoder and TextDecoder built-ins (the Encoding API) which allow ++ ECMAScript code to encode and decode text stored in a buffer. Only UTF-8 is ++ currently supported for decoding. ++ ++ DUK_USE_BUFFEROBJECT_SUPPORT is recommended but not required: If it is ++ disabled, TextEncoder will encode to a plain buffer instead of a Uint8Array. ++ TextDecoder always accepts plain buffers as input. +diff --git a/dist/source/config/config-options/DUK_USE_ERRCREATE.yaml b/dist/source/config/config-options/DUK_USE_ERRCREATE.yaml +new file mode 100644 +index 0000000..f579fdf +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_ERRCREATE.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_ERRCREATE ++introduced: 1.0.0 ++default: true ++tags: ++ - ecmascript ++description: > ++ Call Duktape.errCreate() when augmenting an ECMAScript error object ++ being created. +diff --git a/dist/source/config/config-options/DUK_USE_ERRTHROW.yaml b/dist/source/config/config-options/DUK_USE_ERRTHROW.yaml +new file mode 100644 +index 0000000..fdd2b02 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_ERRTHROW.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_ERRTHROW ++introduced: 1.0.0 ++default: true ++tags: ++ - ecmascript ++description: > ++ Call Duktape.errThrow() when augmenting an ECMAScript error object ++ about to be thrown. +diff --git a/dist/source/config/config-options/DUK_USE_ES6.yaml b/dist/source/config/config-options/DUK_USE_ES6.yaml +new file mode 100644 +index 0000000..886fe9b +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_ES6.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_ES6 ++introduced: 2.0.0 ++default: true ++tags: ++ - ecmascript2015 ++description: > ++ Enable ES6 functionality not covered by other specific config options. +diff --git a/dist/source/config/config-options/DUK_USE_ES6_OBJECT_PROTO_PROPERTY.yaml b/dist/source/config/config-options/DUK_USE_ES6_OBJECT_PROTO_PROPERTY.yaml +new file mode 100644 +index 0000000..d0279d4 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_ES6_OBJECT_PROTO_PROPERTY.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_ES6_OBJECT_PROTO_PROPERTY ++introduced: 1.0.0 ++default: true ++tags: ++ - ecmascript2015 ++description: > ++ Provide the non-standard (ES6) Object.prototype.__proto__ property. +diff --git a/dist/source/config/config-options/DUK_USE_ES6_OBJECT_SETPROTOTYPEOF.yaml b/dist/source/config/config-options/DUK_USE_ES6_OBJECT_SETPROTOTYPEOF.yaml +new file mode 100644 +index 0000000..04b29fb +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_ES6_OBJECT_SETPROTOTYPEOF.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_ES6_OBJECT_SETPROTOTYPEOF ++introduced: 1.0.0 ++default: true ++tags: ++ - ecmascript2015 ++description: > ++ Provide the non-standard (ES6) Object.setPrototypeOf method. +diff --git a/dist/source/config/config-options/DUK_USE_ES6_PROXY.yaml b/dist/source/config/config-options/DUK_USE_ES6_PROXY.yaml +new file mode 100644 +index 0000000..e05952c +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_ES6_PROXY.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_ES6_PROXY ++introduced: 1.0.0 ++default: true ++tags: ++ - ecmascript2015 ++description: > ++ Provide the non-standard (ES6) Proxy object. +diff --git a/dist/source/config/config-options/DUK_USE_ES6_REGEXP_BRACES.yaml b/dist/source/config/config-options/DUK_USE_ES6_REGEXP_BRACES.yaml +new file mode 100644 +index 0000000..ea864f8 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_ES6_REGEXP_BRACES.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_ES6_REGEXP_BRACES ++introduced: 1.5.0 ++removed: 2.0.0 ++default: true ++tags: ++ - ecmascript2015 ++description: > ++ Replaced by DUK_USE_ES6_REGEXP_SYNTAX. +diff --git a/dist/source/config/config-options/DUK_USE_ES6_REGEXP_SYNTAX.yaml b/dist/source/config/config-options/DUK_USE_ES6_REGEXP_SYNTAX.yaml +new file mode 100644 +index 0000000..f9f1c41 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_ES6_REGEXP_SYNTAX.yaml +@@ -0,0 +1,14 @@ ++define: DUK_USE_ES6_REGEXP_SYNTAX ++introduced: 2.0.0 ++default: true ++tags: ++ - ecmascript2015 ++description: > ++ Enable support for additional RegExp syntax from E6 Section B.1.4, such as: ++ (1) dollar escape, (2) unescaped curly braces ('{' and '}'), and (3) ++ unescaped right bracket (']'). This option does not enable all of the ES6 ++ syntax because not all of the extra syntax is implemented; rather, the option ++ enables whatever ES6 extra syntax has been implemented so far. ++ ++ This option is recommended because a lot of existing code depends on literal ++ regexp braces, and other non-ES5 constructs. +diff --git a/dist/source/config/config-options/DUK_USE_ES6_UNICODE_ESCAPE.yaml b/dist/source/config/config-options/DUK_USE_ES6_UNICODE_ESCAPE.yaml +new file mode 100644 +index 0000000..5ed1cb4 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_ES6_UNICODE_ESCAPE.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_ES6_UNICODE_ESCAPE ++introduced: 2.0.0 ++default: true ++tags: ++ - ecmascript2015 ++description: > ++ Enable support for ES6 Unicode escape syntax ("\u{12345}") in source code ++ and RegExps. +diff --git a/dist/source/config/config-options/DUK_USE_ES7.yaml b/dist/source/config/config-options/DUK_USE_ES7.yaml +new file mode 100644 +index 0000000..e195be3 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_ES7.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_ES7 ++introduced: 2.0.0 ++default: true ++tags: ++ - ecmascript2016 ++description: > ++ Enable ES7 functionality not covered by other specific config options. +diff --git a/dist/source/config/config-options/DUK_USE_ES7_EXP_OPERATOR.yaml b/dist/source/config/config-options/DUK_USE_ES7_EXP_OPERATOR.yaml +new file mode 100644 +index 0000000..8cd9fe4 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_ES7_EXP_OPERATOR.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_ES7_EXP_OPERATOR ++introduced: 2.0.0 ++default: true ++tags: ++ - ecmascript2016 ++description: > ++ Support the ES7 exponentiation operator (** and **=). This is optional ++ because the operator pulls in pow() which may have a significant footprint ++ impact on bare metal platforms. +diff --git a/dist/source/config/config-options/DUK_USE_ES8.yaml b/dist/source/config/config-options/DUK_USE_ES8.yaml +new file mode 100644 +index 0000000..ec10b5c +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_ES8.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_ES8 ++introduced: 2.0.0 ++default: true ++tags: ++ - ecmascript2017 ++description: > ++ Enable ES8 functionality not covered by other specific config options. +diff --git a/dist/source/config/config-options/DUK_USE_ES9.yaml b/dist/source/config/config-options/DUK_USE_ES9.yaml +new file mode 100644 +index 0000000..541a00d +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_ES9.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_ES9 ++introduced: 2.0.0 ++default: true ++tags: ++ - ecmascript2018 ++description: > ++ Enable ES9 functionality not covered by other specific config options. +diff --git a/dist/source/config/config-options/DUK_USE_ESBC_LIMITS.yaml b/dist/source/config/config-options/DUK_USE_ESBC_LIMITS.yaml +new file mode 100644 +index 0000000..b96de7a +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_ESBC_LIMITS.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_ESBC_LIMITS ++introduced: 1.0.0 ++default: true ++tags: ++ - ecmascript ++description: > ++ Impose byte and line number limits for compiled function bytecode. +diff --git a/dist/source/config/config-options/DUK_USE_ESBC_MAX_BYTES.yaml b/dist/source/config/config-options/DUK_USE_ESBC_MAX_BYTES.yaml +new file mode 100644 +index 0000000..1230861 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_ESBC_MAX_BYTES.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_ESBC_MAX_BYTES ++introduced: 1.0.0 ++requires: ++ - DUK_USE_ESBC_LIMITS ++default: 0x7fff0000 ++tags: ++ - ecmascript ++description: > ++ Maximum byte count for compiled function bytecode. +diff --git a/dist/source/config/config-options/DUK_USE_ESBC_MAX_LINENUMBER.yaml b/dist/source/config/config-options/DUK_USE_ESBC_MAX_LINENUMBER.yaml +new file mode 100644 +index 0000000..6f1da17 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_ESBC_MAX_LINENUMBER.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_ESBC_MAX_LINENUMBER ++introduced: 1.0.0 ++requires: ++ - DUK_USE_ESBC_LIMITS ++default: 0x7fff0000 ++tags: ++ - ecmascript ++description: > ++ Maximum line number for compiled function bytecode. +diff --git a/dist/source/config/config-options/DUK_USE_EXAMPLE.yaml b/dist/source/config/config-options/DUK_USE_EXAMPLE.yaml +new file mode 100644 +index 0000000..7fa5959 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_EXAMPLE.yaml +@@ -0,0 +1,74 @@ ++# ++# Each Duktape option is described as a YAML file named OPTION_NAME.yaml. ++# YAML is used because it diffs well and has clean support for multiline ++# inline strings. ++# ++ ++# C #define name for the option. Must match filename minus extension. ++define: DUK_USE_OBJSIZES16 ++ ++# Duktape version number where this option was first introduced. ++introduced: 1.1.0 ++ ++# Optional Duktape version number where this option was deprecated, ++# i.e. the option is supported but no longer recommended. ++# XXX: automatic #error with some -DDUK_USE_NO_DEPRECATED flag? ++#deprecated: 1.2.0 ++ ++# Optional Duktape version number where this option was removed, ++# i.e. the option is no longer supported but we may want to issue ++# a clear #error for it. ++#removed: 1.3.0 ++ ++# Optional indication that config option is defined but currently ++# unused, so that it can be omitted from generated header. ++#unused: true ++ ++# Optional list of options that must also be defined to use this option. ++#requires: ++# - DUK_USE_FOO ++# - DUK_USE_BAR ++ ++# Optional list of options that this option conflicts with. ++#conflicts: ++# - DUK_USE_BAZ ++ ++# Optional list of options that are related from a user and documentation ++# perspective. ++#related: ++# - DUK_USE_QUUX ++ ++# Default value for option: ++# - false: undefined (#undef DUK_USE_EXAMPLE) ++# - true: defined with no value (#define DUK_USE_EXAMPLE) ++# - string: defined with string value (#define DUK_USE_EXAMPLE "foo") ++# - number: defined with number value (#define DUK_USE_EXAMPLE 123) ++# - verbatim: define verbatim line(s) ++default: false ++ ++# Tags related to option (required). If present, first tag is used as a ++# primary tag for grouping. Use 'misc' if nothing else is appropriate. ++tags: ++ - lowmemory ++ - experimental ++ ++# When set to true, genconfig.py will warn if no forced value is provided. ++# This should be used sparingly, for options which are really strongly ++# recommended so that configure.py output should warn about it. ++#warn_if_missing: true ++ ++# Description for option, no newlines. Line breaking for e.g. C header ++# is automatic. ++description: > ++ Use a 16-bit object entry and array part sizes (for low memory ++ environments). Also automatically drops support for an object hash ++ part to further reduce memory usage; there are rarely large objects ++ in low memory environments simply because there's no memory to store ++ a lot of properties. ++ ++ By default use "description: >" markup which works well for paragraphs ++ (replacing newlines with spaces) but includes a trailing newline which ++ is also a good default. ++ ++# Marker to avoid processing this file. ++example: true +diff --git a/dist/source/config/config-options/DUK_USE_EXEC_FUN_LOCAL.yaml b/dist/source/config/config-options/DUK_USE_EXEC_FUN_LOCAL.yaml +new file mode 100644 +index 0000000..3d66df2 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_EXEC_FUN_LOCAL.yaml +@@ -0,0 +1,14 @@ ++define: DUK_USE_EXEC_FUN_LOCAL ++introduced: 1.4.0 ++default: false ++tags: ++ - performance ++ - execution ++description: > ++ Use a local variable "fun" pointing to the current function in the bytecode ++ dispatch loop instead of looking up the current function through "thr" every ++ time it is needed. On x64 performance is slightly better without a "fun" ++ local; on x86 performance is slightly better with one. ++ ++ You should only tweak this if you're really interested in performance, and ++ should then do proper testing to see which value works better. +diff --git a/dist/source/config/config-options/DUK_USE_EXEC_INDIRECT_BOUND_CHECK.yaml b/dist/source/config/config-options/DUK_USE_EXEC_INDIRECT_BOUND_CHECK.yaml +new file mode 100644 +index 0000000..16c30f0 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_EXEC_INDIRECT_BOUND_CHECK.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_EXEC_INDIRECT_BOUND_CHECK ++introduced: 1.0.0 ++default: false ++tags: ++ - execution ++ - debug ++description: > ++ For opcodes with indirect indices, check final index against stack size. ++ Useful for diagnosing problems. Normally this check is not necessary ++ because the compiler is trusted, and we don't bound check direct indices ++ either. +diff --git a/dist/source/config/config-options/DUK_USE_EXEC_PREFER_SIZE.yaml b/dist/source/config/config-options/DUK_USE_EXEC_PREFER_SIZE.yaml +new file mode 100644 +index 0000000..19aa8f1 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_EXEC_PREFER_SIZE.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_EXEC_PREFER_SIZE ++introduced: 2.0.0 ++default: false ++tags: ++ - lowmemory ++description: > ++ Prefer size over performance in bytecode executor. +diff --git a/dist/source/config/config-options/DUK_USE_EXEC_REGCONST_OPTIMIZE.yaml b/dist/source/config/config-options/DUK_USE_EXEC_REGCONST_OPTIMIZE.yaml +new file mode 100644 +index 0000000..b7bee88 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_EXEC_REGCONST_OPTIMIZE.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_EXEC_REGCONST_OPTIMIZE ++introduced: 2.0.0 ++default: true ++tags: ++ - performance ++ - execution ++description: > ++ Use an internal optimization to access registers and constants in the ++ bytecode executor. The optimization avoids unnecessary shifts when computing ++ addresses but assumes sizeof(duk_tval) is 8 or 16. This is almost always the ++ case, but when it isn't, disable this option. +diff --git a/dist/source/config/config-options/DUK_USE_EXEC_TIMEOUT_CHECK.yaml b/dist/source/config/config-options/DUK_USE_EXEC_TIMEOUT_CHECK.yaml +new file mode 100644 +index 0000000..27e3e6f +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_EXEC_TIMEOUT_CHECK.yaml +@@ -0,0 +1,31 @@ ++define: DUK_USE_EXEC_TIMEOUT_CHECK ++introduced: 1.2.0 ++requires: ++ - DUK_USE_INTERRUPT_COUNTER ++default: false ++tags: ++ - execution ++ - sandbox ++ - experimental ++description: > ++ NOTE: This mechanism is EXPERIMENTAL and the details may change ++ between releases. ++ ++ Provide a hook to check for bytecode execution timeout. The macro gets ++ a void ptr userdata argument (the userdata given to duk_heap_create()) ++ and must evaluate to a duk_bool_t. Duktape calls the macro as: ++ "if (DUK_USE_EXEC_TIMEOUT_CHECK(udata)) { ... }". ++ ++ The macro is called occasionally by the Duktape bytecode executor (i.e. ++ when executing ECMAScript code), typically from a few times per second ++ to a hundred times per second, but the interval varies a great deal ++ depending on what kind of code is being executed. ++ ++ To indicate an execution timeout, the macro must return a non-zero value. ++ When that happens, Duktape starts to bubble a ``RangeError`` outwards ++ until control has been returned to the original protected call made by ++ the application. Until that happens, the exec timeout macro must always ++ return non-zero to indicate an execution timeout is still in progress. ++ ++ This mechanism and its limitations is described in more detail in ++ doc/sandboxing.rst. +diff --git a/dist/source/config/config-options/DUK_USE_EXPLICIT_NULL_INIT.yaml b/dist/source/config/config-options/DUK_USE_EXPLICIT_NULL_INIT.yaml +new file mode 100644 +index 0000000..0da6c1b +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_EXPLICIT_NULL_INIT.yaml +@@ -0,0 +1,12 @@ ++define: DUK_USE_EXPLICIT_NULL_INIT ++introduced: 1.0.0 ++default: false ++tags: ++ - portability ++description: > ++ When zeroing structures, don't rely on memzero initializing pointers to ++ NULL. Instead, write NULL values explicitly after zeroing. This should ++ only be necessary when a NULL pointer is not represented by zero bytes in ++ memory. ++ ++ # NOTE: Condition in Duktape 1.2 seems wrong, and is linked to packed tval. +diff --git a/dist/source/config/config-options/DUK_USE_EXTSTR_FREE.yaml b/dist/source/config/config-options/DUK_USE_EXTSTR_FREE.yaml +new file mode 100644 +index 0000000..d8c2972 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_EXTSTR_FREE.yaml +@@ -0,0 +1,23 @@ ++define: DUK_USE_EXTSTR_FREE ++introduced: 1.1.0 ++requires: ++ - DUK_USE_HSTRING_EXTDATA ++default: false ++tags: ++ - memory ++ - experimental ++description: > ++ Optional counterpart to DUK_USE_EXTSTR_INTERN_CHECK. Invoked when an ++ external string is about to be freed by Duktape. ++ ++ The argument "ptr" is a void ptr and points to the external string data. ++ Concretely, it is the (non-NULL) value returned by ++ DUK_USE_EXTSTR_INTERN_CHECK. The "udata" argument is the heap userdata ++ which may be ignored if not needed. ++ ++ Also enable DUK_USE_HSTRING_EXTDATA to use this feature. ++ ++ NOTE: Right now there is no API to push external strings; external strings ++ come into being as a result of DUK_USE_EXTSTR_INTERN_CHECK() only. If/when ++ this is changed, this hook will get called for every string, even if pushed ++ by the user using an API call; this may need to be rethought at that time. +diff --git a/dist/source/config/config-options/DUK_USE_EXTSTR_INTERN_CHECK.yaml b/dist/source/config/config-options/DUK_USE_EXTSTR_INTERN_CHECK.yaml +new file mode 100644 +index 0000000..784eb25 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_EXTSTR_INTERN_CHECK.yaml +@@ -0,0 +1,39 @@ ++define: DUK_USE_EXTSTR_INTERN_CHECK ++introduced: 1.1.0 ++requires: ++ - DUK_USE_HSTRING_EXTDATA ++default: false ++tags: ++ - memory ++ - experimental ++description: > ++ Provide a hook for checking if data for a certain string can be used from ++ external memory (outside of Duktape heap, e.g. memory mapped flash). ++ The hook is called during string interning with the following semantics: ++ ++ The string data with no NUL termination resides at "ptr" and has "len" ++ bytes. The "udata" argument is the heap userdata which may be ignored ++ if not needed. If the hook returns NULL, Duktape interns the string ++ normally, i.e. string data is allocated from Duktape heap. Otherwise the ++ hook return value must point to a memory area which contains ++ "len" bytes from "ptr" followed by a NUL byte which is NOT PRESENT ++ in the input data. Data behind the returned pointer may not change after ++ the hook returns. ++ ++ The hook may be called several times for the same input string. This ++ happens when a string is interned, garbage collected, and then interned ++ again. ++ ++ The DUK_USE_EXTSTR_FREE() hook allows application code to detect when ++ an external string is about to be freed. ++ ++ In most cases the hook should reject strings whose "len" is less than 4 ++ because there is no RAM advantage in moving so short strings into external ++ memory. The ordinary "duk_hstring" header followed by the data (and a ++ NUL byte) has the same size as "duk_hstring_external" header which hosts ++ a pointer instead of string data. ++ ++ Also enable DUK_USE_HSTRING_EXTDATA to use this feature. ++ ++ See doc/low-memory.rst for more discussion how to use this feature option ++ in practice. +diff --git a/dist/source/config/config-options/DUK_USE_FASTINT.yaml b/dist/source/config/config-options/DUK_USE_FASTINT.yaml +new file mode 100644 +index 0000000..8726026 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_FASTINT.yaml +@@ -0,0 +1,14 @@ ++define: DUK_USE_FASTINT ++requires: ++ - DUK_USE_64BIT_OPS ++introduced: 1.2.0 ++default: false ++tags: ++ - performance ++ - fastpath ++description: > ++ Enable support for 48-bit signed "fastint" integer values. Fastints are ++ transparent to user code (both C and ECMAScript) but may be faster than ++ IEEE doubles on some platforms, especially those using softints. The ++ downside of fastints is increased code footprint and a small performance ++ penalty for some kinds of code. +diff --git a/dist/source/config/config-options/DUK_USE_FAST_REFCOUNT_DEFAULT.yaml b/dist/source/config/config-options/DUK_USE_FAST_REFCOUNT_DEFAULT.yaml +new file mode 100644 +index 0000000..bc9b2f9 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_FAST_REFCOUNT_DEFAULT.yaml +@@ -0,0 +1,10 @@ ++define: DUK_USE_FAST_REFCOUNT_DEFAULT ++introduced: 1.2.0 ++default: true ++tags: ++ - performance ++ - fastpath ++ - experimental ++description: > ++ When enabled, plain refcount macros (e.g. DUK_TVAL_INCREF) default to ++ fast variants (DUK_TVAL_INCREF_FAST) to improve performance. +diff --git a/dist/source/config/config-options/DUK_USE_FATAL_HANDLER.yaml b/dist/source/config/config-options/DUK_USE_FATAL_HANDLER.yaml +new file mode 100644 +index 0000000..e53c914 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_FATAL_HANDLER.yaml +@@ -0,0 +1,23 @@ ++define: DUK_USE_FATAL_HANDLER ++introduced: 2.0.0 ++default: false ++tags: ++ - portability ++warn_if_missing: true ++description: > ++ Provide a custom default fatal error handler to replace the built-in one ++ (which calls abort() without any error message). The default fatal error ++ handler gets called when (1) a fatal error occurs and application code ++ didn't register a fatal error handler in heap creation or (2) a context-free ++ fatal error happens, concretely e.g. an assertion failure. ++ ++ The handler is called like a C function with the prototype ++ "void fatal_handler(void *udata, const char *msg)". The "msg" argument can ++ be NULL. The "udata" argument matches the heap-related userdata but is ++ NULL for fatal errors unrelated to a heap/thread context (this is the case ++ for e.g. assertions). ++ ++ A custom default fatal error handler is recommended for any environment ++ where recover from fatal errors is important. A custom handler can take ++ appropriate action to recover, e.g. record the error and reboot the target ++ device. +diff --git a/dist/source/config/config-options/DUK_USE_FATAL_MAXLEN.yaml b/dist/source/config/config-options/DUK_USE_FATAL_MAXLEN.yaml +new file mode 100644 +index 0000000..2971c20 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_FATAL_MAXLEN.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_FATAL_MAXLEN ++introduced: 2.2.0 ++default: 128 ++tags: ++ - portability ++description: > ++ Maximum length of fatal error message string when error is thrown by ++ Duktape internals, in particular for uncaught errors. +diff --git a/dist/source/config/config-options/DUK_USE_FILE_IO.yaml b/dist/source/config/config-options/DUK_USE_FILE_IO.yaml +new file mode 100644 +index 0000000..8ff767d +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_FILE_IO.yaml +@@ -0,0 +1,10 @@ ++define: DUK_USE_FILE_IO ++introduced: 1.0.0 ++removed: 2.0.0 ++default: true ++tags: ++ - io ++description: > ++ File I/O support. This is now used in a few API calls to e.g. push ++ a string from file contents or eval a file. For portability it must ++ be possible to disable I/O altogether. +diff --git a/dist/source/config/config-options/DUK_USE_FINALIZER_SUPPORT.yaml b/dist/source/config/config-options/DUK_USE_FINALIZER_SUPPORT.yaml +new file mode 100644 +index 0000000..1bb8694 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_FINALIZER_SUPPORT.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_FINALIZER_SUPPORT ++introduced: 2.0.0 ++default: true ++tags: ++ - execution ++description: > ++ Enable support for object finalizers (Duktape specific). +diff --git a/dist/source/config/config-options/DUK_USE_FINALIZER_TORTURE.yaml b/dist/source/config/config-options/DUK_USE_FINALIZER_TORTURE.yaml +new file mode 100644 +index 0000000..30b32b7 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_FINALIZER_TORTURE.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_FINALIZER_TORTURE ++introduced: 2.1.0 ++default: false ++tags: ++ - gc ++ - memory ++ - development ++ - torture ++description: > ++ Development time option: simulate a fake finalizer call every time when ++ finalizers might be executed (even if the actual finalize_list is empty). +diff --git a/dist/source/config/config-options/DUK_USE_FLEX_C99.yaml b/dist/source/config/config-options/DUK_USE_FLEX_C99.yaml +new file mode 100644 +index 0000000..7ade513 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_FLEX_C99.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_FLEX_C99 ++introduced: 1.0.0 ++default: true ++tags: ++ - portability ++description: > ++ Use C99 flexible array member for defining variable size structures. ++ ++# XXX: currently unused, remove or mark unused so doesn't get emitted? +diff --git a/dist/source/config/config-options/DUK_USE_FLEX_ONESIZE.yaml b/dist/source/config/config-options/DUK_USE_FLEX_ONESIZE.yaml +new file mode 100644 +index 0000000..46894fb +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_FLEX_ONESIZE.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_FLEX_ONESIZE ++introduced: 1.0.0 ++default: false ++tags: ++ - portability ++description: > ++ Use a single element array to define variable size structures. This is the ++ most portable alternative: zero-size arrays are not allowed by all compilers ++ and flexible array member ("char arr[]") is defined in C99. ++ ++# XXX: currently unused, remove or mark unused so doesn't get emitted? +diff --git a/dist/source/config/config-options/DUK_USE_FLEX_ZEROSIZE.yaml b/dist/source/config/config-options/DUK_USE_FLEX_ZEROSIZE.yaml +new file mode 100644 +index 0000000..2d4f756 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_FLEX_ZEROSIZE.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_FLEX_ZEROSIZE ++introduced: 1.0.0 ++default: false ++tags: ++ - portability ++description: > ++ Use a zero element array to define variable size structures. This is not ++ fully portable but works with some compilers and is preferred over using ++ a one element array. ++ ++# XXX: currently unused, remove or mark unused so doesn't get emitted? +diff --git a/dist/source/config/config-options/DUK_USE_FULL_TVAL.yaml b/dist/source/config/config-options/DUK_USE_FULL_TVAL.yaml +new file mode 100644 +index 0000000..796c317 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_FULL_TVAL.yaml +@@ -0,0 +1,16 @@ ++define: DUK_USE_FULL_TVAL ++introduced: 1.0.0 ++removed: 1.2.0 ++default: false ++tags: ++ - misc ++description: > ++ Initialize all bytes of a duk_tval when setting a value into one. ++ ++ By default only needed fields are initialized which reduces code size and ++ improves performance slightly. This should cause no functional issues but ++ may cause valgrind issues in rare cases, e.g. when debugger code dumps the ++ constant table of a function (which then reads uninitialized bits). ++ ++ Removed in 1.2.0 because the option was never enabled and there was no ++ feature option to cause it to be used. +diff --git a/dist/source/config/config-options/DUK_USE_FUNCPTR16.yaml b/dist/source/config/config-options/DUK_USE_FUNCPTR16.yaml +new file mode 100644 +index 0000000..bf54cdb +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_FUNCPTR16.yaml +@@ -0,0 +1,21 @@ ++define: DUK_USE_FUNCPTR16 ++introduced: 1.1.0 ++related: ++ - DUK_USE_FUNCPTR_ENC16 ++ - DUK_USE_FUNCPTR_DEC16 ++default: false ++tags: ++ - lowmemory ++ - experimental ++description: > ++ Enable "compression" of arbitrary data pointers into an unsigned 16-bit ++ value. Use together with DUK_USE_DATAPTR_ENC16 and DUK_USE_DATAPTR_DEC16. ++ ++ Pointers compressed are any void pointers in C code, not just the Duktape ++ heap. Also NULL pointer must encode and decode correctly. ++ ++ Currently it is required that NULL encodes to integer 0, and integer ++ 0 decodes to NULL. No other pointer can be encoded to 0. ++ ++ NOTE: This feature option is currently unimplemented, i.e. Duktape won't ++ compress any data pointers at the moment. +diff --git a/dist/source/config/config-options/DUK_USE_FUNCPTR_DEC16.yaml b/dist/source/config/config-options/DUK_USE_FUNCPTR_DEC16.yaml +new file mode 100644 +index 0000000..f1e10c0 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_FUNCPTR_DEC16.yaml +@@ -0,0 +1,15 @@ ++define: DUK_USE_FUNCPTR_DEC16 ++introduced: 1.1.0 ++requires: ++ - DUK_USE_FUNCPTR16 ++default: false ++tags: ++ - lowmemory ++ - experimental ++description: > ++ Use together with DUK_USE_FUNCPTR16 for arbitrary data pointer compression. ++ DUK_USE_FUNCPTR_ENC16(udata,p) is a macro with a userdata and void ptr ++ argument, and a duk_uint16_t return value. The userdata argument is the ++ heap userdata value given at heap creation. Currently it is required that ++ NULL encodes to integer 0, and integer 0 decodes to NULL. No other pointer ++ can be encoded to 0. +diff --git a/dist/source/config/config-options/DUK_USE_FUNCPTR_ENC16.yaml b/dist/source/config/config-options/DUK_USE_FUNCPTR_ENC16.yaml +new file mode 100644 +index 0000000..51ff363 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_FUNCPTR_ENC16.yaml +@@ -0,0 +1,13 @@ ++define: DUK_USE_FUNCPTR_ENC16 ++introduced: 1.1.0 ++requires: ++ - DUK_USE_FUNCPTR16 ++default: false ++tags: ++ - lowmemory ++ - experimental ++description: > ++ Use together with DUK_USE_FUNCPTR16 for arbitrary data pointer compression. ++ DUK_USE_FUNCPTR_DEC16(udata,x) is a macro with a userdata and duk_uint16_t ++ argument, and a void ptr return value. The userdata argument is the heap ++ userdata value given at heap creation. +diff --git a/dist/source/config/config-options/DUK_USE_FUNCTION_BUILTIN.yaml b/dist/source/config/config-options/DUK_USE_FUNCTION_BUILTIN.yaml +new file mode 100644 +index 0000000..663cd45 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_FUNCTION_BUILTIN.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_FUNCTION_BUILTIN ++introduced: 2.0.0 ++default: true ++tags: ++ - ecmascript ++description: > ++ Provide a Function built-in. +diff --git a/dist/source/config/config-options/DUK_USE_FUNC_FILENAME_PROPERTY.yaml b/dist/source/config/config-options/DUK_USE_FUNC_FILENAME_PROPERTY.yaml +new file mode 100644 +index 0000000..74e98bd +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_FUNC_FILENAME_PROPERTY.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_FUNC_FILENAME_PROPERTY ++introduced: 2.0.0 ++default: true ++tags: ++ - ecmascript ++ - compliance ++description: > ++ Add a non-standard ".fileName" property to function instances. Disabling ++ reduces footprint. +diff --git a/dist/source/config/config-options/DUK_USE_FUNC_NAME_PROPERTY.yaml b/dist/source/config/config-options/DUK_USE_FUNC_NAME_PROPERTY.yaml +new file mode 100644 +index 0000000..fce6625 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_FUNC_NAME_PROPERTY.yaml +@@ -0,0 +1,10 @@ ++define: DUK_USE_FUNC_NAME_PROPERTY ++introduced: 2.0.0 ++default: true ++tags: ++ - ecmascript ++ - compliance ++description: > ++ Add a "name" property to function instances. This is part of ECMAScript ++ requirements, but low memory devices can sometimes opt to not include the ++ .name to reduce footprint. +diff --git a/dist/source/config/config-options/DUK_USE_FUZZILLI.yaml b/dist/source/config/config-options/DUK_USE_FUZZILLI.yaml +new file mode 100644 +index 0000000..baf88d0 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_FUZZILLI.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_FUZZILLI ++introduced: 3.0.0 ++default: false ++tags: ++ - fuzzing ++description: > ++ Needed when running Fuzzilli fuzzing, enables access to duk_assert_wrapper() ++ expected by the integration. +diff --git a/dist/source/config/config-options/DUK_USE_GCC_PRAGMAS.yaml b/dist/source/config/config-options/DUK_USE_GCC_PRAGMAS.yaml +new file mode 100644 +index 0000000..9a68e46 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_GCC_PRAGMAS.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_GCC_PRAGMAS ++introduced: 1.0.0 ++default: false ++tags: ++ - portability ++description: > ++ Use GCC-specific pragmas, e.g. "#pragma GCC diagnostic" to suppress ++ unnecessary warnings. +diff --git a/dist/source/config/config-options/DUK_USE_GC_TORTURE.yaml b/dist/source/config/config-options/DUK_USE_GC_TORTURE.yaml +new file mode 100644 +index 0000000..7f2968a +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_GC_TORTURE.yaml +@@ -0,0 +1,14 @@ ++define: DUK_USE_GC_TORTURE ++introduced: 1.0.0 ++default: false ++tags: ++ - gc ++ - memory ++ - development ++ - torture ++description: > ++ Development time option: force full mark-and-sweep on every allocation and ++ in other chosen places to stress test memory management. ++ ++ Using a low value (e.g. 3) for DUK_USE_MARK_AND_SWEEP_RECLIMIT is also ++ recommended. +diff --git a/dist/source/config/config-options/DUK_USE_GET_MONOTONIC_TIME.yaml b/dist/source/config/config-options/DUK_USE_GET_MONOTONIC_TIME.yaml +new file mode 100644 +index 0000000..d5ed7b3 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_GET_MONOTONIC_TIME.yaml +@@ -0,0 +1,22 @@ ++define: DUK_USE_GET_MONOTONIC_TIME ++introduced: 2.2.0 ++default: false ++tags: ++ - portability ++description: > ++ Optional macro for getting monotonic time in milliseconds from an arbitrary ++ starting point (device startup, program startup, script startup, etc). ++ Fractional time values are allowed (and even recommended). The time returned ++ must increase monotonically, and must not jump discontinuously even if system ++ date/time is reset. The semantics are similar to POSIX clock_gettime() ++ CLOCK_MONOTONIC. ++ ++ Monotonic time is used by Duktape for its internal needs, such as rate ++ limiting debugger transport peek callbacks. It is also used to provide ++ performance.now(). If this option is not provided, Duktape falls back to ++ using DUK_USE_DATE_GET_NOW() which is usually fine. ++ ++ If DUK_USE_DATE_GET_NOW() experiences time jumps or doesn't run in realtime ++ (which may be useful for some time virtualization cases) it's recommended ++ to provide this config option so that internals which need a reliable ++ realtime rate have a reliable time basis. +diff --git a/dist/source/config/config-options/DUK_USE_GET_MONOTONIC_TIME_CLOCK_GETTIME.yaml b/dist/source/config/config-options/DUK_USE_GET_MONOTONIC_TIME_CLOCK_GETTIME.yaml +new file mode 100644 +index 0000000..8f18392 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_GET_MONOTONIC_TIME_CLOCK_GETTIME.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_GET_MONOTONIC_TIME_CLOCK_GETTIME ++introduced: 2.2.0 ++default: false ++tags: ++ - portability ++description: > ++ Use clock_gettime(CLOCK_MONOTONIC, ...) for monotonic time on POSIX ++ platforms. +diff --git a/dist/source/config/config-options/DUK_USE_GET_MONOTONIC_TIME_WINDOWS_QPC.yaml b/dist/source/config/config-options/DUK_USE_GET_MONOTONIC_TIME_WINDOWS_QPC.yaml +new file mode 100644 +index 0000000..ca5bc19 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_GET_MONOTONIC_TIME_WINDOWS_QPC.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_GET_MONOTONIC_TIME_WINDOWS_QPC ++introduced: 2.2.0 ++default: false ++tags: ++ - portability ++description: > ++ Use QueryPerformanceCounter() for monotonic time on Windows. +diff --git a/dist/source/config/config-options/DUK_USE_GET_RANDOM_DOUBLE.yaml b/dist/source/config/config-options/DUK_USE_GET_RANDOM_DOUBLE.yaml +new file mode 100644 +index 0000000..2ae1c20 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_GET_RANDOM_DOUBLE.yaml +@@ -0,0 +1,16 @@ ++define: DUK_USE_GET_RANDOM_DOUBLE ++introduced: 2.0.0 ++default: false ++tags: ++ - portability ++ - performance ++description: > ++ Override the default internal random number generator which is used for ++ Math.random() and some other internal call sites (currently, for example, ++ Array.prototype.sort()). The default random number generator has a very ++ low footprint but is not suitable for serious statistics algorithms or ++ cryptography. Overriding the random number generator may thus be useful ++ in some environments. ++ ++ The macro gets a heap userdata argument and must provide an IEEE double ++ in the range [0,1[. +diff --git a/dist/source/config/config-options/DUK_USE_GLOBAL_BINDING.yaml b/dist/source/config/config-options/DUK_USE_GLOBAL_BINDING.yaml +new file mode 100644 +index 0000000..55b357f +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_GLOBAL_BINDING.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_GLOBAL_BINDING ++introduced: 2.1.0 ++default: true ++tags: ++ - ecmascript ++ - experimental ++description: > ++ Provide a 'globalThis' binding (https://github.com/tc39/proposal-global). +diff --git a/dist/source/config/config-options/DUK_USE_GLOBAL_BUILTIN.yaml b/dist/source/config/config-options/DUK_USE_GLOBAL_BUILTIN.yaml +new file mode 100644 +index 0000000..b8ba6e4 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_GLOBAL_BUILTIN.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_GLOBAL_BUILTIN ++introduced: 2.0.0 ++default: true ++tags: ++ - ecmascript ++description: > ++ Provide miscellaneous global built-ins like encodeURIComponent(), escape(), ++ Infinity, etc. This is a catch-all for globals not covered by other options ++ (like DUK_USE_ARRAY_BUILTIN). +diff --git a/dist/source/config/config-options/DUK_USE_HASHBYTES_UNALIGNED_U32_ACCESS.yaml b/dist/source/config/config-options/DUK_USE_HASHBYTES_UNALIGNED_U32_ACCESS.yaml +new file mode 100644 +index 0000000..fe5e336 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_HASHBYTES_UNALIGNED_U32_ACCESS.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_HASHBYTES_UNALIGNED_U32_ACCESS ++introduced: 1.0.0 ++removed: 1.4.0 ++default: false ++tags: ++ - portability ++description: > ++ Allow unaligned 32-bit unsigned integer access in hashbytes algorithm. +diff --git a/dist/source/config/config-options/DUK_USE_HEAPPTR16.yaml b/dist/source/config/config-options/DUK_USE_HEAPPTR16.yaml +new file mode 100644 +index 0000000..608143f +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_HEAPPTR16.yaml +@@ -0,0 +1,35 @@ ++define: DUK_USE_HEAPPTR16 ++introduced: 1.1.0 ++conflicts: ++ - DUK_USE_DEBUG ++related: ++ - DUK_USE_HEAPPTR_ENC16 ++ - DUK_USE_HEAPPTR_DEC16 ++default: false ++tags: ++ - lowmemory ++ - experimental ++description: > ++ Enable "compression" of Duktape heap pointers into an unsigned 16-bit value. ++ Use together with DUK_USE_HEAPPTR_ENC16 and DUK_USE_HEAPPTR_DEC16. ++ ++ Pointers compressed are those allocated from Duktape heap, using the user ++ provided allocation functions. Also NULL pointer must encode and decode ++ correctly. ++ ++ Currently it is required that NULL encodes to integer 0, and integer ++ 0 decodes to NULL. No other pointer can be encoded to 0. ++ ++ This option reduces memory usage by several kilobytes, but has several ++ downsides. It can only be applied when Duktape heap is limited in size, ++ for instance, with 4-byte aligned allocations a 256kB heap (minus one value ++ for NULL) can be supported. Pointer encoding and decoding may be relatively ++ complicated as they need to correctly handle NULL pointers and ++ non-continuous memory maps used by some targets. The macro may need to call ++ out to a helper function in practice, which is much slower than an inline ++ implementation. ++ ++ Current limitation: Duktape internal debug code enabled with e.g. ++ DUK_USE_DEBUG and DUK_USE_DEBUG_LEVEL=0 doesn't have enough plumbing to be ++ able to decode pointers. Debug printing cannot currently be enabled when ++ pointer compression is active. +diff --git a/dist/source/config/config-options/DUK_USE_HEAPPTR_DEC16.yaml b/dist/source/config/config-options/DUK_USE_HEAPPTR_DEC16.yaml +new file mode 100644 +index 0000000..1262dca +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_HEAPPTR_DEC16.yaml +@@ -0,0 +1,13 @@ ++define: DUK_USE_HEAPPTR_DEC16 ++introduced: 1.1.0 ++requires: ++ - DUK_USE_HEAPPTR16 ++default: false ++tags: ++ - lowmemory ++ - experimental ++description: > ++ Use together with DUK_USE_HEAPPTR16 for heap pointer compression. ++ DUK_USE_HEAPPTR_DEC16(udata,x) is a macro with a userdata and duk_uint16_t ++ argument, and a void ptr return value. The userdata argument is the heap ++ userdata value given at heap creation. +diff --git a/dist/source/config/config-options/DUK_USE_HEAPPTR_ENC16.yaml b/dist/source/config/config-options/DUK_USE_HEAPPTR_ENC16.yaml +new file mode 100644 +index 0000000..7339c49 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_HEAPPTR_ENC16.yaml +@@ -0,0 +1,13 @@ ++define: DUK_USE_HEAPPTR_ENC16 ++introduced: 1.1.0 ++requires: ++ - DUK_USE_HEAPPTR16 ++default: false ++tags: ++ - lowmemory ++ - experimental ++description: > ++ Use together with DUK_USE_HEAPPTR16 for heap pointer compression. ++ DUK_USE_HEAPPTR_ENC16(udata,p) is a macro with a userdata and void ptr ++ argument, and a duk_uint16_t return value. The userdata argument is the ++ heap userdata value given at heap creation. +diff --git a/dist/source/config/config-options/DUK_USE_HEX_FASTPATH.yaml b/dist/source/config/config-options/DUK_USE_HEX_FASTPATH.yaml +new file mode 100644 +index 0000000..c00bbe4 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_HEX_FASTPATH.yaml +@@ -0,0 +1,10 @@ ++define: DUK_USE_HEX_FASTPATH ++introduced: 1.4.0 ++default: true ++tags: ++ - performance ++ - fastpath ++ - lowmemory ++description: > ++ Enable fast path for hex encode/decode. The fast path uses a lookup ++ table at a small cost in footprint. +diff --git a/dist/source/config/config-options/DUK_USE_HEX_SUPPORT.yaml b/dist/source/config/config-options/DUK_USE_HEX_SUPPORT.yaml +new file mode 100644 +index 0000000..9cc26b5 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_HEX_SUPPORT.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_HEX_SUPPORT ++introduced: 2.3.0 ++default: true ++tags: ++ - codec ++description: > ++ Enable hex encoding/decoding support. +diff --git a/dist/source/config/config-options/DUK_USE_HOBJECT_ARRAY_ABANDON_LIMIT.yaml b/dist/source/config/config-options/DUK_USE_HOBJECT_ARRAY_ABANDON_LIMIT.yaml +new file mode 100644 +index 0000000..2e72af2 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_HOBJECT_ARRAY_ABANDON_LIMIT.yaml +@@ -0,0 +1,15 @@ ++define: DUK_USE_HOBJECT_ARRAY_ABANDON_LIMIT ++introduced: 2.1.0 ++default: 2 ++tags: ++ - performance ++ - lowmemory ++description: > ++ Abandon array part if its density is below L and array is larger than ++ DUK_USE_HOBJECT_ARRAY_ABANDON_MINSIZE. The limit L is expressed as ++ a .3 fixed point point, e.g. 2 means 2/8 = 25%. ++ ++ The default limit is quite low: one array entry with packed duk_tval is 8 ++ bytes whereas one normal entry is 4+1+8 = 13 bytes without a hash entry, ++ and 17-21 bytes with a hash entry (load factor 0.5-1.0). So the array part ++ shouldn't be abandoned very easily from a footprint point of view. +diff --git a/dist/source/config/config-options/DUK_USE_HOBJECT_ARRAY_ABANDON_MINSIZE.yaml b/dist/source/config/config-options/DUK_USE_HOBJECT_ARRAY_ABANDON_MINSIZE.yaml +new file mode 100644 +index 0000000..4e83aa2 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_HOBJECT_ARRAY_ABANDON_MINSIZE.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_HOBJECT_ARRAY_ABANDON_MINSIZE ++introduced: 2.5.0 ++default: 257 ++tags: ++ - performance ++ - lowmemory ++description: > ++ Minimum array size required for array to be abandoned. For example, a value ++ of 257 means arrays up to 256 long are never abandoned. The default value ++ ensures 8-bit lookups are not abandoned even if sparse or initialized in ++ random order. +diff --git a/dist/source/config/config-options/DUK_USE_HOBJECT_ARRAY_FAST_RESIZE_LIMIT.yaml b/dist/source/config/config-options/DUK_USE_HOBJECT_ARRAY_FAST_RESIZE_LIMIT.yaml +new file mode 100644 +index 0000000..6429d4b +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_HOBJECT_ARRAY_FAST_RESIZE_LIMIT.yaml +@@ -0,0 +1,13 @@ ++define: DUK_USE_HOBJECT_ARRAY_FAST_RESIZE_LIMIT ++introduced: 2.1.0 ++default: 9 ++tags: ++ - performance ++ - lowmemory ++description: > ++ Skip abandon check in object array part resize if new_size < L * old_size. ++ The limit L is expressed as a .3 fixed point value, e.g. 9 means 9/8 = ++ 112.5% of current size. ++ ++ This is rather technical and you should only change the parameter if you've ++ looked at the internals. +diff --git a/dist/source/config/config-options/DUK_USE_HOBJECT_ARRAY_MINGROW_ADD.yaml b/dist/source/config/config-options/DUK_USE_HOBJECT_ARRAY_MINGROW_ADD.yaml +new file mode 100644 +index 0000000..82e79f7 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_HOBJECT_ARRAY_MINGROW_ADD.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_HOBJECT_ARRAY_MINGROW_ADD ++introduced: 2.1.0 ++default: 16 ++tags: ++ - performance ++description: > ++ Technical internal parameter, see sources for details. Only adjust if ++ you've looked at the internals. +diff --git a/dist/source/config/config-options/DUK_USE_HOBJECT_ARRAY_MINGROW_DIVISOR.yaml b/dist/source/config/config-options/DUK_USE_HOBJECT_ARRAY_MINGROW_DIVISOR.yaml +new file mode 100644 +index 0000000..98fdb29 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_HOBJECT_ARRAY_MINGROW_DIVISOR.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_HOBJECT_ARRAY_MINGROW_DIVISOR ++introduced: 2.1.0 ++default: 8 ++tags: ++ - performance ++description: > ++ Technical internal parameter, see sources for details. Only adjust if ++ you've looked at the internals. +diff --git a/dist/source/config/config-options/DUK_USE_HOBJECT_ENTRY_MINGROW_ADD.yaml b/dist/source/config/config-options/DUK_USE_HOBJECT_ENTRY_MINGROW_ADD.yaml +new file mode 100644 +index 0000000..c31b111 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_HOBJECT_ENTRY_MINGROW_ADD.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_HOBJECT_ENTRY_MINGROW_DIVISOR ++introduced: 2.1.0 ++default: 8 ++tags: ++ - performance ++description: > ++ Technical internal parameter, see sources for details. Only adjust if ++ you've looked at the internals. +diff --git a/dist/source/config/config-options/DUK_USE_HOBJECT_ENTRY_MINGROW_DIVISOR.yaml b/dist/source/config/config-options/DUK_USE_HOBJECT_ENTRY_MINGROW_DIVISOR.yaml +new file mode 100644 +index 0000000..c38fd8f +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_HOBJECT_ENTRY_MINGROW_DIVISOR.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_HOBJECT_ENTRY_MINGROW_ADD ++introduced: 2.1.0 ++default: 16 ++tags: ++ - performance ++description: > ++ Technical internal parameter, see sources for details. Only adjust if ++ you've looked at the internals. +diff --git a/dist/source/config/config-options/DUK_USE_HOBJECT_HASH_PART.yaml b/dist/source/config/config-options/DUK_USE_HOBJECT_HASH_PART.yaml +new file mode 100644 +index 0000000..4e8c82e +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_HOBJECT_HASH_PART.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_HOBJECT_HASH_PART ++introduced: 1.1.0 ++default: true ++tags: ++ - lowmemory ++ - experimental ++description: > ++ Use a hash table for objects that have enough properties. This should be ++ enabled unless the target is very low on memory. ++ ++ If DUK_USE_OBJSIZES16 is defined, this option must not be defined. +diff --git a/dist/source/config/config-options/DUK_USE_HOBJECT_HASH_PROP_LIMIT.yaml b/dist/source/config/config-options/DUK_USE_HOBJECT_HASH_PROP_LIMIT.yaml +new file mode 100644 +index 0000000..008b836 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_HOBJECT_HASH_PROP_LIMIT.yaml +@@ -0,0 +1,20 @@ ++define: DUK_USE_HOBJECT_HASH_PROP_LIMIT ++introduced: 2.1.0 ++default: 8 ++tags: ++ - performance ++ - lowmemory ++description: > ++ Minimum number of properties needed for a hash part to be included in the ++ object property table. This limit is checked whenever an object is resized. ++ ++ A hash part improves property lookup performance even for small objects, ++ starting from roughly 4 properties. However, this ignores the cost of ++ setting up and managing the hash part, which is offset only if property ++ lookups made through the hash part can offset the setup cost. A hash part ++ is worth it for heavily accessed small objects or large objects (even those ++ accessed quite infrequently). The limit doesn't take into account property ++ access frequency, so it is necessarily a compromise. ++ ++ A lower value improves performance (a value as low a 4-8 can be useful) ++ while a higher value conserves memory. +diff --git a/dist/source/config/config-options/DUK_USE_HOBJECT_LAYOUT_1.yaml b/dist/source/config/config-options/DUK_USE_HOBJECT_LAYOUT_1.yaml +new file mode 100644 +index 0000000..23a6883 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_HOBJECT_LAYOUT_1.yaml +@@ -0,0 +1,10 @@ ++define: DUK_USE_HOBJECT_LAYOUT_1 ++introduced: 1.0.0 ++default: false ++tags: ++ - portability ++description: > ++ Use layout variant 1 for object properties. Layout 1 can be used when the ++ target has no alignment restrictions. It is preferable to other layouts ++ because it produces smaller code and provides direct access to property ++ keys. +diff --git a/dist/source/config/config-options/DUK_USE_HOBJECT_LAYOUT_2.yaml b/dist/source/config/config-options/DUK_USE_HOBJECT_LAYOUT_2.yaml +new file mode 100644 +index 0000000..c467e3b +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_HOBJECT_LAYOUT_2.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_HOBJECT_LAYOUT_2 ++introduced: 1.0.0 ++default: true ++tags: ++ - portability ++description: > ++ Use layout variant 2 for object properties. Layout 2 can be used on any ++ target (including targets with alignment restrictions). +diff --git a/dist/source/config/config-options/DUK_USE_HOBJECT_LAYOUT_3.yaml b/dist/source/config/config-options/DUK_USE_HOBJECT_LAYOUT_3.yaml +new file mode 100644 +index 0000000..23ea315 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_HOBJECT_LAYOUT_3.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_HOBJECT_LAYOUT_3 ++introduced: 1.0.0 ++default: false ++tags: ++ - portability ++description: > ++ Use layout variant 3 for object properties. Layout 3 can be used on any ++ target (including targets with alignment restrictions). It's a bit more ++ packed than layout variant 2 but has a bit slower lookups. +diff --git a/dist/source/config/config-options/DUK_USE_HSTRING_ARRIDX.yaml b/dist/source/config/config-options/DUK_USE_HSTRING_ARRIDX.yaml +new file mode 100644 +index 0000000..9b82aab +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_HSTRING_ARRIDX.yaml +@@ -0,0 +1,12 @@ ++define: DUK_USE_HSTRING_ARRIDX ++introduced: 2.0.0 ++default: true ++tags: ++ - lowmemory ++description: > ++ When enabled, duk_hstring stores a precomputed array index (or "not an array ++ index") value related to the string. This reduces code footprint and ++ improves performance a littl ebit. ++ ++ When disabled, duk_hstring has a flag indicating whether it is an array ++ index or not, but the actual value is computed on-the-fly. +diff --git a/dist/source/config/config-options/DUK_USE_HSTRING_CLEN.yaml b/dist/source/config/config-options/DUK_USE_HSTRING_CLEN.yaml +new file mode 100644 +index 0000000..6f4b3c8 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_HSTRING_CLEN.yaml +@@ -0,0 +1,13 @@ ++define: DUK_USE_HSTRING_CLEN ++introduced: 1.5.0 ++default: true ++tags: ++ - lowmemory ++ - experimental ++description: > ++ When DUK_USE_STRLEN16 enabled, indicates whether the character length ++ (clen16) field should be actually present (default) or computed on-the-fly. ++ ++ When clen is computed on-the-fly the duk_hstring structure will be 4 bytes ++ smaller (from 10 bytes + 2 bytes padding to 8 bytes) which may be useful for ++ very low memory targets. +diff --git a/dist/source/config/config-options/DUK_USE_HSTRING_EXTDATA.yaml b/dist/source/config/config-options/DUK_USE_HSTRING_EXTDATA.yaml +new file mode 100644 +index 0000000..6598eb8 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_HSTRING_EXTDATA.yaml +@@ -0,0 +1,12 @@ ++define: DUK_USE_HSTRING_EXTDATA ++introduced: 1.1.0 ++default: false ++tags: ++ - memory ++description: > ++ Enable support for external strings. An external string requires a Duktape ++ heap allocation to store a minimal string header, with the actual string ++ data being held behind a pointer (similarly to how dynamic buffers work). ++ ++ This option is needed to use DUK_USE_EXTSTR_INTERN_CHECK and/or ++ DUK_USE_EXTSTR_FREE. +diff --git a/dist/source/config/config-options/DUK_USE_HSTRING_LAZY_CLEN.yaml b/dist/source/config/config-options/DUK_USE_HSTRING_LAZY_CLEN.yaml +new file mode 100644 +index 0000000..7a49aed +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_HSTRING_LAZY_CLEN.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_HSTRING_LAZY_CLEN ++introduced: 2.2.0 ++default: true ++tags: ++ - performance ++description: > ++ When enabled, duk_hstring charlen is computed only when accessed; because ++ the charlen of most strings is not accessed during their lifetime, this ++ reduces unnecessary charlen calculations. When disabled, charlen is computed ++ during interning which has smaller code footprint at slightly slower charlen ++ handling. +diff --git a/dist/source/config/config-options/DUK_USE_HTML_COMMENTS.yaml b/dist/source/config/config-options/DUK_USE_HTML_COMMENTS.yaml +new file mode 100644 +index 0000000..cd39e80 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_HTML_COMMENTS.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_HTML_COMMENTS ++introduced: 2.1.0 ++default: true ++tags: ++ - ecmascript2015 ++description: > ++ Enable ES2015 Annex B.1.3 HTML comment syntax. +diff --git a/dist/source/config/config-options/DUK_USE_IDCHAR_FASTPATH.yaml b/dist/source/config/config-options/DUK_USE_IDCHAR_FASTPATH.yaml +new file mode 100644 +index 0000000..4748674 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_IDCHAR_FASTPATH.yaml +@@ -0,0 +1,10 @@ ++define: DUK_USE_IDCHAR_FASTPATH ++introduced: 1.5.0 ++default: true ++tags: ++ - performance ++ - fastpath ++ - lowmemory ++description: > ++ Enable fast path for identifier start/part tables, which affect lexing and ++ JSON performance slightly at a small cost in footprint. +diff --git a/dist/source/config/config-options/DUK_USE_INJECT_HEAP_ALLOC_ERROR.yaml b/dist/source/config/config-options/DUK_USE_INJECT_HEAP_ALLOC_ERROR.yaml +new file mode 100644 +index 0000000..a1db4a9 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_INJECT_HEAP_ALLOC_ERROR.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_INJECT_HEAP_ALLOC_ERROR ++introduced: 2.1.0 ++default: false ++tags: ++ - development ++description: > ++ Force heap allocation to fail, value indicates the desired error position. +diff --git a/dist/source/config/config-options/DUK_USE_INTEGER_BE.yaml b/dist/source/config/config-options/DUK_USE_INTEGER_BE.yaml +new file mode 100644 +index 0000000..662db0b +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_INTEGER_BE.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_INTEGER_BE ++introduced: 1.0.0 ++removed: 1.4.0 ++default: false ++conflicts: ++ - DUK_USE_INTEGER_LE ++ - DUK_USE_INTEGER_ME ++tags: ++ - portability ++description: > ++ Integer memory representation is big endian on the target platform. +diff --git a/dist/source/config/config-options/DUK_USE_INTEGER_LE.yaml b/dist/source/config/config-options/DUK_USE_INTEGER_LE.yaml +new file mode 100644 +index 0000000..d3efa21 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_INTEGER_LE.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_INTEGER_LE ++introduced: 1.0.0 ++removed: 1.4.0 ++default: true ++conflicts: ++ - DUK_USE_INTEGER_BE ++ - DUK_USE_INTEGER_ME ++tags: ++ - portability ++description: > ++ Integer memory representation is little endian on the target platform. +diff --git a/dist/source/config/config-options/DUK_USE_INTEGER_ME.yaml b/dist/source/config/config-options/DUK_USE_INTEGER_ME.yaml +new file mode 100644 +index 0000000..a1fdc91 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_INTEGER_ME.yaml +@@ -0,0 +1,14 @@ ++define: DUK_USE_INTEGER_ME ++introduced: 1.0.0 ++removed: 1.4.0 ++default: false ++conflicts: ++ - DUK_USE_INTEGER_LE ++ - DUK_USE_INTEGER_BE ++tags: ++ - portability ++description: > ++ Integer memory representation is mixed endian on the target platform. ++ ++ This option is unused (and unsupported) because no target platform currently ++ needs this. +diff --git a/dist/source/config/config-options/DUK_USE_INTERRUPT_COUNTER.yaml b/dist/source/config/config-options/DUK_USE_INTERRUPT_COUNTER.yaml +new file mode 100644 +index 0000000..614c7fa +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_INTERRUPT_COUNTER.yaml +@@ -0,0 +1,13 @@ ++define: DUK_USE_INTERRUPT_COUNTER ++introduced: 1.1.0 ++related: ++ - DUK_USE_DEBUGGER_SUPPORT ++default: false ++tags: ++ - execution ++ - debugger ++description: > ++ Enable the internal bytecode executor periodic interrupt counter. ++ The mechanism is used to implement e.g. execution step limit, custom ++ profiling, and debugger interaction. Enabling the interrupt counter ++ has a small impact on execution performance. +diff --git a/dist/source/config/config-options/DUK_USE_INTERRUPT_DEBUG_FIXUP.yaml b/dist/source/config/config-options/DUK_USE_INTERRUPT_DEBUG_FIXUP.yaml +new file mode 100644 +index 0000000..48d7df9 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_INTERRUPT_DEBUG_FIXUP.yaml +@@ -0,0 +1,10 @@ ++define: DUK_USE_INTERRUPT_DEBUG_FIXUP ++introduced: 1.4.0 ++default: false ++tags: ++ - development ++description: > ++ For Duktape development only: enable "interrupt fixup" in call handling ++ so that heap->inst_count_exec and heap->inst_count_interrupt can be ++ manually checked to match. Only useful when debugging and/or asserts ++ are enabled. +diff --git a/dist/source/config/config-options/DUK_USE_JC.yaml b/dist/source/config/config-options/DUK_USE_JC.yaml +new file mode 100644 +index 0000000..0bfc818 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_JC.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_JC ++introduced: 1.0.0 ++default: true ++tags: ++ - ecmascript ++description: > ++ Enable support for the JC custom JSON format. +diff --git a/dist/source/config/config-options/DUK_USE_JSON_BUILTIN.yaml b/dist/source/config/config-options/DUK_USE_JSON_BUILTIN.yaml +new file mode 100644 +index 0000000..e858389 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_JSON_BUILTIN.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_JSON_BUILTIN ++introduced: 2.0.0 ++default: true ++tags: ++ - ecmascript ++description: > ++ Provide a JSON built-in. +diff --git a/dist/source/config/config-options/DUK_USE_JSON_DECNUMBER_FASTPATH.yaml b/dist/source/config/config-options/DUK_USE_JSON_DECNUMBER_FASTPATH.yaml +new file mode 100644 +index 0000000..fa47c15 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_JSON_DECNUMBER_FASTPATH.yaml +@@ -0,0 +1,10 @@ ++define: DUK_USE_JSON_DECNUMBER_FASTPATH ++introduced: 1.3.0 ++default: true ++tags: ++ - performance ++ - fastpath ++ - lowmemory ++description: > ++ Enable fast path for decoding numbers in JSON.parse(). The fast path uses ++ a lookup table at a small cost in footprint. +diff --git a/dist/source/config/config-options/DUK_USE_JSON_DECSTRING_FASTPATH.yaml b/dist/source/config/config-options/DUK_USE_JSON_DECSTRING_FASTPATH.yaml +new file mode 100644 +index 0000000..ed0e31b +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_JSON_DECSTRING_FASTPATH.yaml +@@ -0,0 +1,10 @@ ++define: DUK_USE_JSON_DECSTRING_FASTPATH ++introduced: 1.3.0 ++default: true ++tags: ++ - performance ++ - fastpath ++ - lowmemory ++description: > ++ Enable fast path for string decoding in JSON.parse(). The fast path uses ++ a lookup table at a small cost in footprint. +diff --git a/dist/source/config/config-options/DUK_USE_JSON_DEC_RECLIMIT.yaml b/dist/source/config/config-options/DUK_USE_JSON_DEC_RECLIMIT.yaml +new file mode 100644 +index 0000000..cd07d3c +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_JSON_DEC_RECLIMIT.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_JSON_DEC_RECLIMIT ++introduced: 1.3.0 ++default: 1000 ++tags: ++ - portability ++ - cstackdepth ++description: > ++ Maximum native stack recursion for JSON decoding. +diff --git a/dist/source/config/config-options/DUK_USE_JSON_EATWHITE_FASTPATH.yaml b/dist/source/config/config-options/DUK_USE_JSON_EATWHITE_FASTPATH.yaml +new file mode 100644 +index 0000000..49e1b44 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_JSON_EATWHITE_FASTPATH.yaml +@@ -0,0 +1,10 @@ ++define: DUK_USE_JSON_EATWHITE_FASTPATH ++introduced: 1.3.0 ++default: true ++tags: ++ - performance ++ - fastpath ++ - lowmemory ++description: > ++ Enable fast path for eating whitespace in JSON.parse(). The fast path uses ++ a lookup table at a small cost in footprint. +diff --git a/dist/source/config/config-options/DUK_USE_JSON_ENC_RECLIMIT.yaml b/dist/source/config/config-options/DUK_USE_JSON_ENC_RECLIMIT.yaml +new file mode 100644 +index 0000000..1df0e07 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_JSON_ENC_RECLIMIT.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_JSON_ENC_RECLIMIT ++introduced: 1.3.0 ++default: 1000 ++tags: ++ - portability ++ - cstackdepth ++description: > ++ Maximum native stack recursion for JSON encoding. ++ ++ Must be higher than the internal DUK_JSON_ENC_LOOPARRAY define when ++ DUK_USE_JSON_STRINGIFY_FASTPATH is enabled. +diff --git a/dist/source/config/config-options/DUK_USE_JSON_QUOTESTRING_FASTPATH.yaml b/dist/source/config/config-options/DUK_USE_JSON_QUOTESTRING_FASTPATH.yaml +new file mode 100644 +index 0000000..e2ee14e +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_JSON_QUOTESTRING_FASTPATH.yaml +@@ -0,0 +1,10 @@ ++define: DUK_USE_JSON_QUOTESTRING_FASTPATH ++introduced: 1.3.0 ++default: true ++tags: ++ - performance ++ - fastpath ++ - lowmemory ++description: > ++ Enable fast path for string quoting in JSON.stringify(). The fast path uses ++ a lookup table at a small cost in footprint. +diff --git a/dist/source/config/config-options/DUK_USE_JSON_STRINGIFY_FASTPATH.yaml b/dist/source/config/config-options/DUK_USE_JSON_STRINGIFY_FASTPATH.yaml +new file mode 100644 +index 0000000..4d244f6 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_JSON_STRINGIFY_FASTPATH.yaml +@@ -0,0 +1,15 @@ ++define: DUK_USE_JSON_STRINGIFY_FASTPATH ++introduced: 1.3.0 ++default: false ++tags: ++ - performance ++ - fastpath ++ - lowmemory ++description: > ++ Enable fast path for JSON.stringify() serialization. The fast path is used ++ when there is no "replacer" argument. Indent argument and JX/JC format is ++ supported since Duktape 1.4.0. The fast path increases code footprint by ++ roughly 1.5 kB but is up to 4-5x faster than the slow path. ++ ++ Current limitation: assumes "long long" type exists (and covers duk_int64_t ++ range) and that sprintf() format string "%lld" works for "long long". +diff --git a/dist/source/config/config-options/DUK_USE_JSON_SUPPORT.yaml b/dist/source/config/config-options/DUK_USE_JSON_SUPPORT.yaml +new file mode 100644 +index 0000000..f2eca18 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_JSON_SUPPORT.yaml +@@ -0,0 +1,10 @@ ++define: DUK_USE_JSON_SUPPORT ++introduced: 2.0.0 ++default: true ++tags: ++ - ecmascript ++description: > ++ Enable JSON functionality, affects both ECMAScript and C APIs. ++ Note that disabling DUK_USE_JSON_BUILTIN still leaves the C API intact ++ and pulls in the JSON encoding/decoding functionality; disable this ++ option to remove that too. +diff --git a/dist/source/config/config-options/DUK_USE_JX.yaml b/dist/source/config/config-options/DUK_USE_JX.yaml +new file mode 100644 +index 0000000..f037c96 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_JX.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_JX ++introduced: 1.0.0 ++default: true ++tags: ++ - ecmascript ++description: > ++ Enable support for the JX custom JSON format. +diff --git a/dist/source/config/config-options/DUK_USE_LEXER_SLIDING_WINDOW.yaml b/dist/source/config/config-options/DUK_USE_LEXER_SLIDING_WINDOW.yaml +new file mode 100644 +index 0000000..c213f9f +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_LEXER_SLIDING_WINDOW.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_LEXER_SLIDING_WINDOW ++introduced: 1.3.0 ++default: true ++tags: ++ - lowmemory ++description: > ++ Use a sliding window approach for managing the lexer codepoint lookup window ++ (recommended). If disabled, the lexer uses a slower algorithm which has a ++ slightly smaller code and RAM footprint. +diff --git a/dist/source/config/config-options/DUK_USE_LIGHTFUNC_BUILTINS.yaml b/dist/source/config/config-options/DUK_USE_LIGHTFUNC_BUILTINS.yaml +new file mode 100644 +index 0000000..649bdd0 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_LIGHTFUNC_BUILTINS.yaml +@@ -0,0 +1,10 @@ ++define: DUK_USE_LIGHTFUNC_BUILTINS ++introduced: 1.1.0 ++default: false ++tags: ++ - lowmemory ++ - experimental ++description: > ++ Force built-in functions to be lightweight functions. This reduces ++ memory footprint by around 14 kB at the cost of some non-compliant ++ behavior. +diff --git a/dist/source/config/config-options/DUK_USE_LITCACHE_SIZE.yaml b/dist/source/config/config-options/DUK_USE_LITCACHE_SIZE.yaml +new file mode 100644 +index 0000000..b174130 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_LITCACHE_SIZE.yaml +@@ -0,0 +1,27 @@ ++define: DUK_USE_LITCACHE_SIZE ++introduced: 2.3.0 ++default: 256 ++tags: ++ - performance ++ - lowmemory ++description: > ++ Size of the literal cache, which maps C literal memory addresses into ++ pinned duk_hstring heap object addresses. The cache is used when ++ application code calls one of the duk_xxx_literal() API call variants, ++ such as duk_push_literal() or duk_get_prop_literal(), to speed up the ++ string intern check for the literal. In successful cases this caching ++ makes using duk_xxx_literal() almost as fast as using borrowed heap ++ pointers with duk_xxx_heapptr(). ++ ++ When this option is defined, duk_hstrings related to literals encountered ++ in duk_xxx_literal() API calls are automatically pinned between ++ mark-and-sweep rounds. This accomplishes two things. First, it avoids the ++ need for cache invalidation for the literal cache in normal operation between ++ mark-and-sweep rounds. Second, it reduces string table traffic (i.e. freeing ++ and reallocating) for literals which are likely to occur again and again. ++ However, the downside is that some strings that may occur only temporarily ++ will remain pinned until the next mark-and-sweep round. If this matter, you ++ can avoid it by simply using e.g. duk_xxx_string() when dealing with such ++ strings. ++ ++ The literal cache size must be a power of two (2^N). +diff --git a/dist/source/config/config-options/DUK_USE_MARKANDSWEEP_FINALIZER_TORTURE.yaml b/dist/source/config/config-options/DUK_USE_MARKANDSWEEP_FINALIZER_TORTURE.yaml +new file mode 100644 +index 0000000..5589bd1 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_MARKANDSWEEP_FINALIZER_TORTURE.yaml +@@ -0,0 +1,17 @@ ++define: DUK_USE_MARKANDSWEEP_FINALIZER_TORTURE ++introduced: 1.3.0 ++removed: 2.1.0 ++related: ++ - DUK_USE_GC_TORTURE ++ - DUK_USE_REFZERO_FINALIZER_TORTURE ++default: false ++tags: ++ - gc ++ - memory ++ - development ++ - torture ++description: > ++ Development time option: simulate a fake finalizer call during every ++ mark-and-sweep round. This is useful to detect bugs caused by finalizer ++ side effects. Most useful when combined with DUK_USE_GC_TORTURE so that ++ potential finalizer side effects are realized on every allocation. +diff --git a/dist/source/config/config-options/DUK_USE_MARK_AND_SWEEP.yaml b/dist/source/config/config-options/DUK_USE_MARK_AND_SWEEP.yaml +new file mode 100644 +index 0000000..534e8d0 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_MARK_AND_SWEEP.yaml +@@ -0,0 +1,19 @@ ++define: DUK_USE_MARK_AND_SWEEP ++introduced: 1.0.0 ++removed: 2.0.0 ++default: true ++tags: ++ - gc ++ - memory ++description: > ++ Enable mark-and-sweep garbage collection (recommended). ++ ++ When disabled, only reference counting is used for garbage collection. ++ This reduces code footprint and eliminates garbage collection pauses, but ++ objects participating in unreachable reference cycles won't be collected ++ until the Duktape heap is destroyed. In particular, function instances ++ won't be collected because they're always in a reference cycle with their ++ default prototype object. Unreachable objects are collected if you break ++ reference cycles manually (and are always freed when a heap is destroyed). ++ ++ NOTE: Removed in Duktape 2.0.0 because mark-and-sweep is no longer optional. +diff --git a/dist/source/config/config-options/DUK_USE_MARK_AND_SWEEP_RECLIMIT.yaml b/dist/source/config/config-options/DUK_USE_MARK_AND_SWEEP_RECLIMIT.yaml +new file mode 100644 +index 0000000..c34d924 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_MARK_AND_SWEEP_RECLIMIT.yaml +@@ -0,0 +1,10 @@ ++define: DUK_USE_MARK_AND_SWEEP_RECLIMIT ++introduced: 1.3.0 ++default: 256 ++tags: ++ - portability ++ - cstackdepth ++description: > ++ Mark-and-sweep C recursion depth for marking phase; if reached, ++ mark object as a TEMPROOT and use multi-pass marking (slower but ++ same result). +diff --git a/dist/source/config/config-options/DUK_USE_MATH_BUILTIN.yaml b/dist/source/config/config-options/DUK_USE_MATH_BUILTIN.yaml +new file mode 100644 +index 0000000..35dc0be +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_MATH_BUILTIN.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_MATH_BUILTIN ++introduced: 1.0.0 ++default: true ++tags: ++ - ecmascript ++description: > ++ Provide a Math built-in. +diff --git a/dist/source/config/config-options/DUK_USE_MATH_FMAX.yaml b/dist/source/config/config-options/DUK_USE_MATH_FMAX.yaml +new file mode 100644 +index 0000000..bbe8e9a +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_MATH_FMAX.yaml +@@ -0,0 +1,14 @@ ++define: DUK_USE_MATH_FMAX ++introduced: 1.0.0 ++removed: 2.0.0 ++default: true ++tags: ++ - portability ++description: > ++ Assume platform function fmax() is available and works correctly. ++ Some platforms don't have fmax() (it is defined in C99) and on some ++ platforms (e.g. some uclibc environments) it may not be provided even ++ though the compilation environment is nominally C99. ++ ++ Removed in Duktape 2.0.0: if the platform doesn't have fmax(), simply ++ define a replacement for DUK_FMAX(). +diff --git a/dist/source/config/config-options/DUK_USE_MATH_FMIN.yaml b/dist/source/config/config-options/DUK_USE_MATH_FMIN.yaml +new file mode 100644 +index 0000000..dae2d08 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_MATH_FMIN.yaml +@@ -0,0 +1,14 @@ ++define: DUK_USE_MATH_FMIN ++introduced: 1.0.0 ++removed: 2.0.0 ++default: true ++tags: ++ - portability ++description: > ++ Assume platform function fmin() is available and works correctly. ++ Some platforms don't have fmin() (it is defined in C99) and on some ++ platforms (e.g. some uclibc environments) it may not be provided even ++ though the compilation environment is nominally C99. ++ ++ Removed in Duktape 2.0.0: if the platform doesn't have fmin(), simply ++ define a replacement for DUK_FMIN(). +diff --git a/dist/source/config/config-options/DUK_USE_MATH_ROUND.yaml b/dist/source/config/config-options/DUK_USE_MATH_ROUND.yaml +new file mode 100644 +index 0000000..d5af637 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_MATH_ROUND.yaml +@@ -0,0 +1,15 @@ ++define: DUK_USE_MATH_ROUND ++introduced: 1.0.0 ++removed: 2.0.0 ++default: true ++tags: ++ - portability ++description: > ++ Assume platform function round() is available and works correctly. ++ Some platforms don't have round() (it is defined in C99) and on some ++ platforms (e.g. some uclibc environments) it may not be provided even ++ though the compilation environment is nominally C99. ++ ++ Removed in Duktape 2.0.0: if the platform doesn't have round(), simply ++ define a replacement for DUK_ROUND(). Currently DUK_ROUND() isn't used ++ at all however. +diff --git a/dist/source/config/config-options/DUK_USE_MS_STRINGTABLE_RESIZE.yaml b/dist/source/config/config-options/DUK_USE_MS_STRINGTABLE_RESIZE.yaml +new file mode 100644 +index 0000000..bb1b9a4 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_MS_STRINGTABLE_RESIZE.yaml +@@ -0,0 +1,13 @@ ++define: DUK_USE_MS_STRINGTABLE_RESIZE ++introduced: 1.0.0 ++removed: 2.1.0 ++default: true ++tags: ++ - gc ++ - memory ++description: > ++ Enable forced string intern table resize during mark-and-sweep garbage ++ collection. This is the recommended behavior. ++ ++ It may be useful to disable this option when reference counting is disabled, ++ as mark-and-sweep collections will be more frequent and thus more expensive. +diff --git a/dist/source/config/config-options/DUK_USE_NATIVE_CALL_RECLIMIT.yaml b/dist/source/config/config-options/DUK_USE_NATIVE_CALL_RECLIMIT.yaml +new file mode 100644 +index 0000000..a27e032 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_NATIVE_CALL_RECLIMIT.yaml +@@ -0,0 +1,12 @@ ++define: DUK_USE_NATIVE_CALL_RECLIMIT ++introduced: 1.3.0 ++default: 1000 ++tags: ++ - portability ++ - cstackdepth ++description: > ++ Maximum duk_handle_call() / duk_handle_safe_call() C recursion limit. ++ Note that this does not limit bytecode executor internal call depth at ++ all (e.g. for ECMAScript-to-ECMAScript calls, thread yields/resumes, etc). ++ There is a separate callstack depth limit for threads which is independent ++ of this limit. +diff --git a/dist/source/config/config-options/DUK_USE_NATIVE_STACK_CHECK.yaml b/dist/source/config/config-options/DUK_USE_NATIVE_STACK_CHECK.yaml +new file mode 100644 +index 0000000..24774c8 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_NATIVE_STACK_CHECK.yaml +@@ -0,0 +1,22 @@ ++define: DUK_USE_NATIVE_STACK_CHECK ++introduced: 2.4.0 ++default: false ++tags: ++ - portability ++ - execution ++description: > ++ Provide a macro hook to check for available native stack space for the ++ currently executing native thread. The macro must evaluate to zero if ++ there is enough stack space available and non-zero otherwise; a RangeError ++ will then be thrown. ++ ++ The definition of "enough space" depends on the target platform and the ++ compiler because the size of native stack frames cannot be easily known ++ in advance. As a relatively safe estimate, one can check for 8kB of ++ available stack. ++ ++ Duktape doesn't call this macro for every internal native call. The macro ++ is called in code paths that are involved in potentially unlimited ++ recursion (such as making Ecmascript/native function calls, invoking ++ getters and Proxy traps, and resolving Proxy chains) and code paths ++ requiring a lot of stack space temporarily. +diff --git a/dist/source/config/config-options/DUK_USE_NONSTD_ARRAY_CONCAT_TRAILER.yaml b/dist/source/config/config-options/DUK_USE_NONSTD_ARRAY_CONCAT_TRAILER.yaml +new file mode 100644 +index 0000000..9719a45 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_NONSTD_ARRAY_CONCAT_TRAILER.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_NONSTD_ARRAY_CONCAT_TRAILER ++introduced: 1.0.0 ++removed: 2.3.0 ++default: true ++tags: ++ - ecmascript ++ - compliance ++description: > ++ In ES5.1 trailing gaps of an argument array don't count towards the result ++ length. This is in essence a specification "bug" which was fixed in ES2015. ++ This option was removed in 2.3.0, and the remaining behavior matches ES2015. +diff --git a/dist/source/config/config-options/DUK_USE_NONSTD_ARRAY_MAP_TRAILER.yaml b/dist/source/config/config-options/DUK_USE_NONSTD_ARRAY_MAP_TRAILER.yaml +new file mode 100644 +index 0000000..9e46964 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_NONSTD_ARRAY_MAP_TRAILER.yaml +@@ -0,0 +1,13 @@ ++define: DUK_USE_NONSTD_ARRAY_MAP_TRAILER ++introduced: 1.0.0 ++removed: 2.3.0 ++default: true ++tags: ++ - ecmascript ++ - compliance ++description: > ++ This option was removed in 2.3.0 as it was unnecessary and in essence fixing ++ a Duktape bug. ES5.0/ES5.1 already behave like ES2015 in that trailing gaps ++ in the input don't affect the result length. The result array is created ++ with a length based on the input array in Step 6 of ES5.1 Section 15.4.4.19 ++ and subsequent index writes don't affect the length. +diff --git a/dist/source/config/config-options/DUK_USE_NONSTD_ARRAY_SPLICE_DELCOUNT.yaml b/dist/source/config/config-options/DUK_USE_NONSTD_ARRAY_SPLICE_DELCOUNT.yaml +new file mode 100644 +index 0000000..c75a785 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_NONSTD_ARRAY_SPLICE_DELCOUNT.yaml +@@ -0,0 +1,15 @@ ++define: DUK_USE_NONSTD_ARRAY_SPLICE_DELCOUNT ++introduced: 1.0.0 ++default: true ++tags: ++ - ecmascript ++ - compliance ++description: > ++ For better compatibility with existing code, enable non-standard ++ Array.prototype.splice() behavior when the second argument (deleteCount) ++ is not given: the splice operation is extended to the end of the array, see ++ https://github.com/svaarala/duktape/blob/master/tests/ecmascript/test-bi-array-proto-splice-no-delcount.js. ++ ++ If this option is disabled, splice() will behave in a strictly conforming ++ fashion, treating a missing deleteCount the same as an undefined (or 0) ++ value. +diff --git a/dist/source/config/config-options/DUK_USE_NONSTD_FUNC_CALLER_PROPERTY.yaml b/dist/source/config/config-options/DUK_USE_NONSTD_FUNC_CALLER_PROPERTY.yaml +new file mode 100644 +index 0000000..86f4d67 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_NONSTD_FUNC_CALLER_PROPERTY.yaml +@@ -0,0 +1,16 @@ ++define: DUK_USE_NONSTD_FUNC_CALLER_PROPERTY ++introduced: 1.0.0 ++default: false ++tags: ++ - ecmascript ++ - compliance ++description: > ++ Add a non-standard "caller" property to non-strict function instances ++ for better compatibility with existing code. The semantics of this ++ property are not standardized and may vary between engines; Duktape tries ++ to behave close to V8 and Spidermonkey. See ++ https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/caller ++ description of the property. This feature disables tail call support. ++ ++ This feature conflicts with several other features, so you should use it ++ only if it's absolutely necessary. +diff --git a/dist/source/config/config-options/DUK_USE_NONSTD_FUNC_SOURCE_PROPERTY.yaml b/dist/source/config/config-options/DUK_USE_NONSTD_FUNC_SOURCE_PROPERTY.yaml +new file mode 100644 +index 0000000..88ed487 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_NONSTD_FUNC_SOURCE_PROPERTY.yaml +@@ -0,0 +1,12 @@ ++define: DUK_USE_NONSTD_FUNC_SOURCE_PROPERTY ++introduced: 1.0.0 ++default: false ++tags: ++ - ecmascript ++ - compliance ++description: > ++ Add a non-standard "source" property to function instances. This allows ++ function toString() to print out the actual function source. The property ++ is disabled by default because it increases memory footprint. ++ ++ NOTE: Unimplemented as of Duktape 1.3.0. +diff --git a/dist/source/config/config-options/DUK_USE_NONSTD_FUNC_STMT.yaml b/dist/source/config/config-options/DUK_USE_NONSTD_FUNC_STMT.yaml +new file mode 100644 +index 0000000..be0ba88 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_NONSTD_FUNC_STMT.yaml +@@ -0,0 +1,13 @@ ++define: DUK_USE_NONSTD_FUNC_STMT ++introduced: 1.0.0 ++default: true ++tags: ++ - ecmascript ++ - compliance ++description: > ++ Enable support for function declarations outside program or function top ++ level (also known as "function statements"). Such declarations are ++ non-standard and the strictly compliant behavior is to treat them as a ++ SyntaxError. When this option is enabled (recommended), Duktape behavior ++ is to treat them like ordinary function declarations ("hoist" them to ++ function top) with V8-like semantics. +diff --git a/dist/source/config/config-options/DUK_USE_NONSTD_GETTER_KEY_ARGUMENT.yaml b/dist/source/config/config-options/DUK_USE_NONSTD_GETTER_KEY_ARGUMENT.yaml +new file mode 100644 +index 0000000..7ffd48a +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_NONSTD_GETTER_KEY_ARGUMENT.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_NONSTD_GETTER_KEY_ARGUMENT ++introduced: 1.0.0 ++default: true ++tags: ++ - ecmascript ++ - compliance ++description: > ++ Give getter calls the accessed property name as an additional non-standard ++ argument. This allows a single getter function to be reused for multiple ++ properties more easily. See ++ http://duktape.org/guide.html#propertyvirtualization. +diff --git a/dist/source/config/config-options/DUK_USE_NONSTD_JSON_ESC_U2028_U2029.yaml b/dist/source/config/config-options/DUK_USE_NONSTD_JSON_ESC_U2028_U2029.yaml +new file mode 100644 +index 0000000..d82c012 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_NONSTD_JSON_ESC_U2028_U2029.yaml +@@ -0,0 +1,13 @@ ++define: DUK_USE_NONSTD_JSON_ESC_U2028_U2029 ++introduced: 1.1.0 ++default: true ++tags: ++ - ecmascript ++ - compliance ++description: > ++ When enabled, Duktape JSON.stringify() will escape U+2028 and U+2029 which ++ is non-compliant behavior. This is recommended to make JSON.stringify() ++ output valid when embedded in a web page or parsed with eval(). ++ ++ When disabled, Duktape provides the compliant behavior, i.e. no escaping ++ for U+2028 and U+2029. +diff --git a/dist/source/config/config-options/DUK_USE_NONSTD_REGEXP_DOLLAR_ESCAPE.yaml b/dist/source/config/config-options/DUK_USE_NONSTD_REGEXP_DOLLAR_ESCAPE.yaml +new file mode 100644 +index 0000000..edfa774 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_NONSTD_REGEXP_DOLLAR_ESCAPE.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_NONSTD_REGEXP_DOLLAR_ESCAPE ++introduced: 1.0.0 ++removed: 2.0.0 ++default: true ++tags: ++ - ecmascript ++ - compliance ++description: > ++ Replaced by DUK_USE_ES6_REGEXP_SYNTAX. +diff --git a/dist/source/config/config-options/DUK_USE_NONSTD_SETTER_KEY_ARGUMENT.yaml b/dist/source/config/config-options/DUK_USE_NONSTD_SETTER_KEY_ARGUMENT.yaml +new file mode 100644 +index 0000000..b517b03 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_NONSTD_SETTER_KEY_ARGUMENT.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_NONSTD_SETTER_KEY_ARGUMENT ++introduced: 1.0.0 ++default: true ++tags: ++ - ecmascript ++ - compliance ++description: > ++ Give setter calls the accessed property name as an additional non-standard ++ argument. This allows a single setter function to be reused for multiple ++ properties more easily. See ++ http://duktape.org/guide.html#propertyvirtualization. +diff --git a/dist/source/config/config-options/DUK_USE_NONSTD_STRING_FROMCHARCODE_32BIT.yaml b/dist/source/config/config-options/DUK_USE_NONSTD_STRING_FROMCHARCODE_32BIT.yaml +new file mode 100644 +index 0000000..44d0ee3 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_NONSTD_STRING_FROMCHARCODE_32BIT.yaml +@@ -0,0 +1,12 @@ ++define: DUK_USE_NONSTD_STRING_FROMCHARCODE_32BIT ++introduced: 1.2.0 ++default: true ++tags: ++ - ecmascript ++ - compliance ++description: > ++ Allow 32-bit codepoints in String.fromCharCode(). This is non-compliant ++ (the E5.1 specification has a ToUint16() coercion for the codepoints) but ++ useful because Duktape supports non-BMP strings. ++ ++ When disabled, Duktape provides the compliant behavior. +diff --git a/dist/source/config/config-options/DUK_USE_NO_DOUBLE_ALIASING_SELFTEST.yaml b/dist/source/config/config-options/DUK_USE_NO_DOUBLE_ALIASING_SELFTEST.yaml +new file mode 100644 +index 0000000..6c9ff7e +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_NO_DOUBLE_ALIASING_SELFTEST.yaml +@@ -0,0 +1,12 @@ ++define: DUK_USE_NO_DOUBLE_ALIASING_SELFTEST ++introduced: 1.0.0 ++removed: 1.4.0 ++default: false ++tags: ++ - portability ++description: > ++ Disable double aliasing selftest (if self tests enabled). ++ ++ Double aliasing testcase fails when Emscripten-generated code is run. ++ This is not fatal because it only affects packed duk_tval which we ++ avoid with Emscripten. +diff --git a/dist/source/config/config-options/DUK_USE_NUMBER_BUILTIN.yaml b/dist/source/config/config-options/DUK_USE_NUMBER_BUILTIN.yaml +new file mode 100644 +index 0000000..c4b2d87 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_NUMBER_BUILTIN.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_NUMBER_BUILTIN ++introduced: 2.0.0 ++default: true ++tags: ++ - ecmascript ++description: > ++ Provide a Number built-in. +diff --git a/dist/source/config/config-options/DUK_USE_OBJECT_BUILTIN.yaml b/dist/source/config/config-options/DUK_USE_OBJECT_BUILTIN.yaml +new file mode 100644 +index 0000000..983dd9e +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_OBJECT_BUILTIN.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_OBJECT_BUILTIN ++introduced: 2.0.0 ++default: true ++tags: ++ - ecmascript ++description: > ++ Provide an Object built-in. +diff --git a/dist/source/config/config-options/DUK_USE_OBJSIZES16.yaml b/dist/source/config/config-options/DUK_USE_OBJSIZES16.yaml +new file mode 100644 +index 0000000..a10cf1f +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_OBJSIZES16.yaml +@@ -0,0 +1,12 @@ ++define: DUK_USE_OBJSIZES16 ++introduced: 1.1.0 ++default: false ++tags: ++ - lowmemory ++ - experimental ++description: > ++ Use a 16-bit object entry and array part sizes (for low memory ++ environments). Also automatically drops support for an object hash part ++ to further reduce memory usage; there are rarely large objects in low ++ memory environments simply because there's no memory to store a lot of ++ properties. +diff --git a/dist/source/config/config-options/DUK_USE_OCTAL_SUPPORT.yaml b/dist/source/config/config-options/DUK_USE_OCTAL_SUPPORT.yaml +new file mode 100644 +index 0000000..0a0283e +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_OCTAL_SUPPORT.yaml +@@ -0,0 +1,10 @@ ++define: DUK_USE_OCTAL_SUPPORT ++introduced: 1.0.0 ++removed: 2.0.0 ++default: true ++tags: ++ - ecmascript ++description: > ++ Enable optional octal number support (ECMAScript E5/E5.1 ++ Annex B: http://www.ecma-international.org/ecma-262/5.1/#sec-B). ++ Recommended because existing code bases use octal numbers. +diff --git a/dist/source/config/config-options/DUK_USE_OS_STRING.yaml b/dist/source/config/config-options/DUK_USE_OS_STRING.yaml +new file mode 100644 +index 0000000..b73aef0 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_OS_STRING.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_OS_STRING ++introduced: 1.0.0 ++default: ++ string: "unknown" ++tags: ++ - portability ++description: > ++ Human-readable operating system string used in e.g. Duktape.env and debugger ++ protocol (example: linux). +diff --git a/dist/source/config/config-options/DUK_USE_PACKED_TVAL.yaml b/dist/source/config/config-options/DUK_USE_PACKED_TVAL.yaml +new file mode 100644 +index 0000000..1bda282 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_PACKED_TVAL.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_PACKED_TVAL ++introduced: 1.0.0 ++default: false ++tags: ++ - portability ++description: > ++ Use a packed 8-byte representation for duk_tval. The packed representation ++ represents non-number values as special IEEE double NaN values, and is only ++ possible for platforms with 32-bit pointers. When the packed representation ++ is not available, Duktape uses a 12-16 byte struct/union which is more ++ portable. +diff --git a/dist/source/config/config-options/DUK_USE_PACKED_TVAL_POSSIBLE.yaml b/dist/source/config/config-options/DUK_USE_PACKED_TVAL_POSSIBLE.yaml +new file mode 100644 +index 0000000..8061032 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_PACKED_TVAL_POSSIBLE.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_PACKED_TVAL_POSSIBLE ++introduced: 1.0.0 ++removed: 1.4.0 ++default: false ++tags: ++ - portability ++description: > ++ Define when packed, 8-byte duk_tval representation is possible. +diff --git a/dist/source/config/config-options/DUK_USE_PACK_CLANG_ATTR.yaml b/dist/source/config/config-options/DUK_USE_PACK_CLANG_ATTR.yaml +new file mode 100644 +index 0000000..2e8e190 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_PACK_CLANG_ATTR.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_PACK_CLANG_ATTR ++introduced: 1.0.0 ++default: false ++tags: ++ - portability ++description: > ++ Use clang-specific attribute to force struct packing. +diff --git a/dist/source/config/config-options/DUK_USE_PACK_DUMMY_MEMBER.yaml b/dist/source/config/config-options/DUK_USE_PACK_DUMMY_MEMBER.yaml +new file mode 100644 +index 0000000..5362cae +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_PACK_DUMMY_MEMBER.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_PACK_DUMMY_MEMBER ++introduced: 1.0.0 ++default: false ++tags: ++ - portability ++description: > ++ Use dummy struct member to force struct packing. +diff --git a/dist/source/config/config-options/DUK_USE_PACK_GCC_ATTR.yaml b/dist/source/config/config-options/DUK_USE_PACK_GCC_ATTR.yaml +new file mode 100644 +index 0000000..3457640 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_PACK_GCC_ATTR.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_PACK_GCC_ATTR ++introduced: 1.0.0 ++default: false ++tags: ++ - portability ++description: > ++ Use gcc-specific attribute to force struct packing. +diff --git a/dist/source/config/config-options/DUK_USE_PACK_MSVC_PRAGMA.yaml b/dist/source/config/config-options/DUK_USE_PACK_MSVC_PRAGMA.yaml +new file mode 100644 +index 0000000..b648307 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_PACK_MSVC_PRAGMA.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_PACK_MSVC_PRAGMA ++introduced: 1.0.0 ++default: false ++tags: ++ - portability ++description: > ++ Use msvc-specific attribute to force struct packing. +diff --git a/dist/source/config/config-options/DUK_USE_PANIC_ABORT.yaml b/dist/source/config/config-options/DUK_USE_PANIC_ABORT.yaml +new file mode 100644 +index 0000000..c62a4f4 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_PANIC_ABORT.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_PANIC_ABORT ++introduced: 1.0.0 ++removed: 2.0.0 ++default: true ++tags: ++ - portability ++description: > ++ Call abort() when the default panic handler is invoked. +diff --git a/dist/source/config/config-options/DUK_USE_PANIC_EXIT.yaml b/dist/source/config/config-options/DUK_USE_PANIC_EXIT.yaml +new file mode 100644 +index 0000000..4909806 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_PANIC_EXIT.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_PANIC_EXIT ++introduced: 1.0.0 ++removed: 2.0.0 ++default: false ++tags: ++ - portability ++description: > ++ Call exit() when the default panic handler is invoked. +diff --git a/dist/source/config/config-options/DUK_USE_PANIC_HANDLER.yaml b/dist/source/config/config-options/DUK_USE_PANIC_HANDLER.yaml +new file mode 100644 +index 0000000..a6ae86d +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_PANIC_HANDLER.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_PANIC_HANDLER ++introduced: 1.0.0 ++removed: 2.0.0 ++default: false ++tags: ++ - portability ++description: > ++ Provide a custom panic handler. A custom panic handler is recommended for ++ any environment where recovery from fatal errors is important. A custom ++ handler can take appropriate action to recover, e.g. record the error and ++ reboot the target device. +diff --git a/dist/source/config/config-options/DUK_USE_PANIC_SEGFAULT.yaml b/dist/source/config/config-options/DUK_USE_PANIC_SEGFAULT.yaml +new file mode 100644 +index 0000000..4792125 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_PANIC_SEGFAULT.yaml +@@ -0,0 +1,10 @@ ++define: DUK_USE_PANIC_SEGFAULT ++introduced: 1.0.0 ++removed: 2.0.0 ++default: false ++tags: ++ - portability ++description: > ++ Cause an intentional segfault when the default panic handler is invoked. ++ This is useful when debugging with valgrind because a segfault provides ++ a nice C traceback in valgrind. +diff --git a/dist/source/config/config-options/DUK_USE_PARANOID_DATE_COMPUTATION.yaml b/dist/source/config/config-options/DUK_USE_PARANOID_DATE_COMPUTATION.yaml +new file mode 100644 +index 0000000..02a7280 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_PARANOID_DATE_COMPUTATION.yaml +@@ -0,0 +1,15 @@ ++define: DUK_USE_PARANOID_DATE_COMPUTATION ++introduced: 1.0.0 ++default: false # XXX: make default detect C99/C++11? ++tags: ++ - portability ++description: > ++ There was a curious bug where test-bi-date-canceling.js would fail e.g. ++ on 64-bit Ubuntu, gcc-4.8.1, -m32, and no -std=c99. Some date computations ++ using doubles would be optimized which then broke some corner case tests. ++ The problem goes away by adding 'volatile' to the datetime computations. ++ Not sure what the actual triggering conditions are, but using this on ++ non-C99 systems solves the known issues and has relatively little cost ++ on other platforms. ++ ++ Recommended for non-C99 platforms. +diff --git a/dist/source/config/config-options/DUK_USE_PARANOID_ERRORS.yaml b/dist/source/config/config-options/DUK_USE_PARANOID_ERRORS.yaml +new file mode 100644 +index 0000000..66c613b +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_PARANOID_ERRORS.yaml +@@ -0,0 +1,14 @@ ++define: DUK_USE_PARANOID_ERRORS ++introduced: 1.4.0 ++default: false ++tags: ++ - ecmascript ++ - sandbox ++description: > ++ When enabled, error messages won't involve summarization of keys or values. ++ Summaries may be an issue in some security sensitive environments because ++ error messages will include e.g. property keys. ++ ++ The default is to summarize offending base value and key for property access ++ errors such as "null.foo = 123;", invalid calls such as "undefined()", etc. ++ Base values and keys are summarized using duk_push_string_tval_readable(). +diff --git a/dist/source/config/config-options/DUK_USE_PARANOID_MATH.yaml b/dist/source/config/config-options/DUK_USE_PARANOID_MATH.yaml +new file mode 100644 +index 0000000..e150a40 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_PARANOID_MATH.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_PARANOID_MATH ++introduced: 1.0.0 ++default: false ++tags: ++ - portability ++description: > ++ Rely as little as possible on compiler behavior for NaN comparison, ++ signed zero handling, etc. May be needed for (very) broken compilers. +diff --git a/dist/source/config/config-options/DUK_USE_PC2LINE.yaml b/dist/source/config/config-options/DUK_USE_PC2LINE.yaml +new file mode 100644 +index 0000000..c31b757 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_PC2LINE.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_PC2LINE ++introduced: 1.0.0 ++default: true ++tags: ++ - ecmascript ++description: > ++ Record a "pc2line" map into function instances which allows bytecode program ++ counter values to be mapped into line numbers e.g. in error tracebacks. ++ ++ Without this map, exceptions won't have meaningful line numbers but function ++ instances will have a smaller footprint. +diff --git a/dist/source/config/config-options/DUK_USE_PERFORMANCE_BUILTIN.yaml b/dist/source/config/config-options/DUK_USE_PERFORMANCE_BUILTIN.yaml +new file mode 100644 +index 0000000..0b47be6 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_PERFORMANCE_BUILTIN.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_PERFORMANCE_BUILTIN ++introduced: 2.2.0 ++default: true ++tags: ++ - performance-api ++description: > ++ Provide a 'performance' global object based on https://www.w3.org/TR/hr-time/. +diff --git a/dist/source/config/config-options/DUK_USE_POW_NETBSD_WORKAROUND.yaml b/dist/source/config/config-options/DUK_USE_POW_NETBSD_WORKAROUND.yaml +new file mode 100644 +index 0000000..09803a7 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_POW_NETBSD_WORKAROUND.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_POW_NETBSD_WORKAROUND ++introduced: 1.0.0 ++removed: 2.0.0 ++default: false ++tags: ++ - portability ++description: > ++ NetBSD 6.0 x86 (at least) has a few problems with pow() semantics, ++ see test-bug-netbsd-math-pow.js. Use NetBSD specific workaround. ++ ++ (This might be a wider problem; if so, generalize the define name.) +diff --git a/dist/source/config/config-options/DUK_USE_POW_WORKAROUNDS.yaml b/dist/source/config/config-options/DUK_USE_POW_WORKAROUNDS.yaml +new file mode 100644 +index 0000000..91a2113 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_POW_WORKAROUNDS.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_POW_WORKAROUNDS ++introduced: 2.0.0 ++default: false ++tags: ++ - portability ++description: > ++ Enable workarounds to common pow() semantics issues. At least NetBSD ++ 6.0 x86 and Cygwin/MinGW have such issues, see test-bug-netbsd-math-pow.js ++ and test-bug-mingw-math-issues.js. +diff --git a/dist/source/config/config-options/DUK_USE_PREFER_SIZE.yaml b/dist/source/config/config-options/DUK_USE_PREFER_SIZE.yaml +new file mode 100644 +index 0000000..8113ff2 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_PREFER_SIZE.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_PREFER_SIZE ++introduced: 1.2.0 ++default: false ++tags: ++ - lowmemory ++description: > ++ Catch-all flag which can be used to choose between variant algorithms ++ where a speed-size tradeoff exists (e.g. lookup tables). When it really ++ matters, specific use flags may be appropriate. +diff --git a/dist/source/config/config-options/DUK_USE_PROMISE_BUILTIN.yaml b/dist/source/config/config-options/DUK_USE_PROMISE_BUILTIN.yaml +new file mode 100644 +index 0000000..8eb4b38 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_PROMISE_BUILTIN.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_PROMISE_BUILTIN ++introduced: 2.2.0 ++default: false # disabled until fully functional ++tags: ++ - ecmascript ++description: > ++ Enable Promise built-in. ++ ++ At present entirely non-functional, and disabled by default. +diff --git a/dist/source/config/config-options/DUK_USE_PROVIDE_DEFAULT_ALLOC_FUNCTIONS.yaml b/dist/source/config/config-options/DUK_USE_PROVIDE_DEFAULT_ALLOC_FUNCTIONS.yaml +new file mode 100644 +index 0000000..3afcbec +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_PROVIDE_DEFAULT_ALLOC_FUNCTIONS.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_PROVIDE_DEFAULT_ALLOC_FUNCTIONS ++introduced: 1.0.0 ++default: true ++tags: ++ - portability ++description: > ++ Provide default allocation functions. ++ ++ At the moment this option should be enabled. +diff --git a/dist/source/config/config-options/DUK_USE_RDTSC.yaml b/dist/source/config/config-options/DUK_USE_RDTSC.yaml +new file mode 100644 +index 0000000..04b65b1 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_RDTSC.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_RDTSC ++introduced: 1.3.0 ++removed: 1.4.0 ++default: false ++tags: ++ - portability ++description: > ++ Macro to provide an x86/x64 RDTSC timestamp for debug prints. +diff --git a/dist/source/config/config-options/DUK_USE_REFCOUNT16.yaml b/dist/source/config/config-options/DUK_USE_REFCOUNT16.yaml +new file mode 100644 +index 0000000..93d7387 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_REFCOUNT16.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_REFCOUNT16 ++introduced: 1.1.0 ++default: false ++tags: ++ - lowmemory ++ - experimental ++ - gc ++description: > ++ Use a 16-bit reference count field (for low memory environments). +diff --git a/dist/source/config/config-options/DUK_USE_REFCOUNT32.yaml b/dist/source/config/config-options/DUK_USE_REFCOUNT32.yaml +new file mode 100644 +index 0000000..363411f +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_REFCOUNT32.yaml +@@ -0,0 +1,12 @@ ++define: DUK_USE_REFCOUNT32 ++introduced: 2.1.0 ++default: true ++tags: ++ - gc ++description: > ++ Use a 32-bit reference count field. ++ ++ While on some 64-bit systems it's theoretically possible to wrap a 32-bit ++ counter field, assuming a 16-byte duk_tval the Duktape heap would have to ++ be larger than 64GB for that to happen. Because of this the default is to ++ use a 32-bit refcount field. +diff --git a/dist/source/config/config-options/DUK_USE_REFERENCE_COUNTING.yaml b/dist/source/config/config-options/DUK_USE_REFERENCE_COUNTING.yaml +new file mode 100644 +index 0000000..a59bad2 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_REFERENCE_COUNTING.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_REFERENCE_COUNTING ++introduced: 1.0.0 ++default: true ++tags: ++ - gc ++description: > ++ Use reference counting for garbage collection. +diff --git a/dist/source/config/config-options/DUK_USE_REFLECT_BUILTIN.yaml b/dist/source/config/config-options/DUK_USE_REFLECT_BUILTIN.yaml +new file mode 100644 +index 0000000..bedd9c1 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_REFLECT_BUILTIN.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_REFLECT_BUILTIN ++introduced: 2.0.0 ++default: true ++tags: ++ - ecmascript2015 ++description: > ++ Provide a Reflect built-in. The ES6 Reflect object provides a collection of ++ methods for examining and manipulating objects at runtime. +diff --git a/dist/source/config/config-options/DUK_USE_REFZERO_FINALIZER_TORTURE.yaml b/dist/source/config/config-options/DUK_USE_REFZERO_FINALIZER_TORTURE.yaml +new file mode 100644 +index 0000000..de83dae +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_REFZERO_FINALIZER_TORTURE.yaml +@@ -0,0 +1,15 @@ ++define: DUK_USE_REFZERO_FINALIZER_TORTURE ++introduced: 1.3.0 ++removed: 2.1.0 ++related: ++ - DUK_USE_MARKANDSWEEP_FINALIZER_TORTURE ++default: false ++tags: ++ - gc ++ - memory ++ - development ++ - torture ++description: > ++ Development time option: simulate a fake finalizer call for every object ++ going through refzero freeing. This is useful to detect bugs caused by ++ finalizer side effects in e.g. call handling. +diff --git a/dist/source/config/config-options/DUK_USE_REGEXP_CANON_BITMAP.yaml b/dist/source/config/config-options/DUK_USE_REGEXP_CANON_BITMAP.yaml +new file mode 100644 +index 0000000..36ca02f +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_REGEXP_CANON_BITMAP.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_REGEXP_CANON_BITMAP ++introduced: 2.2.0 ++default: true ++tags: ++ - performance ++ - unicode ++description: > ++ Use a small lookup table (footprint impact is ~300-400 bytes) to speed up ++ case insensitive RegExp canonicalization. The result is still much slower ++ than with DUK_USE_REGEXP_CANON_WORKAROUND but ~50x faster than without the ++ lookup. +diff --git a/dist/source/config/config-options/DUK_USE_REGEXP_CANON_WORKAROUND.yaml b/dist/source/config/config-options/DUK_USE_REGEXP_CANON_WORKAROUND.yaml +new file mode 100644 +index 0000000..f363840 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_REGEXP_CANON_WORKAROUND.yaml +@@ -0,0 +1,13 @@ ++define: DUK_USE_REGEXP_CANON_WORKAROUND ++introduced: 1.4.0 ++default: false ++tags: ++ - performance ++ - unicode ++ - experimental ++description: > ++ Use a 128kB lookup table for RegExp codepoint canonicalization to improve ++ performance of case insensitive RegExp handling. ++ ++ This is a temporary workaround until there's better support for faster ++ Unicode handling. +diff --git a/dist/source/config/config-options/DUK_USE_REGEXP_COMPILER_RECLIMIT.yaml b/dist/source/config/config-options/DUK_USE_REGEXP_COMPILER_RECLIMIT.yaml +new file mode 100644 +index 0000000..ea653d7 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_REGEXP_COMPILER_RECLIMIT.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_REGEXP_COMPILER_RECLIMIT ++introduced: 1.3.0 ++default: 10000 ++tags: ++ - portability ++ - cstackdepth ++description: > ++ RegExp compiler native call stack recursion limit. +diff --git a/dist/source/config/config-options/DUK_USE_REGEXP_EXECUTOR_RECLIMIT.yaml b/dist/source/config/config-options/DUK_USE_REGEXP_EXECUTOR_RECLIMIT.yaml +new file mode 100644 +index 0000000..cc1dfca +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_REGEXP_EXECUTOR_RECLIMIT.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_REGEXP_EXECUTOR_RECLIMIT ++introduced: 1.3.0 ++default: 10000 ++tags: ++ - portability ++ - cstackdepth ++description: > ++ RegExp executor native call stack recursion limit. +diff --git a/dist/source/config/config-options/DUK_USE_REGEXP_SUPPORT.yaml b/dist/source/config/config-options/DUK_USE_REGEXP_SUPPORT.yaml +new file mode 100644 +index 0000000..39ed04e +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_REGEXP_SUPPORT.yaml +@@ -0,0 +1,15 @@ ++define: DUK_USE_REGEXP_SUPPORT ++introduced: 1.0.0 ++default: true ++tags: ++ - ecmascript ++ - lowmemory ++description: > ++ Enable support for regular expressions (recommended). ++ ++ When disabled, regexp literals are treated as a SyntaxError, RegExp ++ constructor and prototype functions throw an error, ++ String.prototype.replace() throws an error if given a regexp search value, ++ String.prototype.split() throws an error if given a regexp separator value, ++ String.prototype.search() and String.prototype.match() throw an error ++ unconditionally. +diff --git a/dist/source/config/config-options/DUK_USE_REPL_FPCLASSIFY.yaml b/dist/source/config/config-options/DUK_USE_REPL_FPCLASSIFY.yaml +new file mode 100644 +index 0000000..8a13af1 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_REPL_FPCLASSIFY.yaml +@@ -0,0 +1,12 @@ ++define: DUK_USE_REPL_FPCLASSIFY ++introduced: 1.0.0 ++default: false ++tags: ++ - portability ++description: > ++ Provide a built-in replacement for fpclassify(), duk_repl_fpclassify. ++ ++ When enabled, define DUK_FPCLASSIFY as duk_repl_fpclassify. ++ ++# XXX: the dependency between DUK_USE_REPL_FPCLASSIFY and DUK_FPCLASSIFY ++# is a bit awkward. +diff --git a/dist/source/config/config-options/DUK_USE_REPL_ISFINITE.yaml b/dist/source/config/config-options/DUK_USE_REPL_ISFINITE.yaml +new file mode 100644 +index 0000000..6d66d57 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_REPL_ISFINITE.yaml +@@ -0,0 +1,12 @@ ++define: DUK_USE_REPL_ISFINITE ++introduced: 1.0.0 ++default: false ++tags: ++ - portability ++description: > ++ Provide a built-in replacement for isfinite(), duk_repl_isfinite. ++ ++ When enabled, define DUK_ISFINITE as duk_repl_isfinite. ++ ++# XXX: the dependency between DUK_USE_REPL_ISFINITE and DUK_ISFINITE ++# is a bit awkward. +diff --git a/dist/source/config/config-options/DUK_USE_REPL_ISINF.yaml b/dist/source/config/config-options/DUK_USE_REPL_ISINF.yaml +new file mode 100644 +index 0000000..19afbca +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_REPL_ISINF.yaml +@@ -0,0 +1,12 @@ ++define: DUK_USE_REPL_ISINF ++introduced: 1.0.0 ++default: false ++tags: ++ - portability ++description: > ++ Provide a built-in replacement for isinf(), duk_repl_isinf. ++ ++ When enabled, define DUK_ISINF as duk_repl_isinf. ++ ++# XXX: the dependency between DUK_USE_REPL_ISINF and DUK_ISINF ++# is a bit awkward. +diff --git a/dist/source/config/config-options/DUK_USE_REPL_ISNAN.yaml b/dist/source/config/config-options/DUK_USE_REPL_ISNAN.yaml +new file mode 100644 +index 0000000..9e9558e +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_REPL_ISNAN.yaml +@@ -0,0 +1,12 @@ ++define: DUK_USE_REPL_ISNAN ++introduced: 1.0.0 ++default: false ++tags: ++ - portability ++description: > ++ Provide a built-in replacement for isnan(), duk_repl_isnan. ++ ++ When enabled, define DUK_ISNAN as duk_repl_isnan. ++ ++# XXX: the dependency between DUK_USE_REPL_ISNAN and DUK_ISNAN ++# is a bit awkward. +diff --git a/dist/source/config/config-options/DUK_USE_REPL_SIGNBIT.yaml b/dist/source/config/config-options/DUK_USE_REPL_SIGNBIT.yaml +new file mode 100644 +index 0000000..92a87dd +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_REPL_SIGNBIT.yaml +@@ -0,0 +1,12 @@ ++define: DUK_USE_REPL_SIGNBIT ++introduced: 1.0.0 ++default: false ++tags: ++ - portability ++description: > ++ Provide a built-in replacement for signbit(), duk_repl_signbit. ++ ++ When enabled, define DUK_SIGNBIT as duk_repl_signbit. ++ ++# XXX: the dependency between DUK_USE_REPL_SIGNBIT and DUK_SIGNBIT ++# is a bit awkward. +diff --git a/dist/source/config/config-options/DUK_USE_ROM_GLOBAL_CLONE.yaml b/dist/source/config/config-options/DUK_USE_ROM_GLOBAL_CLONE.yaml +new file mode 100644 +index 0000000..70cdb5f +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_ROM_GLOBAL_CLONE.yaml +@@ -0,0 +1,18 @@ ++define: DUK_USE_ROM_GLOBAL_CLONE ++introduced: 1.5.0 ++requires: ++ - DUK_USE_ROM_STRINGS ++ - DUK_USE_ROM_OBJECTS ++conflicts: ++ - DUK_USE_ROM_GLOBAL_INHERIT ++default: false ++tags: ++ - lowmemory ++ - experimental ++description: > ++ When using ROM built-in objects, create a RAM-based global object by copying ++ the properties of the ROM-based global object into a fresh empty object. ++ ++ Having a writable global object is usually expected; if the global object is ++ not writable, it's not possible to e.g. declare functions outside of CommonJS ++ modules. +diff --git a/dist/source/config/config-options/DUK_USE_ROM_GLOBAL_INHERIT.yaml b/dist/source/config/config-options/DUK_USE_ROM_GLOBAL_INHERIT.yaml +new file mode 100644 +index 0000000..fbfec9a +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_ROM_GLOBAL_INHERIT.yaml +@@ -0,0 +1,23 @@ ++define: DUK_USE_ROM_GLOBAL_INHERIT ++introduced: 1.5.0 ++requires: ++ - DUK_USE_ROM_STRINGS ++ - DUK_USE_ROM_OBJECTS ++conflicts: ++ - DUK_USE_ROM_GLOBAL_CLONE ++default: false ++tags: ++ - lowmemory ++ - experimental ++description: > ++ When using ROM built-in objects, create a RAM-based global object by creating ++ a fresh empty object which inherits from the ROM-based global object. This ++ provides all the standard bindings with a small RAM footprint cost, but still ++ allows the global object to be extended and existing bindings overwritten ++ (but not deleted). The downside of this compared to cloning a global object ++ is that the inheritance is not fully transparent and the result is less ++ compliant. ++ ++ Having a writable global object is usually expected; if the global object is ++ not writable, it's not possible to e.g. declare functions outside of CommonJS ++ modules. +diff --git a/dist/source/config/config-options/DUK_USE_ROM_OBJECTS.yaml b/dist/source/config/config-options/DUK_USE_ROM_OBJECTS.yaml +new file mode 100644 +index 0000000..905319c +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_ROM_OBJECTS.yaml +@@ -0,0 +1,22 @@ ++define: DUK_USE_ROM_OBJECTS ++introduced: 1.5.0 ++requires: ++ - DUK_USE_ROM_STRINGS # use both DUK_USE_ROM_STRINGS + DUK_USE_ROM_OBJECTS together for now ++default: false ++tags: ++ - lowmemory ++ - experimental ++description: > ++ Enable support for built-in objects compiled as constants and placed in a ++ read-only data section. This reduces startup RAM usage considerably at the ++ cost of a larger code footprint and slower performance overall. The built-in ++ objects will be immutable: the objects will be non-extensible. ++ ++ ROM objects will always be non-extensible and properties are forced to be ++ non-configurable. Other property attributes will have their usual values; ++ in particular, properties can be "writable" from a property attributes ++ standpoint, but an attempt to actually change the property value will fail ++ with a TypeError. This may seem strange, but is necessary to allow a ++ property value to be overridden in a RAM object inheriting from a ROM object: ++ if the inherited ROM property was not writable, ECMAScript semantics would ++ prevent a new property from being established in the RAM object. +diff --git a/dist/source/config/config-options/DUK_USE_ROM_PTRCOMP_FIRST.yaml b/dist/source/config/config-options/DUK_USE_ROM_PTRCOMP_FIRST.yaml +new file mode 100644 +index 0000000..50fa63c +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_ROM_PTRCOMP_FIRST.yaml +@@ -0,0 +1,16 @@ ++define: DUK_USE_ROM_PTRCOMP_FIRST ++introduced: 1.5.0 ++related: ++ - DUK_USE_ROM_STRINGS ++ - DUK_USE_ROM_OBJECTS ++default: 0xf800 ++tags: ++ - lowmemory ++ - experimental ++description: > ++ When using ROM pointer compression ROM pointers are compressed to the ++ integer range [DUK_USE_ROM_PTRCOMP_FIRST,0xffff]. The default value ++ allows for 2048 ROM pointers, which can point to objects and strings. ++ ++ You may need to lower this value to support more pointers if there are ++ a lot of custom ROM strings/objects. +diff --git a/dist/source/config/config-options/DUK_USE_ROM_STRINGS.yaml b/dist/source/config/config-options/DUK_USE_ROM_STRINGS.yaml +new file mode 100644 +index 0000000..bdb8871 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_ROM_STRINGS.yaml +@@ -0,0 +1,13 @@ ++define: DUK_USE_ROM_STRINGS ++introduced: 1.5.0 ++requires: ++ - DUK_USE_ROM_OBJECTS # use both DUK_USE_ROM_STRINGS + DUK_USE_ROM_OBJECTS together for now ++default: false ++tags: ++ - lowmemory ++ - experimental ++description: > ++ Enable support for built-in (and optional user-supplied strings) which are ++ compiled as constants and placed in a read-only data section. This reduces ++ startup RAM usage considerably at the cost of a larger code footprint and ++ slower string interning. +diff --git a/dist/source/config/config-options/DUK_USE_SECTION_B.yaml b/dist/source/config/config-options/DUK_USE_SECTION_B.yaml +new file mode 100644 +index 0000000..f6265b6 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_SECTION_B.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_SECTION_B ++introduced: 1.0.0 ++default: true ++tags: ++ - ecmascript ++description: > ++ Enable optional features in ECMAScript specification ++ Annex B: http://www.ecma-international.org/ecma-262/5.1/#sec-B. ++ ++ When disabled, escape(), unescape(), and String.prototype.substr() ++ throw an error. +diff --git a/dist/source/config/config-options/DUK_USE_SELF_TESTS.yaml b/dist/source/config/config-options/DUK_USE_SELF_TESTS.yaml +new file mode 100644 +index 0000000..8c9b77c +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_SELF_TESTS.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_SELF_TESTS ++introduced: 1.0.0 ++default: false ++tags: ++ - debug ++description: > ++ Perform run-time self tests when a Duktape heap is created. Catches ++ platform/compiler problems which cannot be reliably detected during ++ compile time. Not enabled by default because of the extra footprint. +diff --git a/dist/source/config/config-options/DUK_USE_SETJMP.yaml b/dist/source/config/config-options/DUK_USE_SETJMP.yaml +new file mode 100644 +index 0000000..9b18afe +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_SETJMP.yaml +@@ -0,0 +1,17 @@ ++define: DUK_USE_SETJMP ++introduced: 1.1.0 ++removed: 1.5.0 ++default: true ++tags: ++ - portability ++description: > ++ Use setjmp/longjmp for long control transfers. This is the most portable ++ option for long control transfers. ++ ++ The downside of setjmp/longjmp is that signal mask saving behavior is not ++ specified and varies between platforms. Signal mask saving may have a ++ significant performance impact so you may want to force a specific provider ++ if performance matters for your application. (This is the case for macOS, ++ for instance.) ++ ++ Removed in Duktape 1.5.0: edit duk_config.h directly. +diff --git a/dist/source/config/config-options/DUK_USE_SHEBANG_COMMENTS.yaml b/dist/source/config/config-options/DUK_USE_SHEBANG_COMMENTS.yaml +new file mode 100644 +index 0000000..221b0d6 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_SHEBANG_COMMENTS.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_SHEBANG_COMMENTS ++introduced: 2.1.0 ++default: true ++tags: ++ - ecmascript2015 ++description: > ++ Support parsing of a "shebang" comment ('#!...') on the first line of ++ source text. +diff --git a/dist/source/config/config-options/DUK_USE_SHUFFLE_TORTURE.yaml b/dist/source/config/config-options/DUK_USE_SHUFFLE_TORTURE.yaml +new file mode 100644 +index 0000000..822ca6e +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_SHUFFLE_TORTURE.yaml +@@ -0,0 +1,12 @@ ++define: DUK_USE_SHUFFLE_TORTURE ++introduced: 1.2.0 ++default: false ++tags: ++ - gc ++ - memory ++ - development ++ - torture ++description: > ++ Development time option: force compiler to shuffle every possible opcode ++ to stress shuffle behavior which is otherwise difficult to test for ++ comprehensively. +diff --git a/dist/source/config/config-options/DUK_USE_SIGSETJMP.yaml b/dist/source/config/config-options/DUK_USE_SIGSETJMP.yaml +new file mode 100644 +index 0000000..7a9a383 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_SIGSETJMP.yaml +@@ -0,0 +1,12 @@ ++define: DUK_USE_SIGSETJMP ++introduced: 1.1.0 ++removed: 1.5.0 ++default: false ++tags: ++ - portability ++description: > ++ Use sigsetjmp/siglongjmp with savesigs == 0 for long control ++ transfers (i.e. signal mask not saved/restored). See comments in ++ DUK_USE_SETJMP. ++ ++ Removed in Duktape 1.5.0: edit duk_config.h directly. +diff --git a/dist/source/config/config-options/DUK_USE_SOURCE_NONBMP.yaml b/dist/source/config/config-options/DUK_USE_SOURCE_NONBMP.yaml +new file mode 100644 +index 0000000..9682a17 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_SOURCE_NONBMP.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_SOURCE_NONBMP ++introduced: 1.0.0 ++default: true ++tags: ++ - ecmascript ++ - lowmemory ++description: > ++ Enable accurate Unicode support for non-BMP characters in source code. ++ ++ When disabled non-BMP characters are always accepted as identifier ++ characters. Disabling this option saves a little bit of code footprint. +diff --git a/dist/source/config/config-options/DUK_USE_STRHASH16.yaml b/dist/source/config/config-options/DUK_USE_STRHASH16.yaml +new file mode 100644 +index 0000000..f66e36b +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_STRHASH16.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_STRHASH16 ++introduced: 1.1.0 ++default: false ++tags: ++ - lowmemory ++ - experimental ++description: > ++ Use a 16-bit string hash field (for low memory environments). +diff --git a/dist/source/config/config-options/DUK_USE_STRHASH_DENSE.yaml b/dist/source/config/config-options/DUK_USE_STRHASH_DENSE.yaml +new file mode 100644 +index 0000000..af1b28f +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_STRHASH_DENSE.yaml +@@ -0,0 +1,10 @@ ++define: DUK_USE_STRHASH_DENSE ++introduced: 1.4.0 ++default: false ++tags: ++ - performance ++ - sandbox ++description: > ++ Use the slower but more dense string hash algorithm from Duktape 1.3.0 and ++ prior (based on Murmurhash2). This may be useful if you're experiencing ++ collision issues with the default hash algorithm. +diff --git a/dist/source/config/config-options/DUK_USE_STRHASH_SKIP_SHIFT.yaml b/dist/source/config/config-options/DUK_USE_STRHASH_SKIP_SHIFT.yaml +new file mode 100644 +index 0000000..d9a4d04 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_STRHASH_SKIP_SHIFT.yaml +@@ -0,0 +1,12 @@ ++define: DUK_USE_STRHASH_SKIP_SHIFT ++introduced: 1.4.0 ++default: 5 ++tags: ++ - performance ++description: > ++ Shift value to use for string hash skip offset when using the default ++ (fast) string hash. The skip offset is calculated as: ++ ((length >> DUK_USE_STRHASH_SKIP_SHIFT) + 1). A higher value will be ++ slower but sample the string more densely. ++ ++ You should only change this if you run into issues with the default value. +diff --git a/dist/source/config/config-options/DUK_USE_STRICT_DECL.yaml b/dist/source/config/config-options/DUK_USE_STRICT_DECL.yaml +new file mode 100644 +index 0000000..8e6ff32 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_STRICT_DECL.yaml +@@ -0,0 +1,13 @@ ++define: DUK_USE_STRICT_DECL ++introduced: 1.1.0 ++default: true ++tags: ++ - ecmascript ++ - experimental ++description: > ++ Enable support for "use strict" declaration (recommended). ++ ++ When disabled, ECMAScript code is always executed in non-strict mode. ++ Duktape/C functions remain strict. This option is useful in some legacy ++ environments where "use strict" declarations are used in existing code ++ base but the Javascript engine didn't actually support strict mode. +diff --git a/dist/source/config/config-options/DUK_USE_STRICT_UTF8_SOURCE.yaml b/dist/source/config/config-options/DUK_USE_STRICT_UTF8_SOURCE.yaml +new file mode 100644 +index 0000000..ceac807 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_STRICT_UTF8_SOURCE.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_STRICT_UTF8_SOURCE ++introduced: 1.0.0 ++default: false ++tags: ++ - ecmascript ++description: > ++ Enable strict UTF-8 parsing of source code. ++ ++ When disabled, non-shortest encodings (normally invalid UTF-8) and surrogate ++ pair codepoints are accepted as valid source code characters. Disabling ++ this option breaks compatibility with some test262 tests. +diff --git a/dist/source/config/config-options/DUK_USE_STRING_BUILTIN.yaml b/dist/source/config/config-options/DUK_USE_STRING_BUILTIN.yaml +new file mode 100644 +index 0000000..c9d11ed +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_STRING_BUILTIN.yaml +@@ -0,0 +1,7 @@ ++define: DUK_USE_STRING_BUILTIN ++introduced: 2.0.0 ++default: true ++tags: ++ - ecmascript ++description: > ++ Provide a String built-in. +diff --git a/dist/source/config/config-options/DUK_USE_STRLEN16.yaml b/dist/source/config/config-options/DUK_USE_STRLEN16.yaml +new file mode 100644 +index 0000000..4d8a801 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_STRLEN16.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_STRLEN16 ++introduced: 1.1.0 ++default: false ++tags: ++ - lowmemory ++ - experimental ++description: > ++ Use a 16-bit string length field (for low memory environments). +diff --git a/dist/source/config/config-options/DUK_USE_STRTAB_CHAIN.yaml b/dist/source/config/config-options/DUK_USE_STRTAB_CHAIN.yaml +new file mode 100644 +index 0000000..67c3205 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_STRTAB_CHAIN.yaml +@@ -0,0 +1,27 @@ ++define: DUK_USE_STRTAB_CHAIN ++introduced: 1.1.0 ++removed: 2.1.0 ++related: ++ - DUK_USE_STRTAB_CHAIN_SIZE ++default: false ++tags: ++ - lowmemory ++description: > ++ Replace the default (open addressing, probing) string table structure with ++ one based on separate chaining. There is a fixed-size top level hash table ++ (whose size is defined using DUK_USE_STRTAB_CHAIN_SIZE), with each entry in ++ the hash table being: (a) NULL, (b) a duk_hstring pointer, or (c) a pointer ++ to an array of duk_hstring pointers. The pointer arrays are gappy (the gaps ++ are reused on new inserts) and are never shrunk at the moment. ++ ++ This option is intended for low memory environments to make Duktape's memory ++ behavior match a typical pool-based allocator better as follows: ++ ++ The top level fixed structure never changes size, so there is no hash table ++ resize, and thus no need for resize temporaries. The default string table ++ algorithm needs resizing from time to time and doesn't resize in place, so ++ you effectively need twice the string table size temporarily during a resize. ++ ++ The pointer arrays vary in size, but their size (typically 8 to 64 bytes, ++ depending on the load factor) matches that of many other allocations which ++ works well with a pooled allocator. +diff --git a/dist/source/config/config-options/DUK_USE_STRTAB_CHAIN_SIZE.yaml b/dist/source/config/config-options/DUK_USE_STRTAB_CHAIN_SIZE.yaml +new file mode 100644 +index 0000000..a1e4f08 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_STRTAB_CHAIN_SIZE.yaml +@@ -0,0 +1,10 @@ ++define: DUK_USE_STRTAB_CHAIN_SIZE ++introduced: 1.1.0 ++removed: 2.1.0 ++requires: ++ - DUK_USE_STRTAB_CHAIN ++default: false ++tags: ++ - lowmemory ++description: > ++ Define stringtable size for DUK_USE_STRTAB_CHAIN. +diff --git a/dist/source/config/config-options/DUK_USE_STRTAB_GROW_LIMIT.yaml b/dist/source/config/config-options/DUK_USE_STRTAB_GROW_LIMIT.yaml +new file mode 100644 +index 0000000..111a1a8 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_STRTAB_GROW_LIMIT.yaml +@@ -0,0 +1,10 @@ ++define: DUK_USE_STRTAB_GROW_LIMIT ++introduced: 2.1.0 ++default: 17 # 17/16 = 1.0625 ++tags: ++ - performance ++ - lowmemory ++description: > ++ Grow top level strtable allocation when load factor reaches this value. ++ Expressed as a .4 fixed point; the load factor is computed as ++ floor((count / size) * 16.0), e.g. 32 means a load factor of 2.0. +diff --git a/dist/source/config/config-options/DUK_USE_STRTAB_MAXSIZE.yaml b/dist/source/config/config-options/DUK_USE_STRTAB_MAXSIZE.yaml +new file mode 100644 +index 0000000..da7feef +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_STRTAB_MAXSIZE.yaml +@@ -0,0 +1,13 @@ ++define: DUK_USE_STRTAB_MAXSIZE ++introduced: 2.1.0 ++default: 268435456 ++tags: ++ - performance ++ - lowmemory ++description: > ++ Maximum size for Duktape heap string table, must be 2^N, and small enough ++ so that if the value is multiplied by sizeof(duk_hstring *) it won't overflow ++ duk_size_t. ++ ++ To avoid resizing the strtable at all, set DUK_USE_STRTAB_MINSIZE and ++ DUK_USE_STRTAB_MAXSIZE to the same value. +diff --git a/dist/source/config/config-options/DUK_USE_STRTAB_MINSIZE.yaml b/dist/source/config/config-options/DUK_USE_STRTAB_MINSIZE.yaml +new file mode 100644 +index 0000000..55ba319 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_STRTAB_MINSIZE.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_STRTAB_MINSIZE ++introduced: 2.1.0 ++default: 1024 ++tags: ++ - performance ++ - lowmemory ++description: > ++ Minimum size for Duktape heap string table, must be 2^N, and should never ++ be lower than 64. +diff --git a/dist/source/config/config-options/DUK_USE_STRTAB_PROBE.yaml b/dist/source/config/config-options/DUK_USE_STRTAB_PROBE.yaml +new file mode 100644 +index 0000000..c65ac17 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_STRTAB_PROBE.yaml +@@ -0,0 +1,8 @@ ++define: DUK_USE_STRTAB_PROBE ++introduced: 1.1.0 ++removed: 2.1.0 ++default: true ++tags: ++ - lowmemory ++description: > ++ Use the default open addressing (probing) based string table algorithm. +diff --git a/dist/source/config/config-options/DUK_USE_STRTAB_PTRCOMP.yaml b/dist/source/config/config-options/DUK_USE_STRTAB_PTRCOMP.yaml +new file mode 100644 +index 0000000..7b93bff +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_STRTAB_PTRCOMP.yaml +@@ -0,0 +1,14 @@ ++define: DUK_USE_STRTAB_PTRCOMP ++introduced: 2.1.0 ++default: false ++requires: ++ - DUK_USE_HEAPPTR16 ++tags: ++ - performance ++ - lowmemory ++description: > ++ Pointer compress the top level heap->strtable[] string table. On 32-bit ++ targets this saves 2 bytes per entry, e.g. for 256 entries 0.5kB. However, ++ the additional pointer compression code increases footprint by 200-300 ++ bytes. The option also reduces performance a little bit, so this should ++ be enabled when RAM is much more constrained than ROM. +diff --git a/dist/source/config/config-options/DUK_USE_STRTAB_RESIZE_CHECK_MASK.yaml b/dist/source/config/config-options/DUK_USE_STRTAB_RESIZE_CHECK_MASK.yaml +new file mode 100644 +index 0000000..383a434 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_STRTAB_RESIZE_CHECK_MASK.yaml +@@ -0,0 +1,16 @@ ++define: DUK_USE_STRTAB_RESIZE_CHECK_MASK ++introduced: 2.1.0 ++default: 255 ++tags: ++ - performance ++ - lowmemory ++description: > ++ Somewhat technical: bit mask (must be 2^N-1) used against heap->st_count to ++ determine the interval between string table resize checks. A resize check ++ is made when heap->st_count & DUK_USE_STRTAB_RESIZE_CHECK_MASK is zero. ++ ++ A large value makes string table grow/shrink checks less frequent. Usually ++ this has very little practical impact on memory performance. There are ++ corner cases, such as dereferencing a large number of strings simultaneously, ++ where this parameter affects how many new strings need to be inserted before ++ the string table shrinks to a more appropriate size. +diff --git a/dist/source/config/config-options/DUK_USE_STRTAB_SHRINK_LIMIT.yaml b/dist/source/config/config-options/DUK_USE_STRTAB_SHRINK_LIMIT.yaml +new file mode 100644 +index 0000000..372d916 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_STRTAB_SHRINK_LIMIT.yaml +@@ -0,0 +1,10 @@ ++define: DUK_USE_STRTAB_SHRINK_LIMIT ++introduced: 2.1.0 ++default: 6 # 6/16 = 0.375 ++tags: ++ - performance ++ - lowmemory ++description: > ++ Shrink top level strtable allocation when load factor reaches this value. ++ Expressed as a .4 fixed point; the load factor is computed as ++ floor((count / size) * 16.0), e.g. 8 means a load factor of 0.5. +diff --git a/dist/source/config/config-options/DUK_USE_STRTAB_TORTURE.yaml b/dist/source/config/config-options/DUK_USE_STRTAB_TORTURE.yaml +new file mode 100644 +index 0000000..86186e8 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_STRTAB_TORTURE.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_STRTAB_TORTURE ++introduced: 2.1.0 ++default: false ++tags: ++ - torture ++description: > ++ Resize string table (grow, shrink back) for every intern. Ensures string ++ table chaining is correct, and resize side effects are exercised on every ++ resize. +diff --git a/dist/source/config/config-options/DUK_USE_SYMBOL_BUILTIN.yaml b/dist/source/config/config-options/DUK_USE_SYMBOL_BUILTIN.yaml +new file mode 100644 +index 0000000..08a71aa +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_SYMBOL_BUILTIN.yaml +@@ -0,0 +1,10 @@ ++define: DUK_USE_SYMBOL_BUILTIN ++introduced: 2.0.0 ++default: true ++tags: ++ - ecmascript2015 ++description: > ++ Provide ES6 Symbol built-ins. ++ ++ Even with the built-ins disabled, symbols created by C code are still ++ supported. +diff --git a/dist/source/config/config-options/DUK_USE_TAILCALL.yaml b/dist/source/config/config-options/DUK_USE_TAILCALL.yaml +new file mode 100644 +index 0000000..55fc5c2 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_TAILCALL.yaml +@@ -0,0 +1,13 @@ ++define: DUK_USE_TAILCALL ++introduced: 1.0.0 ++conflicts: ++ - DUK_USE_NONSTD_FUNC_CALLER_PROPERTY ++default: true ++tags: ++ - ecmascript ++description: > ++ Enable tail call support (recommended). ++ ++ The non-standard function 'caller' property feature conflicts with ++ tailcalls quite severely so tailcalls must be disabled if the 'caller' ++ property is enabled. +diff --git a/dist/source/config/config-options/DUK_USE_TARGET_INFO.yaml b/dist/source/config/config-options/DUK_USE_TARGET_INFO.yaml +new file mode 100644 +index 0000000..6ea3305 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_TARGET_INFO.yaml +@@ -0,0 +1,10 @@ ++define: DUK_USE_TARGET_INFO ++introduced: 1.2.0 ++default: ++ string: "unknown" ++tags: ++ - debugger ++description: > ++ Define a freeform human readable string to describe the target device (e.g. ++ "Arduino Yun"). This string will be sent as part of version/target info in ++ the debugger protocol and shows up in the debugger UI. +diff --git a/dist/source/config/config-options/DUK_USE_TRACEBACKS.yaml b/dist/source/config/config-options/DUK_USE_TRACEBACKS.yaml +new file mode 100644 +index 0000000..53403c0 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_TRACEBACKS.yaml +@@ -0,0 +1,12 @@ ++define: DUK_USE_TRACEBACKS ++introduced: 1.0.0 ++default: true ++tags: ++ - ecmascript ++description: > ++ Record traceback data into ECMAScript error objects. ++ ++ When disabled, traceback data is not recorded, but fileName/lineNumber of ++ the error cause are still recorded as explicit properties. Disabling this ++ option reduces footprint and makes error handling a bit faster, at the cost ++ of less informative ECMAScript errors. +diff --git a/dist/source/config/config-options/DUK_USE_TRACEBACK_DEPTH.yaml b/dist/source/config/config-options/DUK_USE_TRACEBACK_DEPTH.yaml +new file mode 100644 +index 0000000..6c8093d +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_TRACEBACK_DEPTH.yaml +@@ -0,0 +1,13 @@ ++define: DUK_USE_TRACEBACK_DEPTH ++introduced: 1.0.0 ++default: 10 ++tags: ++ - ecmascript ++description: > ++ Define traceback collection depth. A large number causes tracedata to be ++ larger, taking more time to create and consuming more memory. A small ++ number makes tracebacks less useful. ++ ++ When tracebacks are disabled this option affects .fileName and .lineNumber ++ blaming. Error augmentation code won't look deeper than this value to find ++ a function to blame for error .fileName / .lineNumber. +diff --git a/dist/source/config/config-options/DUK_USE_UNALIGNED_ACCESSES_POSSIBLE.yaml b/dist/source/config/config-options/DUK_USE_UNALIGNED_ACCESSES_POSSIBLE.yaml +new file mode 100644 +index 0000000..ea66754 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_UNALIGNED_ACCESSES_POSSIBLE.yaml +@@ -0,0 +1,9 @@ ++define: DUK_USE_UNALIGNED_ACCESSES_POSSIBLE ++introduced: 1.0.0 ++removed: 1.4.0 ++default: false ++tags: ++ - portability ++description: > ++ Target architecture unaligned memory accesses (e.g. 32-bit integer access ++ from an arbitrary address). +diff --git a/dist/source/config/config-options/DUK_USE_UNDERSCORE_SETJMP.yaml b/dist/source/config/config-options/DUK_USE_UNDERSCORE_SETJMP.yaml +new file mode 100644 +index 0000000..39a0420 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_UNDERSCORE_SETJMP.yaml +@@ -0,0 +1,13 @@ ++define: DUK_USE_UNDERSCORE_SETJMP ++introduced: 1.1.0 ++removed: 1.5.0 ++default: false ++tags: ++ - portability ++description: > ++ Use _setjmp/_longjmp for long control transfers. This ensures signal ++ mask is not saved which can be a lot faster if setjmp/longjmp saves the ++ signal mask (this varies between platforms). See comments in ++ DUK_USE_SETJMP. ++ ++ Removed in Duktape 1.5.0: edit duk_config.h directly. +diff --git a/dist/source/config/config-options/DUK_USE_UNION_INITIALIZERS.yaml b/dist/source/config/config-options/DUK_USE_UNION_INITIALIZERS.yaml +new file mode 100644 +index 0000000..4cd5424 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_UNION_INITIALIZERS.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_UNION_INITIALIZERS ++introduced: 1.5.0 ++default: false ++tags: ++ - portability ++description: > ++ Compiler supports C99-style designated union initializers, e.g. ++ { .foo = 123 }. ++ ++ When disabled, Duktape sometimes needs to resort to less efficient struct ++ initializers for portability. +diff --git a/dist/source/config/config-options/DUK_USE_USER_DECLARE.yaml b/dist/source/config/config-options/DUK_USE_USER_DECLARE.yaml +new file mode 100644 +index 0000000..9393909 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_USER_DECLARE.yaml +@@ -0,0 +1,17 @@ ++define: DUK_USE_USER_DECLARE ++introduced: 1.0.0 ++removed: 2.4.0 ++default: ++ verbatim: '#define DUK_USE_USER_DECLARE() /* no user declarations */' ++tags: ++ - portability ++description: > ++ Provide declarations or additional preprocessor include directives to be ++ used when compiling Duktape. You may need this if you set ++ DUK_USE_PANIC_HANDLER to call your own panic handler function. You can ++ also use this option to cause additional files to be included when compiling ++ Duktape. ++ ++ NOTE: This is only needed if using the default autodetecting duk_config.h ++ header. When providing DUK_USE_xxx flags directly, you should just provide ++ all the necessary declarations in duk_config.h directly. +diff --git a/dist/source/config/config-options/DUK_USE_USER_INITJS.yaml b/dist/source/config/config-options/DUK_USE_USER_INITJS.yaml +new file mode 100644 +index 0000000..cb39223 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_USER_INITJS.yaml +@@ -0,0 +1,13 @@ ++define: DUK_USE_USER_INITJS ++introduced: 1.0.0 ++removed: 2.0.0 ++default: false ++tags: ++ - portability ++description: > ++ Provide a string to evaluate when a thread with new built-ins (a new global ++ environment) is created. This allows you to make minor modifications to the ++ global environment before any code is executed in it. The value must be a ++ string, e.g.:: -DDUK_OPT_USER_INITJS='"this.foo = 123"'. ++ ++ Errors in the initialization code result in a fatal error. +diff --git a/dist/source/config/config-options/DUK_USE_VALSTACK_GROW_SHIFT.yaml b/dist/source/config/config-options/DUK_USE_VALSTACK_GROW_SHIFT.yaml +new file mode 100644 +index 0000000..3f122a5 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_VALSTACK_GROW_SHIFT.yaml +@@ -0,0 +1,13 @@ ++define: DUK_USE_VALSTACK_GROW_SHIFT ++introduced: 2.2.0 ++default: 2 ++tags: ++ - performance ++ - lowmemory ++description: > ++ When growing the value stack, shift minimum size right by this amount to ++ come up with a slack which is allocated on top of the minimum required ++ size. The slack increases memory usage a bit, but reduces value stack ++ reallocations when the minimum size grows. A value of 2 means that a ++ 25% slack is used. Undefine to remove any slack, value stack is then ++ always grown by the minimum amount possible. +diff --git a/dist/source/config/config-options/DUK_USE_VALSTACK_LIMIT.yaml b/dist/source/config/config-options/DUK_USE_VALSTACK_LIMIT.yaml +new file mode 100644 +index 0000000..da9c352 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_VALSTACK_LIMIT.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_VALSTACK_LIMIT ++introduced: 2.2.0 ++default: 1000000 ++tags: ++ - misc ++description: > ++ Maximum value stack size. If value stack is about to be grown beyond this ++ size (the check includes a possible spare so the check isn't exact) reject ++ the resize. The limit must be low enough so that when multiplied by ++ sizeof(duk_tval), typically 8 or 16, the multiplication won't overflow ++ size_t. +diff --git a/dist/source/config/config-options/DUK_USE_VALSTACK_SHRINK_CHECK_SHIFT.yaml b/dist/source/config/config-options/DUK_USE_VALSTACK_SHRINK_CHECK_SHIFT.yaml +new file mode 100644 +index 0000000..a06162a +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_VALSTACK_SHRINK_CHECK_SHIFT.yaml +@@ -0,0 +1,13 @@ ++define: DUK_USE_VALSTACK_SHRINK_CHECK_SHIFT ++introduced: 2.2.0 ++default: 2 ++tags: ++ - performance ++ - lowmemory ++description: > ++ When doing a value stack shrink check, skip shrinking if the difference ++ between the minimum reserve and allocated size is less than ++ (curr_size >> DUK_USE_VALSTACK_SHRINK_CHECK_SHIFT) bytes. A value of 2 ++ means that the difference must be at least 25% for a shrink to happen. ++ If undefined, value stack is always shrunk to the minimum reserved size ++ with no slack. +diff --git a/dist/source/config/config-options/DUK_USE_VALSTACK_SHRINK_SLACK_SHIFT.yaml b/dist/source/config/config-options/DUK_USE_VALSTACK_SHRINK_SLACK_SHIFT.yaml +new file mode 100644 +index 0000000..341e445 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_VALSTACK_SHRINK_SLACK_SHIFT.yaml +@@ -0,0 +1,10 @@ ++define: DUK_USE_VALSTACK_SHRINK_SLACK_SHIFT ++introduced: 2.2.0 ++default: 4 ++tags: ++ - performance ++ - lowmemory ++description: > ++ When shrinking, leave (curr_size >> DUK_USE_VALSTACK_SHRINK_SLACK_SHIFT) ++ bytes as a slack. This shift count must be larger than ++ DUK_USE_VALSTACK_SHRINK_CHECK_SHIFT. +diff --git a/dist/source/config/config-options/DUK_USE_VALSTACK_UNSAFE.yaml b/dist/source/config/config-options/DUK_USE_VALSTACK_UNSAFE.yaml +new file mode 100644 +index 0000000..4c0f881 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_VALSTACK_UNSAFE.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_VALSTACK_UNSAFE ++introduced: 1.2.0 ++default: false ++tags: ++ - performance ++ - experimental ++description: > ++ Don't check allocated value stack size in operations like value stack ++ pushes. Improves performance of API calls but causes unsafe memory ++ behavior (e.g. a segfault) when user code pushes beyond "checked" value ++ stack size. +diff --git a/dist/source/config/config-options/DUK_USE_VARIADIC_MACROS.yaml b/dist/source/config/config-options/DUK_USE_VARIADIC_MACROS.yaml +new file mode 100644 +index 0000000..be08169 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_VARIADIC_MACROS.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_VARIADIC_MACROS ++introduced: 1.0.0 ++default: true ++tags: ++ - portability ++description: > ++ Compiler supports C99-style variadic macros. Highly recommended to enable ++ when possible. ++ ++ When disabled, Duktape needs to resort to various hacks to work around ++ missing support for variadic macros. +diff --git a/dist/source/config/config-options/DUK_USE_VERBOSE_ERRORS.yaml b/dist/source/config/config-options/DUK_USE_VERBOSE_ERRORS.yaml +new file mode 100644 +index 0000000..d5c7b12 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_VERBOSE_ERRORS.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_VERBOSE_ERRORS ++introduced: 1.0.0 ++default: true ++tags: ++ - ecmascript ++description: > ++ Provide error message strings and file/line information for errors generated ++ by Duktape. ++ ++ When disabled, reduces footprint at the cost of much less informative ++ ECMAScript errors. +diff --git a/dist/source/config/config-options/DUK_USE_VERBOSE_EXECUTOR_ERRORS.yaml b/dist/source/config/config-options/DUK_USE_VERBOSE_EXECUTOR_ERRORS.yaml +new file mode 100644 +index 0000000..a23cc9c +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_VERBOSE_EXECUTOR_ERRORS.yaml +@@ -0,0 +1,10 @@ ++define: DUK_USE_VERBOSE_EXECUTOR_ERRORS ++introduced: 1.0.0 ++default: true ++tags: ++ - ecmascript ++description: > ++ Use verbose error messages in bytecode executor (recommended). ++ ++ When disabled, reduces footprint slightly at the cost of more obscure ++ error messages. +diff --git a/dist/source/config/config-options/DUK_USE_VOLUNTARY_GC.yaml b/dist/source/config/config-options/DUK_USE_VOLUNTARY_GC.yaml +new file mode 100644 +index 0000000..530df47 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_VOLUNTARY_GC.yaml +@@ -0,0 +1,16 @@ ++define: DUK_USE_VOLUNTARY_GC ++introduced: 1.0.0 ++default: true ++tags: ++ - gc ++ - memory ++description: > ++ Enable voluntary periodic mark-and-sweep collection. ++ ++ When disabled, a mark-and-sweep collection is still triggered in an ++ out-of-memory condition (known as "emergency GC". When disabling this ++ option it's recommended to use reference counting which collects all ++ non-cyclical garbage. Application code should also request an explicit ++ garbage collection from time to time when appropriate. When this option ++ is disabled, Duktape will have no garbage collection pauses in ordinary ++ use, which is useful for timing sensitive applications like games. +diff --git a/dist/source/config/config-options/DUK_USE_ZERO_BUFFER_DATA.yaml b/dist/source/config/config-options/DUK_USE_ZERO_BUFFER_DATA.yaml +new file mode 100644 +index 0000000..4015c93 +--- /dev/null ++++ b/dist/source/config/config-options/DUK_USE_ZERO_BUFFER_DATA.yaml +@@ -0,0 +1,11 @@ ++define: DUK_USE_ZERO_BUFFER_DATA ++introduced: 1.0.0 ++default: true ++tags: ++ - memory ++ - ecmascript ++description: > ++ Zero data are of newly allocated buffer values (recommended). ++ ++ When disabled, buffers are not zeroed and may contain arbitrary data. ++ Disabling this option only makes sense for performance reasons. +diff --git a/dist/source/config/examples/compliance.yaml b/dist/source/config/examples/compliance.yaml +new file mode 100644 +index 0000000..018add3 +--- /dev/null ++++ b/dist/source/config/examples/compliance.yaml +@@ -0,0 +1,18 @@ ++# Enable compliant behavior, defaults favor "real world" compatibility. ++ ++DUK_USE_NONSTD_ARRAY_SPLICE_DELCOUNT: false ++DUK_USE_NONSTD_FUNC_CALLER_PROPERTY: false ++DUK_USE_NONSTD_FUNC_SOURCE_PROPERTY: false ++DUK_USE_NONSTD_FUNC_STMT: false ++DUK_USE_NONSTD_GETTER_KEY_ARGUMENT: false ++DUK_USE_NONSTD_JSON_ESC_U2028_U2029: false ++DUK_USE_NONSTD_SETTER_KEY_ARGUMENT: false ++DUK_USE_NONSTD_STRING_FROMCHARCODE_32BIT: false ++DUK_USE_ES6_REGEXP_SYNTAX: false # for ES5 compliance, disable ES6-only regexp extra syntax ++ ++# These Array fast paths assume Array.prototype has no inherited index ++# properties which might affect reads/writes. Such properties are very ++# rare so this assumption is usually a good one; for strict compliance, ++# disable the fast paths. ++DUK_USE_ARRAY_PROP_FASTPATH: false ++DUK_USE_ARRAY_FASTPATH: false +diff --git a/dist/source/config/examples/debugger_support.yaml b/dist/source/config/examples/debugger_support.yaml +new file mode 100644 +index 0000000..4986adc +--- /dev/null ++++ b/dist/source/config/examples/debugger_support.yaml +@@ -0,0 +1,24 @@ ++# Enable debugger support in general, also needs interrupt support. ++DUK_USE_INTERRUPT_COUNTER: true ++DUK_USE_DEBUGGER_SUPPORT: true ++ ++# Basic set of Notifys. ++DUK_USE_DEBUGGER_THROW_NOTIFY: true ++ ++# Automatically pause on an uncaught error about to be thrown. ++DUK_USE_DEBUGGER_PAUSE_UNCAUGHT: true ++ ++# Enable DumpHeap command (experimental). ++DUK_USE_DEBUGGER_DUMPHEAP: true ++ ++# Enable object inspection commands: GetHeapObjInfo, GetObjPropDesc, ++# GetObjPropDescRange. ++DUK_USE_DEBUGGER_INSPECT: true ++ ++# Transport torture testing: enable when testing your transport ++# implementation. ++DUK_USE_DEBUGGER_TRANSPORT_TORTURE: false ++ ++# NOTE: If you're using the Duktape command line utility for debugging ++# (duk --debugger ...), remember to enable debugger support for the command ++# line tool when compiling it: -DDUK_CMDLINE_DEBUGGER_SUPPORT. +diff --git a/dist/source/config/examples/disable_bufferobjects.yaml b/dist/source/config/examples/disable_bufferobjects.yaml +new file mode 100644 +index 0000000..23b85a8 +--- /dev/null ++++ b/dist/source/config/examples/disable_bufferobjects.yaml +@@ -0,0 +1,3 @@ ++# Disable support for typed arrays and Node.js Buffer. Duktape will still ++# support plain buffers and Duktape.Buffer. ++DUK_USE_BUFFEROBJECT_SUPPORT: false +diff --git a/dist/source/config/examples/disable_es6.yaml b/dist/source/config/examples/disable_es6.yaml +new file mode 100644 +index 0000000..26c3411 +--- /dev/null ++++ b/dist/source/config/examples/disable_es6.yaml +@@ -0,0 +1,5 @@ ++# Disable ES2015 features. ++ ++DUK_USE_ES6_OBJECT_PROTO_PROPERTY: false ++DUK_USE_ES6_OBJECT_SETPROTOTYPEOFS: false ++DUK_USE_ES6_PROXY: false +diff --git a/dist/source/config/examples/enable_debug_print0.yaml b/dist/source/config/examples/enable_debug_print0.yaml +new file mode 100644 +index 0000000..1f8392e +--- /dev/null ++++ b/dist/source/config/examples/enable_debug_print0.yaml +@@ -0,0 +1,3 @@ ++# Enable debug level 0. ++DUK_USE_DEBUG: true ++DUK_USE_DEBUG_LEVEL: 0 +diff --git a/dist/source/config/examples/enable_debug_print1.yaml b/dist/source/config/examples/enable_debug_print1.yaml +new file mode 100644 +index 0000000..00f3c7e +--- /dev/null ++++ b/dist/source/config/examples/enable_debug_print1.yaml +@@ -0,0 +1,3 @@ ++# Enable debug level 1. ++DUK_USE_DEBUG: true ++DUK_USE_DEBUG_LEVEL: 1 +diff --git a/dist/source/config/examples/enable_debug_print2.yaml b/dist/source/config/examples/enable_debug_print2.yaml +new file mode 100644 +index 0000000..45ac617 +--- /dev/null ++++ b/dist/source/config/examples/enable_debug_print2.yaml +@@ -0,0 +1,3 @@ ++# Enable debug level 2. ++DUK_USE_DEBUG: true ++DUK_USE_DEBUG_LEVEL: 2 +diff --git a/dist/source/config/examples/enable_fastint.yaml b/dist/source/config/examples/enable_fastint.yaml +new file mode 100644 +index 0000000..16ff5c9 +--- /dev/null ++++ b/dist/source/config/examples/enable_fastint.yaml +@@ -0,0 +1,3 @@ ++# Enable fastint support. ++ ++DUK_USE_FASTINT: true +diff --git a/dist/source/config/examples/low_memory.yaml b/dist/source/config/examples/low_memory.yaml +new file mode 100644 +index 0000000..6059b32 +--- /dev/null ++++ b/dist/source/config/examples/low_memory.yaml +@@ -0,0 +1,158 @@ ++# Base configuration for low memory environments, see ++# doc/low-memory.rst: ++# ++# - Strips verbose errors etc ++# - Strips some ES2015 features like Proxy support (re-enable if needed) ++# - Strips some Duktape custom feature like JX/JC; keeps e.g. RegExp ++# and other standard parts ++# - Strips some commonly unnecessary API calls like bytecode dump/load ++# - Does not enable pointer compression or external strings: these ++# need target specific support code ++# - Does not enable ROM string/object support by default, enable manually ++# ++ ++# Consider switching alignment to align-by-1 or align-by-4 for more compact ++# memory layout if the architecture supports it. ++#DUK_USE_ALIGN_BY: 1 ++ ++# With the vast majority of compilers some of the 'undefined behavior' ++# assumptions are fine, and produce smaller and faster code, so enable ++# by default for lowmem targets. You may need to disable this for some ++# compilers. ++DUK_USE_ALLOW_UNDEFINED_BEHAVIOR: true ++ ++DUK_USE_PREFER_SIZE: true ++DUK_USE_EXEC_PREFER_SIZE: true ++DUK_USE_FAST_REFCOUNT_DEFAULT: false ++DUK_USE_AUGMENT_ERROR_CREATE: false ++DUK_USE_AUGMENT_ERROR_THROW: false ++DUK_USE_TRACEBACKS: false ++DUK_USE_ERRCREATE: false ++DUK_USE_ERRTHROW: false ++DUK_USE_VERBOSE_ERRORS: false ++DUK_USE_PARANOID_ERRORS: true ++DUK_USE_FATAL_MAXLEN: 64 ++DUK_USE_VERBOSE_EXECUTOR_ERRORS: false # <100 bytes footprint ++DUK_USE_DEBUGGER_SUPPORT: false # must be disabled if DUK_USE_PC2LINE is disabled ++DUK_USE_PC2LINE: false ++DUK_USE_LEXER_SLIDING_WINDOW: false ++DUK_USE_JSON_STRINGIFY_FASTPATH: false ++DUK_USE_JSON_QUOTESTRING_FASTPATH: false ++DUK_USE_JSON_DECSTRING_FASTPATH: false ++DUK_USE_JSON_DECNUMBER_FASTPATH: false ++DUK_USE_JSON_EATWHITE_FASTPATH: false ++DUK_USE_BASE64_FASTPATH: false ++DUK_USE_HEX_FASTPATH: false ++DUK_USE_IDCHAR_FASTPATH: false ++DUK_USE_ARRAY_PROP_FASTPATH: false ++DUK_USE_ARRAY_FASTPATH: false ++DUK_USE_BYTECODE_DUMP_SUPPORT: false ++DUK_USE_JX: false ++DUK_USE_JC: false ++#DUK_USE_REGEXP_SUPPORT: false ++DUK_USE_DEBUG_BUFSIZE: 2048 ++DUK_USE_LIGHTFUNC_BUILTINS: true ++ ++# Grow value stack without any slack, and shrink to minimum reserved size with ++# no slack. Increases allocation traffic but avoids allocating space not ++# actually needed. ++DUK_USE_VALSTACK_GROW_SHIFT: false ++DUK_USE_VALSTACK_SHRINK_CHECK_SHIFT: false ++DUK_USE_VALSTACK_SHRINK_SLACK_SHIFT: false ++ ++# Using the same minsize and maxsize drops code footprint by around 400 bytes ++# (string table resize code is omitted). Enabling DUK_USE_STRTAB_PTRCOMP ++# saves some RAM (two bytes per strtab entry) at the cost of 200-300 bytes of ++# code footprint. ++DUK_USE_STRTAB_MINSIZE: 128 ++DUK_USE_STRTAB_MAXSIZE: 128 ++DUK_USE_STRTAB_SHRINK_LIMIT: 0 # doesn't matter if minsize==masize ++DUK_USE_STRTAB_GROW_LIMIT: 65536 # -""- ++DUK_USE_STRTAB_RESIZE_CHECK_MASK: 255 # -""- ++#DUK_USE_STRTAB_PTRCOMP: true # sometimes useful with pointer compression ++ ++# Disable literal pinning and litcache. ++DUK_USE_LITCACHE_SIZE: false ++ ++DUK_USE_HSTRING_ARRIDX: false ++DUK_USE_HSTRING_LAZY_CLEN: false # non-lazy charlen is smaller ++ ++# Only add a hash table for quite large objects to conserve memory. Even ++# lower memory targets usually drop hash part support entirely. ++DUK_USE_HOBJECT_HASH_PROP_LIMIT: 64 ++ ++DUK_USE_HOBJECT_ARRAY_ABANDON_MINSIZE: 32 ++ ++# Disable freelist caching. ++DUK_USE_CACHE_ACTIVATION: false ++DUK_USE_CACHE_CATCHER: false ++ ++# Consider using pointer compression, see doc/low-memory.rst. ++#DUK_USE_REFCOUNT16: true ++#DUK_USE_REFCOUNT32: false ++#DUK_USE_STRHASH16: true ++#DUK_USE_STRLEN16: true ++#DUK_USE_BUFLEN16: true ++#DUK_USE_OBJSIZES16: true ++#DUK_USE_HSTRING_CLEN: false ++#DUK_USE_HSTRING_LAZY_CLEN: false ++#DUK_USE_HOBJECT_HASH_PART: false ++#DUK_USE_HEAPPTR16 ++#DUK_USE_HEAPPTR_DEC16 ++#DUK_USE_HEAPPTR_ENC16 ++ ++# Consider using external strings, see doc/low_memory.rst. ++#DUK_USE_EXTERNAL_STRINGS: true ++#DUK_USE_EXTSTR_INTERN_CHECK ++#DUK_USE_EXTSTR_FREE ++ ++# Consider removing Node.js Buffer and ES2015 typed array support if not ++# needed (about 10 kB code footprint difference on x64) ++DUK_USE_BUFFEROBJECT_SUPPORT: false ++ ++# Consider to reduce code footprint at the expense of more erratic RAM usage ++#DUK_USE_REFERENCE_COUNTING: false ++#DUK_USE_DOUBLE_LINKED_HEAP: false ++ ++# Consider to reduce code footprint at the expense of less safeguards against ++# bugs in calling C code. ++DUK_USE_VALSTACK_UNSAFE: true ++ ++# Disable optimizations for case insensitive regexps. If code involves ++# case insensitive regexps with large character classes, consider enabling ++# at least DUK_USE_REGEXP_CANON_BITMAP. ++DUK_USE_REGEXP_CANON_WORKAROUND: false # very large footprint (~128kB) ++DUK_USE_REGEXP_CANON_BITMAP: false # small footprint (~300-400 bytes) ++ ++# Consider using ROM strings/objects to reduce footprint, see doc/low_memory.rst. ++# ROM strings/objects reduce startup RAM usage at the expense of code footprint ++# and some compliance. ++#DUK_USE_ROM_STRINGS: true ++#DUK_USE_ROM_OBJECTS: true ++#DUK_USE_ROM_GLOBAL_INHERIT: true # select inherit or clone; inherit recommended ++#DUK_USE_ROM_GLOBAL_CLONE: false ++ ++# Function footprint size reduction. ++DUK_USE_FUNC_NAME_PROPERTY: true # compliance ++DUK_USE_FUNC_FILENAME_PROPERTY: false # non-standard, can be removed ++ ++# Consider these; disabled by default because they don't impact E5 compliance. ++DUK_USE_ES6: false ++DUK_USE_ES7: false ++DUK_USE_ES8: false ++DUK_USE_ES9: false ++DUK_USE_BASE64_SUPPORT: false ++DUK_USE_HEX_SUPPORT: false ++DUK_USE_COROUTINE_SUPPORT: false ++DUK_USE_SOURCE_NONBMP: false # <300 bytes footprint ++DUK_USE_ES6_PROXY: false # roughly 2kB footprint ++DUK_USE_ES7_EXP_OPERATOR: false # pulls in pow() ++DUK_USE_ENCODING_BUILTINS: false ++DUK_USE_PERFORMANCE_BUILTIN: false ++DUK_USE_ES6_UNICODE_ESCAPE: false ++DUK_USE_HTML_COMMENTS: false ++DUK_USE_SHEBANG_COMMENTS: false ++DUK_USE_REFLECT_BUILTIN: false ++DUK_USE_SYMBOL_BUILTIN: false ++DUK_USE_CBOR_SUPPORT: false ++DUK_USE_CBOR_BUILTIN: false +diff --git a/dist/source/config/examples/low_memory_strip.yaml b/dist/source/config/examples/low_memory_strip.yaml +new file mode 100644 +index 0000000..1aefac1 +--- /dev/null ++++ b/dist/source/config/examples/low_memory_strip.yaml +@@ -0,0 +1,49 @@ ++# More stripping for low memory environments, removing e.g. RegExp support, ++# coroutine support, etc. ++ ++DUK_USE_BUFFEROBJECT_SUPPORT: false ++ ++DUK_USE_REGEXP_SUPPORT: false ++ ++DUK_USE_REFERENCE_COUNTING: false ++DUK_USE_DOUBLE_LINKED_HEAP: false ++ ++# Consider to reduce code footprint at the expense of less safeguards against ++# bugs in calling C code ++DUK_USE_VALSTACK_UNSAFE: true ++ ++# Short term workaround with large footprint, disable. ++DUK_USE_REGEXP_CANON_WORKAROUND: false ++ ++# Coroutine support has about 2kB footprint. ++DUK_USE_COROUTINE_SUPPORT: false ++ ++# Finalizer support has about 0.8kB footprint. ++DUK_USE_FINALIZER_SUPPORT: false ++ ++# ES2015 Proxy has about 2kB footprint. ++DUK_USE_ES6_PROXY: false ++ ++# Don't support non-BMP characters in source code (UTF-8 otherwise OK). ++DUK_USE_SOURCE_NONBMP: false ++ ++# Don't include even .name property for functions. ++DUK_USE_FUNC_NAME_PROPERTY: false ++ ++# Remove built-in bindings. ++DUK_USE_MATH_BUILTIN: false ++DUK_USE_DATE_BUILTIN: false ++DUK_USE_ARRAY_BUILTIN: false ++DUK_USE_STRING_BUILTIN: false ++DUK_USE_BOOLEAN_BUILTIN: false ++DUK_USE_NUMBER_BUILTIN: false ++DUK_USE_FUNCTION_BUILTIN: false ++DUK_USE_OBJECT_BUILTIN: false ++DUK_USE_DUKTAPE_BUILTIN: false ++DUK_USE_JSON_BUILTIN: false ++DUK_USE_ENCODING_BUILTINS: false ++DUK_USE_REFLECT_BUILTIN: false ++DUK_USE_JSON_SUPPORT: false # also disables JSON support for C API ++DUK_USE_GLOBAL_BUILTIN: false ++DUK_USE_CBOR_SUPPORT: false ++DUK_USE_CBOR_BUILTIN: false +diff --git a/dist/source/config/examples/performance_sensitive.yaml b/dist/source/config/examples/performance_sensitive.yaml +new file mode 100644 +index 0000000..f1da1e4 +--- /dev/null ++++ b/dist/source/config/examples/performance_sensitive.yaml +@@ -0,0 +1,38 @@ ++# Base configuration for performance sensitive environments, see ++# doc/performance-sensitive.rst. ++ ++# You should choose the fastest setjmp/longjmp for your platform. ++ ++# With the vast majority of compilers some of the 'undefined behavior' ++# assumptions are fine, and produce smaller and faster code, so enable ++# by default for performance oriented targets. You may need to disable ++# this for some compilers. ++DUK_USE_ALLOW_UNDEFINED_BEHAVIOR: true ++ ++DUK_USE_PREFER_SIZE: false ++DUK_USE_PACKED_TVAL: false # packed duk_tval slower in most cases ++DUK_USE_FASTINT: true ++DUK_USE_VALSTACK_UNSAFE: true ++DUK_USE_FAST_REFCOUNT_DEFAULT: true ++ ++DUK_USE_JSON_STRINGIFY_FASTPATH: true # not fully portable right now ++DUK_USE_JSON_QUOTESTRING_FASTPATH: true ++DUK_USE_JSON_DECSTRING_FASTPATH: true ++DUK_USE_JSON_DECNUMBER_FASTPATH: true ++DUK_USE_JSON_EATWHITE_FASTPATH: true ++DUK_USE_BASE64_FASTPATH: true ++DUK_USE_HEX_FASTPATH: true ++DUK_USE_IDCHAR_FASTPATH: true ++DUK_USE_ARRAY_PROP_FASTPATH: true ++DUK_USE_ARRAY_FASTPATH: true ++DUK_USE_INTERRUPT_COUNTER: false ++ ++DUK_USE_DEBUGGER_SUPPORT: false ++ ++DUK_USE_STRHASH_DENSE: false ++DUK_USE_STRHASH_SKIP_SHIFT: 5 # may be able to reduce ++#DUK_USE_EXEC_FUN_LOCAL: false # test both values, marginal benefit ++ ++DUK_USE_LITCACHE_SIZE: 1024 ++ ++DUK_USE_REGEXP_CANON_WORKAROUND: true # high footprint impact (128kB), enabled until a better solution +diff --git a/dist/source/config/examples/rom_builtins.yaml b/dist/source/config/examples/rom_builtins.yaml +new file mode 100644 +index 0000000..a5ba4a8 +--- /dev/null ++++ b/dist/source/config/examples/rom_builtins.yaml +@@ -0,0 +1,19 @@ ++# Move built-in strings and objects to ROM (read-only data section) to reduce ++# RAM usage of Duktape heaps; all heaps will share the same built-in strings ++# and objects, except for the global object which is writable. The downside ++# is that built-in objects (other than global object) will be read-only. ++ ++# These should be used together. ++DUK_USE_ROM_STRINGS: true ++DUK_USE_ROM_OBJECTS: true ++ ++# Provide a writable global object: ++# - DUK_USE_ROM_GLOBAL_INHERIT: empty RAM global object inherits from ROM ++# global object, uses very little RAM. ++# - DUK_USE_ROM_GLOBAL_CLONE: ROM global object is cloned into RAM, more ++# compliant but uses more RAM. ++# ++# If both are set to 'false', global object will be non-writable which ++# may surprise users. ++DUK_USE_ROM_GLOBAL_INHERIT: true ++DUK_USE_ROM_GLOBAL_CLONE: false +diff --git a/dist/source/config/examples/security_sensitive.yaml b/dist/source/config/examples/security_sensitive.yaml +new file mode 100644 +index 0000000..bdcbb81 +--- /dev/null ++++ b/dist/source/config/examples/security_sensitive.yaml +@@ -0,0 +1,17 @@ ++# Base configuration for security sensitive environments. ++ ++# Avoid summary of object/key for rejected property operations. May be ++# relevant if keys contain potentially sensitive information. ++DUK_USE_PARANOID_ERRORS: true ++ ++# Disable tracebacks, minimizes attacker knowledge of call chains. Access ++# to the internal error _Tracedata property provides access to all functions ++# in the call chain (even when they're otherwise not visible to sandboxed ++# code). ++DUK_USE_TRACEBACKS: false ++ ++# Dense string hashing may be useful against accidental string hash collisions. ++# This won't prevent an attacker from finding intentional collisions. ++DUK_USE_STRHASH_DENSE: true ++ ++# TBD +diff --git a/dist/source/config/examples/shallow_c_stack.yaml b/dist/source/config/examples/shallow_c_stack.yaml +new file mode 100644 +index 0000000..4bbf8b4 +--- /dev/null ++++ b/dist/source/config/examples/shallow_c_stack.yaml +@@ -0,0 +1,10 @@ ++# Reduce recursion limits for shallow C stack targets. ++# These values are based on the (removed) DUK_USE_DEEP_C_STACK option. ++ ++DUK_USE_NATIVE_CALL_RECLIMIT: 60 ++DUK_USE_COMPILER_RECLIMIT: 50 ++DUK_USE_REGEXP_COMPILER_RECLIMIT: 100 ++DUK_USE_REGEXP_EXECUTOR_RECLIMIT: 100 ++DUK_USE_JSON_ENC_RECLIMIT: 100 ++DUK_USE_JSON_DEC_RECLIMIT: 100 ++DUK_USE_MARK_AND_SWEEP_RECLIMIT: 32 +diff --git a/dist/source/config/examples/timing_sensitive.yaml b/dist/source/config/examples/timing_sensitive.yaml +new file mode 100644 +index 0000000..dd11081 +--- /dev/null ++++ b/dist/source/config/examples/timing_sensitive.yaml +@@ -0,0 +1,6 @@ ++# Base configuration for timing sensitive environments, see ++# doc/timing-sensitive.rst: ++ ++DUK_USE_MARK_AND_SWEEP: true ++DUK_USE_REFERENCE_COUNTING: true ++DUK_USE_VOLUNTARY_GC: false +diff --git a/dist/source/config/feature-options/DUK_OPT_ASSERTIONS.yaml b/dist/source/config/feature-options/DUK_OPT_ASSERTIONS.yaml +new file mode 100644 +index 0000000..497c2cc +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_ASSERTIONS.yaml +@@ -0,0 +1,8 @@ ++define: DUK_OPT_ASSERTIONS ++introduced: 1.0.0 ++tags: ++ - development ++ - debug ++description: > ++ Enable internal assert checks. These slow down execution considerably ++ so only use when debugging. +diff --git a/dist/source/config/feature-options/DUK_OPT_BUFFEROBJECT_SUPPORT.yaml b/dist/source/config/feature-options/DUK_OPT_BUFFEROBJECT_SUPPORT.yaml +new file mode 100644 +index 0000000..1cb1dae +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_BUFFEROBJECT_SUPPORT.yaml +@@ -0,0 +1,8 @@ ++define: DUK_OPT_BUFFEROBJECT_SUPPORT ++introduced: 1.3.0 ++tags: ++ - ecmascript6 ++description: > ++ Enable support for Khronos/ES6 typed arrays and Node.js Buffer objects. ++ This adds about 8-9 kB of code footprint on x64. When disabled, Duktape ++ custom plain buffers and Duktape.Buffer are still supported. +diff --git a/dist/source/config/feature-options/DUK_OPT_BUFLEN16.yaml b/dist/source/config/feature-options/DUK_OPT_BUFLEN16.yaml +new file mode 100644 +index 0000000..682b1f7 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_BUFLEN16.yaml +@@ -0,0 +1,7 @@ ++define: DUK_OPT_BUFLEN16 ++introduced: 1.1.0 ++tags: ++ - lowmemory ++ - experimental ++description: > ++ Use a 16-bit buffer length field (for low memory environments). +diff --git a/dist/source/config/feature-options/DUK_OPT_DATAPTR16.yaml b/dist/source/config/feature-options/DUK_OPT_DATAPTR16.yaml +new file mode 100644 +index 0000000..e86d313 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_DATAPTR16.yaml +@@ -0,0 +1,20 @@ ++define: DUK_OPT_DATAPTR16 ++introduced: 1.1.0 ++related: ++ - DUK_OPT_DATAPTR_ENC16 ++ - DUK_OPT_DATAPTR_DEC16 ++tags: ++ - lowmemory ++ - experimental ++description: > ++ Enable "compression" of arbitrary data pointers into an unsigned 16-bit ++ value. Use together with DUK_OPT_DATAPTR_ENC16 and DUK_OPT_DATAPTR_DEC16. ++ ++ Pointers compressed are any void pointers in C code, not just the Duktape ++ heap. Also NULL pointer must encode and decode correctly. ++ ++ Currently it is required that NULL encodes to integer 0, and integer ++ 0 decodes to NULL. No other pointer can be encoded to 0. ++ ++ NOTE: This feature option is currently unimplemented, i.e. Duktape won't ++ compress any data pointers at the moment. +diff --git a/dist/source/config/feature-options/DUK_OPT_DATAPTR_DEC16.yaml b/dist/source/config/feature-options/DUK_OPT_DATAPTR_DEC16.yaml +new file mode 100644 +index 0000000..9a1c693 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_DATAPTR_DEC16.yaml +@@ -0,0 +1,12 @@ ++define: DUK_OPT_DATAPTR_DEC16 ++introduced: 1.1.0 ++requires: ++ - DUK_OPT_DATAPTR16 ++tags: ++ - lowmemory ++ - experimental ++description: > ++ Use together with DUK_OPT_DATAPTR16 for arbitrary data pointer compression. ++ DUK_OPT_DATAPTR_DEC16(udata,x) is a macro with a userdata and duk_uint16_t ++ argument, and a void ptr return value. The userdata argument is the heap ++ userdata value given at heap creation. +diff --git a/dist/source/config/feature-options/DUK_OPT_DATAPTR_ENC16.yaml b/dist/source/config/feature-options/DUK_OPT_DATAPTR_ENC16.yaml +new file mode 100644 +index 0000000..14684bf +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_DATAPTR_ENC16.yaml +@@ -0,0 +1,14 @@ ++define: DUK_OPT_DATAPTR_ENC16 ++introduced: 1.1.0 ++requires: ++ - DUK_OPT_DATAPTR16 ++tags: ++ - lowmemory ++ - experimental ++description: > ++ Use together with DUK_OPT_DATAPTR16 for arbitrary data pointer compression. ++ DUK_OPT_DATAPTR_ENC16(udata,p) is a macro with a userdata and void ptr ++ argument, and a duk_uint16_t return value. The userdata argument is the ++ heap userdata value given at heap creation. Currently it is required that ++ NULL encodes to integer 0, and integer 0 decodes to NULL. No other pointer ++ can be encoded to 0. +diff --git a/dist/source/config/feature-options/DUK_OPT_DDDPRINT.yaml b/dist/source/config/feature-options/DUK_OPT_DDDPRINT.yaml +new file mode 100644 +index 0000000..ebdc0d5 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_DDDPRINT.yaml +@@ -0,0 +1,9 @@ ++define: DUK_OPT_DDDPRINT ++introduced: 1.0.0 ++requires: ++ - DUK_OPT_DEBUG ++tags: ++ - debug ++description: > ++ Enable even more debug printouts. Not recommended unless you have ++ grep handy. +diff --git a/dist/source/config/feature-options/DUK_OPT_DDPRINT.yaml b/dist/source/config/feature-options/DUK_OPT_DDPRINT.yaml +new file mode 100644 +index 0000000..26b7699 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_DDPRINT.yaml +@@ -0,0 +1,8 @@ ++define: DUK_OPT_DDPRINT ++introduced: 1.0.0 ++requires: ++ - DUK_OPT_DEBUG ++tags: ++ - debug ++description: > ++ Enable more debug printouts. +diff --git a/dist/source/config/feature-options/DUK_OPT_DEBUG.yaml b/dist/source/config/feature-options/DUK_OPT_DEBUG.yaml +new file mode 100644 +index 0000000..9c68c77 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_DEBUG.yaml +@@ -0,0 +1,7 @@ ++define: DUK_OPT_DEBUG ++introduced: 1.0.0 ++tags: ++ - debug ++description: > ++ Enable debug code in Duktape internals. Without this option other ++ debugging options (such as DUK_OPT_DPRINT) have no effect. +diff --git a/dist/source/config/feature-options/DUK_OPT_DEBUGGER_DUMPHEAP.yaml b/dist/source/config/feature-options/DUK_OPT_DEBUGGER_DUMPHEAP.yaml +new file mode 100644 +index 0000000..d501ce8 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_DEBUGGER_DUMPHEAP.yaml +@@ -0,0 +1,10 @@ ++define: DUK_OPT_DEBUGGER_DUMPHEAP ++introduced: 1.2.0 ++requires: ++ - DUK_OPT_DEBUGGER_SUPPORT ++tags: ++ - debugger ++description: > ++ Support the DumpHeap command. This is optional because the command is not ++ always needed. The command also has a relatively large footprint (about ++ 10% of debugger code); in absolute terms it's about 1kB of code footprint. +diff --git a/dist/source/config/feature-options/DUK_OPT_DEBUGGER_FWD_LOGGING.yaml b/dist/source/config/feature-options/DUK_OPT_DEBUGGER_FWD_LOGGING.yaml +new file mode 100644 +index 0000000..87497d7 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_DEBUGGER_FWD_LOGGING.yaml +@@ -0,0 +1,11 @@ ++define: DUK_OPT_DEBUGGER_FWD_LOGGING ++introduced: 1.2.0 ++requires: ++ - DUK_OPT_DEBUGGER_SUPPORT ++tags: ++ - debugger ++description: > ++ Forward log writes using the built-in logging framework to the debug client. ++ Forwarding happens from the Duktape.Logger.prototype.info() etc calls before ++ the raw() function is called, so that logging is forwarded even if you ++ replace the backend. +diff --git a/dist/source/config/feature-options/DUK_OPT_DEBUGGER_FWD_PRINTALERT.yaml b/dist/source/config/feature-options/DUK_OPT_DEBUGGER_FWD_PRINTALERT.yaml +new file mode 100644 +index 0000000..afb5e64 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_DEBUGGER_FWD_PRINTALERT.yaml +@@ -0,0 +1,9 @@ ++define: DUK_OPT_DEBUGGER_FWD_PRINTALERT ++introduced: 1.2.0 ++requires: ++ - DUK_OPT_DEBUGGER_SUPPORT ++tags: ++ - debugger ++description: > ++ Forward calls to the built-in print() and alert() function to the debug ++ client. +diff --git a/dist/source/config/feature-options/DUK_OPT_DEBUGGER_SUPPORT.yaml b/dist/source/config/feature-options/DUK_OPT_DEBUGGER_SUPPORT.yaml +new file mode 100644 +index 0000000..bf1d79a +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_DEBUGGER_SUPPORT.yaml +@@ -0,0 +1,12 @@ ++define: DUK_OPT_DEBUGGER_SUPPORT ++introduced: 1.2.0 ++requires: ++ - DUK_OPT_INTERRUPT_COUNTER ++tags: ++ - debugger ++description: > ++ Enable support for Duktape debug protocol (see doc/debugger.rst) and the ++ debug API calls (duk_debugger_attach(), duk_debugger_detach(), etc). ++ This adds about 10kB of code footprint at the moment. ++ ++ This option requires DUK_OPT_INTERRUPT_COUNTER. +diff --git a/dist/source/config/feature-options/DUK_OPT_DEBUGGER_TRANSPORT_TORTURE.yaml b/dist/source/config/feature-options/DUK_OPT_DEBUGGER_TRANSPORT_TORTURE.yaml +new file mode 100644 +index 0000000..0d8c858 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_DEBUGGER_TRANSPORT_TORTURE.yaml +@@ -0,0 +1,11 @@ ++define: DUK_OPT_DEBUGGER_TRANSPORT_TORTURE ++introduced: 1.2.0 ++requires: ++ - DUK_OPT_DEBUGGER_SUPPORT ++tags: ++ - debugger ++ - development ++description: > ++ Development time option: force debugger transport torture. Concretely this ++ now causes Duktape to read/write debug protocol data in 1-byte increments, ++ which stresses message parsing and transport code. +diff --git a/dist/source/config/feature-options/DUK_OPT_DEBUG_BUFSIZE.yaml b/dist/source/config/feature-options/DUK_OPT_DEBUG_BUFSIZE.yaml +new file mode 100644 +index 0000000..ae87d23 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_DEBUG_BUFSIZE.yaml +@@ -0,0 +1,11 @@ ++define: DUK_OPT_DEBUG_BUFSIZE ++introduced: 1.0.0 ++requires: ++ - DUK_OPT_DEBUG ++tags: ++ - debug ++description: > ++ Debug code uses a static buffer as a formatting temporary to avoid side ++ effects in debug prints. The static buffer is large by default, which ++ may be an issue in constrained environments. You can set the buffer size ++ manually with this option. Example: -DDUK_OPT_DEBUG_BUFSIZE=2048. +diff --git a/dist/source/config/feature-options/DUK_OPT_DECLARE.yaml b/dist/source/config/feature-options/DUK_OPT_DECLARE.yaml +new file mode 100644 +index 0000000..e8f766e +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_DECLARE.yaml +@@ -0,0 +1,10 @@ ++define: DUK_OPT_DECLARE ++introduced: 1.0.0 ++tags: ++ - portability ++description: > ++ Provide declarations or additional preprocessor include directives to be ++ used when compiling Duktape. You may need this if you set ++ DUK_OPT_PANIC_HANDLER to call your own panic handler function. You can ++ also use this option to cause additional files to be included when compiling ++ Duktape. +diff --git a/dist/source/config/feature-options/DUK_OPT_DEEP_C_STACK.yaml b/dist/source/config/feature-options/DUK_OPT_DEEP_C_STACK.yaml +new file mode 100644 +index 0000000..7de8932 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_DEEP_C_STACK.yaml +@@ -0,0 +1,15 @@ ++define: DUK_OPT_DEEP_C_STACK ++introduced: 1.0.0 ++removed: 1.3.0 ++tags: ++ - memory ++ - portability ++description: > ++ By default Duktape imposes a sanity limit on the depth of the C stack ++ because it is often limited in embedded environments. This option ++ forces Duktape to use a deep C stack which relaxes e.g. recursion limits. ++ Automatic feature detection enables deep C stacks on some platforms known ++ to have them (e.g. Linux, BSD, Windows). ++ ++ Removed in Duktape 1.3.0, use explicit config options to control recursion ++ limits for shallow stack targets. +diff --git a/dist/source/config/feature-options/DUK_OPT_DLL_BUILD.yaml b/dist/source/config/feature-options/DUK_OPT_DLL_BUILD.yaml +new file mode 100644 +index 0000000..3ec70b3 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_DLL_BUILD.yaml +@@ -0,0 +1,12 @@ ++define: DUK_OPT_DLL_BUILD ++introduced: 1.0.0 ++tags: ++ - portability ++description: > ++ Add this define to both Duktape and application build when Duktape is ++ compiled as a DLL. This is especially critical on Windows: the option ++ makes Duktape use __declspec(dllexport) and __declspec(dllimport) for ++ public symbols. ++ ++ While this is not currently needed for Unix platforms, it should always ++ be used if you build as a DLL. +diff --git a/dist/source/config/feature-options/DUK_OPT_DPRINT.yaml b/dist/source/config/feature-options/DUK_OPT_DPRINT.yaml +new file mode 100644 +index 0000000..5da8a46 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_DPRINT.yaml +@@ -0,0 +1,8 @@ ++define: DUK_OPT_DPRINT ++introduced: 1.0.0 ++requires: ++ - DUK_OPT_DEBUG ++tags: ++ - debug ++description: > ++ Enable debug printouts. +diff --git a/dist/source/config/feature-options/DUK_OPT_DPRINT_COLORS.yaml b/dist/source/config/feature-options/DUK_OPT_DPRINT_COLORS.yaml +new file mode 100644 +index 0000000..6308b0b +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_DPRINT_COLORS.yaml +@@ -0,0 +1,8 @@ ++define: DUK_OPT_DPRINT_COLORS ++introduced: 1.0.0 ++tags: ++ - debug ++description: > ++ Enable coloring of debug prints with ANSI escape codes ++ (http://en.wikipedia.org/wiki/ANSI_escape_code). The behavior is not ++ sensitive to terminal settings. +diff --git a/dist/source/config/feature-options/DUK_OPT_DPRINT_RDTSC.yaml b/dist/source/config/feature-options/DUK_OPT_DPRINT_RDTSC.yaml +new file mode 100644 +index 0000000..a393d2a +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_DPRINT_RDTSC.yaml +@@ -0,0 +1,7 @@ ++define: DUK_OPT_DPRINT_RDTSC ++introduced: 1.0.0 ++removed: 1.4.0 ++tags: ++ - debug ++description: > ++ Print RDTSC cycle count in debug prints if available. +diff --git a/dist/source/config/feature-options/DUK_OPT_EXAMPLE.yaml b/dist/source/config/feature-options/DUK_OPT_EXAMPLE.yaml +new file mode 100644 +index 0000000..277b837 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_EXAMPLE.yaml +@@ -0,0 +1,52 @@ ++# ++# Each Duktape option is described as a YAML file named OPTION_NAME.yaml. ++# YAML is used because it diffs well and has clean support for multiline ++# inline strings. ++# ++# Metadata for feature options (DUK_OPT_xxx) is sparse, it's needed for ++# legacy support. ++# ++ ++# C #define name for the option. Must match filename minus extension. ++define: DUK_OPT_OBJSIZES16 ++ ++# Duktape version number where this option was first introduced. ++introduced: 1.1.0 ++#deprecated: 1.2.0 ++#removed: 1.3.0 ++ ++# Optional indication that feature option is defined but currently ++# unused, so that it can be omitted from generated header. ++#unused: true ++ ++# Optional list of options that must also be defined to use this option. ++#requires: ++# - DUK_OPT_FOO ++# - DUK_OPT_BAR ++ ++# Optional list of options that this option conflicts with. ++#conflicts: ++# - DUK_OPT_BAZ ++ ++# Optional list of options that are related from a user and documentation ++# perspective. ++#related: ++# - DUK_OPT_QUUX ++ ++# Tags related to option (required). If present, first tag is used as a ++# primary tag for grouping. Use 'misc' if nothing else is appropriate. ++tags: ++ - lowmemory ++ - experimental ++ ++# Description for option, no newlines. Line breaking for e.g. C header ++# is automatic. ++description: > ++ Use a 16-bit object entry and array part sizes (for low memory ++ environments). Also automatically drops support for an object hash ++ part to further reduce memory usage; there are rarely large objects ++ in low memory environments simply because there's no memory to store ++ a lot of properties. ++ ++# Marker to avoid processing this file. ++example: true +diff --git a/dist/source/config/feature-options/DUK_OPT_EXEC_TIMEOUT_CHECK.yaml b/dist/source/config/feature-options/DUK_OPT_EXEC_TIMEOUT_CHECK.yaml +new file mode 100644 +index 0000000..0e51e1f +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_EXEC_TIMEOUT_CHECK.yaml +@@ -0,0 +1,30 @@ ++define: DUK_OPT_EXEC_TIMEOUT_CHECK ++introduced: 1.2.0 ++requires: ++ - DUK_OPT_INTERRUPT_COUNTER ++tags: ++ - execution ++ - sandbox ++ - experimental ++description: > ++ NOTE: This mechanism is EXPERIMENTAL and the details may change ++ between releases. ++ ++ Provide a hook to check for bytecode execution timeout. The macro gets ++ a void ptr userdata argument (the userdata given to duk_heap_create()) ++ and must evaluate to a duk_bool_t. Duktape calls the macro as: ++ "if (DUK_OPT_EXEC_TIMEOUT_CHECK(udata)) { ... }". ++ ++ The macro is called occasionally by the Duktape bytecode executor (i.e. ++ when executing ECMAScript code), typically from a few times per second ++ to a hundred times per second, but the interval varies a great deal ++ depending on what kind of code is being executed. ++ ++ To indicate an execution timeout, the macro must return a non-zero value. ++ When that happens, Duktape starts to bubble a ``RangeError`` outwards ++ until control has been returned to the original protected call made by ++ the application. Until that happens, the exec timeout macro must always ++ return non-zero to indicate an execution timeout is still in progress. ++ ++ This mechanism and its limitations is described in more detail in ++ doc/sandboxing.rst. +diff --git a/dist/source/config/feature-options/DUK_OPT_EXTERNAL_STRINGS.yaml b/dist/source/config/feature-options/DUK_OPT_EXTERNAL_STRINGS.yaml +new file mode 100644 +index 0000000..dfb44ee +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_EXTERNAL_STRINGS.yaml +@@ -0,0 +1,11 @@ ++define: DUK_OPT_EXTERNAL_STRINGS ++introduced: 1.1.0 ++tags: ++ - memory ++description: > ++ Enable support for external strings. An external string requires a Duktape ++ heap allocation to store a minimal string header, with the actual string ++ data being held behind a pointer (similarly to how dynamic buffers work). ++ ++ This option is needed to use DUK_OPT_EXTSTR_INTERN_CHECK and/or ++ DUK_OPT_EXTSTR_FREE. +diff --git a/dist/source/config/feature-options/DUK_OPT_EXTSTR_FREE.yaml b/dist/source/config/feature-options/DUK_OPT_EXTSTR_FREE.yaml +new file mode 100644 +index 0000000..77b687d +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_EXTSTR_FREE.yaml +@@ -0,0 +1,22 @@ ++define: DUK_OPT_EXTSTR_FREE ++introduced: 1.1.0 ++requires: ++ - DUK_OPT_EXTERNAL_STRINGS ++tags: ++ - memory ++ - experimental ++description: > ++ Optional counterpart to DUK_OPT_EXTSTR_INTERN_CHECK. Invoked when an ++ external string is about to be freed by Duktape. ++ ++ The argument "ptr" is a void ptr and points to the external string data. ++ Concretely, it is the (non-NULL) value returned by ++ DUK_OPT_EXTSTR_INTERN_CHECK. The "udata" argument is the heap userdata ++ which may be ignored if not needed. ++ ++ Also enable DUK_OPT_EXTERNAL_STRINGS to use this feature. ++ ++ NOTE: Right now there is no API to push external strings; external strings ++ come into being as a result of DUK_OPT_EXTSTR_INTERN_CHECK() only. If/when ++ this is changed, this hook will get called for every string, even if pushed ++ by the user using an API call; this may need to be rethought at that time. +diff --git a/dist/source/config/feature-options/DUK_OPT_EXTSTR_INTERN_CHECK.yaml b/dist/source/config/feature-options/DUK_OPT_EXTSTR_INTERN_CHECK.yaml +new file mode 100644 +index 0000000..0a3bea1 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_EXTSTR_INTERN_CHECK.yaml +@@ -0,0 +1,38 @@ ++define: DUK_OPT_EXTSTR_INTERN_CHECK ++introduced: 1.1.0 ++requires: ++ - DUK_OPT_EXTERNAL_STRINGS ++tags: ++ - memory ++ - experimental ++description: > ++ Provide a hook for checking if data for a certain string can be used from ++ external memory (outside of Duktape heap, e.g. memory mapped flash). ++ The hook is called during string interning with the following semantics: ++ ++ The string data with no NUL termination resides at "ptr" and has "len" ++ bytes. The "udata" argument is the heap userdata which may be ignored ++ if not needed. If the hook returns NULL, Duktape interns the string ++ normally, i.e. string data is allocated from Duktape heap. Otherwise the ++ hook return value must point to a memory area which contains ++ "len" bytes from "ptr" followed by a NUL byte which is NOT PRESENT ++ in the input data. Data behind the returned pointer may not change after ++ the hook returns. ++ ++ The hook may be called several times for the same input string. This ++ happens when a string is interned, garbage collected, and then interned ++ again. ++ ++ The DUK_OPT_EXTSTR_FREE() hook allows application code to detect when ++ an external string is about to be freed. ++ ++ In most cases the hook should reject strings whose "len" is less than 4 ++ because there is no RAM advantage in moving so short strings into external ++ memory. The ordinary "duk_hstring" header followed by the data (and a ++ NUL byte) has the same size as "duk_hstring_external" header which hosts ++ a pointer instead of string data. ++ ++ Also enable DUK_OPT_EXTERNAL_STRINGS to use this feature. ++ ++ See doc/low-memory.rst for more discussion how to use this feature option ++ in practice. +diff --git a/dist/source/config/feature-options/DUK_OPT_FASTINT.yaml b/dist/source/config/feature-options/DUK_OPT_FASTINT.yaml +new file mode 100644 +index 0000000..704a8dd +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_FASTINT.yaml +@@ -0,0 +1,10 @@ ++define: DUK_OPT_FASTINT ++introduced: 1.2.0 ++tags: ++ - performance ++description: > ++ Enable support for 48-bit signed "fastint" integer values. Fastints are ++ transparent to user code (both C and ECMAScript) but may be faster than ++ IEEE doubles on some platforms, especially those using softints. The ++ downside of fastints is increased code footprint and a small performance ++ penalty for some kinds of code. +diff --git a/dist/source/config/feature-options/DUK_OPT_FORCE_ALIGN.yaml b/dist/source/config/feature-options/DUK_OPT_FORCE_ALIGN.yaml +new file mode 100644 +index 0000000..3682c47 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_FORCE_ALIGN.yaml +@@ -0,0 +1,8 @@ ++define: DUK_OPT_FORCE_ALIGN ++introduced: 1.0.0 ++tags: ++ - portability ++description: > ++ Use -DDUK_OPT_FORCE_ALIGN=4 or -DDUK_OPT_FORCE_ALIGN=8 to force a ++ specific struct/value alignment instead of relying on Duktape's ++ automatic detection. This shouldn't normally be needed. +diff --git a/dist/source/config/feature-options/DUK_OPT_FORCE_BYTEORDER.yaml b/dist/source/config/feature-options/DUK_OPT_FORCE_BYTEORDER.yaml +new file mode 100644 +index 0000000..69e9dc0 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_FORCE_BYTEORDER.yaml +@@ -0,0 +1,10 @@ ++define: DUK_OPT_FORCE_BYTEORDER ++introduced: 1.0.0 ++tags: ++ - portability ++description: > ++ Use this to skip byte order detection and force a specific byte order: ++ 1 for little endian, 2 for ARM "mixed" endian (integers little endian, ++ IEEE doubles mixed endian), 3 for big endian. Byte order detection ++ relies on unstandardized platform specific header files, so this may be ++ required for custom platforms if compilation fails in endianness detection. +diff --git a/dist/source/config/feature-options/DUK_OPT_FUNCPTR16.yaml b/dist/source/config/feature-options/DUK_OPT_FUNCPTR16.yaml +new file mode 100644 +index 0000000..aa592b1 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_FUNCPTR16.yaml +@@ -0,0 +1,21 @@ ++define: DUK_OPT_FUNCPTR16 ++introduced: 1.1.0 ++related: ++ - DUK_OPT_FUNCPTR_ENC16 ++ - DUK_OPT_FUNCPTR_DEC16 ++tags: ++ - lowmemory ++ - experimental ++description: > ++ Enable "compression" of arbitrary C function pointers into an unsigned 16-bit ++ value. Use together with DUK_OPT_FUNCPTR_ENC16 and DUK_OPT_FUNCPTR_DEC16. ++ ++ Pointers compressed are any C function pointers. Also NULL pointer must ++ encode and decode correctly. ++ ++ Currently it is required that NULL encodes to integer 0, and integer ++ 0 decodes to NULL. No other pointer can be encoded to 0. ++ ++ NOTE: This feature option is currently unimplemented, i.e. Duktape won't ++ compress any function pointers at the moment. It might not be necessary ++ to support a NULL function pointer (uncertain until taken into use). +diff --git a/dist/source/config/feature-options/DUK_OPT_FUNCPTR_DEC16.yaml b/dist/source/config/feature-options/DUK_OPT_FUNCPTR_DEC16.yaml +new file mode 100644 +index 0000000..f91bafd +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_FUNCPTR_DEC16.yaml +@@ -0,0 +1,12 @@ ++define: DUK_OPT_FUNCPTR_DEC16 ++introduced: 1.1.0 ++requires: ++ - DUK_OPT_FUNCPTR16 ++tags: ++ - lowmemory ++ - experimental ++description: > ++ Use together with DUK_OPT_FUNCPTR16 for function pointer compression. ++ DUK_OPT_FUNCPTR_DEC16(udata,x) is a macro with a userdata and duk_uint16_t ++ argument, and a void ptr return value. The userdata argument is the heap ++ userdata value given at heap creation. +diff --git a/dist/source/config/feature-options/DUK_OPT_FUNCPTR_ENC16.yaml b/dist/source/config/feature-options/DUK_OPT_FUNCPTR_ENC16.yaml +new file mode 100644 +index 0000000..2304e9e +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_FUNCPTR_ENC16.yaml +@@ -0,0 +1,12 @@ ++define: DUK_OPT_FUNCPTR_ENC16 ++introduced: 1.1.0 ++requires: ++ - DUK_OPT_FUNCPTR16 ++tags: ++ - lowmemory ++ - experimental ++description: > ++ Use together with DUK_OPT_FUNCPTR16 for function pointer compression. ++ DUK_OPT_FUNCPTR_ENC16(udata,p) is a macro with a userdata and void ptr ++ argument, and a duk_uint16_t return value. The userdata arguments is the ++ heap userdata value given at heap creation. +diff --git a/dist/source/config/feature-options/DUK_OPT_FUNC_NONSTD_CALLER_PROPERTY.yaml b/dist/source/config/feature-options/DUK_OPT_FUNC_NONSTD_CALLER_PROPERTY.yaml +new file mode 100644 +index 0000000..8444dac +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_FUNC_NONSTD_CALLER_PROPERTY.yaml +@@ -0,0 +1,9 @@ ++define: DUK_OPT_FUNC_NONSTD_CALLER_PROPERTY ++introduced: 1.0.0 ++removed: 1.0.0 ++related: ++ - DUK_OPT_NONSTD_FUNC_CALLER_PROPERTY ++tags: ++ - ecmascript ++description: > ++ Removed, use DUK_OPT_NONSTD_FUNC_CALLER_PROPERTY instead. +diff --git a/dist/source/config/feature-options/DUK_OPT_FUNC_NONSTD_SOURCE_PROPERTY.yaml b/dist/source/config/feature-options/DUK_OPT_FUNC_NONSTD_SOURCE_PROPERTY.yaml +new file mode 100644 +index 0000000..ba4d7dd +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_FUNC_NONSTD_SOURCE_PROPERTY.yaml +@@ -0,0 +1,9 @@ ++define: DUK_OPT_FUNC_NONSTD_SOURCE_PROPERTY ++introduced: 1.0.0 ++removed: 1.0.0 ++related: ++ - DUK_OPT_NONSTD_FUNC_SOURCE_PROPERTY ++tags: ++ - ecmascript ++description: > ++ Removed, use DUK_OPT_NONSTD_FUNC_SOURCE_PROPERTY instead. +diff --git a/dist/source/config/feature-options/DUK_OPT_GC_TORTURE.yaml b/dist/source/config/feature-options/DUK_OPT_GC_TORTURE.yaml +new file mode 100644 +index 0000000..5298dd9 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_GC_TORTURE.yaml +@@ -0,0 +1,9 @@ ++define: DUK_OPT_GC_TORTURE ++introduced: 1.0.0 ++tags: ++ - gc ++ - memory ++ - development ++description: > ++ Development time option: force full mark-and-sweep on every allocation to ++ stress test memory management. +diff --git a/dist/source/config/feature-options/DUK_OPT_HAVE_CUSTOM_H.yaml b/dist/source/config/feature-options/DUK_OPT_HAVE_CUSTOM_H.yaml +new file mode 100644 +index 0000000..1ae881d +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_HAVE_CUSTOM_H.yaml +@@ -0,0 +1,26 @@ ++define: DUK_OPT_HAVE_CUSTOM_H ++introduced: 1.0.0 ++deprecated: 1.3.0 ++tags: ++ - portability ++description: > ++ Normally you define DUK_OPT_xxx feature options and the autodetecting ++ duk_config.h (previously internal duk_features.h.in) header resolves ++ these with platform/compiler constraints to determine effective compilation ++ options for Duktape internals. The effective options are provided as ++ DUK_USE_xxx defines which you normally never see. ++ ++ If you define DUK_OPT_HAVE_CUSTOM_H, Duktape will include "duk_custom.h" ++ after determining the appropriate DUK_USE_xxx defines but before compiling ++ any code. The duk_custom.h header, which you provide, can then tweak the ++ active DUK_USE_xxx defines freely. See duk_config.h and genconfig option ++ metadata for the available defines. ++ ++ This approach is useful when the DUK_OPT_xxx feature options don't ++ provide enough flexibility to tweak the build. The downside is that you can ++ easily create inconsistent DUK_USE_xxx flags, the customization header ++ will be version specific, and you need to peek into Duktape internals to ++ know what defines to tweak. ++ ++ NOTE: This option is deprecated. The preferred method for customization ++ starting from Duktape 1.3.0 is to create/modify a "duk_config.h" header. +diff --git a/dist/source/config/feature-options/DUK_OPT_HEAPPTR16.yaml b/dist/source/config/feature-options/DUK_OPT_HEAPPTR16.yaml +new file mode 100644 +index 0000000..a93a181 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_HEAPPTR16.yaml +@@ -0,0 +1,34 @@ ++define: DUK_OPT_HEAPPTR16 ++introduced: 1.1.0 ++conflicts: ++ - DUK_OPT_DEBUG ++related: ++ - DUK_OPT_HEAPPTR_ENC16 ++ - DUK_OPT_HEAPPTR_DEC16 ++tags: ++ - lowmemory ++ - experimental ++description: > ++ Enable "compression" of Duktape heap pointers into an unsigned 16-bit value. ++ Use together with DUK_OPT_HEAPPTR_ENC16 and DUK_OPT_HEAPPTR_DEC16. ++ ++ Pointers compressed are those allocated from Duktape heap, using the user ++ provided allocation functions. Also NULL pointer must encode and decode ++ correctly. ++ ++ Currently it is required that NULL encodes to integer 0, and integer ++ 0 decodes to NULL. No other pointer can be encoded to 0. ++ ++ This option reduces memory usage by several kilobytes, but has several ++ downsides. It can only be applied when Duktape heap is limited in size, ++ for instance, with 4-byte aligned allocations a 256kB heap (minus one value ++ for NULL) can be supported. Pointer encoding and decoding may be relatively ++ complicated as they need to correctly handle NULL pointers and ++ non-continuous memory maps used by some targets. The macro may need to call ++ out to a helper function in practice, which is much slower than an inline ++ implementation. ++ ++ Current limitation: Duktape internal debug code enabled with e.g. ++ DUK_OPT_DEBUG and DUK_OPT_DPRINT doesn't have enough plumbing to be able to ++ decode pointers. Debug printing cannot currently be enabled when pointer ++ compression is active. +diff --git a/dist/source/config/feature-options/DUK_OPT_HEAPPTR_DEC16.yaml b/dist/source/config/feature-options/DUK_OPT_HEAPPTR_DEC16.yaml +new file mode 100644 +index 0000000..9428827 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_HEAPPTR_DEC16.yaml +@@ -0,0 +1,12 @@ ++define: DUK_OPT_HEAPPTR_DEC16 ++introduced: 1.1.0 ++requires: ++ - DUK_OPT_HEAPPTR16 ++tags: ++ - lowmemory ++ - experimental ++description: > ++ Use together with DUK_OPT_HEAPPTR16 for heap pointer compression. ++ DUK_OPT_HEAPPTR_DEC16(udata,x) is a macro with a userdata and duk_uint16_t ++ argument, and a void ptr return value. The userdata argument is the heap ++ userdata value given at heap creation. +diff --git a/dist/source/config/feature-options/DUK_OPT_HEAPPTR_ENC16.yaml b/dist/source/config/feature-options/DUK_OPT_HEAPPTR_ENC16.yaml +new file mode 100644 +index 0000000..ca64ca1 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_HEAPPTR_ENC16.yaml +@@ -0,0 +1,12 @@ ++define: DUK_OPT_HEAPPTR_ENC16 ++introduced: 1.1.0 ++requires: ++ - DUK_OPT_HEAPPTR16 ++tags: ++ - lowmemory ++ - experimental ++description: > ++ Use together with DUK_OPT_HEAPPTR16 for heap pointer compression. ++ DUK_OPT_HEAPPTR_ENC16(udata,p) is a macro with a userdata and void ptr ++ argument, and a duk_uint16_t return value. The userdata argument is the ++ heap userdata value given at heap creation. +diff --git a/dist/source/config/feature-options/DUK_OPT_INTERRUPT_COUNTER.yaml b/dist/source/config/feature-options/DUK_OPT_INTERRUPT_COUNTER.yaml +new file mode 100644 +index 0000000..835af64 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_INTERRUPT_COUNTER.yaml +@@ -0,0 +1,12 @@ ++define: DUK_OPT_INTERRUPT_COUNTER ++introduced: 1.1.0 ++related: ++ - DUK_OPT_DEBUGGER_SUPPORT ++tags: ++ - execution ++ - debugger ++description: > ++ Enable the internal bytecode executor periodic interrupt counter. ++ The mechanism is used to implement e.g. execution step limit, custom ++ profiling, and debugger interaction. Enabling the interrupt counter ++ has a small impact on execution performance. +diff --git a/dist/source/config/feature-options/DUK_OPT_JSON_STRINGIFY_FASTPATH.yaml b/dist/source/config/feature-options/DUK_OPT_JSON_STRINGIFY_FASTPATH.yaml +new file mode 100644 +index 0000000..6d6431c +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_JSON_STRINGIFY_FASTPATH.yaml +@@ -0,0 +1,9 @@ ++define: DUK_OPT_JSON_STRINGIFY_FASTPATH ++introduced: 1.3.0 ++tags: ++ - performance ++ - fastpath ++ - lowmemory ++description: > ++ Enable fast apth for JSON.stringify() serialization. See ++ DUK_USE_JSON_STRINGIFY_FASTPATH for details. +diff --git a/dist/source/config/feature-options/DUK_OPT_LIGHTFUNC_BUILTINS.yaml b/dist/source/config/feature-options/DUK_OPT_LIGHTFUNC_BUILTINS.yaml +new file mode 100644 +index 0000000..c28a0e6 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_LIGHTFUNC_BUILTINS.yaml +@@ -0,0 +1,9 @@ ++define: DUK_OPT_LIGHTFUNC_BUILTINS ++introduced: 1.1.0 ++tags: ++ - lowmemory ++ - experimental ++description: > ++ Force built-in functions to be lightweight functions. This reduces ++ memory footprint by around 14 kB at the cost of some non-compliant ++ behavior. +diff --git a/dist/source/config/feature-options/DUK_OPT_NONSTD_FUNC_CALLER_PROPERTY.yaml b/dist/source/config/feature-options/DUK_OPT_NONSTD_FUNC_CALLER_PROPERTY.yaml +new file mode 100644 +index 0000000..4220071 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NONSTD_FUNC_CALLER_PROPERTY.yaml +@@ -0,0 +1,14 @@ ++define: DUK_OPT_NONSTD_FUNC_CALLER_PROPERTY ++introduced: 1.0.0 ++tags: ++ - ecmascript ++description: > ++ Add a non-standard "caller" property to non-strict function instances ++ for better compatibility with existing code. The semantics of this ++ property are not standardized and may vary between engines; Duktape tries ++ to behave close to V8 and Spidermonkey. See ++ https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/caller ++ description of the property. This feature disables tail call support. ++ ++ This feature conflicts with several other features, so you should use it ++ only if it's absolutely necessary. +diff --git a/dist/source/config/feature-options/DUK_OPT_NONSTD_FUNC_SOURCE_PROPERTY.yaml b/dist/source/config/feature-options/DUK_OPT_NONSTD_FUNC_SOURCE_PROPERTY.yaml +new file mode 100644 +index 0000000..e9abe9f +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NONSTD_FUNC_SOURCE_PROPERTY.yaml +@@ -0,0 +1,10 @@ ++define: DUK_OPT_NONSTD_FUNC_SOURCE_PROPERTY ++introduced: 1.0.0 ++tags: ++ - ecmascript ++description: > ++ Add a non-standard "source" property to function instances. This allows ++ function toString() to print out the actual function source. The property ++ is disabled by default because it increases memory footprint. ++ ++ NOTE: Unimplemented as of Duktape 1.2.0. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_ARRAY_SPLICE_NONSTD_DELCOUNT.yaml b/dist/source/config/feature-options/DUK_OPT_NO_ARRAY_SPLICE_NONSTD_DELCOUNT.yaml +new file mode 100644 +index 0000000..9f771e8 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_ARRAY_SPLICE_NONSTD_DELCOUNT.yaml +@@ -0,0 +1,9 @@ ++define: DUK_OPT_NO_ARRAY_SPLICE_NONSTD_DELCOUNT ++introduced: 1.0.0 ++removed: 1.0.0 ++related: ++ - DUK_OPT_NO_NONSTD_ARRAY_SPLICE_DELCOUNT ++tags: ++ - ecmascript ++description: > ++ Removed, use DUK_OPT_NO_NONSTD_ARRAY_SPLICE_DELCOUNT instead. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_AUGMENT_ERRORS.yaml b/dist/source/config/feature-options/DUK_OPT_NO_AUGMENT_ERRORS.yaml +new file mode 100644 +index 0000000..9faffd8 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_AUGMENT_ERRORS.yaml +@@ -0,0 +1,10 @@ ++define: DUK_OPT_NO_AUGMENT_ERRORS ++introduced: 1.0.0 ++related: ++ - DUK_OPT_NO_TRACEBACKS ++tags: ++ - ecmascript ++description: > ++ Don't augment ECMAScript error objects with custom fields like fileName, ++ lineNumber, and traceback data. Also disables Duktape.errCreate and ++ Duktape.errThrow error handler callbacks. Implies DUK_OPT_NO_TRACEBACKS. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_BROWSER_LIKE.yaml b/dist/source/config/feature-options/DUK_OPT_NO_BROWSER_LIKE.yaml +new file mode 100644 +index 0000000..002861e +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_BROWSER_LIKE.yaml +@@ -0,0 +1,9 @@ ++define: DUK_OPT_NO_BROWSER_LIKE ++introduced: 1.0.0 ++tags: ++ - ecmascript ++description: > ++ Disable browser-like functions. Makes print() and alert() throw an error. ++ ++ This option is confusing when used with the Duktape command line tool, as ++ the command like tool will immediately panic on startup. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_BUFFEROBJECT_SUPPORT.yaml b/dist/source/config/feature-options/DUK_OPT_NO_BUFFEROBJECT_SUPPORT.yaml +new file mode 100644 +index 0000000..395aaf5 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_BUFFEROBJECT_SUPPORT.yaml +@@ -0,0 +1,8 @@ ++define: DUK_OPT_NO_BUFFEROBJECT_SUPPORT ++introduced: 1.3.0 ++tags: ++ - ecmascript6 ++description: > ++ Disable support for Khronos/ES6 typed arrays and Node.js Buffer objects ++ which reduces code footprint. When disabled, Duktape custom plain buffers ++ and Duktape.Buffer are still supported. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_BYTECODE_DUMP_SUPPORT.yaml b/dist/source/config/feature-options/DUK_OPT_NO_BYTECODE_DUMP_SUPPORT.yaml +new file mode 100644 +index 0000000..26c5117 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_BYTECODE_DUMP_SUPPORT.yaml +@@ -0,0 +1,7 @@ ++define: DUK_OPT_NO_BYTECODE_DUMP_SUPPORT ++introduced: 1.3.0 ++tags: ++ - api ++description: > ++ Disable API calls to dump/load functions to bytecode (reduces code ++ footprint). +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_COMMONJS_MODULES.yaml b/dist/source/config/feature-options/DUK_OPT_NO_COMMONJS_MODULES.yaml +new file mode 100644 +index 0000000..61e8273 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_COMMONJS_MODULES.yaml +@@ -0,0 +1,6 @@ ++define: DUK_OPT_NO_COMMONJS_MODULES ++introduced: 1.0.0 ++tags: ++ - ecmascript ++description: > ++ Disable support for CommonJS modules. Causes require() to throw an error. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_ES6_OBJECT_PROTO_PROPERTY.yaml b/dist/source/config/feature-options/DUK_OPT_NO_ES6_OBJECT_PROTO_PROPERTY.yaml +new file mode 100644 +index 0000000..e89fbdd +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_ES6_OBJECT_PROTO_PROPERTY.yaml +@@ -0,0 +1,8 @@ ++define: DUK_OPT_NO_ES6_OBJECT_PROTO_PROPERTY ++introduced: 1.0.0 ++tags: ++ - ecmascript ++ - ecmascript6 ++description: > ++ Disable the non-standard (ES6) Object.prototype.__proto__ property ++ which is enabled by default. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_ES6_OBJECT_SETPROTOTYPEOF.yaml b/dist/source/config/feature-options/DUK_OPT_NO_ES6_OBJECT_SETPROTOTYPEOF.yaml +new file mode 100644 +index 0000000..e1f3cc4 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_ES6_OBJECT_SETPROTOTYPEOF.yaml +@@ -0,0 +1,8 @@ ++define: DUK_OPT_NO_ES6_OBJECT_SETPROTOTYPEOF ++introduced: 1.0.0 ++tags: ++ - ecmascript ++ - ecmascript6 ++description: > ++ Disable the non-standard (ES6) Object.setPrototypeOf method which ++ is enabled by default. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_ES6_PROXY.yaml b/dist/source/config/feature-options/DUK_OPT_NO_ES6_PROXY.yaml +new file mode 100644 +index 0000000..dcddbd2 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_ES6_PROXY.yaml +@@ -0,0 +1,8 @@ ++define: DUK_OPT_NO_ES6_PROXY ++introduced: 1.0.0 ++tags: ++ - ecmascript ++ - ecmascript6 ++description: > ++ Disable the non-standard (ES6) Proxy object which is enabled by ++ default. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_FILE_IO.yaml b/dist/source/config/feature-options/DUK_OPT_NO_FILE_IO.yaml +new file mode 100644 +index 0000000..52646a5 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_FILE_IO.yaml +@@ -0,0 +1,10 @@ ++define: DUK_OPT_NO_FILE_IO ++introduced: 1.0.0 ++tags: ++ - portability ++description: > ++ Disable use of ANSI C file I/O which might be a portability issue on some ++ platforms. Causes duk_eval_file() to throw an error, makes built-in ++ print() and alert() no-ops, and suppresses writing of a panic ++ message to stderr on panic. This option does not suppress debug ++ printing so don't enable debug printing if you wish to avoid I/O. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_FUNC_STMT.yaml b/dist/source/config/feature-options/DUK_OPT_NO_FUNC_STMT.yaml +new file mode 100644 +index 0000000..5a309a6 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_FUNC_STMT.yaml +@@ -0,0 +1,9 @@ ++define: DUK_OPT_NO_FUNC_STMT ++introduced: 1.0.0 ++removed: 1.0.0 ++related: ++ - DUK_OPT_NO_NONSTD_FUNC_STMT ++tags: ++ - ecmascript ++description: > ++ Removed, use DUK_OPT_NO_NONSTD_FUNC_STMT instead. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_JC.yaml b/dist/source/config/feature-options/DUK_OPT_NO_JC.yaml +new file mode 100644 +index 0000000..ba7d86f +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_JC.yaml +@@ -0,0 +1,7 @@ ++define: DUK_OPT_NO_JC ++introduced: 1.0.0 ++tags: ++ - ecmascript ++description: > ++ Disable support for the JC format. Reduces code footprint. An attempt ++ to encode or decode the format causes an error. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_JSONC.yaml b/dist/source/config/feature-options/DUK_OPT_NO_JSONC.yaml +new file mode 100644 +index 0000000..8c4e0e7 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_JSONC.yaml +@@ -0,0 +1,9 @@ ++define: DUK_OPT_NO_JSONC ++introduced: 1.0.0 ++removed: 1.0.0 ++related: ++ - DUK_OPT_NO_JC ++tags: ++ - ecmascript ++description: > ++ Removed, use DUK_OPT_NO_JC instead. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_JSONX.yaml b/dist/source/config/feature-options/DUK_OPT_NO_JSONX.yaml +new file mode 100644 +index 0000000..d141a27 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_JSONX.yaml +@@ -0,0 +1,9 @@ ++define: DUK_OPT_NO_JSONX ++introduced: 1.0.0 ++removed: 1.0.0 ++related: ++ - DUK_OPT_NO_JX ++tags: ++ - ecmascript ++description: > ++ Removed, use DUK_OPT_NO_JX instead. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_JX.yaml b/dist/source/config/feature-options/DUK_OPT_NO_JX.yaml +new file mode 100644 +index 0000000..be22edd +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_JX.yaml +@@ -0,0 +1,7 @@ ++define: DUK_OPT_NO_JX ++introduced: 1.0.0 ++tags: ++ - ecmascript ++description: > ++ Disable support for the JX format. Reduces code footprint. An attempt ++ to encode or decode the format causes an error. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_MARK_AND_SWEEP.yaml b/dist/source/config/feature-options/DUK_OPT_NO_MARK_AND_SWEEP.yaml +new file mode 100644 +index 0000000..3283488 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_MARK_AND_SWEEP.yaml +@@ -0,0 +1,14 @@ ++define: DUK_OPT_NO_MARK_AND_SWEEP ++introduced: 1.0.0 ++tags: ++ - gc ++ - memory ++description: > ++ Disable mark-and-sweep and use only reference counting for garbage ++ collection. This reduces code footprint and eliminates garbage collection ++ pauses, but objects participating in unreachable reference cycles won't be ++ collected until the Duktape heap is destroyed. In particular, function ++ instances won't be collected because they're always in a reference cycle ++ with their default prototype object. Unreachable objects are collected if ++ you break reference cycles manually (and are always freed when a heap is ++ destroyed). +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_MS_STRINGTABLE_RESIZE.yaml b/dist/source/config/feature-options/DUK_OPT_NO_MS_STRINGTABLE_RESIZE.yaml +new file mode 100644 +index 0000000..9b0e6af +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_MS_STRINGTABLE_RESIZE.yaml +@@ -0,0 +1,9 @@ ++define: DUK_OPT_NO_MS_STRINGTABLE_RESIZE ++introduced: 1.0.0 ++tags: ++ - gc ++ - memory ++description: > ++ Disable forced string intern table resize during mark-and-sweep garbage ++ collection. This may be useful when reference counting is disabled, as ++ mark-and-sweep collections will be more frequent and thus more expensive. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_NONSTD_ACCESSOR_KEY_ARGUMENT.yaml b/dist/source/config/feature-options/DUK_OPT_NO_NONSTD_ACCESSOR_KEY_ARGUMENT.yaml +new file mode 100644 +index 0000000..df5ecda +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_NONSTD_ACCESSOR_KEY_ARGUMENT.yaml +@@ -0,0 +1,8 @@ ++define: DUK_OPT_NO_NONSTD_ACCESSOR_KEY_ARGUMENT ++introduced: 1.0.0 ++tags: ++ - ecmascript ++description: > ++ Don't give setter/getter calls the property name being accessed as ++ an additional, non-standard property. See ++ http://duktape.org/guide.html#propertyvirtualization. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_NONSTD_ARRAY_CONCAT_TRAILER.yaml b/dist/source/config/feature-options/DUK_OPT_NO_NONSTD_ARRAY_CONCAT_TRAILER.yaml +new file mode 100644 +index 0000000..eb7f322 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_NONSTD_ARRAY_CONCAT_TRAILER.yaml +@@ -0,0 +1,12 @@ ++define: DUK_OPT_NO_NONSTD_ARRAY_CONCAT_TRAILER ++introduced: 1.0.0 ++tags: ++ - ecmascript ++description: > ++ For better compatibility with existing code, Array.prototype.concat() has ++ non-standard behavior by default for trailing non-existent elements of ++ the concat result, see ++ https://github.com/svaarala/duktape/blob/master/ecmascript-testcases/test-bi-array-proto-concat-nonstd-trailing.js. ++ ++ If this option is given, concat() will behave in a strictly conforming ++ fashion, ignoring non-existent trailing elements in the result length. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_NONSTD_ARRAY_MAP_TRAILER.yaml b/dist/source/config/feature-options/DUK_OPT_NO_NONSTD_ARRAY_MAP_TRAILER.yaml +new file mode 100644 +index 0000000..a43a81c +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_NONSTD_ARRAY_MAP_TRAILER.yaml +@@ -0,0 +1,12 @@ ++define: DUK_OPT_NO_NONSTD_ARRAY_MAP_TRAILER ++introduced: 1.0.0 ++tags: ++ - ecmascript ++description: > ++ For better compatibility with existing code, Array.prototype.map() has ++ non-standard behavior by default for trailing non-existent elements of ++ the map result, see ++ https://github.com/svaarala/duktape/blob/master/ecmascript-testcases/test-bi-array-proto-map-nonstd-trailing.js. ++ ++ If this option is given, map() will behave in a strictly conforming ++ fashion, ignoring non-existent trailing elements in the result length. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_NONSTD_ARRAY_SPLICE_DELCOUNT.yaml b/dist/source/config/feature-options/DUK_OPT_NO_NONSTD_ARRAY_SPLICE_DELCOUNT.yaml +new file mode 100644 +index 0000000..308d720 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_NONSTD_ARRAY_SPLICE_DELCOUNT.yaml +@@ -0,0 +1,13 @@ ++define: DUK_OPT_NO_NONSTD_ARRAY_SPLICE_DELCOUNT ++introduced: 1.0.0 ++tags: ++ - ecmascript ++description: > ++ For better compatibility with existing code, Array.prototype.splice() has ++ non-standard behavior by default when the second argument (deleteCount) ++ is not given: the splice operation is extended to the end of the array, see ++ https://github.com/svaarala/duktape/blob/master/ecmascript-testcases/test-bi-array-proto-splice-no-delcount.js. ++ ++ If this option is given, splice() will behave in a strictly conforming ++ fashion, treating a missing deleteCount the same as an undefined (or 0) ++ value. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_NONSTD_FUNC_STMT.yaml b/dist/source/config/feature-options/DUK_OPT_NO_NONSTD_FUNC_STMT.yaml +new file mode 100644 +index 0000000..b27b4fc +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_NONSTD_FUNC_STMT.yaml +@@ -0,0 +1,10 @@ ++define: DUK_OPT_NO_NONSTD_FUNC_STMT ++introduced: 1.0.0 ++tags: ++ - ecmascript ++description: > ++ Disable support for function declarations outside program or function top ++ level (also known as "function statements"). Such declarations are ++ non-standard and the strictly compliant behavior is to treat them as a ++ SyntaxError. Default behavior is to treat them like ordinary function ++ declarations ("hoist" them to function top) with V8-like semantics. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_NONSTD_JSON_ESC_U2028_U2029.yaml b/dist/source/config/feature-options/DUK_OPT_NO_NONSTD_JSON_ESC_U2028_U2029.yaml +new file mode 100644 +index 0000000..b1d87e4 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_NONSTD_JSON_ESC_U2028_U2029.yaml +@@ -0,0 +1,10 @@ ++define: DUK_OPT_NO_NONSTD_JSON_ESC_U2028_U2029 ++introduced: 1.1.0 ++tags: ++ - ecmascript ++description: > ++ By default Duktape JSON.stringify() will escape U+2028 and U+2029 which ++ is non-compliant behavior. This is the default to make JSON.stringify() ++ output valid when embedded in a web page or parsed with eval(). This ++ feature option enables the compliant behavior, i.e. no escaping for U+2028 ++ and U+2029. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_NONSTD_STRING_FROMCHARCODE_32BIT.yaml b/dist/source/config/feature-options/DUK_OPT_NO_NONSTD_STRING_FROMCHARCODE_32BIT.yaml +new file mode 100644 +index 0000000..ed5bfbd +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_NONSTD_STRING_FROMCHARCODE_32BIT.yaml +@@ -0,0 +1,9 @@ ++define: DUK_OPT_NO_NONSTD_STRING_FROMCHARCODE_32BIT ++introduced: 1.2.0 ++tags: ++ - ecmascript ++description: > ++ By default Duktape String.fromCharCode() allows 32-bit codepoints which is ++ non-compliant (the E5.1 specification has a ToUint16() coercion for the ++ codepoints) but useful because Duktape supports non-BMP strings. This ++ feature option restores the compliant behavior. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_OBJECT_ES6_PROTO_PROPERTY.yaml b/dist/source/config/feature-options/DUK_OPT_NO_OBJECT_ES6_PROTO_PROPERTY.yaml +new file mode 100644 +index 0000000..c25ce8a +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_OBJECT_ES6_PROTO_PROPERTY.yaml +@@ -0,0 +1,10 @@ ++define: DUK_OPT_NO_OBJECT_ES6_PROTO_PROPERTY ++introduced: 1.0.0 ++removed: 1.0.0 ++related: ++ - DUK_OPT_NO_ES6_OBJECT_PROTO_PROPERTY ++tags: ++ - ecmascript ++ - ecmascript6 ++description: > ++ Removed, use DUK_OPT_NO_ES6_OBJECT_PROTO_PROPERTY instead. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_OBJECT_ES6_SETPROTOTYPEOF.yaml b/dist/source/config/feature-options/DUK_OPT_NO_OBJECT_ES6_SETPROTOTYPEOF.yaml +new file mode 100644 +index 0000000..f2b930e +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_OBJECT_ES6_SETPROTOTYPEOF.yaml +@@ -0,0 +1,10 @@ ++define: DUK_OPT_NO_OBJECT_ES6_SETPROTOTYPEOF ++introduced: 1.0.0 ++removed: 1.0.0 ++related: ++ - DUK_OPT_NO_ES6_OBJECT_SETPROTOTYPEOF ++tags: ++ - ecmascript ++ - ecmascript6 ++description: > ++ Removed, use DUK_OPT_NO_ES6_OBJECT_SETPROTOTYPEOF instead. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_OCTAL_SUPPORT.yaml b/dist/source/config/feature-options/DUK_OPT_NO_OCTAL_SUPPORT.yaml +new file mode 100644 +index 0000000..482d9cc +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_OCTAL_SUPPORT.yaml +@@ -0,0 +1,7 @@ ++define: DUK_OPT_NO_OCTAL_SUPPORT ++introduced: 1.0.0 ++tags: ++ - ecmascript ++description: > ++ Disable optional octal number support (ECMAScript E5/E5.1 ++ Annex B: http://www.ecma-international.org/ecma-262/5.1/#sec-B). +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_PACKED_TVAL.yaml b/dist/source/config/feature-options/DUK_OPT_NO_PACKED_TVAL.yaml +new file mode 100644 +index 0000000..a00e468 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_PACKED_TVAL.yaml +@@ -0,0 +1,9 @@ ++define: DUK_OPT_NO_PACKED_TVAL ++introduced: 1.0.0 ++tags: ++ - memory ++ - portability ++description: > ++ Don't use the packed 8-byte internal value representation even if otherwise ++ possible. The packed representation has more platform/compiler portability ++ issues than the unpacked one. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_PC2LINE.yaml b/dist/source/config/feature-options/DUK_OPT_NO_PC2LINE.yaml +new file mode 100644 +index 0000000..dd46056 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_PC2LINE.yaml +@@ -0,0 +1,9 @@ ++define: DUK_OPT_NO_PC2LINE ++introduced: 1.0.0 ++tags: ++ - ecmascript ++description: > ++ Don't record a "pc2line" map into function instances. Without this map, ++ exceptions won't have meaningful line numbers (virtual machine program ++ counter values cannot be translated to line numbers) but function instances ++ will have a smaller footprint. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_REFERENCE_COUNTING.yaml b/dist/source/config/feature-options/DUK_OPT_NO_REFERENCE_COUNTING.yaml +new file mode 100644 +index 0000000..df1a218 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_REFERENCE_COUNTING.yaml +@@ -0,0 +1,9 @@ ++define: DUK_OPT_NO_REFERENCE_COUNTING ++introduced: 1.0.0 ++tags: ++ - gc ++ - memory ++description: > ++ Disable reference counting and use only mark-and-sweep for garbage ++ collection. Although this reduces memory footprint of heap objects, the ++ downside is much more fluctuation in memory usage. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_REGEXP_SUPPORT.yaml b/dist/source/config/feature-options/DUK_OPT_NO_REGEXP_SUPPORT.yaml +new file mode 100644 +index 0000000..fe20fa1 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_REGEXP_SUPPORT.yaml +@@ -0,0 +1,11 @@ ++define: DUK_OPT_NO_REGEXP_SUPPORT ++introduced: 1.0.0 ++tags: ++ - ecmascript ++description: > ++ Disable support for regular expressions. Regexp literals are treated as ++ a SyntaxError, RegExp constructor and prototype functions throw an error, ++ String.prototype.replace() throws an error if given a regexp search value, ++ String.prototype.split() throws an error if given a regexp separator value, ++ String.prototype.search() and String.prototype.match() throw an error ++ unconditionally. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_SECTION_B.yaml b/dist/source/config/feature-options/DUK_OPT_NO_SECTION_B.yaml +new file mode 100644 +index 0000000..683ad73 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_SECTION_B.yaml +@@ -0,0 +1,9 @@ ++define: DUK_OPT_NO_SECTION_B ++introduced: 1.0.0 ++tags: ++ - ecmascript ++description: > ++ Disable optional features in ECMAScript specification ++ Annex B: http://www.ecma-international.org/ecma-262/5.1/#sec-B. ++ Causes escape(), unescape(), and String.prototype.substr() to throw ++ an error. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_SOURCE_NONBMP.yaml b/dist/source/config/feature-options/DUK_OPT_NO_SOURCE_NONBMP.yaml +new file mode 100644 +index 0000000..0ddfad3 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_SOURCE_NONBMP.yaml +@@ -0,0 +1,7 @@ ++define: DUK_OPT_NO_SOURCE_NONBMP ++introduced: 1.0.0 ++tags: ++ - ecmascript ++description: > ++ Disable accurate Unicode support for non-BMP characters in source code. ++ Non-BMP characters are then always accepted as identifier characters. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_STRICT_DECL.yaml b/dist/source/config/feature-options/DUK_OPT_NO_STRICT_DECL.yaml +new file mode 100644 +index 0000000..e383290 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_STRICT_DECL.yaml +@@ -0,0 +1,8 @@ ++define: DUK_OPT_NO_STRICT_DECL ++introduced: 1.1.0 ++tags: ++ - ecmascript ++ - experimental ++description: > ++ Disable support for "use strict" declaration so that ECMAScript code is ++ always executed in non-strict mode. Duktape/C functions remain strict. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_TRACEBACKS.yaml b/dist/source/config/feature-options/DUK_OPT_NO_TRACEBACKS.yaml +new file mode 100644 +index 0000000..5f4d2c1 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_TRACEBACKS.yaml +@@ -0,0 +1,8 @@ ++define: DUK_OPT_NO_TRACEBACKS ++introduced: 1.0.0 ++tags: ++ - ecmascript ++description: > ++ Don't record traceback data into ECMAScript error objects (but still record ++ fileName and lineNumber). Reduces footprint and makes error handling a bit ++ faster, at the cost of less informative ECMAScript errors. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_VERBOSE_ERRORS.yaml b/dist/source/config/feature-options/DUK_OPT_NO_VERBOSE_ERRORS.yaml +new file mode 100644 +index 0000000..cadabc6 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_VERBOSE_ERRORS.yaml +@@ -0,0 +1,8 @@ ++define: DUK_OPT_NO_VERBOSE_ERRORS ++introduced: 1.0.0 ++tags: ++ - ecmascript ++description: > ++ Don't provide error message strings or filename/line information for errors ++ generated by Duktape. Reduces footprint, at the cost of much less ++ informative ECMAScript errors. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_VOLUNTARY_GC.yaml b/dist/source/config/feature-options/DUK_OPT_NO_VOLUNTARY_GC.yaml +new file mode 100644 +index 0000000..ff2f0c0 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_VOLUNTARY_GC.yaml +@@ -0,0 +1,13 @@ ++define: DUK_OPT_NO_VOLUNTARY_GC ++introduced: 1.0.0 ++tags: ++ - gc ++ - memory ++description: > ++ Disable voluntary periodic mark-and-sweep collection. A mark-and-sweep ++ collection is still triggered in an out-of-memory condition. This option ++ should usually be combined with reference counting, which collects all ++ non-cyclical garbage. Application code should also request an explicit ++ garbage collection from time to time when appropriate. When this option ++ is used, Duktape will have no garbage collection pauses in ordinary use, ++ which is useful for timing sensitive applications like games. +diff --git a/dist/source/config/feature-options/DUK_OPT_NO_ZERO_BUFFER_DATA.yaml b/dist/source/config/feature-options/DUK_OPT_NO_ZERO_BUFFER_DATA.yaml +new file mode 100644 +index 0000000..4696d49 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_NO_ZERO_BUFFER_DATA.yaml +@@ -0,0 +1,8 @@ ++define: DUK_OPT_NO_ZERO_BUFFER_DATA ++introduced: 1.0.0 ++tags: ++ - memory ++ - ecmascript ++description: > ++ By default Duktape zeroes data allocated for buffer values. Define ++ this to disable the zeroing (perhaps for performance reasons). +diff --git a/dist/source/config/feature-options/DUK_OPT_OBJSIZES16.yaml b/dist/source/config/feature-options/DUK_OPT_OBJSIZES16.yaml +new file mode 100644 +index 0000000..c0bd3da +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_OBJSIZES16.yaml +@@ -0,0 +1,11 @@ ++define: DUK_OPT_OBJSIZES16 ++introduced: 1.1.0 ++tags: ++ - lowmemory ++ - experimental ++description: > ++ Use a 16-bit object entry and array part sizes (for low memory ++ environments). Also automatically drops support for an object hash part ++ to further reduce memory usage; there are rarely large objects in low ++ memory environments simply because there's no memory to store a lot of ++ properties. +diff --git a/dist/source/config/feature-options/DUK_OPT_PANIC_HANDLER.yaml b/dist/source/config/feature-options/DUK_OPT_PANIC_HANDLER.yaml +new file mode 100644 +index 0000000..543061a +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_PANIC_HANDLER.yaml +@@ -0,0 +1,6 @@ ++define: DUK_OPT_PANIC_HANDLER ++introduced: 1.0.0 ++tags: ++ - portability ++description: > ++ Provide a custom panic handler. +diff --git a/dist/source/config/feature-options/DUK_OPT_REFCOUNT16.yaml b/dist/source/config/feature-options/DUK_OPT_REFCOUNT16.yaml +new file mode 100644 +index 0000000..3e824df +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_REFCOUNT16.yaml +@@ -0,0 +1,7 @@ ++define: DUK_OPT_REFCOUNT16 ++introduced: 1.1.0 ++tags: ++ - lowmemory ++ - experimental ++description: > ++ Use a 16-bit reference count field (for low memory environments). +diff --git a/dist/source/config/feature-options/DUK_OPT_SEGFAULT_ON_PANIC.yaml b/dist/source/config/feature-options/DUK_OPT_SEGFAULT_ON_PANIC.yaml +new file mode 100644 +index 0000000..bc2288c +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_SEGFAULT_ON_PANIC.yaml +@@ -0,0 +1,8 @@ ++define: DUK_OPT_SEGFAULT_ON_PANIC ++introduced: 1.0.0 ++tags: ++ - portability ++description: > ++ Cause the default panic handler to cause a segfault instead of using ++ abort() or exit(). This is useful when debugging with valgrind, ++ as a segfault provides a nice C traceback in valgrind. +diff --git a/dist/source/config/feature-options/DUK_OPT_SELF_TESTS.yaml b/dist/source/config/feature-options/DUK_OPT_SELF_TESTS.yaml +new file mode 100644 +index 0000000..e59c9d5 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_SELF_TESTS.yaml +@@ -0,0 +1,8 @@ ++define: DUK_OPT_SELF_TESTS ++introduced: 1.0.0 ++tags: ++ - debug ++description: > ++ Perform run-time self tests when a Duktape heap is created. Catches ++ platform/compiler problems which cannot be reliably detected during ++ compile time. Not enabled by default because of the extra footprint. +diff --git a/dist/source/config/feature-options/DUK_OPT_SETJMP.yaml b/dist/source/config/feature-options/DUK_OPT_SETJMP.yaml +new file mode 100644 +index 0000000..ff00d5b +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_SETJMP.yaml +@@ -0,0 +1,18 @@ ++define: DUK_OPT_SETJMP ++introduced: 1.1.0 ++removed: 1.5.0 ++tags: ++ - portability ++description: > ++ Force setjmp/longjmp for long control transfers. ++ ++ The default long control transfer provider is setjmp/longjmp because it ++ is the most portable option. When a better provider is known for a platform, ++ Duktape may default to that (e.g. _setjmp/_longjmp is the default for ++ macOS/iOS, see GH-55). With this feature option you can force Duktape to ++ explicitly use setjmp/longjmp even in these cases. ++ ++ The downside of setjmp/longjmp is that signal mask saving behavior is not ++ specified and varies between platforms. Signal mask saving may have a ++ significant performance impact so you may want to force a specific provider ++ if performance matters for your application. +diff --git a/dist/source/config/feature-options/DUK_OPT_SHUFFLE_TORTURE.yaml b/dist/source/config/feature-options/DUK_OPT_SHUFFLE_TORTURE.yaml +new file mode 100644 +index 0000000..fb2a5c3 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_SHUFFLE_TORTURE.yaml +@@ -0,0 +1,10 @@ ++define: DUK_OPT_SHUFFLE_TORTURE ++introduced: 1.2.0 ++tags: ++ - gc ++ - memory ++ - development ++description: > ++ Development time option: force compiler to shuffle every possible opcode ++ to stress shuffle behavior which is otherwise difficult to test for ++ comprehensively. +diff --git a/dist/source/config/feature-options/DUK_OPT_SIGSETJMP.yaml b/dist/source/config/feature-options/DUK_OPT_SIGSETJMP.yaml +new file mode 100644 +index 0000000..fdaede2 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_SIGSETJMP.yaml +@@ -0,0 +1,9 @@ ++define: DUK_OPT_SIGSETJMP ++introduced: 1.1.0 ++removed: 1.5.0 ++tags: ++ - portability ++description: > ++ Force sigsetjmp/siglongjmp with savesigs == 0 for long control ++ transfers (i.e. signal mask not saved/restored). See comments in ++ DUK_OPT_SETJMP. +diff --git a/dist/source/config/feature-options/DUK_OPT_STRHASH16.yaml b/dist/source/config/feature-options/DUK_OPT_STRHASH16.yaml +new file mode 100644 +index 0000000..2adeeef +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_STRHASH16.yaml +@@ -0,0 +1,7 @@ ++define: DUK_OPT_STRHASH16 ++introduced: 1.1.0 ++tags: ++ - lowmemory ++ - experimental ++description: > ++ Use a 16-bit string hash field (for low memory environments). +diff --git a/dist/source/config/feature-options/DUK_OPT_STRICT_UTF8_SOURCE.yaml b/dist/source/config/feature-options/DUK_OPT_STRICT_UTF8_SOURCE.yaml +new file mode 100644 +index 0000000..2d602fa +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_STRICT_UTF8_SOURCE.yaml +@@ -0,0 +1,9 @@ ++define: DUK_OPT_STRICT_UTF8_SOURCE ++introduced: 1.0.0 ++tags: ++ - ecmascript ++description: > ++ Enable strict UTF-8 parsing of source code. When enabled, non-shortest ++ encodings (normally invalid UTF-8) and surrogate pair codepoints are ++ accepted as valid source code characters. This option breaks compatibility ++ with some test262 tests. +diff --git a/dist/source/config/feature-options/DUK_OPT_STRLEN16.yaml b/dist/source/config/feature-options/DUK_OPT_STRLEN16.yaml +new file mode 100644 +index 0000000..848aa52 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_STRLEN16.yaml +@@ -0,0 +1,7 @@ ++define: DUK_OPT_STRLEN16 ++introduced: 1.1.0 ++tags: ++ - lowmemory ++ - experimental ++description: > ++ Use a 16-bit string length field (for low memory environments). +diff --git a/dist/source/config/feature-options/DUK_OPT_STRTAB_CHAIN.yaml b/dist/source/config/feature-options/DUK_OPT_STRTAB_CHAIN.yaml +new file mode 100644 +index 0000000..cefb795 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_STRTAB_CHAIN.yaml +@@ -0,0 +1,25 @@ ++define: DUK_OPT_STRTAB_CHAIN ++introduced: 1.1.0 ++related: ++ - DUK_OPT_STRTAB_CHAIN_SIZE ++tags: ++ - lowmemory ++description: > ++ Replace the default (open addressing, probing) string table structure with ++ one based on separate chaining. There is a fixed-size top level hash table ++ (whose size is defined using DUK_OPT_STRTAB_CHAIN_SIZE), with each entry in ++ the hash table being: (a) NULL, (b) a duk_hstring pointer, or (c) a pointer ++ to an array of duk_hstring pointers. The pointer arrays are gappy (the gaps ++ are reused on new inserts) and are never shrunk at the moment. ++ ++ This option is intended for low memory environments to make Duktape's memory ++ behavior match a typical pool-based allocator better as follows: ++ ++ The top level fixed structure never changes size, so there is no hash table ++ resize, and thus no need for resize temporaries. The default string table ++ algorithm needs resizing from time to time and doesn't resize in place, so ++ you effectively need twice the string table size temporarily during a resize. ++ ++ The pointer arrays vary in size, but their size (typically 8 to 64 bytes, ++ depending on the load factor) matches that of many other allocations which ++ works well with a pooled allocator. +diff --git a/dist/source/config/feature-options/DUK_OPT_STRTAB_CHAIN_SIZE.yaml b/dist/source/config/feature-options/DUK_OPT_STRTAB_CHAIN_SIZE.yaml +new file mode 100644 +index 0000000..4137a7c +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_STRTAB_CHAIN_SIZE.yaml +@@ -0,0 +1,8 @@ ++define: DUK_OPT_STRTAB_CHAIN_SIZE ++introduced: 1.1.0 ++requires: ++ - DUK_OPT_STRTAB_CHAIN ++tags: ++ - lowmemory ++description: > ++ Define stringtable size for DUK_OPT_STRTAB_CHAIN. +diff --git a/dist/source/config/feature-options/DUK_OPT_TARGET_INFO.yaml b/dist/source/config/feature-options/DUK_OPT_TARGET_INFO.yaml +new file mode 100644 +index 0000000..59d550c +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_TARGET_INFO.yaml +@@ -0,0 +1,8 @@ ++define: DUK_OPT_TARGET_INFO ++introduced: 1.2.0 ++tags: ++ - debugger ++description: > ++ Define a freeform human readable string to describe the target device (e.g. ++ "Arduino Yun"). This string will be sent as part of version/target info in ++ the debugger protocol and shows up in the debugger UI. +diff --git a/dist/source/config/feature-options/DUK_OPT_TRACEBACK_DEPTH.yaml b/dist/source/config/feature-options/DUK_OPT_TRACEBACK_DEPTH.yaml +new file mode 100644 +index 0000000..88e73b8 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_TRACEBACK_DEPTH.yaml +@@ -0,0 +1,6 @@ ++define: DUK_OPT_TRACEBACK_DEPTH ++introduced: 1.0.0 ++tags: ++ - ecmascript ++description: > ++ Override default traceback collection depth. The default is currently 10. +diff --git a/dist/source/config/feature-options/DUK_OPT_UNDERSCORE_SETJMP.yaml b/dist/source/config/feature-options/DUK_OPT_UNDERSCORE_SETJMP.yaml +new file mode 100644 +index 0000000..eeaf0ca +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_UNDERSCORE_SETJMP.yaml +@@ -0,0 +1,10 @@ ++define: DUK_OPT_UNDERSCORE_SETJMP ++introduced: 1.1.0 ++removed: 1.5.0 ++tags: ++ - portability ++description: > ++ Force _setjmp/_longjmp for long control transfers. This ensures signal ++ mask is not saved which can be a lot faster if setjmp/longjmp saves the ++ signal mask (this varies between platforms). See comments in ++ DUK_OPT_SETJMP. +diff --git a/dist/source/config/feature-options/DUK_OPT_USER_INITJS.yaml b/dist/source/config/feature-options/DUK_OPT_USER_INITJS.yaml +new file mode 100644 +index 0000000..af03442 +--- /dev/null ++++ b/dist/source/config/feature-options/DUK_OPT_USER_INITJS.yaml +@@ -0,0 +1,11 @@ ++define: DUK_OPT_USER_INITJS ++introduced: 1.0.0 ++tags: ++ - portability ++description: > ++ Provide a string to evaluate when a thread with new built-ins (a new global ++ environment) is created. This allows you to make minor modifications to the ++ global environment before any code is executed in it. The value must be a ++ string, e.g.:: -DDUK_OPT_USER_INITJS='"this.foo = 123"'. ++ ++ Errors in the initialization code result in a fatal error. +diff --git a/dist/source/config/header-snippets/64bitops.h.in b/dist/source/config/header-snippets/64bitops.h.in +new file mode 100644 +index 0000000..2dbbf11 +--- /dev/null ++++ b/dist/source/config/header-snippets/64bitops.h.in +@@ -0,0 +1,11 @@ ++/* Check whether we should use 64-bit integers or not. ++ * ++ * Quite incomplete now. Use 64-bit types if detected (C99 or other detection) ++ * unless they are known to be unreliable. For instance, 64-bit types are ++ * available on VBCC but seem to misbehave. ++ */ ++#if defined(DUK_F_HAVE_64BIT) && !defined(DUK_F_VBCC) ++#define DUK_USE_64BIT_OPS ++#else ++#undef DUK_USE_64BIT_OPS ++#endif +diff --git a/dist/source/config/header-snippets/alignment_fillin.h.in b/dist/source/config/header-snippets/alignment_fillin.h.in +new file mode 100644 +index 0000000..680f335 +--- /dev/null ++++ b/dist/source/config/header-snippets/alignment_fillin.h.in +@@ -0,0 +1,26 @@ ++/* ++ * Alignment requirement and support for unaligned accesses ++ * ++ * Assume unaligned accesses are not supported unless specifically allowed ++ * in the target platform. Some platforms may support unaligned accesses ++ * but alignment to 4 or 8 may still be desirable. Note that unaligned ++ * accesses (and even pointers) relative to natural alignment (regardless ++ * of target alignment) are technically undefined behavior and thus ++ * compiler/architecture specific. ++ */ ++ ++/* If not forced, use safe default for alignment. */ ++#if !defined(DUK_USE_ALIGN_BY) ++#define DUK_USE_ALIGN_BY 8 ++#endif ++ ++/* Compiler specific hackery needed to force struct size to match alignment, ++ * see e.g. duk_hbuffer.h. ++ * ++ * http://stackoverflow.com/questions/11130109/c-struct-size-alignment ++ * http://stackoverflow.com/questions/10951039/specifying-64-bit-alignment ++ */ ++#if !(defined(DUK_USE_PACK_MSVC_PRAGMA) || defined(DUK_USE_PACK_GCC_ATTR) || \ ++ defined(DUK_USE_PACK_CLANG_ATTR) || defined(DUK_USE_PACK_DUMMY_MEMBER)) ++#define DUK_USE_PACK_DUMMY_MEMBER ++#endif +diff --git a/dist/source/config/header-snippets/architecture_fillins.h.in b/dist/source/config/header-snippets/architecture_fillins.h.in +new file mode 100644 +index 0000000..e69de29 +diff --git a/dist/source/config/header-snippets/byteorder_derived.h.in b/dist/source/config/header-snippets/byteorder_derived.h.in +new file mode 100644 +index 0000000..5284b47 +--- /dev/null ++++ b/dist/source/config/header-snippets/byteorder_derived.h.in +@@ -0,0 +1,21 @@ ++/* ++ * Convert DUK_USE_BYTEORDER, from whatever source, into currently used ++ * internal defines. If detection failed, #error out. ++ */ ++ ++#if defined(DUK_USE_BYTEORDER) ++#if (DUK_USE_BYTEORDER == 1) ++#define DUK_USE_INTEGER_LE ++#define DUK_USE_DOUBLE_LE ++#elif (DUK_USE_BYTEORDER == 2) ++#define DUK_USE_INTEGER_LE /* integer endianness is little on purpose */ ++#define DUK_USE_DOUBLE_ME ++#elif (DUK_USE_BYTEORDER == 3) ++#define DUK_USE_INTEGER_BE ++#define DUK_USE_DOUBLE_BE ++#else ++#error unsupported: byte order invalid ++#endif /* byte order */ ++#else ++#error unsupported: byte order detection failed ++#endif /* defined(DUK_USE_BYTEORDER) */ +diff --git a/dist/source/config/header-snippets/byteorder_fillin.h.in b/dist/source/config/header-snippets/byteorder_fillin.h.in +new file mode 100644 +index 0000000..4432126 +--- /dev/null ++++ b/dist/source/config/header-snippets/byteorder_fillin.h.in +@@ -0,0 +1,109 @@ ++/* ++ * Byte order and double memory layout detection ++ * ++ * Endianness detection is a major portability hassle because the macros ++ * and headers are not standardized. There's even variance across UNIX ++ * platforms. Even with "standard" headers, details like underscore count ++ * varies between platforms, e.g. both __BYTE_ORDER and _BYTE_ORDER are used ++ * (Crossbridge has a single underscore, for instance). ++ * ++ * The checks below are structured with this in mind: several approaches are ++ * used, and at the end we check if any of them worked. This allows generic ++ * approaches to be tried first, and platform/compiler specific hacks tried ++ * last. As a last resort, the user can force a specific endianness, as it's ++ * not likely that automatic detection will work on the most exotic platforms. ++ * ++ * Duktape supports little and big endian machines. There's also support ++ * for a hybrid used by some ARM machines where integers are little endian ++ * but IEEE double values use a mixed order (12345678 -> 43218765). This ++ * byte order for doubles is referred to as "mixed endian". ++ */ ++ ++/* GCC and Clang provide endianness defines as built-in predefines, with ++ * leading and trailing double underscores (e.g. __BYTE_ORDER__). See ++ * output of "make gccpredefs" and "make clangpredefs". Clang doesn't ++ * seem to provide __FLOAT_WORD_ORDER__; assume not mixed endian for clang. ++ * http://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html ++ */ ++#if !defined(DUK_USE_BYTEORDER) && defined(__BYTE_ORDER__) ++#if defined(__ORDER_LITTLE_ENDIAN__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) ++#if defined(__FLOAT_WORD_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && (__FLOAT_WORD_ORDER__ == __ORDER_LITTLE_ENDIAN__) ++#define DUK_USE_BYTEORDER 1 ++#elif defined(__FLOAT_WORD_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && (__FLOAT_WORD_ORDER__ == __ORDER_BIG_ENDIAN__) ++#define DUK_USE_BYTEORDER 2 ++#elif !defined(__FLOAT_WORD_ORDER__) ++/* Float word order not known, assume not a hybrid. */ ++#define DUK_USE_BYTEORDER 1 ++#else ++/* Byte order is little endian but cannot determine IEEE double word order. */ ++#endif /* float word order */ ++#elif defined(__ORDER_BIG_ENDIAN__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) ++#if defined(__FLOAT_WORD_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && (__FLOAT_WORD_ORDER__ == __ORDER_BIG_ENDIAN__) ++#define DUK_USE_BYTEORDER 3 ++#elif !defined(__FLOAT_WORD_ORDER__) ++/* Float word order not known, assume not a hybrid. */ ++#define DUK_USE_BYTEORDER 3 ++#else ++/* Byte order is big endian but cannot determine IEEE double word order. */ ++#endif /* float word order */ ++#else ++/* Cannot determine byte order; __ORDER_PDP_ENDIAN__ is related to 32-bit ++ * integer ordering and is not relevant. ++ */ ++#endif /* integer byte order */ ++#endif /* !defined(DUK_USE_BYTEORDER) && defined(__BYTE_ORDER__) */ ++ ++/* More or less standard endianness predefines provided by header files. ++ * The ARM hybrid case is detected by assuming that __FLOAT_WORD_ORDER ++ * will be big endian, see: http://lists.mysql.com/internals/443. ++ * On some platforms some defines may be present with an empty value which ++ * causes comparisons to fail: https://github.com/svaarala/duktape/issues/453. ++ */ ++#if !defined(DUK_USE_BYTEORDER) ++#if defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && (__BYTE_ORDER == __LITTLE_ENDIAN) || \ ++ defined(_BYTE_ORDER) && defined(_LITTLE_ENDIAN) && (_BYTE_ORDER == _LITTLE_ENDIAN) || \ ++ defined(__LITTLE_ENDIAN__) ++#if defined(__FLOAT_WORD_ORDER) && defined(__LITTLE_ENDIAN) && (__FLOAT_WORD_ORDER == __LITTLE_ENDIAN) || \ ++ defined(_FLOAT_WORD_ORDER) && defined(_LITTLE_ENDIAN) && (_FLOAT_WORD_ORDER == _LITTLE_ENDIAN) ++#define DUK_USE_BYTEORDER 1 ++#elif defined(__FLOAT_WORD_ORDER) && defined(__BIG_ENDIAN) && (__FLOAT_WORD_ORDER == __BIG_ENDIAN) || \ ++ defined(_FLOAT_WORD_ORDER) && defined(_BIG_ENDIAN) && (_FLOAT_WORD_ORDER == _BIG_ENDIAN) ++#define DUK_USE_BYTEORDER 2 ++#elif !defined(__FLOAT_WORD_ORDER) && !defined(_FLOAT_WORD_ORDER) ++/* Float word order not known, assume not a hybrid. */ ++#define DUK_USE_BYTEORDER 1 ++#else ++/* Byte order is little endian but cannot determine IEEE double word order. */ ++#endif /* float word order */ ++#elif defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && (__BYTE_ORDER == __BIG_ENDIAN) || \ ++ defined(_BYTE_ORDER) && defined(_BIG_ENDIAN) && (_BYTE_ORDER == _BIG_ENDIAN) || \ ++ defined(__BIG_ENDIAN__) ++#if defined(__FLOAT_WORD_ORDER) && defined(__BIG_ENDIAN) && (__FLOAT_WORD_ORDER == __BIG_ENDIAN) || \ ++ defined(_FLOAT_WORD_ORDER) && defined(_BIG_ENDIAN) && (_FLOAT_WORD_ORDER == _BIG_ENDIAN) ++#define DUK_USE_BYTEORDER 3 ++#elif !defined(__FLOAT_WORD_ORDER) && !defined(_FLOAT_WORD_ORDER) ++/* Float word order not known, assume not a hybrid. */ ++#define DUK_USE_BYTEORDER 3 ++#else ++/* Byte order is big endian but cannot determine IEEE double word order. */ ++#endif /* float word order */ ++#else ++/* Cannot determine byte order. */ ++#endif /* integer byte order */ ++#endif /* !defined(DUK_USE_BYTEORDER) */ ++ ++/* QNX gcc cross compiler seems to define e.g. __LITTLEENDIAN__ or __BIGENDIAN__: ++ * $ /opt/qnx650/host/linux/x86/usr/bin/i486-pc-nto-qnx6.5.0-gcc -dM -E - > 56U) | \ ++ ((((duk_uint64_t) (x)) >> 40U) & DUK_U64_CONSTANT(0xff00)) | \ ++ ((((duk_uint64_t) (x)) >> 24U) & DUK_U64_CONSTANT(0xff0000)) | \ ++ ((((duk_uint64_t) (x)) >> 8U) & DUK_U64_CONSTANT(0xff000000)) | \ ++ ((((duk_uint64_t) (x)) << 8U) & DUK_U64_CONSTANT(0xff00000000)) | \ ++ ((((duk_uint64_t) (x)) << 24U) & DUK_U64_CONSTANT(0xff0000000000)) | \ ++ ((((duk_uint64_t) (x)) << 40U) & DUK_U64_CONSTANT(0xff000000000000)) | \ ++ (((duk_uint64_t) (x)) << 56U)) ++#endif ++#endif ++#if !defined(DUK_BSWAP32) ++#define DUK_BSWAP32(x) \ ++ ((((duk_uint32_t) (x)) >> 24U) | \ ++ ((((duk_uint32_t) (x)) >> 8U) & 0xff00UL) | \ ++ ((((duk_uint32_t) (x)) << 8U) & 0xff0000UL) | \ ++ (((duk_uint32_t) (x)) << 24U)) ++#endif ++#if !defined(DUK_BSWAP16) ++#define DUK_BSWAP16(x) \ ++ ((duk_uint16_t) (x) >> 8U) | \ ++ ((duk_uint16_t) (x) << 8U) ++#endif ++ ++/* DUK_USE_VARIADIC_MACROS: required from compilers, so no fill-in. */ ++/* DUK_USE_UNION_INITIALIZERS: required from compilers, so no fill-in. */ ++ ++#if !(defined(DUK_USE_FLEX_C99) || defined(DUK_USE_FLEX_ZEROSIZE) || defined(DUK_USE_FLEX_ONESIZE)) ++#if defined(DUK_F_C99) ++#define DUK_USE_FLEX_C99 ++#else ++#define DUK_USE_FLEX_ZEROSIZE /* Not standard but common enough */ ++#endif ++#endif ++ ++#if !(defined(DUK_USE_PACK_GCC_ATTR) || defined(DUK_USE_PACK_CLANG_ATTR) || \ ++ defined(DUK_USE_PACK_MSVC_PRAGMA) || defined(DUK_USE_PACK_DUMMY_MEMBER)) ++#define DUK_USE_PACK_DUMMY_MEMBER ++#endif ++ ++#if 0 /* not defined by default */ ++#undef DUK_USE_GCC_PRAGMAS ++#endif +diff --git a/dist/source/config/header-snippets/cpp_exception_sanity.h.in b/dist/source/config/header-snippets/cpp_exception_sanity.h.in +new file mode 100644 +index 0000000..334f926 +--- /dev/null ++++ b/dist/source/config/header-snippets/cpp_exception_sanity.h.in +@@ -0,0 +1,3 @@ ++#if defined(DUK_USE_CPP_EXCEPTIONS) && !defined(__cplusplus) ++#error DUK_USE_CPP_EXCEPTIONS enabled but not compiling with a C++ compiler ++#endif +diff --git a/dist/source/config/header-snippets/date_provider.h.in b/dist/source/config/header-snippets/date_provider.h.in +new file mode 100644 +index 0000000..15a4ac7 +--- /dev/null ++++ b/dist/source/config/header-snippets/date_provider.h.in +@@ -0,0 +1,66 @@ ++/* ++ * Date provider selection ++ * ++ * User may define DUK_USE_DATE_GET_NOW() etc directly, in which case we'll ++ * rely on an external provider. If this is not done, revert to previous ++ * behavior and use Unix/Windows built-in provider. ++ */ ++ ++#if defined(DUK_COMPILING_DUKTAPE) ++ ++#if defined(DUK_USE_DATE_GET_NOW) ++/* External provider already defined. */ ++#elif defined(DUK_USE_DATE_NOW_GETTIMEOFDAY) ++#define DUK_USE_DATE_GET_NOW(ctx) duk_bi_date_get_now_gettimeofday() ++#elif defined(DUK_USE_DATE_NOW_TIME) ++#define DUK_USE_DATE_GET_NOW(ctx) duk_bi_date_get_now_time() ++#elif defined(DUK_USE_DATE_NOW_WINDOWS) ++#define DUK_USE_DATE_GET_NOW(ctx) duk_bi_date_get_now_windows() ++#elif defined(DUK_USE_DATE_NOW_WINDOWS_SUBMS) ++#define DUK_USE_DATE_GET_NOW(ctx) duk_bi_date_get_now_windows_subms() ++#else ++#error no provider for DUK_USE_DATE_GET_NOW() ++#endif ++ ++#if defined(DUK_USE_DATE_GET_LOCAL_TZOFFSET) ++/* External provider already defined. */ ++#elif defined(DUK_USE_DATE_TZO_GMTIME_R) || defined(DUK_USE_DATE_TZO_GMTIME_S) || defined(DUK_USE_DATE_TZO_GMTIME) ++#define DUK_USE_DATE_GET_LOCAL_TZOFFSET(d) duk_bi_date_get_local_tzoffset_gmtime((d)) ++#elif defined(DUK_USE_DATE_TZO_WINDOWS) ++#define DUK_USE_DATE_GET_LOCAL_TZOFFSET(d) duk_bi_date_get_local_tzoffset_windows((d)) ++#elif defined(DUK_USE_DATE_TZO_WINDOWS_NO_DST) ++#define DUK_USE_DATE_GET_LOCAL_TZOFFSET(d) duk_bi_date_get_local_tzoffset_windows_no_dst((d)) ++#else ++#error no provider for DUK_USE_DATE_GET_LOCAL_TZOFFSET() ++#endif ++ ++#if defined(DUK_USE_DATE_PARSE_STRING) ++/* External provider already defined. */ ++#elif defined(DUK_USE_DATE_PRS_STRPTIME) ++#define DUK_USE_DATE_PARSE_STRING(ctx,str) duk_bi_date_parse_string_strptime((ctx), (str)) ++#elif defined(DUK_USE_DATE_PRS_GETDATE) ++#define DUK_USE_DATE_PARSE_STRING(ctx,str) duk_bi_date_parse_string_getdate((ctx), (str)) ++#else ++/* No provider for DUK_USE_DATE_PARSE_STRING(), fall back to ISO 8601 only. */ ++#endif ++ ++#if defined(DUK_USE_DATE_FORMAT_STRING) ++/* External provider already defined. */ ++#elif defined(DUK_USE_DATE_FMT_STRFTIME) ++#define DUK_USE_DATE_FORMAT_STRING(ctx,parts,tzoffset,flags) \ ++ duk_bi_date_format_parts_strftime((ctx), (parts), (tzoffset), (flags)) ++#else ++/* No provider for DUK_USE_DATE_FORMAT_STRING(), fall back to ISO 8601 only. */ ++#endif ++ ++#if defined(DUK_USE_GET_MONOTONIC_TIME) ++/* External provider already defined. */ ++#elif defined(DUK_USE_GET_MONOTONIC_TIME_CLOCK_GETTIME) ++#define DUK_USE_GET_MONOTONIC_TIME(ctx) duk_bi_date_get_monotonic_time_clock_gettime() ++#elif defined(DUK_USE_GET_MONOTONIC_TIME_WINDOWS_QPC) ++#define DUK_USE_GET_MONOTONIC_TIME(ctx) duk_bi_date_get_monotonic_time_windows_qpc() ++#else ++/* No provider for DUK_USE_GET_MONOTONIC_TIME(), fall back to DUK_USE_DATE_GET_NOW(). */ ++#endif ++ ++#endif /* DUK_COMPILING_DUKTAPE */ +diff --git a/dist/source/config/header-snippets/gcc_clang_visibility.h.in b/dist/source/config/header-snippets/gcc_clang_visibility.h.in +new file mode 100644 +index 0000000..7e806e2 +--- /dev/null ++++ b/dist/source/config/header-snippets/gcc_clang_visibility.h.in +@@ -0,0 +1,26 @@ ++#define DUK_EXTERNAL_DECL __attribute__ ((visibility("default"))) extern ++#define DUK_EXTERNAL __attribute__ ((visibility("default"))) ++#if defined(DUK_SINGLE_FILE) ++#if (defined(DUK_F_GCC_VERSION) && DUK_F_GCC_VERSION >= 30101) || defined(DUK_F_CLANG) ++/* Minimize warnings for unused internal functions with GCC >= 3.1.1 and ++ * Clang. Based on documentation it should suffice to have the attribute ++ * in the declaration only, but in practice some warnings are generated unless ++ * the attribute is also applied to the definition. ++ */ ++#define DUK_INTERNAL_DECL static __attribute__ ((unused)) ++#define DUK_INTERNAL static __attribute__ ((unused)) ++#else ++#define DUK_INTERNAL_DECL static ++#define DUK_INTERNAL static ++#endif ++#else ++#if (defined(DUK_F_GCC_VERSION) && DUK_F_GCC_VERSION >= 30101) || defined(DUK_F_CLANG) ++#define DUK_INTERNAL_DECL __attribute__ ((visibility("hidden"))) __attribute__ ((unused)) extern ++#define DUK_INTERNAL __attribute__ ((visibility("hidden"))) __attribute__ ((unused)) ++#else ++#define DUK_INTERNAL_DECL __attribute__ ((visibility("hidden"))) extern ++#define DUK_INTERNAL __attribute__ ((visibility("hidden"))) ++#endif ++#endif ++#define DUK_LOCAL_DECL static ++#define DUK_LOCAL static +diff --git a/dist/source/config/header-snippets/inline_workaround.h.in b/dist/source/config/header-snippets/inline_workaround.h.in +new file mode 100644 +index 0000000..75587c9 +--- /dev/null ++++ b/dist/source/config/header-snippets/inline_workaround.h.in +@@ -0,0 +1,11 @@ ++/* Workaround for GH-323: avoid inlining control when compiling from ++ * multiple sources, as it causes compiler portability trouble. ++ */ ++#if !defined(DUK_SINGLE_FILE) ++#undef DUK_NOINLINE ++#undef DUK_INLINE ++#undef DUK_ALWAYS_INLINE ++#define DUK_NOINLINE /*nop*/ ++#define DUK_INLINE /*nop*/ ++#define DUK_ALWAYS_INLINE /*nop*/ ++#endif +diff --git a/dist/source/config/header-snippets/msvc_visibility.h.in b/dist/source/config/header-snippets/msvc_visibility.h.in +new file mode 100644 +index 0000000..332a2b2 +--- /dev/null ++++ b/dist/source/config/header-snippets/msvc_visibility.h.in +@@ -0,0 +1,19 @@ ++/* MSVC dllexport/dllimport: appropriate __declspec depends on whether we're ++ * compiling Duktape or the application. ++ */ ++#if defined(DUK_COMPILING_DUKTAPE) ++#define DUK_EXTERNAL_DECL extern __declspec(dllexport) ++#define DUK_EXTERNAL __declspec(dllexport) ++#else ++#define DUK_EXTERNAL_DECL extern __declspec(dllimport) ++#define DUK_EXTERNAL should_not_happen ++#endif ++#if defined(DUK_SINGLE_FILE) ++#define DUK_INTERNAL_DECL static ++#define DUK_INTERNAL static ++#else ++#define DUK_INTERNAL_DECL extern ++#define DUK_INTERNAL /*empty*/ ++#endif ++#define DUK_LOCAL_DECL static ++#define DUK_LOCAL static +diff --git a/dist/source/config/header-snippets/object_layout.h.in b/dist/source/config/header-snippets/object_layout.h.in +new file mode 100644 +index 0000000..7b01c0a +--- /dev/null ++++ b/dist/source/config/header-snippets/object_layout.h.in +@@ -0,0 +1,21 @@ ++/* Object property allocation layout has implications for memory and code ++ * footprint and generated code size/speed. The best layout also depends ++ * on whether the platform has alignment requirements or benefits from ++ * having mostly aligned accesses. ++ */ ++#undef DUK_USE_HOBJECT_LAYOUT_1 ++#undef DUK_USE_HOBJECT_LAYOUT_2 ++#undef DUK_USE_HOBJECT_LAYOUT_3 ++#if (DUK_USE_ALIGN_BY == 1) ++/* On platforms without any alignment issues, layout 1 is preferable ++ * because it compiles to slightly less code and provides direct access ++ * to property keys. ++ */ ++#define DUK_USE_HOBJECT_LAYOUT_1 ++#else ++/* On other platforms use layout 2, which requires some padding but ++ * is a bit more natural than layout 3 in ordering the entries. Layout ++ * 3 is currently not used. ++ */ ++#define DUK_USE_HOBJECT_LAYOUT_2 ++#endif +diff --git a/dist/source/config/header-snippets/packed_tval_fillin.h.in b/dist/source/config/header-snippets/packed_tval_fillin.h.in +new file mode 100644 +index 0000000..3acdc20 +--- /dev/null ++++ b/dist/source/config/header-snippets/packed_tval_fillin.h.in +@@ -0,0 +1,40 @@ ++/* ++ * Check whether or not a packed duk_tval representation is possible. ++ * What's basically required is that pointers are 32-bit values ++ * (sizeof(void *) == 4). Best effort check, not always accurate. ++ * If guess goes wrong, crashes may result; self tests also verify ++ * the guess. ++ */ ++ ++/* Explicit marker needed; may be 'defined', 'undefined, 'or 'not provided'. */ ++#if !defined(DUK_F_PACKED_TVAL_PROVIDED) ++#undef DUK_F_PACKED_TVAL_POSSIBLE ++ ++/* Strict C99 case: DUK_UINTPTR_MAX (= UINTPTR_MAX) should be very reliable */ ++#if !defined(DUK_F_PACKED_TVAL_POSSIBLE) && defined(DUK_UINTPTR_MAX) ++#if (DUK_UINTPTR_MAX <= 0xffffffffUL) ++#define DUK_F_PACKED_TVAL_POSSIBLE ++#endif ++#endif ++ ++/* Non-C99 case, still relying on DUK_UINTPTR_MAX, as long as it is not a computed value */ ++#if !defined(DUK_F_PACKED_TVAL_POSSIBLE) && defined(DUK_UINTPTR_MAX) && !defined(DUK_UINTPTR_MAX_COMPUTED) ++#if (DUK_UINTPTR_MAX <= 0xffffffffUL) ++#define DUK_F_PACKED_TVAL_POSSIBLE ++#endif ++#endif ++ ++/* DUK_SIZE_MAX (= SIZE_MAX) is often reliable */ ++#if !defined(DUK_F_PACKED_TVAL_POSSIBLE) && defined(DUK_SIZE_MAX) && !defined(DUK_SIZE_MAX_COMPUTED) ++#if (DUK_SIZE_MAX <= 0xffffffffUL) ++#define DUK_F_PACKED_TVAL_POSSIBLE ++#endif ++#endif ++ ++#undef DUK_USE_PACKED_TVAL ++#if defined(DUK_F_PACKED_TVAL_POSSIBLE) ++#define DUK_USE_PACKED_TVAL ++#endif ++#undef DUK_F_PACKED_TVAL_POSSIBLE ++ ++#endif /* DUK_F_PACKED_TVAL_PROVIDED */ +diff --git a/dist/source/config/header-snippets/platform_conditionalincludes.h.in b/dist/source/config/header-snippets/platform_conditionalincludes.h.in +new file mode 100644 +index 0000000..0ad50b3 +--- /dev/null ++++ b/dist/source/config/header-snippets/platform_conditionalincludes.h.in +@@ -0,0 +1,4 @@ ++#if defined(DUK_F_CPP) && defined(DUK_USE_CPP_EXCEPTIONS) ++#include /* std::exception */ ++#include /* std::runtime_error */ ++#endif +diff --git a/dist/source/config/header-snippets/platform_cppextras.h.in b/dist/source/config/header-snippets/platform_cppextras.h.in +new file mode 100644 +index 0000000..2beccfd +--- /dev/null ++++ b/dist/source/config/header-snippets/platform_cppextras.h.in +@@ -0,0 +1,9 @@ ++/* Workaround for older C++ compilers before including , ++ * see e.g.: https://sourceware.org/bugzilla/show_bug.cgi?id=15366 ++ */ ++#if defined(__cplusplus) && !defined(__STDC_LIMIT_MACROS) ++#define __STDC_LIMIT_MACROS ++#endif ++#if defined(__cplusplus) && !defined(__STDC_CONSTANT_MACROS) ++#define __STDC_CONSTANT_MACROS ++#endif +diff --git a/dist/source/config/header-snippets/platform_fillins.h.in b/dist/source/config/header-snippets/platform_fillins.h.in +new file mode 100644 +index 0000000..be209b8 +--- /dev/null ++++ b/dist/source/config/header-snippets/platform_fillins.h.in +@@ -0,0 +1,327 @@ ++/* An abort()-like primitive is needed by the default fatal error handler. */ ++#if !defined(DUK_ABORT) ++#define DUK_ABORT abort ++#endif ++ ++#if !defined(DUK_SETJMP) ++#define DUK_JMPBUF_TYPE jmp_buf ++#define DUK_SETJMP(jb) setjmp((jb)) ++#define DUK_LONGJMP(jb) longjmp((jb), 1) ++#endif ++ ++#if 0 ++/* sigsetjmp() alternative */ ++#define DUK_JMPBUF_TYPE sigjmp_buf ++#define DUK_SETJMP(jb) sigsetjmp((jb)) ++#define DUK_LONGJMP(jb) siglongjmp((jb), 1) ++#endif ++ ++/* Special naming to avoid conflict with e.g. DUK_FREE() in duk_heap.h ++ * (which is unfortunately named). May sometimes need replacement, e.g. ++ * some compilers don't handle zero length or NULL correctly in realloc(). ++ */ ++#if !defined(DUK_ANSI_MALLOC) ++#define DUK_ANSI_MALLOC malloc ++#endif ++#if !defined(DUK_ANSI_REALLOC) ++#define DUK_ANSI_REALLOC realloc ++#endif ++#if !defined(DUK_ANSI_CALLOC) ++#define DUK_ANSI_CALLOC calloc ++#endif ++#if !defined(DUK_ANSI_FREE) ++#define DUK_ANSI_FREE free ++#endif ++ ++/* ANSI C (various versions) and some implementations require that the ++ * pointer arguments to memset(), memcpy(), and memmove() be valid values ++ * even when byte size is 0 (even a NULL pointer is considered invalid in ++ * this context). Zero-size operations as such are allowed, as long as their ++ * pointer arguments point to a valid memory area. The DUK_MEMSET(), ++ * DUK_MEMCPY(), and DUK_MEMMOVE() macros require this same behavior, i.e.: ++ * (1) pointers must be valid and non-NULL, (2) zero size must otherwise be ++ * allowed. If these are not fulfilled, a macro wrapper is needed. ++ * ++ * http://stackoverflow.com/questions/5243012/is-it-guaranteed-to-be-safe-to-perform-memcpy0-0-0 ++ * http://lists.cs.uiuc.edu/pipermail/llvmdev/2007-October/011065.html ++ * ++ * Not sure what's the required behavior when a pointer points just past the ++ * end of a buffer, which often happens in practice (e.g. zero size memmoves). ++ * For example, if allocation size is 3, the following pointer would not ++ * technically point to a valid memory byte: ++ * ++ * <-- alloc --> ++ * | 0 | 1 | 2 | ..... ++ * ^-- p=3, points after last valid byte (2) ++ */ ++#if !defined(DUK_MEMCPY) ++#if defined(DUK_F_UCLIBC) ++/* Old uclibcs have a broken memcpy so use memmove instead (this is overly wide ++ * now on purpose): http://lists.uclibc.org/pipermail/uclibc-cvs/2008-October/025511.html ++ */ ++#define DUK_MEMCPY memmove ++#else ++#define DUK_MEMCPY memcpy ++#endif ++#endif ++#if !defined(DUK_MEMMOVE) ++#define DUK_MEMMOVE memmove ++#endif ++#if !defined(DUK_MEMCMP) ++#define DUK_MEMCMP memcmp ++#endif ++#if !defined(DUK_MEMSET) ++#define DUK_MEMSET memset ++#endif ++#if !defined(DUK_STRLEN) ++#define DUK_STRLEN strlen ++#endif ++#if !defined(DUK_STRCMP) ++#define DUK_STRCMP strcmp ++#endif ++#if !defined(DUK_STRNCMP) ++#define DUK_STRNCMP strncmp ++#endif ++#if !defined(DUK_SPRINTF) ++#define DUK_SPRINTF sprintf ++#endif ++#if !defined(DUK_SNPRINTF) ++/* snprintf() is technically not part of C89 but usually available. */ ++#define DUK_SNPRINTF snprintf ++#endif ++#if !defined(DUK_VSPRINTF) ++#define DUK_VSPRINTF vsprintf ++#endif ++#if !defined(DUK_VSNPRINTF) ++/* vsnprintf() is technically not part of C89 but usually available. */ ++#define DUK_VSNPRINTF vsnprintf ++#endif ++#if !defined(DUK_SSCANF) ++#define DUK_SSCANF sscanf ++#endif ++#if !defined(DUK_VSSCANF) ++#define DUK_VSSCANF vsscanf ++#endif ++#if !defined(DUK_MEMZERO) ++#define DUK_MEMZERO(p,n) DUK_MEMSET((p), 0, (n)) ++#endif ++ ++#if !defined(DUK_DOUBLE_INFINITY) ++#undef DUK_USE_COMPUTED_INFINITY ++#if defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION < 40600) ++/* GCC older than 4.6: avoid overflow warnings related to using INFINITY */ ++#define DUK_DOUBLE_INFINITY (__builtin_inf()) ++#elif defined(INFINITY) ++#define DUK_DOUBLE_INFINITY ((double) INFINITY) ++#elif !defined(DUK_F_VBCC) && !defined(DUK_F_MSVC) && !defined(DUK_F_BCC) && \ ++ !defined(DUK_F_OLD_SOLARIS) && !defined(DUK_F_AIX) ++#define DUK_DOUBLE_INFINITY (1.0 / 0.0) ++#else ++/* In VBCC (1.0 / 0.0) results in a warning and 0.0 instead of infinity. ++ * Use a computed infinity (initialized when a heap is created at the ++ * latest). ++ */ ++#define DUK_USE_COMPUTED_INFINITY ++#define DUK_DOUBLE_INFINITY duk_computed_infinity ++#endif ++#endif ++ ++#if !defined(DUK_DOUBLE_NAN) ++#undef DUK_USE_COMPUTED_NAN ++#if defined(NAN) ++#define DUK_DOUBLE_NAN NAN ++#elif !defined(DUK_F_VBCC) && !defined(DUK_F_MSVC) && !defined(DUK_F_BCC) && \ ++ !defined(DUK_F_OLD_SOLARIS) && !defined(DUK_F_AIX) ++#define DUK_DOUBLE_NAN (0.0 / 0.0) ++#else ++/* In VBCC (0.0 / 0.0) results in a warning and 0.0 instead of NaN. ++ * In MSVC (VS2010 Express) (0.0 / 0.0) results in a compile error. ++ * Use a computed NaN (initialized when a heap is created at the ++ * latest). ++ */ ++#define DUK_USE_COMPUTED_NAN ++#define DUK_DOUBLE_NAN duk_computed_nan ++#endif ++#endif ++ ++/* Many platforms are missing fpclassify() and friends, so use replacements ++ * if necessary. The replacement constants (FP_NAN etc) can be anything but ++ * match Linux constants now. ++ */ ++#undef DUK_USE_REPL_FPCLASSIFY ++#undef DUK_USE_REPL_SIGNBIT ++#undef DUK_USE_REPL_ISFINITE ++#undef DUK_USE_REPL_ISNAN ++#undef DUK_USE_REPL_ISINF ++ ++/* Complex condition broken into separate parts. */ ++#undef DUK_F_USE_REPL_ALL ++#if !(defined(FP_NAN) && defined(FP_INFINITE) && defined(FP_ZERO) && \ ++ defined(FP_SUBNORMAL) && defined(FP_NORMAL)) ++/* Missing some obvious constants. */ ++#define DUK_F_USE_REPL_ALL ++#elif defined(DUK_F_AMIGAOS) && defined(DUK_F_VBCC) ++/* VBCC is missing the built-ins even in C99 mode (perhaps a header issue). */ ++#define DUK_F_USE_REPL_ALL ++#elif defined(DUK_F_AMIGAOS) && defined(DUK_F_M68K) ++/* AmigaOS + M68K seems to have math issues even when using GCC cross ++ * compilation. Use replacements for all AmigaOS versions on M68K ++ * regardless of compiler. ++ */ ++#define DUK_F_USE_REPL_ALL ++#elif defined(DUK_F_FREEBSD) && defined(DUK_F_CLANG) ++/* Placeholder fix for (detection is wider than necessary): ++ * http://llvm.org/bugs/show_bug.cgi?id=17788 ++ */ ++#define DUK_F_USE_REPL_ALL ++#elif defined(DUK_F_UCLIBC) ++/* At least some uclibc versions have broken floating point math. For ++ * example, fpclassify() can incorrectly classify certain NaN formats. ++ * To be safe, use replacements. ++ */ ++#define DUK_F_USE_REPL_ALL ++#elif defined(DUK_F_AIX) ++/* Older versions may be missing isnan(), etc. */ ++#define DUK_F_USE_REPL_ALL ++#endif ++ ++#if defined(DUK_F_USE_REPL_ALL) ++#define DUK_USE_REPL_FPCLASSIFY ++#define DUK_USE_REPL_SIGNBIT ++#define DUK_USE_REPL_ISFINITE ++#define DUK_USE_REPL_ISNAN ++#define DUK_USE_REPL_ISINF ++#define DUK_FPCLASSIFY duk_repl_fpclassify ++#define DUK_SIGNBIT duk_repl_signbit ++#define DUK_ISFINITE duk_repl_isfinite ++#define DUK_ISNAN duk_repl_isnan ++#define DUK_ISINF duk_repl_isinf ++#define DUK_FP_NAN 0 ++#define DUK_FP_INFINITE 1 ++#define DUK_FP_ZERO 2 ++#define DUK_FP_SUBNORMAL 3 ++#define DUK_FP_NORMAL 4 ++#else ++#define DUK_FPCLASSIFY fpclassify ++#define DUK_SIGNBIT signbit ++#define DUK_ISFINITE isfinite ++#define DUK_ISNAN isnan ++#define DUK_ISINF isinf ++#define DUK_FP_NAN FP_NAN ++#define DUK_FP_INFINITE FP_INFINITE ++#define DUK_FP_ZERO FP_ZERO ++#define DUK_FP_SUBNORMAL FP_SUBNORMAL ++#define DUK_FP_NORMAL FP_NORMAL ++#endif ++ ++#if defined(DUK_F_USE_REPL_ALL) ++#undef DUK_F_USE_REPL_ALL ++#endif ++ ++/* These functions don't currently need replacement but are wrapped for ++ * completeness. Because these are used as function pointers, they need ++ * to be defined as concrete C functions (not macros). ++ */ ++#if !defined(DUK_FABS) ++#define DUK_FABS fabs ++#endif ++#if !defined(DUK_FLOOR) ++#define DUK_FLOOR floor ++#endif ++#if !defined(DUK_CEIL) ++#define DUK_CEIL ceil ++#endif ++#if !defined(DUK_FMOD) ++#define DUK_FMOD fmod ++#endif ++#if !defined(DUK_POW) ++#define DUK_POW pow ++#endif ++#if !defined(DUK_ACOS) ++#define DUK_ACOS acos ++#endif ++#if !defined(DUK_ASIN) ++#define DUK_ASIN asin ++#endif ++#if !defined(DUK_ATAN) ++#define DUK_ATAN atan ++#endif ++#if !defined(DUK_ATAN2) ++#define DUK_ATAN2 atan2 ++#endif ++#if !defined(DUK_SIN) ++#define DUK_SIN sin ++#endif ++#if !defined(DUK_COS) ++#define DUK_COS cos ++#endif ++#if !defined(DUK_TAN) ++#define DUK_TAN tan ++#endif ++#if !defined(DUK_EXP) ++#define DUK_EXP exp ++#endif ++#if !defined(DUK_LOG) ++#define DUK_LOG log ++#endif ++#if !defined(DUK_SQRT) ++#define DUK_SQRT sqrt ++#endif ++ ++/* The functions below exist only in C99/C++11 or later and need a workaround ++ * for platforms that don't include them. MSVC isn't detected as C99, but ++ * these functions also exist in MSVC 2013 and later so include a clause for ++ * that too. Android doesn't have log2; disable all of these for Android. ++ */ ++#if (defined(DUK_F_C99) || defined(DUK_F_CPP11) || (defined(_MSC_VER) && (_MSC_VER >= 1800))) && \ ++ !defined(DUK_F_ANDROID) && !defined(DUK_F_MINT) ++#if !defined(DUK_CBRT) ++#define DUK_CBRT cbrt ++#endif ++#if !defined(DUK_LOG2) ++#define DUK_LOG2 log2 ++#endif ++#if !defined(DUK_LOG10) ++#define DUK_LOG10 log10 ++#endif ++#if !defined(DUK_TRUNC) ++#define DUK_TRUNC trunc ++#endif ++#endif /* DUK_F_C99 etc */ ++ ++/* NetBSD 6.0 x86 (at least) has a few problems with pow() semantics, ++ * see test-bug-netbsd-math-pow.js. MinGW has similar (but different) ++ * issues, see test-bug-mingw-math-issues.js. Enable pow() workarounds ++ * for these targets. ++ */ ++#undef DUK_USE_POW_WORKAROUNDS ++#if defined(DUK_F_NETBSD) || defined(DUK_F_MINGW) ++#define DUK_USE_POW_WORKAROUNDS ++#endif ++ ++/* Similar workarounds for atan2() semantics issues. MinGW issues are ++ * documented in test-bug-mingw-math-issues.js. ++ */ ++#undef DUK_USE_ATAN2_WORKAROUNDS ++#if defined(DUK_F_MINGW) ++#define DUK_USE_ATAN2_WORKAROUNDS ++#endif ++ ++/* Rely as little as possible on compiler behavior for NaN comparison, ++ * signed zero handling, etc. Currently never activated but may be needed ++ * for broken compilers. ++ */ ++#undef DUK_USE_PARANOID_MATH ++ ++/* There was a curious bug where test-bi-date-canceling.js would fail e.g. ++ * on 64-bit Ubuntu, gcc-4.8.1, -m32, and no -std=c99. Some date computations ++ * using doubles would be optimized which then broke some corner case tests. ++ * The problem goes away by adding 'volatile' to the datetime computations. ++ * Not sure what the actual triggering conditions are, but using this on ++ * non-C99 systems solves the known issues and has relatively little cost ++ * on other platforms. ++ */ ++#undef DUK_USE_PARANOID_DATE_COMPUTATION ++#if !defined(DUK_F_C99) ++#define DUK_USE_PARANOID_DATE_COMPUTATION ++#endif +diff --git a/dist/source/config/header-snippets/platform_sharedincludes.h.in b/dist/source/config/header-snippets/platform_sharedincludes.h.in +new file mode 100644 +index 0000000..4a3b8e3 +--- /dev/null ++++ b/dist/source/config/header-snippets/platform_sharedincludes.h.in +@@ -0,0 +1,24 @@ ++/* Shared includes: C89 */ ++#include ++#include ++#include ++#include /* varargs */ ++#include ++#include /* e.g. ptrdiff_t */ ++#include ++#include ++ ++/* date.h is omitted, and included per platform */ ++ ++/* Shared includes: stdint.h is C99 */ ++#if defined(DUK_F_NO_STDINT_H) ++/* stdint.h not available */ ++#else ++/* Technically C99 (C++11) but found in many systems. On some systems ++ * __STDC_LIMIT_MACROS and __STDC_CONSTANT_MACROS must be defined before ++ * including stdint.h (see above). ++ */ ++#include ++#endif ++ ++/* is only included if needed, based on DUK_USE_xxx flags. */ +diff --git a/dist/source/config/header-snippets/reject_fast_math.h.in b/dist/source/config/header-snippets/reject_fast_math.h.in +new file mode 100644 +index 0000000..d842023 +--- /dev/null ++++ b/dist/source/config/header-snippets/reject_fast_math.h.in +@@ -0,0 +1,6 @@ ++/* GCC/clang inaccurate math would break compliance and probably duk_tval, ++ * so refuse to compile. Relax this if -ffast-math is tested to work. ++ */ ++#if defined(__FAST_MATH__) ++#error __FAST_MATH__ defined, refusing to compile ++#endif +diff --git a/dist/source/config/header-snippets/types1.h.in b/dist/source/config/header-snippets/types1.h.in +new file mode 100644 +index 0000000..5544dae +--- /dev/null ++++ b/dist/source/config/header-snippets/types1.h.in +@@ -0,0 +1,78 @@ ++/* ++ * Wrapper typedefs and constants for integer types, also sanity check types. ++ * ++ * C99 typedefs are quite good but not always available, and we want to avoid ++ * forcibly redefining the C99 typedefs. So, there are Duktape wrappers for ++ * all C99 typedefs and Duktape code should only use these typedefs. Type ++ * detection when C99 is not supported is best effort and may end up detecting ++ * some types incorrectly. ++ * ++ * Pointer sizes are a portability problem: pointers to different types may ++ * have a different size and function pointers are very difficult to manage ++ * portably. ++ * ++ * http://en.wikipedia.org/wiki/C_data_types#Fixed-width_integer_types ++ * ++ * Note: there's an interesting corner case when trying to define minimum ++ * signed integer value constants which leads to the current workaround of ++ * defining e.g. -0x80000000 as (-0x7fffffffL - 1L). See doc/code-issues.txt ++ * for a longer discussion. ++ * ++ * Note: avoid typecasts and computations in macro integer constants as they ++ * can then no longer be used in macro relational expressions (such as ++ * #if DUK_SIZE_MAX < 0xffffffffUL). There is internal code which relies on ++ * being able to compare DUK_SIZE_MAX against a limit. ++ */ ++ ++/* XXX: add feature options to force basic types from outside? */ ++ ++#if !defined(INT_MAX) ++#error INT_MAX not defined ++#endif ++ ++/* Check that architecture is two's complement, standard C allows e.g. ++ * INT_MIN to be -2**31+1 (instead of -2**31). ++ */ ++#if defined(INT_MAX) && defined(INT_MIN) ++#if INT_MAX != -(INT_MIN + 1) ++#error platform does not seem complement of two ++#endif ++#else ++#error cannot check complement of two ++#endif ++ ++/* Pointer size determination based on __WORDSIZE or architecture when ++ * that's not available. ++ */ ++#if defined(DUK_F_X86) || defined(DUK_F_X32) || \ ++ defined(DUK_F_M68K) || defined(DUK_F_PPC32) || \ ++ defined(DUK_F_BCC) || \ ++ (defined(__WORDSIZE) && (__WORDSIZE == 32)) || \ ++ ((defined(DUK_F_OLD_SOLARIS) || defined(DUK_F_AIX) || \ ++ defined(DUK_F_HPUX)) && defined(_ILP32)) || \ ++ defined(DUK_F_ARM32) ++#define DUK_F_32BIT_PTRS ++#elif defined(DUK_F_X64) || \ ++ (defined(__WORDSIZE) && (__WORDSIZE == 64)) || \ ++ ((defined(DUK_F_OLD_SOLARIS) || defined(DUK_F_AIX) || \ ++ defined(DUK_F_HPUX)) && defined(_LP64)) || \ ++ defined(DUK_F_ARM64) ++#define DUK_F_64BIT_PTRS ++#else ++/* not sure, not needed with C99 anyway */ ++#endif ++ ++/* Intermediate define for 'have inttypes.h' */ ++#undef DUK_F_HAVE_INTTYPES ++#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ ++ !(defined(DUK_F_AMIGAOS) && defined(DUK_F_VBCC)) ++/* vbcc + AmigaOS has C99 but no inttypes.h */ ++#define DUK_F_HAVE_INTTYPES ++#elif defined(__cplusplus) && (__cplusplus >= 201103L) ++/* C++11 apparently ratified stdint.h */ ++#define DUK_F_HAVE_INTTYPES ++#endif ++ ++/* Basic integer typedefs and limits, preferably from inttypes.h, otherwise ++ * through automatic detection. ++ */ +diff --git a/dist/source/config/header-snippets/types2.h.in b/dist/source/config/header-snippets/types2.h.in +new file mode 100644 +index 0000000..39eaf32 +--- /dev/null ++++ b/dist/source/config/header-snippets/types2.h.in +@@ -0,0 +1,132 @@ ++/* A few types are assumed to always exist. */ ++typedef size_t duk_size_t; ++typedef ptrdiff_t duk_ptrdiff_t; ++ ++/* The best type for an "all around int" in Duktape internals is "at least ++ * 32 bit signed integer" which is most convenient. Same for unsigned type. ++ * Prefer 'int' when large enough, as it is almost always a convenient type. ++ */ ++#if defined(UINT_MAX) && (UINT_MAX >= 0xffffffffUL) ++typedef int duk_int_t; ++typedef unsigned int duk_uint_t; ++#define DUK_INT_MIN INT_MIN ++#define DUK_INT_MAX INT_MAX ++#define DUK_UINT_MIN 0 ++#define DUK_UINT_MAX UINT_MAX ++#else ++typedef duk_int_fast32_t duk_int_t; ++typedef duk_uint_fast32_t duk_uint_t; ++#define DUK_INT_MIN DUK_INT_FAST32_MIN ++#define DUK_INT_MAX DUK_INT_FAST32_MAX ++#define DUK_UINT_MIN DUK_UINT_FAST32_MIN ++#define DUK_UINT_MAX DUK_UINT_FAST32_MAX ++#endif ++ ++/* Same as 'duk_int_t' but guaranteed to be a 'fast' variant if this ++ * distinction matters for the CPU. These types are used mainly in the ++ * executor where it might really matter. ++ */ ++typedef duk_int_fast32_t duk_int_fast_t; ++typedef duk_uint_fast32_t duk_uint_fast_t; ++#define DUK_INT_FAST_MIN DUK_INT_FAST32_MIN ++#define DUK_INT_FAST_MAX DUK_INT_FAST32_MAX ++#define DUK_UINT_FAST_MIN DUK_UINT_FAST32_MIN ++#define DUK_UINT_FAST_MAX DUK_UINT_FAST32_MAX ++ ++/* Small integers (16 bits or more) can fall back to the 'int' type, but ++ * have a typedef so they are marked "small" explicitly. ++ */ ++typedef int duk_small_int_t; ++typedef unsigned int duk_small_uint_t; ++#define DUK_SMALL_INT_MIN INT_MIN ++#define DUK_SMALL_INT_MAX INT_MAX ++#define DUK_SMALL_UINT_MIN 0 ++#define DUK_SMALL_UINT_MAX UINT_MAX ++ ++/* Fast variants of small integers, again for really fast paths like the ++ * executor. ++ */ ++typedef duk_int_fast16_t duk_small_int_fast_t; ++typedef duk_uint_fast16_t duk_small_uint_fast_t; ++#define DUK_SMALL_INT_FAST_MIN DUK_INT_FAST16_MIN ++#define DUK_SMALL_INT_FAST_MAX DUK_INT_FAST16_MAX ++#define DUK_SMALL_UINT_FAST_MIN DUK_UINT_FAST16_MIN ++#define DUK_SMALL_UINT_FAST_MAX DUK_UINT_FAST16_MAX ++ ++/* Boolean values are represented with the platform 'unsigned int'. */ ++typedef duk_small_uint_t duk_bool_t; ++#define DUK_BOOL_MIN DUK_SMALL_UINT_MIN ++#define DUK_BOOL_MAX DUK_SMALL_UINT_MAX ++ ++/* Index values must have at least 32-bit signed range. */ ++typedef duk_int_t duk_idx_t; ++#define DUK_IDX_MIN DUK_INT_MIN ++#define DUK_IDX_MAX DUK_INT_MAX ++ ++/* Unsigned index variant. */ ++typedef duk_uint_t duk_uidx_t; ++#define DUK_UIDX_MIN DUK_UINT_MIN ++#define DUK_UIDX_MAX DUK_UINT_MAX ++ ++/* Array index values, could be exact 32 bits. ++ * Currently no need for signed duk_arridx_t. ++ */ ++typedef duk_uint_t duk_uarridx_t; ++#define DUK_UARRIDX_MIN DUK_UINT_MIN ++#define DUK_UARRIDX_MAX DUK_UINT_MAX ++ ++/* Duktape/C function return value, platform int is enough for now to ++ * represent 0, 1, or negative error code. Must be compatible with ++ * assigning truth values (e.g. duk_ret_t rc = (foo == bar);). ++ */ ++typedef duk_small_int_t duk_ret_t; ++#define DUK_RET_MIN DUK_SMALL_INT_MIN ++#define DUK_RET_MAX DUK_SMALL_INT_MAX ++ ++/* Error codes are represented with platform int. High bits are used ++ * for flags and such, so 32 bits are needed. ++ */ ++typedef duk_int_t duk_errcode_t; ++#define DUK_ERRCODE_MIN DUK_INT_MIN ++#define DUK_ERRCODE_MAX DUK_INT_MAX ++ ++/* Codepoint type. Must be 32 bits or more because it is used also for ++ * internal codepoints. The type is signed because negative codepoints ++ * are used as internal markers (e.g. to mark EOF or missing argument). ++ * (X)UTF-8/CESU-8 encode/decode take and return an unsigned variant to ++ * ensure duk_uint32_t casts back and forth nicely. Almost everything ++ * else uses the signed one. ++ */ ++typedef duk_int_t duk_codepoint_t; ++typedef duk_uint_t duk_ucodepoint_t; ++#define DUK_CODEPOINT_MIN DUK_INT_MIN ++#define DUK_CODEPOINT_MAX DUK_INT_MAX ++#define DUK_UCODEPOINT_MIN DUK_UINT_MIN ++#define DUK_UCODEPOINT_MAX DUK_UINT_MAX ++ ++/* IEEE float/double typedef. */ ++typedef float duk_float_t; ++typedef double duk_double_t; ++ ++/* We're generally assuming that we're working on a platform with a 32-bit ++ * address space. If DUK_SIZE_MAX is a typecast value (which is necessary ++ * if SIZE_MAX is missing), the check must be avoided because the ++ * preprocessor can't do a comparison. ++ */ ++#if !defined(DUK_SIZE_MAX) ++#error DUK_SIZE_MAX is undefined, probably missing SIZE_MAX ++#elif !defined(DUK_SIZE_MAX_COMPUTED) ++#if DUK_SIZE_MAX < 0xffffffffUL ++/* On some systems SIZE_MAX can be smaller than max unsigned 32-bit value ++ * which seems incorrect if size_t is (at least) an unsigned 32-bit type. ++ * However, it doesn't seem useful to error out compilation if this is the ++ * case. ++ */ ++#endif ++#endif ++ ++/* Type used in public API declarations and user code. Typedef maps to ++ * 'struct duk_hthread' like the 'duk_hthread' typedef which is used ++ * exclusively in internals. ++ */ ++typedef struct duk_hthread duk_context; +diff --git a/dist/source/config/header-snippets/types_c99.h.in b/dist/source/config/header-snippets/types_c99.h.in +new file mode 100644 +index 0000000..62afeab +--- /dev/null ++++ b/dist/source/config/header-snippets/types_c99.h.in +@@ -0,0 +1,95 @@ ++#define DUK_F_HAVE_64BIT ++#include ++ ++typedef uint8_t duk_uint8_t; ++typedef int8_t duk_int8_t; ++typedef uint16_t duk_uint16_t; ++typedef int16_t duk_int16_t; ++typedef uint32_t duk_uint32_t; ++typedef int32_t duk_int32_t; ++typedef uint64_t duk_uint64_t; ++typedef int64_t duk_int64_t; ++typedef uint_least8_t duk_uint_least8_t; ++typedef int_least8_t duk_int_least8_t; ++typedef uint_least16_t duk_uint_least16_t; ++typedef int_least16_t duk_int_least16_t; ++typedef uint_least32_t duk_uint_least32_t; ++typedef int_least32_t duk_int_least32_t; ++typedef uint_least64_t duk_uint_least64_t; ++typedef int_least64_t duk_int_least64_t; ++typedef uint_fast8_t duk_uint_fast8_t; ++typedef int_fast8_t duk_int_fast8_t; ++typedef uint_fast16_t duk_uint_fast16_t; ++typedef int_fast16_t duk_int_fast16_t; ++typedef uint_fast32_t duk_uint_fast32_t; ++typedef int_fast32_t duk_int_fast32_t; ++typedef uint_fast64_t duk_uint_fast64_t; ++typedef int_fast64_t duk_int_fast64_t; ++typedef uintptr_t duk_uintptr_t; ++typedef intptr_t duk_intptr_t; ++typedef uintmax_t duk_uintmax_t; ++typedef intmax_t duk_intmax_t; ++ ++#define DUK_UINT8_MIN 0 ++#define DUK_UINT8_MAX UINT8_MAX ++#define DUK_INT8_MIN INT8_MIN ++#define DUK_INT8_MAX INT8_MAX ++#define DUK_UINT_LEAST8_MIN 0 ++#define DUK_UINT_LEAST8_MAX UINT_LEAST8_MAX ++#define DUK_INT_LEAST8_MIN INT_LEAST8_MIN ++#define DUK_INT_LEAST8_MAX INT_LEAST8_MAX ++#define DUK_UINT_FAST8_MIN 0 ++#define DUK_UINT_FAST8_MAX UINT_FAST8_MAX ++#define DUK_INT_FAST8_MIN INT_FAST8_MIN ++#define DUK_INT_FAST8_MAX INT_FAST8_MAX ++#define DUK_UINT16_MIN 0 ++#define DUK_UINT16_MAX UINT16_MAX ++#define DUK_INT16_MIN INT16_MIN ++#define DUK_INT16_MAX INT16_MAX ++#define DUK_UINT_LEAST16_MIN 0 ++#define DUK_UINT_LEAST16_MAX UINT_LEAST16_MAX ++#define DUK_INT_LEAST16_MIN INT_LEAST16_MIN ++#define DUK_INT_LEAST16_MAX INT_LEAST16_MAX ++#define DUK_UINT_FAST16_MIN 0 ++#define DUK_UINT_FAST16_MAX UINT_FAST16_MAX ++#define DUK_INT_FAST16_MIN INT_FAST16_MIN ++#define DUK_INT_FAST16_MAX INT_FAST16_MAX ++#define DUK_UINT32_MIN 0 ++#define DUK_UINT32_MAX UINT32_MAX ++#define DUK_INT32_MIN INT32_MIN ++#define DUK_INT32_MAX INT32_MAX ++#define DUK_UINT_LEAST32_MIN 0 ++#define DUK_UINT_LEAST32_MAX UINT_LEAST32_MAX ++#define DUK_INT_LEAST32_MIN INT_LEAST32_MIN ++#define DUK_INT_LEAST32_MAX INT_LEAST32_MAX ++#define DUK_UINT_FAST32_MIN 0 ++#define DUK_UINT_FAST32_MAX UINT_FAST32_MAX ++#define DUK_INT_FAST32_MIN INT_FAST32_MIN ++#define DUK_INT_FAST32_MAX INT_FAST32_MAX ++#define DUK_UINT64_MIN 0 ++#define DUK_UINT64_MAX UINT64_MAX ++#define DUK_INT64_MIN INT64_MIN ++#define DUK_INT64_MAX INT64_MAX ++#define DUK_UINT_LEAST64_MIN 0 ++#define DUK_UINT_LEAST64_MAX UINT_LEAST64_MAX ++#define DUK_INT_LEAST64_MIN INT_LEAST64_MIN ++#define DUK_INT_LEAST64_MAX INT_LEAST64_MAX ++#define DUK_UINT_FAST64_MIN 0 ++#define DUK_UINT_FAST64_MAX UINT_FAST64_MAX ++#define DUK_INT_FAST64_MIN INT_FAST64_MIN ++#define DUK_INT_FAST64_MAX INT_FAST64_MAX ++ ++#define DUK_UINTPTR_MIN 0 ++#define DUK_UINTPTR_MAX UINTPTR_MAX ++#define DUK_INTPTR_MIN INTPTR_MIN ++#define DUK_INTPTR_MAX INTPTR_MAX ++#undef DUK_UINTPTR_MAX_COMPUTED ++ ++#define DUK_UINTMAX_MIN 0 ++#define DUK_UINTMAX_MAX UINTMAX_MAX ++#define DUK_INTMAX_MIN INTMAX_MIN ++#define DUK_INTMAX_MAX INTMAX_MAX ++ ++#define DUK_SIZE_MIN 0 ++#define DUK_SIZE_MAX SIZE_MAX ++#undef DUK_SIZE_MAX_COMPUTED +diff --git a/dist/source/config/header-snippets/types_legacy.h.in b/dist/source/config/header-snippets/types_legacy.h.in +new file mode 100644 +index 0000000..45ffbd3 +--- /dev/null ++++ b/dist/source/config/header-snippets/types_legacy.h.in +@@ -0,0 +1,242 @@ ++/* When C99 types are not available, we use heuristic detection to get ++ * the basic 8, 16, 32, and (possibly) 64 bit types. The fast/least ++ * types are then assumed to be exactly the same for now: these could ++ * be improved per platform but C99 types are very often now available. ++ * 64-bit types are not available on all platforms; this is OK at least ++ * on 32-bit platforms. ++ * ++ * This detection code is necessarily a bit hacky and can provide typedefs ++ * and defines that won't work correctly on some exotic platform. ++ */ ++ ++#if (defined(CHAR_BIT) && (CHAR_BIT == 8)) || \ ++ (defined(UCHAR_MAX) && (UCHAR_MAX == 255)) ++typedef unsigned char duk_uint8_t; ++typedef signed char duk_int8_t; ++#else ++#error cannot detect 8-bit type ++#endif ++ ++#if defined(USHRT_MAX) && (USHRT_MAX == 65535UL) ++typedef unsigned short duk_uint16_t; ++typedef signed short duk_int16_t; ++#elif defined(UINT_MAX) && (UINT_MAX == 65535UL) ++/* On some platforms int is 16-bit but long is 32-bit (e.g. PureC) */ ++typedef unsigned int duk_uint16_t; ++typedef signed int duk_int16_t; ++#else ++#error cannot detect 16-bit type ++#endif ++ ++#if defined(UINT_MAX) && (UINT_MAX == 4294967295UL) ++typedef unsigned int duk_uint32_t; ++typedef signed int duk_int32_t; ++#elif defined(ULONG_MAX) && (ULONG_MAX == 4294967295UL) ++/* On some platforms int is 16-bit but long is 32-bit (e.g. PureC) */ ++typedef unsigned long duk_uint32_t; ++typedef signed long duk_int32_t; ++#else ++#error cannot detect 32-bit type ++#endif ++ ++/* 64-bit type detection is a bit tricky. ++ * ++ * ULLONG_MAX is a standard define. __LONG_LONG_MAX__ and __ULONG_LONG_MAX__ ++ * are used by at least GCC (even if system headers don't provide ULLONG_MAX). ++ * Some GCC variants may provide __LONG_LONG_MAX__ but not __ULONG_LONG_MAX__. ++ * ++ * ULL / LL constants are rejected / warned about by some compilers, even if ++ * the compiler has a 64-bit type and the compiler/system headers provide an ++ * unsupported constant (ULL/LL)! Try to avoid using ULL / LL constants. ++ * As a side effect we can only check that e.g. ULONG_MAX is larger than 32 ++ * bits but can't be sure it is exactly 64 bits. Self tests will catch such ++ * cases. ++ */ ++#undef DUK_F_HAVE_64BIT ++#if !defined(DUK_F_HAVE_64BIT) && defined(ULONG_MAX) ++#if (ULONG_MAX > 4294967295UL) ++#define DUK_F_HAVE_64BIT ++typedef unsigned long duk_uint64_t; ++typedef signed long duk_int64_t; ++#endif ++#endif ++#if !defined(DUK_F_HAVE_64BIT) && defined(ULLONG_MAX) ++#if (ULLONG_MAX > 4294967295UL) ++#define DUK_F_HAVE_64BIT ++typedef unsigned long long duk_uint64_t; ++typedef signed long long duk_int64_t; ++#endif ++#endif ++#if !defined(DUK_F_HAVE_64BIT) && defined(__ULONG_LONG_MAX__) ++#if (__ULONG_LONG_MAX__ > 4294967295UL) ++#define DUK_F_HAVE_64BIT ++typedef unsigned long long duk_uint64_t; ++typedef signed long long duk_int64_t; ++#endif ++#endif ++#if !defined(DUK_F_HAVE_64BIT) && defined(__LONG_LONG_MAX__) ++#if (__LONG_LONG_MAX__ > 2147483647L) ++#define DUK_F_HAVE_64BIT ++typedef unsigned long long duk_uint64_t; ++typedef signed long long duk_int64_t; ++#endif ++#endif ++#if !defined(DUK_F_HAVE_64BIT) && defined(DUK_F_MINGW) ++#define DUK_F_HAVE_64BIT ++typedef unsigned long duk_uint64_t; ++typedef signed long duk_int64_t; ++#endif ++#if !defined(DUK_F_HAVE_64BIT) && defined(DUK_F_MSVC) ++#define DUK_F_HAVE_64BIT ++typedef unsigned __int64 duk_uint64_t; ++typedef signed __int64 duk_int64_t; ++#endif ++#if !defined(DUK_F_HAVE_64BIT) ++/* cannot detect 64-bit type, not always needed so don't error */ ++#endif ++ ++typedef duk_uint8_t duk_uint_least8_t; ++typedef duk_int8_t duk_int_least8_t; ++typedef duk_uint16_t duk_uint_least16_t; ++typedef duk_int16_t duk_int_least16_t; ++typedef duk_uint32_t duk_uint_least32_t; ++typedef duk_int32_t duk_int_least32_t; ++typedef duk_uint8_t duk_uint_fast8_t; ++typedef duk_int8_t duk_int_fast8_t; ++typedef duk_uint16_t duk_uint_fast16_t; ++typedef duk_int16_t duk_int_fast16_t; ++typedef duk_uint32_t duk_uint_fast32_t; ++typedef duk_int32_t duk_int_fast32_t; ++#if defined(DUK_F_HAVE_64BIT) ++typedef duk_uint64_t duk_uint_least64_t; ++typedef duk_int64_t duk_int_least64_t; ++typedef duk_uint64_t duk_uint_fast64_t; ++typedef duk_int64_t duk_int_fast64_t; ++#endif ++#if defined(DUK_F_HAVE_64BIT) ++typedef duk_uint64_t duk_uintmax_t; ++typedef duk_int64_t duk_intmax_t; ++#else ++typedef duk_uint32_t duk_uintmax_t; ++typedef duk_int32_t duk_intmax_t; ++#endif ++ ++/* Note: the funny looking computations for signed minimum 16-bit, 32-bit, and ++ * 64-bit values are intentional as the obvious forms (e.g. -0x80000000L) are ++ * -not- portable. See code-issues.txt for a detailed discussion. ++ */ ++#define DUK_UINT8_MIN 0UL ++#define DUK_UINT8_MAX 0xffUL ++#define DUK_INT8_MIN (-0x80L) ++#define DUK_INT8_MAX 0x7fL ++#define DUK_UINT_LEAST8_MIN 0UL ++#define DUK_UINT_LEAST8_MAX 0xffUL ++#define DUK_INT_LEAST8_MIN (-0x80L) ++#define DUK_INT_LEAST8_MAX 0x7fL ++#define DUK_UINT_FAST8_MIN 0UL ++#define DUK_UINT_FAST8_MAX 0xffUL ++#define DUK_INT_FAST8_MIN (-0x80L) ++#define DUK_INT_FAST8_MAX 0x7fL ++#define DUK_UINT16_MIN 0UL ++#define DUK_UINT16_MAX 0xffffUL ++#define DUK_INT16_MIN (-0x7fffL - 1L) ++#define DUK_INT16_MAX 0x7fffL ++#define DUK_UINT_LEAST16_MIN 0UL ++#define DUK_UINT_LEAST16_MAX 0xffffUL ++#define DUK_INT_LEAST16_MIN (-0x7fffL - 1L) ++#define DUK_INT_LEAST16_MAX 0x7fffL ++#define DUK_UINT_FAST16_MIN 0UL ++#define DUK_UINT_FAST16_MAX 0xffffUL ++#define DUK_INT_FAST16_MIN (-0x7fffL - 1L) ++#define DUK_INT_FAST16_MAX 0x7fffL ++#define DUK_UINT32_MIN 0UL ++#define DUK_UINT32_MAX 0xffffffffUL ++#define DUK_INT32_MIN (-0x7fffffffL - 1L) ++#define DUK_INT32_MAX 0x7fffffffL ++#define DUK_UINT_LEAST32_MIN 0UL ++#define DUK_UINT_LEAST32_MAX 0xffffffffUL ++#define DUK_INT_LEAST32_MIN (-0x7fffffffL - 1L) ++#define DUK_INT_LEAST32_MAX 0x7fffffffL ++#define DUK_UINT_FAST32_MIN 0UL ++#define DUK_UINT_FAST32_MAX 0xffffffffUL ++#define DUK_INT_FAST32_MIN (-0x7fffffffL - 1L) ++#define DUK_INT_FAST32_MAX 0x7fffffffL ++ ++/* 64-bit constants. Since LL / ULL constants are not always available, ++ * use computed values. These values can't be used in preprocessor ++ * comparisons; flag them as such. ++ */ ++#if defined(DUK_F_HAVE_64BIT) ++#define DUK_UINT64_MIN ((duk_uint64_t) 0) ++#define DUK_UINT64_MAX ((duk_uint64_t) -1) ++#define DUK_INT64_MIN ((duk_int64_t) (~(DUK_UINT64_MAX >> 1))) ++#define DUK_INT64_MAX ((duk_int64_t) (DUK_UINT64_MAX >> 1)) ++#define DUK_UINT_LEAST64_MIN DUK_UINT64_MIN ++#define DUK_UINT_LEAST64_MAX DUK_UINT64_MAX ++#define DUK_INT_LEAST64_MIN DUK_INT64_MIN ++#define DUK_INT_LEAST64_MAX DUK_INT64_MAX ++#define DUK_UINT_FAST64_MIN DUK_UINT64_MIN ++#define DUK_UINT_FAST64_MAX DUK_UINT64_MAX ++#define DUK_INT_FAST64_MIN DUK_INT64_MIN ++#define DUK_INT_FAST64_MAX DUK_INT64_MAX ++#define DUK_UINT64_MIN_COMPUTED ++#define DUK_UINT64_MAX_COMPUTED ++#define DUK_INT64_MIN_COMPUTED ++#define DUK_INT64_MAX_COMPUTED ++#define DUK_UINT_LEAST64_MIN_COMPUTED ++#define DUK_UINT_LEAST64_MAX_COMPUTED ++#define DUK_INT_LEAST64_MIN_COMPUTED ++#define DUK_INT_LEAST64_MAX_COMPUTED ++#define DUK_UINT_FAST64_MIN_COMPUTED ++#define DUK_UINT_FAST64_MAX_COMPUTED ++#define DUK_INT_FAST64_MIN_COMPUTED ++#define DUK_INT_FAST64_MAX_COMPUTED ++#endif ++ ++#if defined(DUK_F_HAVE_64BIT) ++#define DUK_UINTMAX_MIN DUK_UINT64_MIN ++#define DUK_UINTMAX_MAX DUK_UINT64_MAX ++#define DUK_INTMAX_MIN DUK_INT64_MIN ++#define DUK_INTMAX_MAX DUK_INT64_MAX ++#define DUK_UINTMAX_MIN_COMPUTED ++#define DUK_UINTMAX_MAX_COMPUTED ++#define DUK_INTMAX_MIN_COMPUTED ++#define DUK_INTMAX_MAX_COMPUTED ++#else ++#define DUK_UINTMAX_MIN 0UL ++#define DUK_UINTMAX_MAX 0xffffffffUL ++#define DUK_INTMAX_MIN (-0x7fffffffL - 1L) ++#define DUK_INTMAX_MAX 0x7fffffffL ++#endif ++ ++/* This detection is not very reliable. */ ++#if defined(DUK_F_32BIT_PTRS) ++typedef duk_int32_t duk_intptr_t; ++typedef duk_uint32_t duk_uintptr_t; ++#define DUK_UINTPTR_MIN DUK_UINT32_MIN ++#define DUK_UINTPTR_MAX DUK_UINT32_MAX ++#define DUK_INTPTR_MIN DUK_INT32_MIN ++#define DUK_INTPTR_MAX DUK_INT32_MAX ++#elif defined(DUK_F_64BIT_PTRS) && defined(DUK_F_HAVE_64BIT) ++typedef duk_int64_t duk_intptr_t; ++typedef duk_uint64_t duk_uintptr_t; ++#define DUK_UINTPTR_MIN DUK_UINT64_MIN ++#define DUK_UINTPTR_MAX DUK_UINT64_MAX ++#define DUK_INTPTR_MIN DUK_INT64_MIN ++#define DUK_INTPTR_MAX DUK_INT64_MAX ++#define DUK_UINTPTR_MIN_COMPUTED ++#define DUK_UINTPTR_MAX_COMPUTED ++#define DUK_INTPTR_MIN_COMPUTED ++#define DUK_INTPTR_MAX_COMPUTED ++#else ++#error cannot determine intptr type ++#endif ++ ++/* SIZE_MAX may be missing so use an approximate value for it. */ ++#undef DUK_SIZE_MAX_COMPUTED ++#if !defined(SIZE_MAX) ++#define DUK_SIZE_MAX_COMPUTED ++#define SIZE_MAX ((size_t) (-1)) ++#endif ++#define DUK_SIZE_MIN 0 ++#define DUK_SIZE_MAX SIZE_MAX +diff --git a/dist/source/config/helper-snippets/DUK_F_AIX.h.in b/dist/source/config/helper-snippets/DUK_F_AIX.h.in +new file mode 100644 +index 0000000..d026f18 +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_AIX.h.in +@@ -0,0 +1,5 @@ ++/* AIX */ ++#if defined(_AIX) ++/* defined(__xlc__) || defined(__IBMC__): works but too wide */ ++#define DUK_F_AIX ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_AMIGAOS.h.in b/dist/source/config/helper-snippets/DUK_F_AMIGAOS.h.in +new file mode 100644 +index 0000000..6bdc5d6 +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_AMIGAOS.h.in +@@ -0,0 +1,6 @@ ++/* AmigaOS. Neither AMIGA nor __amigaos__ is defined on VBCC, so user must ++ * define 'AMIGA' manually when using VBCC. ++ */ ++#if defined(AMIGA) || defined(__amigaos__) ++#define DUK_F_AMIGAOS ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_ANDROID.h.in b/dist/source/config/helper-snippets/DUK_F_ANDROID.h.in +new file mode 100644 +index 0000000..5ebb645 +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_ANDROID.h.in +@@ -0,0 +1,3 @@ ++#if defined(ANDROID) || defined(__ANDROID__) ++#define DUK_F_ANDROID ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_APPLE.h.in b/dist/source/config/helper-snippets/DUK_F_APPLE.h.in +new file mode 100644 +index 0000000..4a934ee +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_APPLE.h.in +@@ -0,0 +1,4 @@ ++/* Apple macOS, iOS, watchOS, tvOS, Darwin, etc */ ++#if defined(__APPLE__) ++#define DUK_F_APPLE ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_ARM.h.in b/dist/source/config/helper-snippets/DUK_F_ARM.h.in +new file mode 100644 +index 0000000..df84337 +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_ARM.h.in +@@ -0,0 +1,9 @@ ++/* ARM */ ++#if defined(__arm__) || defined(__thumb__) || defined(_ARM) || defined(_M_ARM) || defined(_M_ARM64) || defined(__aarch64__) ++#define DUK_F_ARM ++#if defined(__LP64__) || defined(_LP64) || defined(__arm64) || defined(__arm64__) || defined(_M_ARM64) || defined(__aarch64__) ++#define DUK_F_ARM64 ++#else ++#define DUK_F_ARM32 ++#endif ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_BCC.h.in b/dist/source/config/helper-snippets/DUK_F_BCC.h.in +new file mode 100644 +index 0000000..f8442cb +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_BCC.h.in +@@ -0,0 +1,4 @@ ++/* BCC (Bruce's C compiler): this is a "torture target" for compilation */ ++#if defined(__BCC__) || defined(__BCC_VERSION__) ++#define DUK_F_BCC ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_BSD.h.in b/dist/source/config/helper-snippets/DUK_F_BSD.h.in +new file mode 100644 +index 0000000..b1577e6 +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_BSD.h.in +@@ -0,0 +1,5 @@ ++/* BSD variant */ ++#if defined(DUK_F_FREEBSD) || defined(DUK_F_NETBSD) || defined(DUK_F_OPENBSD) || \ ++ defined(__bsdi__) || defined(__DragonFly__) ++#define DUK_F_BSD ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_C99.h.in b/dist/source/config/helper-snippets/DUK_F_C99.h.in +new file mode 100644 +index 0000000..bc44022 +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_C99.h.in +@@ -0,0 +1,5 @@ ++/* C99 or above */ ++#undef DUK_F_C99 ++#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) ++#define DUK_F_C99 ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_CLANG.h.in b/dist/source/config/helper-snippets/DUK_F_CLANG.h.in +new file mode 100644 +index 0000000..33f7f65 +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_CLANG.h.in +@@ -0,0 +1,4 @@ ++/* Clang */ ++#if defined(__clang__) ++#define DUK_F_CLANG ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_CPP.h.in b/dist/source/config/helper-snippets/DUK_F_CPP.h.in +new file mode 100644 +index 0000000..8489fc1 +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_CPP.h.in +@@ -0,0 +1,5 @@ ++/* C++ */ ++#undef DUK_F_CPP ++#if defined(__cplusplus) ++#define DUK_F_CPP ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_CPP11.h.in b/dist/source/config/helper-snippets/DUK_F_CPP11.h.in +new file mode 100644 +index 0000000..015259a +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_CPP11.h.in +@@ -0,0 +1,5 @@ ++/* C++11 or above */ ++#undef DUK_F_CPP11 ++#if defined(__cplusplus) && (__cplusplus >= 201103L) ++#define DUK_F_CPP11 ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_CYGWIN.h.in b/dist/source/config/helper-snippets/DUK_F_CYGWIN.h.in +new file mode 100644 +index 0000000..9d631a7 +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_CYGWIN.h.in +@@ -0,0 +1,4 @@ ++/* Cygwin */ ++#if defined(__CYGWIN__) ++#define DUK_F_CYGWIN ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_DURANGO.h.in b/dist/source/config/helper-snippets/DUK_F_DURANGO.h.in +new file mode 100644 +index 0000000..8e64e98 +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_DURANGO.h.in +@@ -0,0 +1,4 @@ ++/* Durango (Xbox One) */ ++#if defined(_DURANGO) || defined(_XBOX_ONE) ++#define DUK_F_DURANGO ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_EMSCRIPTEN.h.in b/dist/source/config/helper-snippets/DUK_F_EMSCRIPTEN.h.in +new file mode 100644 +index 0000000..ba299cd +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_EMSCRIPTEN.h.in +@@ -0,0 +1,4 @@ ++/* Emscripten (provided explicitly by user), improve if possible */ ++#if defined(EMSCRIPTEN) ++#define DUK_F_EMSCRIPTEN ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_FLASHPLAYER.h.in b/dist/source/config/helper-snippets/DUK_F_FLASHPLAYER.h.in +new file mode 100644 +index 0000000..39060c1 +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_FLASHPLAYER.h.in +@@ -0,0 +1,4 @@ ++/* Flash player (e.g. Crossbridge) */ ++#if defined(__FLASHPLAYER__) ++#define DUK_F_FLASHPLAYER ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_FREEBSD.h.in b/dist/source/config/helper-snippets/DUK_F_FREEBSD.h.in +new file mode 100644 +index 0000000..621b92e +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_FREEBSD.h.in +@@ -0,0 +1,4 @@ ++/* FreeBSD */ ++#if defined(__FreeBSD__) || defined(__FreeBSD) ++#define DUK_F_FREEBSD ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_GCC.h.in b/dist/source/config/helper-snippets/DUK_F_GCC.h.in +new file mode 100644 +index 0000000..ccb4ffb +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_GCC.h.in +@@ -0,0 +1,10 @@ ++/* GCC. Clang also defines __GNUC__ so don't detect GCC if using Clang. */ ++#if defined(__GNUC__) && !defined(__clang__) && !defined(DUK_F_CLANG) ++#define DUK_F_GCC ++#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) ++/* Convenience, e.g. gcc 4.5.1 == 40501; http://stackoverflow.com/questions/6031819/emulating-gccs-builtin-unreachable */ ++#define DUK_F_GCC_VERSION (__GNUC__ * 10000L + __GNUC_MINOR__ * 100L + __GNUC_PATCHLEVEL__) ++#else ++#error cannot figure out gcc version ++#endif ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_HPUX.h.in b/dist/source/config/helper-snippets/DUK_F_HPUX.h.in +new file mode 100644 +index 0000000..33531c6 +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_HPUX.h.in +@@ -0,0 +1,7 @@ ++/* HPUX */ ++#if defined(__hpux) ++#define DUK_F_HPUX ++#if defined(__ia64) ++#define DUK_F_HPUX_ITANIUM ++#endif ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_LINUX.h.in b/dist/source/config/helper-snippets/DUK_F_LINUX.h.in +new file mode 100644 +index 0000000..79ace6f +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_LINUX.h.in +@@ -0,0 +1,4 @@ ++/* Linux */ ++#if defined(__linux) || defined(__linux__) || defined(linux) ++#define DUK_F_LINUX ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_M68K.h.in b/dist/source/config/helper-snippets/DUK_F_M68K.h.in +new file mode 100644 +index 0000000..f377888 +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_M68K.h.in +@@ -0,0 +1,6 @@ ++/* Motorola 68K. Not defined by VBCC, so user must define one of these ++ * manually when using VBCC. ++ */ ++#if defined(__m68k__) || defined(M68000) || defined(__MC68K__) ++#define DUK_F_M68K ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_MINGW.h.in b/dist/source/config/helper-snippets/DUK_F_MINGW.h.in +new file mode 100644 +index 0000000..8b8a16d +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_MINGW.h.in +@@ -0,0 +1,4 @@ ++/* MinGW. Also GCC flags (DUK_F_GCC) are enabled now. */ ++#if defined(__MINGW32__) || defined(__MINGW64__) ++#define DUK_F_MINGW ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_MINT.h.in b/dist/source/config/helper-snippets/DUK_F_MINT.h.in +new file mode 100644 +index 0000000..ae269cd +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_MINT.h.in +@@ -0,0 +1,4 @@ ++/* Atari Mint */ ++#if defined(__MINT__) ++#define DUK_F_MINT ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_MIPS.h.in b/dist/source/config/helper-snippets/DUK_F_MIPS.h.in +new file mode 100644 +index 0000000..1d57239 +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_MIPS.h.in +@@ -0,0 +1,14 @@ ++/* MIPS. Related defines: __MIPSEB__, __MIPSEL__, __mips_isa_rev, __LP64__ */ ++#if defined(__mips__) || defined(mips) || defined(_MIPS_ISA) || \ ++ defined(_R3000) || defined(_R4000) || defined(_R5900) || \ ++ defined(_MIPS_ISA_MIPS1) || defined(_MIPS_ISA_MIPS2) || \ ++ defined(_MIPS_ISA_MIPS3) || defined(_MIPS_ISA_MIPS4) || \ ++ defined(__mips) || defined(__MIPS__) ++#define DUK_F_MIPS ++#if defined(__LP64__) || defined(_LP64) || defined(__mips64) || \ ++ defined(__mips64__) || defined(__mips_n64) ++#define DUK_F_MIPS64 ++#else ++#define DUK_F_MIPS32 ++#endif ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_MSVC.h.in b/dist/source/config/helper-snippets/DUK_F_MSVC.h.in +new file mode 100644 +index 0000000..2ddd894 +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_MSVC.h.in +@@ -0,0 +1,15 @@ ++/* MSVC */ ++#if defined(_MSC_VER) ++/* MSVC preprocessor defines: http://msdn.microsoft.com/en-us/library/b0084kay.aspx ++ * _MSC_FULL_VER includes the build number, but it has at least two formats, see e.g. ++ * BOOST_MSVC_FULL_VER in http://www.boost.org/doc/libs/1_52_0/boost/config/compiler/visualc.hpp ++ */ ++#define DUK_F_MSVC ++#if defined(_MSC_FULL_VER) ++#if (_MSC_FULL_VER > 100000000) ++#define DUK_F_MSVC_FULL_VER _MSC_FULL_VER ++#else ++#define DUK_F_MSCV_FULL_VER (_MSC_FULL_VER * 10) ++#endif ++#endif ++#endif /* _MSC_VER */ +diff --git a/dist/source/config/helper-snippets/DUK_F_NETBSD.h.in b/dist/source/config/helper-snippets/DUK_F_NETBSD.h.in +new file mode 100644 +index 0000000..2705bc8 +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_NETBSD.h.in +@@ -0,0 +1,4 @@ ++/* NetBSD */ ++#if defined(__NetBSD__) || defined(__NetBSD) ++#define DUK_F_NETBSD ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_NO_STDINT_H.h.in b/dist/source/config/helper-snippets/DUK_F_NO_STDINT_H.h.in +new file mode 100644 +index 0000000..47e16ef +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_NO_STDINT_H.h.in +@@ -0,0 +1,10 @@ ++/* stdint.h not available */ ++#if defined(DUK_F_WINDOWS) && defined(_MSC_VER) ++#if (_MSC_VER < 1700) ++/* VS2012+ has stdint.h, < VS2012 does not (but it's available for download). */ ++#define DUK_F_NO_STDINT_H ++#endif ++#endif ++#if !defined(DUK_F_NO_STDINT_H) && (defined(DUK_F_TOS) || defined(DUK_F_BCC)) ++#define DUK_F_NO_STDINT_H ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_OPENBSD.h.in b/dist/source/config/helper-snippets/DUK_F_OPENBSD.h.in +new file mode 100644 +index 0000000..e882b6c +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_OPENBSD.h.in +@@ -0,0 +1,4 @@ ++/* OpenBSD */ ++#if defined(__OpenBSD__) || defined(__OpenBSD) ++#define DUK_F_OPENBSD ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_ORBIS.h.in b/dist/source/config/helper-snippets/DUK_F_ORBIS.h.in +new file mode 100644 +index 0000000..717918e +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_ORBIS.h.in +@@ -0,0 +1,4 @@ ++/* Orbis (PS4) variant */ ++#if defined(DUK_F_FREEBSD) && defined(__ORBIS__) ++#define DUK_F_ORBIS ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_POSIX.h.in b/dist/source/config/helper-snippets/DUK_F_POSIX.h.in +new file mode 100644 +index 0000000..ea153c6 +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_POSIX.h.in +@@ -0,0 +1,4 @@ ++/* POSIX */ ++#if defined(__posix) ++#define DUK_F_POSIX ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_PPC.h.in b/dist/source/config/helper-snippets/DUK_F_PPC.h.in +new file mode 100644 +index 0000000..b1ab82c +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_PPC.h.in +@@ -0,0 +1,9 @@ ++/* PowerPC */ ++#if defined(__powerpc) || defined(__powerpc__) || defined(__PPC__) ++#define DUK_F_PPC ++#if defined(__PPC64__) || defined(__LP64__) || defined(_LP64) ++#define DUK_F_PPC64 ++#else ++#define DUK_F_PPC32 ++#endif ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_QNX.h.in b/dist/source/config/helper-snippets/DUK_F_QNX.h.in +new file mode 100644 +index 0000000..9fe1d01 +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_QNX.h.in +@@ -0,0 +1,4 @@ ++/* QNX */ ++#if defined(__QNX__) ++#define DUK_F_QNX ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_RISCV.h.in b/dist/source/config/helper-snippets/DUK_F_RISCV.h.in +new file mode 100644 +index 0000000..144215f +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_RISCV.h.in +@@ -0,0 +1,15 @@ ++/* RISC-V, https://github.com/riscv/riscv-toolchain-conventions#cc-preprocessor-definitions */ ++#if defined(__riscv) ++#define DUK_F_RISCV ++#if defined(__riscv_xlen) ++#if (__riscv_xlen == 32) ++#define DUK_F_RISCV32 ++#elif (__riscv_xlen == 64) ++#define DUK_F_RISCV64 ++#else ++#error __riscv_xlen has unsupported value (not 32 or 64) ++#endif ++#else ++#error __riscv defined without __riscv_xlen ++#endif ++#endif /* __riscv */ +diff --git a/dist/source/config/helper-snippets/DUK_F_SPARC.h.in b/dist/source/config/helper-snippets/DUK_F_SPARC.h.in +new file mode 100644 +index 0000000..37e1e70 +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_SPARC.h.in +@@ -0,0 +1,9 @@ ++/* SPARC */ ++#if defined(sparc) || defined(__sparc) || defined(__sparc__) ++#define DUK_F_SPARC ++#if defined(__LP64__) || defined(_LP64) ++#define DUK_F_SPARC64 ++#else ++#define DUK_F_SPARC32 ++#endif ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_SUN.h.in b/dist/source/config/helper-snippets/DUK_F_SUN.h.in +new file mode 100644 +index 0000000..9ae871d +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_SUN.h.in +@@ -0,0 +1,12 @@ ++/* illumos / Solaris */ ++#if defined(__sun) && defined(__SVR4) ++#define DUK_F_SUN ++#if defined(__SUNPRO_C) && (__SUNPRO_C < 0x550) ++#define DUK_F_OLD_SOLARIS ++/* Defines _ILP32 / _LP64 required by DUK_F_X86/DUK_F_X64. Platforms ++ * are processed before architectures, so this happens before the ++ * DUK_F_X86/DUK_F_X64 detection is emitted. ++ */ ++#include ++#endif ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_SUPERH.h.in b/dist/source/config/helper-snippets/DUK_F_SUPERH.h.in +new file mode 100644 +index 0000000..cd9eca5 +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_SUPERH.h.in +@@ -0,0 +1,9 @@ ++/* SuperH */ ++#if defined(__sh__) || \ ++ defined(__sh1__) || defined(__SH1__) || \ ++ defined(__sh2__) || defined(__SH2__) || \ ++ defined(__sh3__) || defined(__SH3__) || \ ++ defined(__sh4__) || defined(__SH4__) || \ ++ defined(__sh5__) || defined(__SH5__) ++#define DUK_F_SUPERH ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_TINSPIRE.h.in b/dist/source/config/helper-snippets/DUK_F_TINSPIRE.h.in +new file mode 100644 +index 0000000..7451979 +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_TINSPIRE.h.in +@@ -0,0 +1,4 @@ ++/* TI-Nspire (using Ndless) */ ++#if defined(_TINSPIRE) ++#define DUK_F_TINSPIRE ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_TINYC.h.in b/dist/source/config/helper-snippets/DUK_F_TINYC.h.in +new file mode 100644 +index 0000000..43a0cff +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_TINYC.h.in +@@ -0,0 +1,5 @@ ++/* TinyC */ ++#if defined(__TINYC__) ++/* http://bellard.org/tcc/tcc-doc.html#SEC9 */ ++#define DUK_F_TINYC ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_TOS.h.in b/dist/source/config/helper-snippets/DUK_F_TOS.h.in +new file mode 100644 +index 0000000..fcc296d +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_TOS.h.in +@@ -0,0 +1,6 @@ ++/* Atari ST TOS. __TOS__ defined by PureC. No platform define in VBCC ++ * apparently, so to use with VBCC user must define __TOS__ manually. ++ */ ++#if defined(__TOS__) ++#define DUK_F_TOS ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_UCLIBC.h.in b/dist/source/config/helper-snippets/DUK_F_UCLIBC.h.in +new file mode 100644 +index 0000000..7706962 +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_UCLIBC.h.in +@@ -0,0 +1,4 @@ ++/* uclibc */ ++#if defined(__UCLIBC__) ++#define DUK_F_UCLIBC ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_ULL_CONSTS.h.in b/dist/source/config/helper-snippets/DUK_F_ULL_CONSTS.h.in +new file mode 100644 +index 0000000..188f628 +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_ULL_CONSTS.h.in +@@ -0,0 +1,9 @@ ++/* ULL / LL preprocessor constants should be avoided because they're not ++ * always available. With suitable options, some compilers will support ++ * 64-bit integer types but won't support ULL / LL preprocessor constants. ++ * Assume C99/C++11 environments have these. However, BCC is nominally ++ * C99 but doesn't support these constants. ++ */ ++#if (defined(DUK_F_C99) || defined(DUK_F_CPP11)) && !defined(DUK_F_BCC) ++#define DUK_F_ULL_CONSTS ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_UNIX.h.in b/dist/source/config/helper-snippets/DUK_F_UNIX.h.in +new file mode 100644 +index 0000000..ab15b35 +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_UNIX.h.in +@@ -0,0 +1,5 @@ ++/* Generic Unix (includes Cygwin) */ ++#if defined(__unix) || defined(__unix__) || defined(unix) || \ ++ defined(DUK_F_LINUX) || defined(DUK_F_BSD) ++#define DUK_F_UNIX ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_VBCC.h.in b/dist/source/config/helper-snippets/DUK_F_VBCC.h.in +new file mode 100644 +index 0000000..f50e481 +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_VBCC.h.in +@@ -0,0 +1,4 @@ ++/* VBCC */ ++#if defined(__VBCC__) ++#define DUK_F_VBCC ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_WINDOWS.h.in b/dist/source/config/helper-snippets/DUK_F_WINDOWS.h.in +new file mode 100644 +index 0000000..20aa769 +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_WINDOWS.h.in +@@ -0,0 +1,10 @@ ++/* Windows, both 32-bit and 64-bit */ ++#if defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64) || \ ++ defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__) ++#define DUK_F_WINDOWS ++#if defined(_WIN64) || defined(WIN64) ++#define DUK_F_WIN64 ++#else ++#define DUK_F_WIN32 ++#endif ++#endif +diff --git a/dist/source/config/helper-snippets/DUK_F_X86.h.in b/dist/source/config/helper-snippets/DUK_F_X86.h.in +new file mode 100644 +index 0000000..0df3360 +--- /dev/null ++++ b/dist/source/config/helper-snippets/DUK_F_X86.h.in +@@ -0,0 +1,26 @@ ++/* Intel x86 (32-bit), x64 (64-bit) or x32 (64-bit but 32-bit pointers), ++ * define only one of DUK_F_X86, DUK_F_X64, DUK_F_X32. ++ * https://sites.google.com/site/x32abi/ ++ * ++ * With DUK_F_OLD_SOLARIS the header must be included ++ * before this. ++ */ ++#if defined(__amd64__) || defined(__amd64) || \ ++ defined(__x86_64__) || defined(__x86_64) || \ ++ defined(_M_X64) || defined(_M_AMD64) ++#if defined(__ILP32__) || defined(_ILP32) ++#define DUK_F_X32 ++#else ++#define DUK_F_X64 ++#endif ++#elif defined(i386) || defined(__i386) || defined(__i386__) || \ ++ defined(__i486__) || defined(__i586__) || defined(__i686__) || \ ++ defined(__IA32__) || defined(_M_IX86) || defined(__X86__) || \ ++ defined(_X86_) || defined(__THW_INTEL__) || defined(__I86__) ++#if defined(__LP64__) || defined(_LP64) ++/* This should not really happen, but would indicate x64. */ ++#define DUK_F_X64 ++#else ++#define DUK_F_X86 ++#endif ++#endif +diff --git a/dist/source/config/platforms.yaml b/dist/source/config/platforms.yaml +new file mode 100644 +index 0000000..d8a39b4 +--- /dev/null ++++ b/dist/source/config/platforms.yaml +@@ -0,0 +1,89 @@ ++# Platform metadata. ++ ++autodetect: ++ # Platforms for autodetect header. Order matters because some defines ++ # overlap, so rules select for more specific define first. ++ - ++ name: macOS, iOS, watchOS, tvOS, Darwin, etc ++ check: DUK_F_APPLE ++ include: platform_apple.h.in ++ - ++ name: Orbis ++ check: DUK_F_ORBIS ++ include: platform_orbis.h.in ++ - ++ name: OpenBSD ++ check: DUK_F_OPENBSD ++ include: platform_openbsd.h.in ++ - ++ name: Generic BSD ++ check: DUK_F_BSD ++ include: platform_genericbsd.h.in ++ - ++ name: Atari ST TOS ++ check: DUK_F_TOS ++ include: platform_tos.h.in ++ - ++ name: AmigaOS ++ check: DUK_F_AMIGAOS ++ include: platform_amigaos.h.in ++ - ++ name: Durango (XboxOne) ++ check: DUK_F_DURANGO ++ include: platform_durango.h.in ++ - ++ name: Windows ++ check: DUK_F_WINDOWS ++ include: platform_windows.h.in ++ - ++ name: Flashplayer (Crossbridge) ++ check: DUK_F_FLASHPLAYER ++ include: platform_flashplayer.h.in ++ - ++ name: QNX ++ check: DUK_F_QNX ++ include: platform_qnx.h.in ++ - ++ name: TI-Nspire ++ check: DUK_F_TINSPIRE ++ include: platform_tinspire.h.in ++ - ++ name: Emscripten ++ check: DUK_F_EMSCRIPTEN ++ include: platform_emscripten.h.in ++ - ++ name: Android ++ check: DUK_F_ANDROID ++ include: platform_android.h.in ++ - ++ name: Linux ++ check: DUK_F_LINUX ++ include: platform_linux.h.in ++ - ++ name: Solaris ++ check: DUK_F_SUN ++ include: platform_solaris.h.in ++ - ++ name: AIX ++ check: DUK_F_AIX ++ include: platform_aix.h.in ++ - ++ name: HPUX ++ check: DUK_F_HPUX ++ include: platform_hpux.h.in ++ - ++ name: Generic POSIX ++ check: DUK_F_POSIX ++ include: platform_posix.h.in ++ - ++ name: Cygwin ++ check: DUK_F_CYGWIN ++ include: platform_cygwin.h.in ++ - ++ name: Generic UNIX ++ check: DUK_F_UNIX ++ include: platform_genericunix.h.in ++ - ++ name: Generic fallback ++ check: null ++ include: platform_generic.h.in # if nothing else +diff --git a/dist/source/config/platforms/platform_aix.h.in b/dist/source/config/platforms/platform_aix.h.in +new file mode 100644 +index 0000000..328f45c +--- /dev/null ++++ b/dist/source/config/platforms/platform_aix.h.in +@@ -0,0 +1,12 @@ ++#if !defined(DUK_USE_BYTEORDER) ++#define DUK_USE_BYTEORDER 3 ++#endif ++#define DUK_USE_DATE_NOW_GETTIMEOFDAY ++#define DUK_USE_DATE_TZO_GMTIME_R ++#define DUK_USE_DATE_PRS_STRPTIME ++#define DUK_USE_DATE_FMT_STRFTIME ++#include ++#include ++#include ++ ++#define DUK_USE_OS_STRING "aix" +diff --git a/dist/source/config/platforms/platform_amigaos.h.in b/dist/source/config/platforms/platform_amigaos.h.in +new file mode 100644 +index 0000000..d71192e +--- /dev/null ++++ b/dist/source/config/platforms/platform_amigaos.h.in +@@ -0,0 +1,26 @@ ++#if defined(DUK_F_M68K) ++/* AmigaOS on M68k */ ++#define DUK_USE_DATE_NOW_TIME ++#define DUK_USE_DATE_TZO_GMTIME ++/* no parsing (not an error) */ ++#define DUK_USE_DATE_FMT_STRFTIME ++#include ++#elif defined(DUK_F_PPC) ++#define DUK_USE_DATE_NOW_GETTIMEOFDAY ++#define DUK_USE_DATE_TZO_GMTIME_R ++#define DUK_USE_DATE_PRS_STRPTIME ++#define DUK_USE_DATE_FMT_STRFTIME ++#include ++#if !defined(UINTPTR_MAX) ++#define UINTPTR_MAX UINT_MAX ++#endif ++#else ++#error AmigaOS but not M68K/PPC, not supported now ++#endif ++ ++#define DUK_USE_OS_STRING "amigaos" ++ ++/* AmigaOS on M68K or PPC is always big endian. */ ++#if !defined(DUK_USE_BYTEORDER) && (defined(DUK_F_M68K) || defined(DUK_F_PPC)) ++#define DUK_USE_BYTEORDER 3 ++#endif +diff --git a/dist/source/config/platforms/platform_android.h.in b/dist/source/config/platforms/platform_android.h.in +new file mode 100644 +index 0000000..dc12cc8 +--- /dev/null ++++ b/dist/source/config/platforms/platform_android.h.in +@@ -0,0 +1,33 @@ ++#if defined(DUK_COMPILING_DUKTAPE) ++#if !defined(_POSIX_C_SOURCE) ++#define _POSIX_C_SOURCE 200809L ++#endif ++#if !defined(_GNU_SOURCE) ++#define _GNU_SOURCE /* e.g. getdate_r */ ++#endif ++#if !defined(_XOPEN_SOURCE) ++#define _XOPEN_SOURCE /* e.g. strptime */ ++#endif ++#endif /* DUK_COMPILING_DUKTAPE */ ++ ++#include ++#if defined(DUK_F_BCC) ++/* no endian.h or stdint.h */ ++#else ++#include ++#include ++#endif /* DUK_F_BCC */ ++#include ++#include ++#include ++ ++#define DUK_USE_DATE_NOW_GETTIMEOFDAY ++#define DUK_USE_DATE_TZO_GMTIME_R ++#define DUK_USE_DATE_PRS_STRPTIME ++#define DUK_USE_DATE_FMT_STRFTIME ++ ++#if 0 /* XXX: safe condition? */ ++#define DUK_USE_GET_MONOTONIC_TIME_CLOCK_GETTIME ++#endif ++ ++#define DUK_USE_OS_STRING "android" +diff --git a/dist/source/config/platforms/platform_apple.h.in b/dist/source/config/platforms/platform_apple.h.in +new file mode 100644 +index 0000000..e467604 +--- /dev/null ++++ b/dist/source/config/platforms/platform_apple.h.in +@@ -0,0 +1,49 @@ ++#define DUK_USE_DATE_NOW_GETTIMEOFDAY ++#define DUK_USE_DATE_TZO_GMTIME_R ++#define DUK_USE_DATE_PRS_STRPTIME ++#define DUK_USE_DATE_FMT_STRFTIME ++#include ++#include ++#include ++#include ++#include ++ ++/* http://stackoverflow.com/questions/5919996/how-to-detect-reliably-mac-os-x-ios-linux-windows-in-c-preprocessor */ ++/* https://stackoverflow.com/questions/12132933/preprocessor-macro-for-os-x-targets */ ++/* Some of the combinations below probably don't actually exist ++ * (like macOS simulator) but include them for completeness. ++ */ ++#if TARGET_OS_SIMULATOR && TARGET_OS_MACCATALYST ++#define DUK_USE_OS_STRING "catalyst-sim" ++#elif TARGET_OS_SIMULATOR && TARGET_OS_BRIDGE ++#define DUK_USE_OS_STRING "bridgeos-sim" ++#elif TARGET_OS_SIMULATOR && TARGET_OS_WATCH ++#define DUK_USE_OS_STRING "watchos-sim" ++#elif TARGET_OS_SIMULATOR && TARGET_OS_TV ++#define DUK_USE_OS_STRING "tvos-sim" ++#elif TARGET_OS_SIMULATOR && TARGET_OS_IOS ++#define DUK_USE_OS_STRING "ios-sim" ++#elif TARGET_IPHONE_SIMULATOR && TARGET_OS_IOS /* deprecated */ ++#define DUK_USE_OS_STRING "ios-sim" ++#elif TARGET_OS_SIMULATOR && TARGET_OS_MAC ++#define DUK_USE_OS_STRING "macos-sim" ++#elif TARGET_OS_MACCATALYST ++#define DUK_USE_OS_STRING "catalyst" ++#elif TARGET_OS_BRIDGE ++#define DUK_USE_OS_STRING "bridgeos" ++#elif TARGET_OS_WATCH ++#define DUK_USE_OS_STRING "watchos" ++#elif TARGET_OS_TV ++#define DUK_USE_OS_STRING "tvos" ++#elif TARGET_OS_IOS ++#define DUK_USE_OS_STRING "ios" ++#elif TARGET_OS_MAC ++#define DUK_USE_OS_STRING "macos" ++#else ++#define DUK_USE_OS_STRING "osx-unknown" ++#endif ++ ++/* Use _setjmp() on Apple by default, see GH-55. */ ++#define DUK_JMPBUF_TYPE jmp_buf ++#define DUK_SETJMP(jb) _setjmp((jb)) ++#define DUK_LONGJMP(jb) _longjmp((jb), 1) +diff --git a/dist/source/config/platforms/platform_cygwin.h.in b/dist/source/config/platforms/platform_cygwin.h.in +new file mode 100644 +index 0000000..cfe458b +--- /dev/null ++++ b/dist/source/config/platforms/platform_cygwin.h.in +@@ -0,0 +1,15 @@ ++/* don't use strptime() for now */ ++#define DUK_USE_DATE_NOW_GETTIMEOFDAY ++#define DUK_USE_DATE_TZO_GMTIME_R ++#define DUK_USE_DATE_FMT_STRFTIME ++#include ++#include ++#include ++#include ++#include ++ ++#define DUK_JMPBUF_TYPE jmp_buf ++#define DUK_SETJMP(jb) _setjmp((jb)) ++#define DUK_LONGJMP(jb) _longjmp((jb), 1) ++ ++#define DUK_USE_OS_STRING "windows" +diff --git a/dist/source/config/platforms/platform_durango.h.in b/dist/source/config/platforms/platform_durango.h.in +new file mode 100644 +index 0000000..36326d8 +--- /dev/null ++++ b/dist/source/config/platforms/platform_durango.h.in +@@ -0,0 +1,32 @@ ++/* Durango = XboxOne ++ * Configuration is nearly identical to Windows, except for ++ * DUK_USE_DATE_TZO_WINDOWS. ++ */ ++ ++/* Initial fix: disable secure CRT related warnings when compiling Duktape ++ * itself (must be defined before including Windows headers). Don't define ++ * for user code including duktape.h. ++ */ ++#if defined(DUK_COMPILING_DUKTAPE) && !defined(_CRT_SECURE_NO_WARNINGS) ++#define _CRT_SECURE_NO_WARNINGS ++#endif ++ ++/* MSVC does not have sys/param.h */ ++#define DUK_USE_DATE_NOW_WINDOWS ++#define DUK_USE_DATE_TZO_WINDOWS_NO_DST ++/* Note: PRS and FMT are intentionally left undefined for now. This means ++ * there is no platform specific date parsing/formatting but there is still ++ * the ISO 8601 standard format. ++ */ ++#if defined(DUK_COMPILING_DUKTAPE) ++/* Only include when compiling Duktape to avoid polluting application build ++ * with a lot of unnecessary defines. ++ */ ++#include ++#endif ++ ++#define DUK_USE_OS_STRING "durango" ++ ++#if !defined(DUK_USE_BYTEORDER) ++#define DUK_USE_BYTEORDER 1 ++#endif +diff --git a/dist/source/config/platforms/platform_emscripten.h.in b/dist/source/config/platforms/platform_emscripten.h.in +new file mode 100644 +index 0000000..afc559d +--- /dev/null ++++ b/dist/source/config/platforms/platform_emscripten.h.in +@@ -0,0 +1,29 @@ ++#if defined(DUK_COMPILING_DUKTAPE) ++#if !defined(_POSIX_C_SOURCE) ++#define _POSIX_C_SOURCE 200809L ++#endif ++#if !defined(_GNU_SOURCE) ++#define _GNU_SOURCE /* e.g. getdate_r */ ++#endif ++#if !defined(_XOPEN_SOURCE) ++#define _XOPEN_SOURCE /* e.g. strptime */ ++#endif ++#endif /* DUK_COMPILING_DUKTAPE */ ++ ++#include ++#if defined(DUK_F_BCC) ++/* no endian.h */ ++#else ++#include ++#endif /* DUK_F_BCC */ ++#include ++#include ++#include ++#include ++ ++#define DUK_USE_DATE_NOW_GETTIMEOFDAY ++#define DUK_USE_DATE_TZO_GMTIME_R ++#define DUK_USE_DATE_PRS_STRPTIME ++#define DUK_USE_DATE_FMT_STRFTIME ++ ++#define DUK_USE_OS_STRING "emscripten" +diff --git a/dist/source/config/platforms/platform_flashplayer.h.in b/dist/source/config/platforms/platform_flashplayer.h.in +new file mode 100644 +index 0000000..fa7f6f6 +--- /dev/null ++++ b/dist/source/config/platforms/platform_flashplayer.h.in +@@ -0,0 +1,14 @@ ++#define DUK_USE_DATE_NOW_GETTIMEOFDAY ++#define DUK_USE_DATE_TZO_GMTIME_R ++#define DUK_USE_DATE_PRS_STRPTIME ++#define DUK_USE_DATE_FMT_STRFTIME ++#include ++#include ++#include ++#include ++ ++#define DUK_USE_OS_STRING "flashplayer" ++ ++#if !defined(DUK_USE_BYTEORDER) && defined(DUK_F_FLASHPLAYER) ++#define DUK_USE_BYTEORDER 1 ++#endif +diff --git a/dist/source/config/platforms/platform_generic.h.in b/dist/source/config/platforms/platform_generic.h.in +new file mode 100644 +index 0000000..24b552f +--- /dev/null ++++ b/dist/source/config/platforms/platform_generic.h.in +@@ -0,0 +1,18 @@ ++/* The most portable current time provider is time(), but it only has a ++ * one second resolution. ++ */ ++#define DUK_USE_DATE_NOW_TIME ++ ++/* The most portable way to figure out local time offset is gmtime(), ++ * but it's not thread safe so use with caution. ++ */ ++#define DUK_USE_DATE_TZO_GMTIME ++ ++/* Avoid custom date parsing and formatting for portability. */ ++#undef DUK_USE_DATE_PRS_STRPTIME ++#undef DUK_USE_DATE_FMT_STRFTIME ++ ++/* Rely on C89 headers only; time.h must be here. */ ++#include ++ ++#define DUK_USE_OS_STRING "unknown" +diff --git a/dist/source/config/platforms/platform_genericbsd.h.in b/dist/source/config/platforms/platform_genericbsd.h.in +new file mode 100644 +index 0000000..494f7b1 +--- /dev/null ++++ b/dist/source/config/platforms/platform_genericbsd.h.in +@@ -0,0 +1,11 @@ ++#define DUK_USE_DATE_NOW_GETTIMEOFDAY ++#define DUK_USE_DATE_TZO_GMTIME_R ++#define DUK_USE_DATE_PRS_STRPTIME ++#define DUK_USE_DATE_FMT_STRFTIME ++#include ++#include ++#include ++#include ++#include ++ ++#define DUK_USE_OS_STRING "bsd" +diff --git a/dist/source/config/platforms/platform_genericunix.h.in b/dist/source/config/platforms/platform_genericunix.h.in +new file mode 100644 +index 0000000..ff3d4e3 +--- /dev/null ++++ b/dist/source/config/platforms/platform_genericunix.h.in +@@ -0,0 +1,7 @@ ++#define DUK_USE_DATE_NOW_GETTIMEOFDAY ++#define DUK_USE_DATE_TZO_GMTIME_R ++#define DUK_USE_DATE_PRS_STRPTIME ++#define DUK_USE_DATE_FMT_STRFTIME ++#include ++#include ++#define DUK_USE_OS_STRING "unknown" +diff --git a/dist/source/config/platforms/platform_hpux.h.in b/dist/source/config/platforms/platform_hpux.h.in +new file mode 100644 +index 0000000..dac6e19 +--- /dev/null ++++ b/dist/source/config/platforms/platform_hpux.h.in +@@ -0,0 +1,13 @@ ++#define DUK_F_NO_STDINT_H ++#if !defined(DUK_USE_BYTEORDER) ++#define DUK_USE_BYTEORDER 3 ++#endif ++#define DUK_USE_DATE_NOW_GETTIMEOFDAY ++#define DUK_USE_DATE_TZO_GMTIME_R ++#define DUK_USE_DATE_PRS_STRPTIME ++#define DUK_USE_DATE_FMT_STRFTIME ++#include ++#include ++#include ++ ++#define DUK_USE_OS_STRING "hpux" +diff --git a/dist/source/config/platforms/platform_linux.h.in b/dist/source/config/platforms/platform_linux.h.in +new file mode 100644 +index 0000000..cc3a69c +--- /dev/null ++++ b/dist/source/config/platforms/platform_linux.h.in +@@ -0,0 +1,33 @@ ++#if defined(DUK_COMPILING_DUKTAPE) ++#if !defined(_POSIX_C_SOURCE) ++#define _POSIX_C_SOURCE 200809L ++#endif ++#if !defined(_GNU_SOURCE) ++#define _GNU_SOURCE /* e.g. getdate_r */ ++#endif ++#if !defined(_XOPEN_SOURCE) ++#define _XOPEN_SOURCE /* e.g. strptime */ ++#endif ++#endif /* DUK_COMPILING_DUKTAPE */ ++ ++#include ++#if defined(DUK_F_BCC) ++/* no endian.h or stdint.h */ ++#else ++#include ++#include ++#endif /* DUK_F_BCC */ ++#include ++#include ++#include ++ ++#define DUK_USE_DATE_NOW_GETTIMEOFDAY ++#define DUK_USE_DATE_TZO_GMTIME_R ++#define DUK_USE_DATE_PRS_STRPTIME ++#define DUK_USE_DATE_FMT_STRFTIME ++ ++#if 0 /* XXX: safe condition? */ ++#define DUK_USE_GET_MONOTONIC_TIME_CLOCK_GETTIME ++#endif ++ ++#define DUK_USE_OS_STRING "linux" +diff --git a/dist/source/config/platforms/platform_openbsd.h.in b/dist/source/config/platforms/platform_openbsd.h.in +new file mode 100644 +index 0000000..f4b4977 +--- /dev/null ++++ b/dist/source/config/platforms/platform_openbsd.h.in +@@ -0,0 +1,12 @@ ++/* http://www.monkey.org/openbsd/archive/ports/0401/msg00089.html */ ++#define DUK_USE_DATE_NOW_GETTIMEOFDAY ++#define DUK_USE_DATE_TZO_GMTIME_R ++#define DUK_USE_DATE_PRS_STRPTIME ++#define DUK_USE_DATE_FMT_STRFTIME ++#include ++#include ++#include ++#include ++#include ++ ++#define DUK_USE_OS_STRING "openbsd" +diff --git a/dist/source/config/platforms/platform_orbis.h.in b/dist/source/config/platforms/platform_orbis.h.in +new file mode 100644 +index 0000000..684ebfd +--- /dev/null ++++ b/dist/source/config/platforms/platform_orbis.h.in +@@ -0,0 +1,12 @@ ++/* Orbis = PS4 */ ++#define DUK_USE_DATE_NOW_GETTIMEOFDAY ++#define DUK_USE_DATE_TZO_GMTIME_S ++/* no parsing (not an error) */ ++#define DUK_USE_DATE_FMT_STRFTIME ++#include ++#include ++#include ++#include ++#include ++ ++#define DUK_USE_OS_STRING "orbis" +diff --git a/dist/source/config/platforms/platform_posix.h.in b/dist/source/config/platforms/platform_posix.h.in +new file mode 100644 +index 0000000..fa42b1b +--- /dev/null ++++ b/dist/source/config/platforms/platform_posix.h.in +@@ -0,0 +1,11 @@ ++#define DUK_USE_DATE_NOW_GETTIMEOFDAY ++#define DUK_USE_DATE_TZO_GMTIME_R ++#define DUK_USE_DATE_PRS_STRPTIME ++#define DUK_USE_DATE_FMT_STRFTIME ++#include ++#include ++#include ++#include ++#include ++ ++#define DUK_USE_OS_STRING "posix" +diff --git a/dist/source/config/platforms/platform_qnx.h.in b/dist/source/config/platforms/platform_qnx.h.in +new file mode 100644 +index 0000000..c4e2ee2 +--- /dev/null ++++ b/dist/source/config/platforms/platform_qnx.h.in +@@ -0,0 +1,16 @@ ++#if defined(DUK_F_QNX) && defined(DUK_COMPILING_DUKTAPE) ++/* See: /opt/qnx650/target/qnx6/usr/include/sys/platform.h */ ++#define _XOPEN_SOURCE 600 ++#define _POSIX_C_SOURCE 200112L ++#endif ++ ++#define DUK_USE_DATE_NOW_GETTIMEOFDAY ++#define DUK_USE_DATE_TZO_GMTIME_R ++#define DUK_USE_DATE_PRS_STRPTIME ++#define DUK_USE_DATE_FMT_STRFTIME ++#include ++#include ++#include ++#include ++ ++#define DUK_USE_OS_STRING "qnx" +diff --git a/dist/source/config/platforms/platform_solaris.h.in b/dist/source/config/platforms/platform_solaris.h.in +new file mode 100644 +index 0000000..b49fa2f +--- /dev/null ++++ b/dist/source/config/platforms/platform_solaris.h.in +@@ -0,0 +1,21 @@ ++#define DUK_USE_DATE_NOW_GETTIMEOFDAY ++#define DUK_USE_DATE_TZO_GMTIME_R ++#define DUK_USE_DATE_PRS_STRPTIME ++#define DUK_USE_DATE_FMT_STRFTIME ++ ++#include ++#if defined(DUK_F_OLD_SOLARIS) ++/* Old Solaris with no endian.h, stdint.h */ ++#define DUK_F_NO_STDINT_H ++#if !defined(DUK_USE_BYTEORDER) ++#define DUK_USE_BYTEORDER 3 ++#endif ++#else /* DUK_F_OLD_SOLARIS */ ++#include ++#endif /* DUK_F_OLD_SOLARIS */ ++ ++#include ++#include ++#include ++ ++#define DUK_USE_OS_STRING "solaris" +diff --git a/dist/source/config/platforms/platform_tinspire.h.in b/dist/source/config/platforms/platform_tinspire.h.in +new file mode 100644 +index 0000000..6c54267 +--- /dev/null ++++ b/dist/source/config/platforms/platform_tinspire.h.in +@@ -0,0 +1,14 @@ ++#if defined(DUK_COMPILING_DUKTAPE) && !defined(_XOPEN_SOURCE) ++#define _XOPEN_SOURCE /* e.g. strptime */ ++#endif ++ ++#define DUK_USE_DATE_NOW_GETTIMEOFDAY ++#define DUK_USE_DATE_TZO_GMTIME_R ++#define DUK_USE_DATE_PRS_STRPTIME ++#define DUK_USE_DATE_FMT_STRFTIME ++#include ++#include ++#include ++#include ++ ++#define DUK_USE_OS_STRING "tinspire" +diff --git a/dist/source/config/platforms/platform_tos.h.in b/dist/source/config/platforms/platform_tos.h.in +new file mode 100644 +index 0000000..8c810e7 +--- /dev/null ++++ b/dist/source/config/platforms/platform_tos.h.in +@@ -0,0 +1,12 @@ ++#define DUK_USE_DATE_NOW_TIME ++#define DUK_USE_DATE_TZO_GMTIME ++/* no parsing (not an error) */ ++#define DUK_USE_DATE_FMT_STRFTIME ++#include ++ ++#define DUK_USE_OS_STRING "tos" ++ ++/* TOS on M68K is always big endian. */ ++#if !defined(DUK_USE_BYTEORDER) && defined(DUK_F_M68K) ++#define DUK_USE_BYTEORDER 3 ++#endif +diff --git a/dist/source/config/platforms/platform_windows.h.in b/dist/source/config/platforms/platform_windows.h.in +new file mode 100644 +index 0000000..9e6fcf9 +--- /dev/null ++++ b/dist/source/config/platforms/platform_windows.h.in +@@ -0,0 +1,59 @@ ++/* Windows version can't obviously be determined at compile time, ++ * but _WIN32_WINNT indicates the minimum version targeted: ++ * - https://msdn.microsoft.com/en-us/library/6sehtctf.aspx ++ */ ++ ++/* Initial fix: disable secure CRT related warnings when compiling Duktape ++ * itself (must be defined before including Windows headers). Don't define ++ * for user code including duktape.h. ++ */ ++#if defined(DUK_COMPILING_DUKTAPE) && !defined(_CRT_SECURE_NO_WARNINGS) ++#define _CRT_SECURE_NO_WARNINGS ++#endif ++ ++/* Windows 32-bit and 64-bit are currently the same. */ ++/* MSVC does not have sys/param.h */ ++ ++#if defined(DUK_COMPILING_DUKTAPE) ++/* Only include when compiling Duktape to avoid polluting application build ++ * with a lot of unnecessary defines. ++ */ ++#include ++#endif ++ ++/* GetSystemTimePreciseAsFileTime() available from Windows 8: ++ * https://msdn.microsoft.com/en-us/library/windows/desktop/hh706895(v=vs.85).aspx ++ */ ++#if defined(DUK_USE_DATE_NOW_WINDOWS_SUBMS) || defined(DUK_USE_DATE_NOW_WINDOWS) ++/* User forced provider. */ ++#else ++#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0602) ++#define DUK_USE_DATE_NOW_WINDOWS_SUBMS ++#else ++#define DUK_USE_DATE_NOW_WINDOWS ++#endif ++#endif ++ ++#define DUK_USE_DATE_TZO_WINDOWS ++ ++/* Note: PRS and FMT are intentionally left undefined for now. This means ++ * there is no platform specific date parsing/formatting but there is still ++ * the ISO 8601 standard format. ++ */ ++ ++/* QueryPerformanceCounter() may go backwards in Windows XP, so enable for ++ * Vista and later: https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx#qpc_support_in_windows_versions ++ */ ++#if !defined(DUK_USE_GET_MONOTONIC_TIME_WINDOWS_QPC) && \ ++ defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0600) ++#define DUK_USE_GET_MONOTONIC_TIME_WINDOWS_QPC ++#endif ++ ++#define DUK_USE_OS_STRING "windows" ++ ++/* On Windows, assume we're little endian. Even Itanium which has a ++ * configurable endianness runs little endian in Windows. ++ */ ++#if !defined(DUK_USE_BYTEORDER) ++#define DUK_USE_BYTEORDER 1 ++#endif +diff --git a/dist/source/config/tags.yaml b/dist/source/config/tags.yaml +new file mode 100644 +index 0000000..fbd6c83 +--- /dev/null ++++ b/dist/source/config/tags.yaml +@@ -0,0 +1,105 @@ ++# Properties for each tag: ++# ++# - title: title to use in header files ++# ++ ++ecmascript: ++ title: ECMAScript Edition 5 (ES5) options ++ ++ecmascript2015: ++ title: ECMAScript 2015 (ES6) options ++ ++ecmascript2016: ++ title: ECMAScript 2016 (ES7) options ++ ++ecmascript2017: ++ title: ECMAScript 2017 (ES8) options ++ ++ecmascript2018: ++ title: ECMAScript 2018 (ES9) options ++ ++duktape: ++ title: Duktape specific options ++ ++compliance: ++ title: Compliance related options ++ ++debugger: ++ title: Debugger options ++ ++debug: ++ title: Debug options ++ ++execution: ++ title: Execution options ++ ++sandbox: ++ title: Sandboxing options ++ ++performance: ++ title: Performance options ++ ++portability: ++ title: Platform and portability options ++ ++memory: ++ title: Memory management options ++ ++lowmemory: ++ title: Low memory options ++ ++gc: ++ title: Garbage collection options ++ ++misc: ++ title: Miscellaneous options ++ ++io: ++ title: I/O options ++ ++development: ++ title: Developer-only options ++ description: > ++ Developer options which are not intended for end users and are not ++ part of semantic versioning guarantees (e.g. torture options). ++ ++legacy: ++ title: Backwards compatibility options ++ description: > ++ Options for providing backwards compatible behavior. ++ ++date: ++ title: Date handling options ++ ++experimental: ++ title: Experimental options ++ ++api: ++ title: API options ++ ++fastpath: ++ title: Fast path performance options ++ ++cstackdepth: ++ title: C stack depth ++ ++torture: ++ title: Development time torture options ++ description: > ++ Development time options to stress test corner case handling by e.g. ++ causing a garbage collection on every allocation. ++ ++fuzzing: ++ title: Fuzzing ++ ++cbor: ++ title: CBOR ++ ++codec: ++ title: Codecs ++ ++encoding-api: ++ title: WHATWG Encoding API ++ ++performance-api: ++ title: Performance API (High Resolution Time) +diff --git a/dist/source/debugger/Makefile b/dist/source/debugger/Makefile +new file mode 100644 +index 0000000..f142c25 +--- /dev/null ++++ b/dist/source/debugger/Makefile +@@ -0,0 +1,101 @@ ++NODE:=$(shell { command -v nodejs || command -v node; } 2>/dev/null) ++DUKLUV:=$(shell command -v dukluv 2>/dev/null) ++ ++# 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) ++ ++rundebug: 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) --verbose --log-messages /tmp/dukdebug-messages --dump-debug-pretty /tmp/dukdebug-pretty ++ ++.PHONY: runproxynodejs ++runproxynodejs: 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 ++ @echo "Running Node.js based debug proxy" ++ "$(NODE)" duk_debug.js --json-proxy ++ ++.PHONY: runproxydukluv ++runproxydukluv: duk_debug_meta.json ++ @echo "Running Dukluv based debug proxy (you may need to edit DUKLUV in the Makefile)" ++ "$(DUKLUV)" duk_debug_proxy.js --log-level 2 --metadata duk_debug_meta.json --readable-numbers ++ ++.PHONY: runproxy ++runproxy: runproxydukluv ++ ++.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 ++ @rm -f duk_debug_meta.json ++ ++node_modules: ++ npm install ++ ++duk_debug_meta.json: ++ python2 ../tools/merge_debug_meta.py --output $@ \ ++ --class-names duk_classnames.yaml \ ++ --opcodes duk_opcodes.yaml \ ++ --debug-commands duk_debugcommands.yaml \ ++ --debug-errors duk_debugerrors.yaml ++ ++static/socket.io-1.2.0.js: ++ wget -O $@ http://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/ +diff --git a/dist/source/debugger/README.rst b/dist/source/debugger/README.rst +new file mode 100644 +index 0000000..163d800 +--- /dev/null ++++ b/dist/source/debugger/README.rst +@@ -0,0 +1,385 @@ ++========================================= ++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``) ++on Unix and Windows. ++ ++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: ++ ++* Enable ``DUK_USE_DEBUGGER_SUPPORT`` and ``DUK_USE_INTERRUPT_COUNTER`` for ++ ``tools/configure.py``. ++ ++* Enable ``DUK_CMDLINE_DEBUGGER_SUPPORT`` on compiler command line for Duktape ++ command line utility. ++ ++The source distributable contains a Makefile to build a "duk" command with ++debugger support:: ++ ++ $ cd ++ $ 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 ++ $ ./duk --debugger mandel.js ++ ++ # Using Duktape Git repo ++ $ cd /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 ++========================== ++ ++There are two JSON debug proxy implementations: one implemented in DukLuv ++and another in Node.js. ++ ++DukLuv JSON proxy ++----------------- ++ ++DukLuv (https://github.com/creationix/dukluv) is a small and portable event ++loop based on LibUV and Duktape with MIT license (like Duktape). As such it's ++easy to embed in a custom debug client: you just include the DukLuv executable ++and the JSON proxy source file in your debug client. ++ ++Install DukLuv: ++ ++* Ensure ``cmake`` is installed ++ ++* ``git clone https://github.com/creationix/dukluv.git`` ++ ++* ``cd dukluv`` ++ ++* ``git submodule init; git submodule update`` ++ ++* ``make`` ++ ++* Binary should appear in: ++ ++ - ``./build/dukluv`` on Linux ++ ++ - ``.\build\Debug\dukluv.exe`` on Windows ++ ++Run the proxy:: ++ ++ # Using Makefile; autogenerates duk_debug_meta.json ++ # (You may need to edit DUKLUV in Makefile to point to your DukLuv) ++ $ make runproxydukluv ++ ++ # Manually: see "dukluv duk_debug_proxy.js --help" for help ++ $ .../path/to/dukluv duk_debug_proxy.js ++ ++Start Duktape command line (or whatever your target is):: ++ ++ $ cd /tests/ecmascript/ ++ $ ../../duk --debugger test-dev-mandel2-func.js ++ ++Now connect to the proxy using e.g. telnet:: ++ ++ $ telnet localhost 9093 ++ ++The proxy will then connect to the target and you can start issuing commands:: ++ ++ $ telnet localhost 9093 ++ Trying 127.0.0.1... ++ Connected to localhost. ++ Escape character is '^]'. ++ {"notify":"_TargetConnecting","args":["127.0.0.1",9091]} ++ {"notify":"_TargetConnected","args":["1 10499 v1.4.0-140-gc9a6c7c 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":[10499,"v1.4.0-140-gc9a6c7c","duk command built from Duktape repo",1]} ++ {"request":"Eval","args":["print('Hello world!'); 123;"]} ++ {"notify":"Print","command":2,"args":["Hello world!\n"]} ++ {"reply":true,"args":[0,{"type":"number","data":"405ec00000000000"}]} ++ [...] ++ ++The proxy log provides dumps both JSON and dvalue binary traffic which is ++quite useful in development:: ++ ++ $ make runproxydukluv ++ Running Dukluv based debug proxy ++ "dukluv" duk_debug_proxy.js --log-level 2 --metadata duk_debug_meta.json ++ 2016-02-17T13:59:42.308Z INF Proxy: Read proxy metadata from duk_debug_meta.json ++ 2016-02-17T13:59:42.325Z INF Proxy: Listening for incoming JSON debug connection on 0.0.0.0:9093, target is 127.0.0.1:9091 ++ 2016-02-17T13:59:47.994Z INF Proxy: JSON proxy client connected ++ 2016-02-17T13:59:47.994Z INF Proxy: Connecting to debug target at 127.0.0.1:9091 ++ 2016-02-17T13:59:47.994Z INF Proxy: PROXY --> CLIENT: {"notify":"_TargetConnecting","args":["127.0.0.1",9091]} ++ 2016-02-17T13:59:47.994Z INF Proxy: Connected to debug target at 127.0.0.1:9091 ++ 2016-02-17T13:59:48.003Z INF Proxy: PROXY --> CLIENT: {"notify":"_TargetConnected","args":["1 10499 v1.4.0-140-gc9a6c7c duk command built from Duktape repo"]} ++ 2016-02-17T13:59:48.003Z INF Proxy: Target handshake: {"line":"1 10499 v1.4.0-140-gc9a6c7c duk command built from Duktape repo","protocolVersion":1,"text":"10499 v1.4.0-140-gc9a6c7c duk command built from Duktape repo","dukVersion":"1","dukGitDescribe":"10499","targetString":"v1.4.0-140-gc9a6c7c"} ++ 2016-02-17T13:59:48.151Z INF Proxy: PROXY <-- TARGET: |04| ++ 2016-02-17T13:59:48.152Z INF Proxy: PROXY <-- TARGET: |81| ++ 2016-02-17T13:59:48.152Z INF Proxy: PROXY <-- TARGET: |81| ++ 2016-02-17T13:59:48.160Z INF Proxy: PROXY <-- TARGET: |78746573742d6465762d6d616e64656c322d66756e632e6a73| ++ 2016-02-17T13:59:48.161Z INF Proxy: PROXY <-- TARGET: |66676c6f62616c| ++ 2016-02-17T13:59:48.165Z INF Proxy: PROXY <-- TARGET: |ba| ++ 2016-02-17T13:59:48.165Z INF Proxy: PROXY <-- TARGET: |80| ++ 2016-02-17T13:59:48.165Z INF Proxy: PROXY <-- TARGET: |00| ++ 2016-02-17T13:59:48.165Z INF Proxy: PROXY --> CLIENT: {"notify":"Status","command":1,"args":[1,"test-dev-mandel2-func.js","global",58,0]} ++ 2016-02-17T13:59:51.289Z INF Proxy: PROXY <-- CLIENT: {"request":"BasicInfo"} ++ 2016-02-17T13:59:51.289Z INF Proxy: PROXY --> TARGET: |01| ++ 2016-02-17T13:59:51.289Z INF Proxy: PROXY --> TARGET: |90| ++ 2016-02-17T13:59:51.289Z INF Proxy: PROXY --> TARGET: |00| ++ 2016-02-17T13:59:51.291Z INF Proxy: PROXY <-- TARGET: |02| ++ 2016-02-17T13:59:51.291Z INF Proxy: PROXY <-- TARGET: |e903| ++ 2016-02-17T13:59:51.292Z INF Proxy: PROXY <-- TARGET: |7376312e342e302d3134302d6763396136633763| ++ 2016-02-17T13:59:51.293Z INF Proxy: PROXY <-- TARGET: |12002364756b20636f6d6d616e64206275696c742066726f6d2044756b74617065207265706f| ++ 2016-02-17T13:59:51.293Z INF Proxy: PROXY <-- TARGET: |81| ++ 2016-02-17T13:59:51.293Z INF Proxy: PROXY <-- TARGET: |00| ++ 2016-02-17T13:59:51.293Z INF Proxy: PROXY --> CLIENT: {"reply":true,"args":[10499,"v1.4.0-140-gc9a6c7c","duk command built from Duktape repo",1]} ++ 2016-02-17T14:00:06.105Z INF Proxy: PROXY <-- CLIENT: {"request":"Eval","args":["print('Hello world!'); 123;"]} ++ 2016-02-17T14:00:06.105Z INF Proxy: PROXY --> TARGET: |01| ++ 2016-02-17T14:00:06.105Z INF Proxy: PROXY --> TARGET: |9e| ++ 2016-02-17T14:00:06.105Z INF Proxy: PROXY --> TARGET: |7b7072696e74282748656c6c6f20776f726c642127293b203132333b| ++ 2016-02-17T14:00:06.105Z INF Proxy: PROXY --> TARGET: |00| ++ 2016-02-17T14:00:06.167Z INF Proxy: PROXY <-- TARGET: |04| ++ 2016-02-17T14:00:06.167Z INF Proxy: PROXY <-- TARGET: |82| ++ 2016-02-17T14:00:06.167Z INF Proxy: PROXY <-- TARGET: |6d48656c6c6f20776f726c64210a| ++ 2016-02-17T14:00:06.168Z INF Proxy: PROXY <-- TARGET: |00| ++ 2016-02-17T14:00:06.168Z INF Proxy: PROXY --> CLIENT: {"notify":"Print","command":2,"args":["Hello world!\n"]} ++ 2016-02-17T14:00:06.171Z INF Proxy: PROXY <-- TARGET: |02| ++ 2016-02-17T14:00:06.171Z INF Proxy: PROXY <-- TARGET: |80| ++ 2016-02-17T14:00:06.173Z INF Proxy: PROXY <-- TARGET: |1a405ec00000000000| ++ 2016-02-17T14:00:06.173Z INF Proxy: PROXY <-- TARGET: |00| ++ 2016-02-17T14:00:06.174Z INF Proxy: PROXY --> CLIENT: {"reply":true,"args":[0,{"type":"number","data":"405ec00000000000"}]} ++ [...] ++ ++Node.js JSON proxy ++------------------ ++ ++A Node.js-based JSON debug proxy is also provided by ``duk_debug.js``:: ++ ++ # Same prerequisites as for running the debug client ++ $ make runproxynodejs ++ ++Start Duktape command line (or whatever your target is):: ++ ++ $ cd /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":"_TargetConnected","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. +diff --git a/dist/source/debugger/duk_classnames.yaml b/dist/source/debugger/duk_classnames.yaml +new file mode 100644 +index 0000000..ded0277 +--- /dev/null ++++ b/dist/source/debugger/duk_classnames.yaml +@@ -0,0 +1,32 @@ ++# Must match C header. ++class_names: ++ - none ++ - Object ++ - Array ++ - Function ++ - Arguments ++ - Boolean ++ - Date ++ - Error ++ - JSON ++ - Math ++ - Number ++ - RegExp ++ - String ++ - global ++ - Symbol ++ - ObjEnv ++ - DecEnv ++ - Pointer ++ - Thread ++ - ArrayBuffer ++ - DataView ++ - Int8Array ++ - Uint8Array ++ - Uint8ClampedArray ++ - Int16Array ++ - Uint16Array ++ - Int32Array ++ - Uint32Array ++ - Float32Array ++ - Float64Array +diff --git a/dist/source/debugger/duk_debug.js b/dist/source/debugger/duk_debug.js +new file mode 100644 +index 0000000..e2d0bda +--- /dev/null ++++ b/dist/source/debugger/duk_debug.js +@@ -0,0 +1,2491 @@ ++/* ++ * Minimal debug web console for Duktape command line tool ++ * ++ * See debugger/README.rst. ++ * ++ * The web UI socket.io communication can easily become a bottleneck and ++ * it's important to ensure that the web UI remains responsive. Basic rate ++ * limiting mechanisms (token buckets, suppressing identical messages, etc) ++ * are used here now. Ideally the web UI would pull data on its own terms ++ * which would provide natural rate limiting. ++ * ++ * Promises are used to structure callback chains. ++ * ++ * https://github.com/petkaantonov/bluebird ++ * https://github.com/petkaantonov/bluebird/blob/master/API.md ++ * https://github.com/petkaantonov/bluebird/wiki/Promise-anti-patterns ++ */ ++ ++var Promise = require('bluebird'); ++var events = require('events'); ++var stream = require('stream'); ++var path = require('path'); ++var fs = require('fs'); ++var net = require('net'); ++var byline = require('byline'); ++var util = require('util'); ++var readline = require('readline'); ++var sprintf = require('sprintf').sprintf; ++var utf8 = require('utf8'); ++var yaml = require('yamljs'); ++var recursiveReadSync = require('recursive-readdir-sync'); ++ ++// Command line options (defaults here, overwritten if necessary) ++var optTargetHost = '127.0.0.1'; ++var optTargetPort = 9091; ++var optHttpPort = 9092; ++var optJsonProxyPort = 9093; ++var optJsonProxy = false; ++var optSourceSearchDirs = [ '../tests/ecmascript' ]; ++var optDumpDebugRead = null; ++var optDumpDebugWrite = null; ++var optDumpDebugPretty = null; ++var optLogMessages = false; ++ ++// Constants ++var UI_MESSAGE_CLIPLEN = 128; ++var LOCALS_CLIPLEN = 64; ++var EVAL_CLIPLEN = 4096; ++var GETVAR_CLIPLEN = 4096; ++var SUPPORTED_DEBUG_PROTOCOL_VERSION = 2; ++ ++// Commands initiated by Duktape ++var CMD_STATUS = 0x01; ++var CMD_UNUSED_2 = 0x02; // Duktape 1.x: print notify ++var CMD_UNUSED_3 = 0x03; // Duktape 1.x: alert notify ++var CMD_UNUSED_4 = 0x04; // Duktape 1.x: log notify ++var CMD_THROW = 0x05; ++var CMD_DETACHING = 0x06; ++ ++// Commands initiated by the debug client (= us) ++var CMD_BASICINFO = 0x10; ++var CMD_TRIGGERSTATUS = 0x11; ++var CMD_PAUSE = 0x12; ++var CMD_RESUME = 0x13; ++var CMD_STEPINTO = 0x14; ++var CMD_STEPOVER = 0x15; ++var CMD_STEPOUT = 0x16; ++var CMD_LISTBREAK = 0x17; ++var CMD_ADDBREAK = 0x18; ++var CMD_DELBREAK = 0x19; ++var CMD_GETVAR = 0x1a; ++var CMD_PUTVAR = 0x1b; ++var CMD_GETCALLSTACK = 0x1c; ++var CMD_GETLOCALS = 0x1d; ++var CMD_EVAL = 0x1e; ++var CMD_DETACH = 0x1f; ++var CMD_DUMPHEAP = 0x20; ++var CMD_GETBYTECODE = 0x21; ++ ++// Errors ++var ERR_UNKNOWN = 0x00; ++var ERR_UNSUPPORTED = 0x01; ++var ERR_TOOMANY = 0x02; ++var ERR_NOTFOUND = 0x03; ++ ++// Marker objects for special protocol values ++var DVAL_EOM = { type: 'eom' }; ++var DVAL_REQ = { type: 'req' }; ++var DVAL_REP = { type: 'rep' }; ++var DVAL_ERR = { type: 'err' }; ++var DVAL_NFY = { type: 'nfy' }; ++ ++// String map for commands (debug dumping). A single map works (instead of ++// separate maps for each direction) because command numbers don't currently ++// overlap. So merge the YAML metadata. ++var debugCommandMeta = yaml.load('duk_debugcommands.yaml'); ++var debugCommandNames = []; // list of command names, merged client/target ++debugCommandMeta.target_commands.forEach(function (k, i) { ++ debugCommandNames[i] = k; ++}); ++debugCommandMeta.client_commands.forEach(function (k, i) { // override ++ debugCommandNames[i] = k; ++}); ++var debugCommandNumbers = {}; // map from (merged) command name to number ++debugCommandNames.forEach(function (k, i) { ++ debugCommandNumbers[k] = i; ++}); ++ ++// Duktape heaphdr type constants, must match C headers ++var DUK_HTYPE_STRING = 0; ++var DUK_HTYPE_OBJECT = 1; ++var DUK_HTYPE_BUFFER = 2; ++ ++// Duktape internal class numbers, must match C headers ++var dukClassNameMeta = yaml.load('duk_classnames.yaml'); ++var dukClassNames = dukClassNameMeta.class_names; ++ ++// Bytecode opcode/extraop metadata ++var dukOpcodes = yaml.load('duk_opcodes.yaml'); ++if (dukOpcodes.opcodes.length != 256) { ++ throw new Error('opcode metadata length incorrect'); ++} ++ ++/* ++ * Miscellaneous helpers ++ */ ++ ++var nybbles = '0123456789abcdef'; ++ ++/* Convert a buffer into a string using Unicode codepoints U+0000...U+00FF. ++ * This is the NodeJS 'binary' encoding, but since it's being deprecated, ++ * reimplement it here. We need to avoid parsing strings as e.g. UTF-8: ++ * although Duktape strings are usually UTF-8/CESU-8 that's not always the ++ * case, e.g. for internal strings. Buffer values are also represented as ++ * strings in the debug protocol, so we must deal accurately with arbitrary ++ * byte arrays. ++ */ ++function bufferToDebugString(buf) { ++ var cp = []; ++ var i, n; ++ ++/* ++ // This fails with "RangeError: Maximum call stack size exceeded" for some ++ // reason, so use a much slower variant. ++ ++ for (i = 0, n = buf.length; i < n; i++) { ++ cp[i] = buf[i]; ++ } ++ ++ return String.fromCharCode.apply(String, cp); ++*/ ++ ++ for (i = 0, n = buf.length; i < n; i++) { ++ cp[i] = String.fromCharCode(buf[i]); ++ } ++ ++ return cp.join(''); ++} ++ ++/* Write a string into a buffer interpreting codepoints U+0000...U+00FF ++ * as bytes. Drop higher bits. ++ */ ++function writeDebugStringToBuffer(str, buf, off) { ++ var i, n; ++ ++ for (i = 0, n = str.length; i < n; i++) { ++ buf[off + i] = str.charCodeAt(i) & 0xff; // truncate higher bits ++ } ++} ++ ++/* Encode an ordinary Unicode string into a dvalue compatible format, i.e. ++ * into a byte array represented as codepoints U+0000...U+00FF. Concretely, ++ * encode with UTF-8 and then represent the bytes with U+0000...U+00FF. ++ */ ++function stringToDebugString(str) { ++ return utf8.encode(str); ++} ++ ++/* Pretty print a dvalue. Useful for dumping etc. */ ++function prettyDebugValue(x) { ++ if (typeof x === 'object' && x !== null) { ++ if (x.type === 'eom') { ++ return 'EOM'; ++ } else if (x.type === 'req') { ++ return 'REQ'; ++ } else if (x.type === 'rep') { ++ return 'REP'; ++ } else if (x.type === 'err') { ++ return 'ERR'; ++ } else if (x.type === 'nfy') { ++ return 'NFY'; ++ } ++ } ++ return JSON.stringify(x); ++} ++ ++/* Pretty print a number for UI usage. Types and values should be easy to ++ * read and typing should be obvious. For numbers, support Infinity, NaN, ++ * and signed zeroes properly. ++ */ ++function prettyUiNumber(x) { ++ if (x === 1 / 0) { return 'Infinity'; } ++ if (x === -1 / 0) { return '-Infinity'; } ++ if (Number.isNaN(x)) { return 'NaN'; } ++ if (x === 0 && 1 / x > 0) { return '0'; } ++ if (x === 0 && 1 / x < 0) { return '-0'; } ++ return x.toString(); ++} ++ ++/* Pretty print a dvalue string (bytes represented as U+0000...U+00FF) ++ * for UI usage. Try UTF-8 decoding to get a nice Unicode string (JSON ++ * encoded) but if that fails, ensure that bytes are encoded transparently. ++ * The result is a quoted string with a special quote marker for a "raw" ++ * string when UTF-8 decoding fails. Very long strings are optionally ++ * clipped. ++ */ ++function prettyUiString(x, cliplen) { ++ var ret; ++ ++ if (typeof x !== 'string') { ++ throw new Error('invalid input to prettyUiString: ' + typeof x); ++ } ++ try { ++ // Here utf8.decode() is better than decoding using NodeJS buffer ++ // operations because we want strict UTF-8 interpretation. ++ ret = JSON.stringify(utf8.decode(x)); ++ } catch (e) { ++ // When we fall back to representing bytes, indicate that the string ++ // is "raw" with a 'r"' prefix (a somewhat arbitrary convention). ++ // U+0022 = ", U+0027 = ' ++ ret = 'r"' + x.replace(/[\u0022\u0027\u0000-\u001f\u0080-\uffff]/g, function (match) { ++ var cp = match.charCodeAt(0); ++ return '\\x' + nybbles[(cp >> 4) & 0x0f] + nybbles[cp & 0x0f]; ++ }) + '"'; ++ } ++ ++ if (cliplen && ret.length > cliplen) { ++ ret = ret.substring(0, cliplen) + '...'; // trailing '"' intentionally missing ++ } ++ return ret; ++} ++ ++/* Pretty print a dvalue string (bytes represented as U+0000...U+00FF) ++ * for UI usage without quotes. ++ */ ++function prettyUiStringUnquoted(x, cliplen) { ++ var ret; ++ ++ if (typeof x !== 'string') { ++ throw new Error('invalid input to prettyUiStringUnquoted: ' + typeof x); ++ } ++ ++ try { ++ // Here utf8.decode() is better than decoding using NodeJS buffer ++ // operations because we want strict UTF-8 interpretation. ++ ++ // XXX: unprintable characters etc? In some UI cases we'd want to ++ // e.g. escape newlines and in others not. ++ ret = utf8.decode(x); ++ } catch (e) { ++ // For the unquoted version we don't need to escape single or double ++ // quotes. ++ ret = x.replace(/[\u0000-\u001f\u0080-\uffff]/g, function (match) { ++ var cp = match.charCodeAt(0); ++ return '\\x' + nybbles[(cp >> 4) & 0x0f] + nybbles[cp & 0x0f]; ++ }); ++ } ++ ++ if (cliplen && ret.length > cliplen) { ++ ret = ret.substring(0, cliplen) + '...'; ++ } ++ return ret; ++} ++ ++/* Pretty print a dvalue for UI usage. Everything comes out as a ready-to-use ++ * string. ++ * ++ * XXX: Currently the debug client formats all values for UI use. A better ++ * solution would be to pass values in typed form and let the UI format them, ++ * so that styling etc. could take typing into account. ++ */ ++function prettyUiDebugValue(x, cliplen) { ++ if (typeof x === 'object' && x !== null) { ++ // Note: typeof null === 'object', so null special case explicitly ++ if (x.type === 'eom') { ++ return 'EOM'; ++ } else if (x.type === 'req') { ++ return 'REQ'; ++ } else if (x.type === 'rep') { ++ return 'REP'; ++ } else if (x.type === 'err') { ++ return 'ERR'; ++ } else if (x.type === 'nfy') { ++ return 'NFY'; ++ } else if (x.type === 'unused') { ++ return 'unused'; ++ } else if (x.type === 'undefined') { ++ return 'undefined'; ++ } else if (x.type === 'buffer') { ++ return '|' + x.data + '|'; ++ } else if (x.type === 'object') { ++ return '[object ' + (dukClassNames[x.class] || ('class ' + x.class)) + ' ' + x.pointer + ']'; ++ } else if (x.type === 'pointer') { ++ return ''; ++ } else if (x.type === 'lightfunc') { ++ return ''; ++ } else if (x.type === 'number') { ++ // duk_tval number, any IEEE double ++ var tmp = new Buffer(x.data, 'hex'); // decode into hex ++ var val = tmp.readDoubleBE(0); // big endian ieee double ++ return prettyUiNumber(val); ++ } ++ } else if (x === null) { ++ return 'null'; ++ } else if (typeof x === 'boolean') { ++ return x ? 'true' : 'false'; ++ } else if (typeof x === 'string') { ++ return prettyUiString(x, cliplen); ++ } else if (typeof x === 'number') { ++ // Debug protocol integer ++ return prettyUiNumber(x); ++ } ++ ++ // We shouldn't come here, but if we do, JSON is a reasonable default. ++ return JSON.stringify(x); ++} ++ ++/* Pretty print a debugger message given as an array of parsed dvalues. ++ * Result should be a pure ASCII one-liner. ++ */ ++function prettyDebugMessage(msg) { ++ return msg.map(prettyDebugValue).join(' '); ++} ++ ++/* Pretty print a debugger command. */ ++function prettyDebugCommand(cmd) { ++ return debugCommandNames[cmd] || String(cmd); ++} ++ ++/* Decode and normalize source file contents: UTF-8, tabs to 8, ++ * CR LF to LF. ++ */ ++function decodeAndNormalizeSource(data) { ++ var tmp; ++ var lines, line, repl; ++ var i, n; ++ var j, m; ++ ++ try { ++ tmp = data.toString('utf8'); ++ } catch (e) { ++ console.log('Failed to UTF-8 decode source file, ignoring: ' + e); ++ tmp = String(data); ++ } ++ ++ lines = tmp.split(/\r?\n/); ++ for (i = 0, n = lines.length; i < n; i++) { ++ line = lines[i]; ++ if (/\t/.test(line)) { ++ repl = ''; ++ for (j = 0, m = line.length; j < m; j++) { ++ if (line.charAt(j) === '\t') { ++ repl += ' '; ++ while ((repl.length % 8) != 0) { ++ repl += ' '; ++ } ++ } else { ++ repl += line.charAt(j); ++ } ++ } ++ lines[i] = repl; ++ } ++ } ++ ++ // XXX: normalize last newline (i.e. force a newline if contents don't ++ // end with a newline)? ++ ++ return lines.join('\n'); ++} ++ ++/* Token bucket rate limiter for a given callback. Calling code calls ++ * trigger() to request 'cb' to be called, and the rate limiter ensures ++ * that 'cb' is not called too often. ++ */ ++function RateLimited(tokens, rate, cb) { ++ var _this = this; ++ this.maxTokens = tokens; ++ this.tokens = this.maxTokens; ++ this.rate = rate; ++ this.cb = cb; ++ this.delayedCb = false; ++ ++ // Right now the implementation is setInterval-based, but could also be ++ // made timerless. There are so few rate limited resources that this ++ // doesn't matter in practice. ++ ++ this.tokenAdder = setInterval(function () { ++ if (_this.tokens < _this.maxTokens) { ++ _this.tokens++; ++ } ++ if (_this.delayedCb) { ++ _this.delayedCb = false; ++ _this.tokens--; ++ _this.cb(); ++ } ++ }, this.rate); ++} ++RateLimited.prototype.trigger = function () { ++ if (this.tokens > 0) { ++ this.tokens--; ++ this.cb(); ++ } else { ++ this.delayedCb = true; ++ } ++}; ++ ++/* ++ * Source file manager ++ * ++ * Scan the list of search directories for ECMAScript source files and ++ * build an index of them. Provides a mechanism to find a source file ++ * based on a raw 'fileName' property provided by the debug target, and ++ * to provide a file list for the web UI. ++ * ++ * NOTE: it's tempting to do loose matching for filenames, but this does ++ * not work in practice. Filenames must match 1:1 with the debug target ++ * so that e.g. breakpoints assigned based on filenames found from the ++ * search paths will match 1:1 on the debug target. If this is not the ++ * case, breakpoints won't work as expected. ++ */ ++ ++function SourceFileManager(directories) { ++ this.directories = directories; ++ this.extensions = { '.js': true, '.jsm': true }; ++ this.fileMap = {}; // filename as seen by debug target -> file path ++ this.files = []; // filenames as seen by debug target ++} ++ ++SourceFileManager.prototype.scan = function () { ++ var _this = this; ++ var fileMap = {}; // relative path -> file path ++ var files; ++ ++ this.directories.forEach(function (dir) { ++ console.log('Scanning source files: ' + dir); ++ try { ++ recursiveReadSync(dir).forEach(function (fn) { ++ // Example: dir ../../tests ++ // normFn ../../tests/foo/bar.js ++ // relFn foo/bar.js ++ var normDir = path.normalize(dir); ++ var normFn = path.normalize(fn); ++ var relFn = path.relative(normDir, normFn); ++ ++ if (fs.existsSync(normFn) && fs.lstatSync(normFn).isFile() && ++ _this.extensions[path.extname(normFn)]) { ++ // We want the fileMap to contain the filename relative to ++ // the search dir root. First directory containing a ++ // certail relFn wins. ++ if (relFn in fileMap) { ++ console.log('Found', relFn, 'multiple times, first match wins'); ++ } else { ++ fileMap[relFn] = normFn; ++ } ++ } ++ }); ++ } catch (e) { ++ console.log('Failed to scan ' + dir + ': ' + e); ++ } ++ }); ++ ++ files = Object.keys(fileMap); ++ files.sort(); ++ this.files = files; ++ ++ this.fileMap = fileMap; ++ ++ console.log('Found ' + files.length + ' source files in ' + this.directories.length + ' search directories'); ++}; ++ ++SourceFileManager.prototype.getFiles = function () { ++ return this.files; ++}; ++ ++SourceFileManager.prototype.search = function (fileName) { ++ var _this = this; ++ ++ // Loose matching is tempting but counterproductive: filenames must ++ // match 1:1 between the debug client and the debug target for e.g. ++ // breakpoints to work as expected. Note that a breakpoint may be ++ // assigned by selecting a file from a dropdown populated by scanning ++ // the filesystem for available sources and there's no way of knowing ++ // if the debug target uses the exact same name. ++ // ++ // We intentionally allow any files from the search paths, not just ++ // those scanned to this.fileMap. ++ ++ function tryLookup() { ++ var i, fn, data; ++ ++ for (i = 0; i < _this.directories.length; i++) { ++ fn = path.join(_this.directories[i], fileName); ++ if (fs.existsSync(fn) && fs.lstatSync(fn).isFile()) { ++ data = fs.readFileSync(fn); // Raw bytes ++ return decodeAndNormalizeSource(data); // Unicode string ++ } ++ } ++ return null; ++ } ++ ++ return tryLookup(fileName); ++}; ++ ++/* ++ * Debug protocol parser ++ * ++ * The debug protocol parser is an EventEmitter which parses debug messages ++ * from an input stream and emits 'debug-message' events for completed ++ * messages ending in an EOM. The parser also provides debug dumping, stream ++ * logging functionality, and statistics gathering functionality. ++ * ++ * This parser is used to parse both incoming and outgoing messages. For ++ * outgoing messages the only function is to validate and debug dump the ++ * messages we're about to send. The downside of dumping at this low level ++ * is that we can't match request and reply/error messages here. ++ * ++ * http://www.sitepoint.com/nodejs-events-and-eventemitter/ ++ */ ++ ++function DebugProtocolParser(inputStream, ++ protocolVersion, ++ rawDumpFileName, ++ textDumpFileName, ++ textDumpFilePrefix, ++ hexDumpConsolePrefix, ++ textDumpConsolePrefix) { ++ var _this = this; ++ this.inputStream = inputStream; ++ this.closed = false; // stream is closed/broken, don't parse anymore ++ this.bytes = 0; ++ this.dvalues = 0; ++ this.messages = 0; ++ this.requests = 0; ++ this.prevBytes = 0; ++ this.bytesPerSec = 0; ++ this.statsTimer = null; ++ this.readableNumberValue = true; ++ ++ events.EventEmitter.call(this); ++ ++ var buf = new Buffer(0); // accumulate data ++ var msg = []; // accumulated message until EOM ++ var versionIdentification; ++ ++ var statsInterval = 2000; ++ var statsIntervalSec = statsInterval / 1000; ++ this.statsTimer = setInterval(function () { ++ _this.bytesPerSec = (_this.bytes - _this.prevBytes) / statsIntervalSec; ++ _this.prevBytes = _this.bytes; ++ _this.emit('stats-update'); ++ }, statsInterval); ++ ++ function consume(n) { ++ var tmp = new Buffer(buf.length - n); ++ buf.copy(tmp, 0, n); ++ buf = tmp; ++ } ++ ++ inputStream.on('data', function (data) { ++ var i, n, x, v, gotValue, len, t, tmpbuf, verstr; ++ var prettyMsg; ++ ++ if (_this.closed || !_this.inputStream) { ++ console.log('Ignoring incoming data from closed input stream, len ' + data.length); ++ return; ++ } ++ ++ _this.bytes += data.length; ++ if (rawDumpFileName) { ++ fs.appendFileSync(rawDumpFileName, data); ++ } ++ if (hexDumpConsolePrefix) { ++ console.log(hexDumpConsolePrefix + data.toString('hex')); ++ } ++ ++ buf = Buffer.concat([ buf, data ]); ++ ++ // Protocol version handling. When dumping an output stream, the ++ // caller gives a non-null protocolVersion so we don't read one here. ++ if (protocolVersion == null) { ++ if (buf.length > 1024) { ++ _this.emit('transport-error', 'Parse error (version identification too long), dropping connection'); ++ _this.close(); ++ return; ++ } ++ ++ for (i = 0, n = buf.length; i < n; i++) { ++ if (buf[i] == 0x0a) { ++ tmpbuf = new Buffer(i); ++ buf.copy(tmpbuf, 0, 0, i); ++ consume(i + 1); ++ verstr = tmpbuf.toString('utf-8'); ++ t = verstr.split(' '); ++ protocolVersion = Number(t[0]); ++ versionIdentification = verstr; ++ ++ _this.emit('protocol-version', { ++ protocolVersion: protocolVersion, ++ versionIdentification: versionIdentification ++ }); ++ break; ++ } ++ } ++ ++ if (protocolVersion == null) { ++ // Still waiting for version identification to complete. ++ return; ++ } ++ } ++ ++ // Parse complete dvalues (quite inefficient now) by trial parsing. ++ // Consume a value only when it's fully present in 'buf'. ++ // See doc/debugger.rst for format description. ++ ++ while (buf.length > 0) { ++ x = buf[0]; ++ v = undefined; ++ gotValue = false; // used to flag special values like undefined ++ ++ if (x >= 0xc0) { ++ // 0xc0...0xff: integers 0-16383 ++ if (buf.length >= 2) { ++ v = ((x - 0xc0) << 8) + buf[1]; ++ consume(2); ++ } ++ } else if (x >= 0x80) { ++ // 0x80...0xbf: integers 0-63 ++ v = x - 0x80; ++ consume(1); ++ } else if (x >= 0x60) { ++ // 0x60...0x7f: strings with length 0-31 ++ len = x - 0x60; ++ if (buf.length >= 1 + len) { ++ v = new Buffer(len); ++ buf.copy(v, 0, 1, 1 + len); ++ v = bufferToDebugString(v); ++ consume(1 + len); ++ } ++ } else { ++ switch (x) { ++ case 0x00: v = DVAL_EOM; consume(1); break; ++ case 0x01: v = DVAL_REQ; consume(1); break; ++ case 0x02: v = DVAL_REP; consume(1); break; ++ case 0x03: v = DVAL_ERR; consume(1); break; ++ case 0x04: v = DVAL_NFY; consume(1); break; ++ case 0x10: // 4-byte signed integer ++ if (buf.length >= 5) { ++ v = buf.readInt32BE(1); ++ consume(5); ++ } ++ break; ++ case 0x11: // 4-byte string ++ if (buf.length >= 5) { ++ len = buf.readUInt32BE(1); ++ if (buf.length >= 5 + len) { ++ v = new Buffer(len); ++ buf.copy(v, 0, 5, 5 + len); ++ v = bufferToDebugString(v); ++ consume(5 + len); ++ } ++ } ++ break; ++ case 0x12: // 2-byte string ++ if (buf.length >= 3) { ++ len = buf.readUInt16BE(1); ++ if (buf.length >= 3 + len) { ++ v = new Buffer(len); ++ buf.copy(v, 0, 3, 3 + len); ++ v = bufferToDebugString(v); ++ consume(3 + len); ++ } ++ } ++ break; ++ case 0x13: // 4-byte buffer ++ if (buf.length >= 5) { ++ len = buf.readUInt32BE(1); ++ if (buf.length >= 5 + len) { ++ v = new Buffer(len); ++ buf.copy(v, 0, 5, 5 + len); ++ v = { type: 'buffer', data: v.toString('hex') }; ++ consume(5 + len); ++ // Value could be a Node.js buffer directly, but ++ // we prefer all dvalues to be JSON compatible ++ } ++ } ++ break; ++ case 0x14: // 2-byte buffer ++ if (buf.length >= 3) { ++ len = buf.readUInt16BE(1); ++ if (buf.length >= 3 + len) { ++ v = new Buffer(len); ++ buf.copy(v, 0, 3, 3 + len); ++ v = { type: 'buffer', data: v.toString('hex') }; ++ consume(3 + len); ++ // Value could be a Node.js buffer directly, but ++ // we prefer all dvalues to be JSON compatible ++ } ++ } ++ break; ++ case 0x15: // unused/none ++ v = { type: 'unused' }; ++ consume(1); ++ break; ++ case 0x16: // undefined ++ v = { type: 'undefined' }; ++ gotValue = true; // indicate 'v' is actually set ++ consume(1); ++ break; ++ case 0x17: // null ++ v = null; ++ gotValue = true; // indicate 'v' is actually set ++ consume(1); ++ break; ++ case 0x18: // true ++ v = true; ++ consume(1); ++ break; ++ case 0x19: // false ++ v = false; ++ consume(1); ++ break; ++ case 0x1a: // number (IEEE double), big endian ++ if (buf.length >= 9) { ++ v = new Buffer(8); ++ buf.copy(v, 0, 1, 9); ++ v = { type: 'number', data: v.toString('hex') }; ++ ++ if (_this.readableNumberValue) { ++ // The value key should not be used programmatically, ++ // it is just there to make the dumps more readable. ++ v.value = buf.readDoubleBE(1); ++ } ++ consume(9); ++ } ++ break; ++ case 0x1b: // object ++ if (buf.length >= 3) { ++ len = buf[2]; ++ if (buf.length >= 3 + len) { ++ v = new Buffer(len); ++ buf.copy(v, 0, 3, 3 + len); ++ v = { type: 'object', 'class': buf[1], pointer: v.toString('hex') }; ++ consume(3 + len); ++ } ++ } ++ break; ++ case 0x1c: // pointer ++ if (buf.length >= 2) { ++ len = buf[1]; ++ if (buf.length >= 2 + len) { ++ v = new Buffer(len); ++ buf.copy(v, 0, 2, 2 + len); ++ v = { type: 'pointer', pointer: v.toString('hex') }; ++ consume(2 + len); ++ } ++ } ++ break; ++ case 0x1d: // lightfunc ++ if (buf.length >= 4) { ++ len = buf[3]; ++ if (buf.length >= 4 + len) { ++ v = new Buffer(len); ++ buf.copy(v, 0, 4, 4 + len); ++ v = { type: 'lightfunc', flags: buf.readUInt16BE(1), pointer: v.toString('hex') }; ++ consume(4 + len); ++ } ++ } ++ break; ++ case 0x1e: // heapptr ++ if (buf.length >= 2) { ++ len = buf[1]; ++ if (buf.length >= 2 + len) { ++ v = new Buffer(len); ++ buf.copy(v, 0, 2, 2 + len); ++ v = { type: 'heapptr', pointer: v.toString('hex') }; ++ consume(2 + len); ++ } ++ } ++ break; ++ default: ++ _this.emit('transport-error', 'Parse error, dropping connection'); ++ _this.close(); ++ } ++ } ++ ++ if (typeof v === 'undefined' && !gotValue) { ++ break; ++ } ++ msg.push(v); ++ _this.dvalues++; ++ ++ // Could emit a 'debug-value' event here, but that's not necessary ++ // because the receiver will just collect statistics which can also ++ // be done using the finished message. ++ ++ if (v === DVAL_EOM) { ++ _this.messages++; ++ ++ if (textDumpFileName || textDumpConsolePrefix) { ++ prettyMsg = prettyDebugMessage(msg); ++ if (textDumpFileName) { ++ fs.appendFileSync(textDumpFileName, (textDumpFilePrefix || '') + prettyMsg + '\n'); ++ } ++ if (textDumpConsolePrefix) { ++ console.log(textDumpConsolePrefix + prettyMsg); ++ } ++ } ++ ++ _this.emit('debug-message', msg); ++ msg = []; // new object, old may be in circulation for a while ++ } ++ } ++ }); ++ ++ // Not all streams will emit this. ++ inputStream.on('error', function (err) { ++ _this.emit('transport-error', err); ++ _this.close(); ++ }); ++ ++ // Not all streams will emit this. ++ inputStream.on('close', function () { ++ _this.close(); ++ }); ++} ++DebugProtocolParser.prototype = Object.create(events.EventEmitter.prototype); ++ ++DebugProtocolParser.prototype.close = function () { ++ // Although the underlying transport may not have a close() or destroy() ++ // method or even a 'close' event, this method is always available and ++ // will generate a 'transport-close'. ++ // ++ // The caller is responsible for closing the underlying stream if that ++ // is necessary. ++ ++ if (this.closed) { return; } ++ ++ this.closed = true; ++ if (this.statsTimer) { ++ clearInterval(this.statsTimer); ++ this.statsTimer = null; ++ } ++ this.emit('transport-close'); ++}; ++ ++/* ++ * Debugger output formatting ++ */ ++ ++function formatDebugValue(v) { ++ var buf, dec, len; ++ ++ // See doc/debugger.rst for format description. ++ ++ if (typeof v === 'object' && v !== null) { ++ // Note: typeof null === 'object', so null special case explicitly ++ if (v.type === 'eom') { ++ return new Buffer([ 0x00 ]); ++ } else if (v.type === 'req') { ++ return new Buffer([ 0x01 ]); ++ } else if (v.type === 'rep') { ++ return new Buffer([ 0x02 ]); ++ } else if (v.type === 'err') { ++ return new Buffer([ 0x03 ]); ++ } else if (v.type === 'nfy') { ++ return new Buffer([ 0x04 ]); ++ } else if (v.type === 'unused') { ++ return new Buffer([ 0x15 ]); ++ } else if (v.type === 'undefined') { ++ return new Buffer([ 0x16 ]); ++ } else if (v.type === 'number') { ++ dec = new Buffer(v.data, 'hex'); ++ len = dec.length; ++ if (len !== 8) { ++ throw new TypeError('value cannot be converted to dvalue: ' + JSON.stringify(v)); ++ } ++ buf = new Buffer(1 + len); ++ buf[0] = 0x1a; ++ dec.copy(buf, 1); ++ return buf; ++ } else if (v.type === 'buffer') { ++ dec = new Buffer(v.data, 'hex'); ++ len = dec.length; ++ if (len <= 0xffff) { ++ buf = new Buffer(3 + len); ++ buf[0] = 0x14; ++ buf[1] = (len >> 8) & 0xff; ++ buf[2] = (len >> 0) & 0xff; ++ dec.copy(buf, 3); ++ return buf; ++ } else { ++ buf = new Buffer(5 + len); ++ buf[0] = 0x13; ++ buf[1] = (len >> 24) & 0xff; ++ buf[2] = (len >> 16) & 0xff; ++ buf[3] = (len >> 8) & 0xff; ++ buf[4] = (len >> 0) & 0xff; ++ dec.copy(buf, 5); ++ return buf; ++ } ++ } else if (v.type === 'object') { ++ dec = new Buffer(v.pointer, 'hex'); ++ len = dec.length; ++ buf = new Buffer(3 + len); ++ buf[0] = 0x1b; ++ buf[1] = v.class; ++ buf[2] = len; ++ dec.copy(buf, 3); ++ return buf; ++ } else if (v.type === 'pointer') { ++ dec = new Buffer(v.pointer, 'hex'); ++ len = dec.length; ++ buf = new Buffer(2 + len); ++ buf[0] = 0x1c; ++ buf[1] = len; ++ dec.copy(buf, 2); ++ return buf; ++ } else if (v.type === 'lightfunc') { ++ dec = new Buffer(v.pointer, 'hex'); ++ len = dec.length; ++ buf = new Buffer(4 + len); ++ buf[0] = 0x1d; ++ buf[1] = (v.flags >> 8) & 0xff; ++ buf[2] = v.flags & 0xff; ++ buf[3] = len; ++ dec.copy(buf, 4); ++ return buf; ++ } else if (v.type === 'heapptr') { ++ dec = new Buffer(v.pointer, 'hex'); ++ len = dec.length; ++ buf = new Buffer(2 + len); ++ buf[0] = 0x1e; ++ buf[1] = len; ++ dec.copy(buf, 2); ++ return buf; ++ } ++ } else if (v === null) { ++ return new Buffer([ 0x17 ]); ++ } else if (typeof v === 'boolean') { ++ return new Buffer([ v ? 0x18 : 0x19 ]); ++ } else if (typeof v === 'number') { ++ if (Math.floor(v) === v && /* whole */ ++ (v !== 0 || 1 / v > 0) && /* not negative zero */ ++ v >= -0x80000000 && v <= 0x7fffffff) { ++ // Represented signed 32-bit integers as plain integers. ++ // Debugger code expects this for all fields that are not ++ // duk_tval representations (e.g. command numbers and such). ++ if (v >= 0x00 && v <= 0x3f) { ++ return new Buffer([ 0x80 + v ]); ++ } else if (v >= 0x0000 && v <= 0x3fff) { ++ return new Buffer([ 0xc0 + (v >> 8), v & 0xff ]); ++ } else if (v >= -0x80000000 && v <= 0x7fffffff) { ++ return new Buffer([ 0x10, ++ (v >> 24) & 0xff, ++ (v >> 16) & 0xff, ++ (v >> 8) & 0xff, ++ (v >> 0) & 0xff ]); ++ } else { ++ throw new Error('internal error when encoding integer to dvalue: ' + v); ++ } ++ } else { ++ // Represent non-integers as IEEE double dvalues ++ buf = new Buffer(1 + 8); ++ buf[0] = 0x1a; ++ buf.writeDoubleBE(v, 1); ++ return buf; ++ } ++ } else if (typeof v === 'string') { ++ if (v.length < 0 || v.length > 0xffffffff) { ++ // Not possible in practice. ++ throw new TypeError('cannot convert to dvalue, invalid string length: ' + v.length); ++ } ++ if (v.length <= 0x1f) { ++ buf = new Buffer(1 + v.length); ++ buf[0] = 0x60 + v.length; ++ writeDebugStringToBuffer(v, buf, 1); ++ return buf; ++ } else if (v.length <= 0xffff) { ++ buf = new Buffer(3 + v.length); ++ buf[0] = 0x12; ++ buf[1] = (v.length >> 8) & 0xff; ++ buf[2] = (v.length >> 0) & 0xff; ++ writeDebugStringToBuffer(v, buf, 3); ++ return buf; ++ } else { ++ buf = new Buffer(5 + v.length); ++ buf[0] = 0x11; ++ buf[1] = (v.length >> 24) & 0xff; ++ buf[2] = (v.length >> 16) & 0xff; ++ buf[3] = (v.length >> 8) & 0xff; ++ buf[4] = (v.length >> 0) & 0xff; ++ writeDebugStringToBuffer(v, buf, 5); ++ return buf; ++ } ++ } ++ ++ // Shouldn't come here. ++ throw new TypeError('value cannot be converted to dvalue: ' + JSON.stringify(v)); ++} ++ ++/* ++ * Debugger implementation ++ * ++ * A debugger instance communicates with the debug target and maintains ++ * persistent debug state so that the current state can be resent to the ++ * socket.io client (web UI) if it reconnects. Whenever the debugger state ++ * is changed an event is generated. The socket.io handler will listen to ++ * state change events and push the necessary updates to the web UI, often ++ * in a rate limited fashion or using a client pull to ensure the web UI ++ * is not overloaded. ++ * ++ * The debugger instance assumes that if the debug protocol connection is ++ * re-established, it is always to the same target. There is no separate ++ * abstraction for a debugger session. ++ */ ++ ++function Debugger() { ++ events.EventEmitter.call(this); ++ ++ this.web = null; // web UI singleton ++ this.targetStream = null; // transport connection to target ++ this.outputPassThroughStream = null; // dummy passthrough for message dumping ++ this.inputParser = null; // parser for incoming debug messages ++ this.outputParser = null; // parser for outgoing debug messages (stats, dumping) ++ this.protocolVersion = null; ++ this.dukVersion = null; ++ this.dukGitDescribe = null; ++ this.targetInfo = null; ++ this.attached = false; ++ this.handshook = false; ++ this.reqQueue = null; ++ this.stats = { // stats for current debug connection ++ rxBytes: 0, rxDvalues: 0, rxMessages: 0, rxBytesPerSec: 0, ++ txBytes: 0, txDvalues: 0, txMessages: 0, txBytesPerSec: 0 ++ }; ++ this.execStatus = { ++ attached: false, ++ state: 'detached', ++ fileName: '', ++ funcName: '', ++ line: 0, ++ pc: 0 ++ }; ++ this.breakpoints = []; ++ this.callstack = []; ++ this.locals = []; ++ this.messageLines = []; ++ this.messageScrollBack = 100; ++} ++Debugger.prototype = events.EventEmitter.prototype; ++ ++Debugger.prototype.decodeBytecodeFromBuffer = function (buf, consts, funcs) { ++ var i, j, n, m, ins, pc; ++ var res = []; ++ var op, str, args, comments; ++ ++ // XXX: add constants inline to preformatted output (e.g. for strings, ++ // add a short escaped snippet as a comment on the line after the ++ // compact argument list). ++ ++ for (i = 0, n = buf.length; i < n; i += 4) { ++ pc = i / 4; ++ ++ // shift forces unsigned ++ if (this.endianness === 'little') { ++ ins = buf.readInt32LE(i) >>> 0; ++ } else { ++ ins = buf.readInt32BE(i) >>> 0; ++ } ++ ++ op = dukOpcodes.opcodes[ins & 0xff]; ++ ++ args = []; ++ comments = []; ++ if (op.args) { ++ for (j = 0, m = op.args.length; j < m; j++) { ++ var A = (ins >>> 8) & 0xff; ++ var B = (ins >>> 16) & 0xff; ++ var C = (ins >>> 24) & 0xff; ++ var BC = (ins >>> 16) & 0xffff; ++ var ABC = (ins >>> 8) & 0xffffff; ++ var Bconst = op & 0x01; ++ var Cconst = op & 0x02; ++ ++ switch (op.args[j]) { ++ case 'A_R': args.push('r' + A); break; ++ case 'A_RI': args.push('r' + A + '(indirect)'); break; ++ case 'A_C': args.push('c' + A); break; ++ case 'A_H': args.push('0x' + A.toString(16)); break; ++ case 'A_I': args.push(A.toString(10)); break; ++ case 'A_B': args.push(A ? 'true' : 'false'); break; ++ case 'B_RC': args.push((Bconst ? 'c' : 'r') + B); break; ++ case 'B_R': args.push('r' + B); break; ++ case 'B_RI': args.push('r' + B + '(indirect)'); break; ++ case 'B_C': args.push('c' + B); break; ++ case 'B_H': args.push('0x' + B.toString(16)); break; ++ case 'B_I': args.push(B.toString(10)); break; ++ case 'C_RC': args.push((Cconst ? 'c' : 'r') + C); break; ++ case 'C_R': args.push('r' + C); break; ++ case 'C_RI': args.push('r' + C + '(indirect)'); break; ++ case 'C_C': args.push('c' + C); break; ++ case 'C_H': args.push('0x' + C.toString(16)); break; ++ case 'C_I': args.push(C.toString(10)); break; ++ case 'BC_R': args.push('r' + BC); break; ++ case 'BC_C': args.push('c' + BC); break; ++ case 'BC_H': args.push('0x' + BC.toString(16)); break; ++ case 'BC_I': args.push(BC.toString(10)); break; ++ case 'ABC_H': args.push(ABC.toString(16)); break; ++ case 'ABC_I': args.push(ABC.toString(10)); break; ++ case 'BC_LDINT': args.push(BC - (1 << 15)); break; ++ case 'BC_LDINTX': args.push(BC - 0); break; // no bias in LDINTX ++ case 'ABC_JUMP': { ++ var pc_add = ABC - (1 << 23) + 1; // pc is preincremented before adding ++ var pc_dst = pc + pc_add; ++ args.push(pc_dst + ' (' + (pc_add >= 0 ? '+' : '') + pc_add + ')'); ++ break; ++ } ++ default: args.push('?'); break; ++ } ++ } ++ } ++ if (op.flags) { ++ for (j = 0, m = op.flags.length; j < m; j++) { ++ if (ins & op.flags[j].mask) { ++ comments.push(op.flags[j].name); ++ } ++ } ++ } ++ ++ if (args.length > 0) { ++ str = sprintf('%05d %08x %-12s %s', pc, ins, op.name, args.join(', ')); ++ } else { ++ str = sprintf('%05d %08x %-12s', pc, ins, op.name); ++ } ++ if (comments.length > 0) { ++ str = sprintf('%-44s ; %s', str, comments.join(', ')); ++ } ++ ++ res.push({ ++ str: str, ++ ins: ins ++ }); ++ } ++ ++ return res; ++}; ++ ++Debugger.prototype.uiMessage = function (type, val) { ++ var msg; ++ if (typeof type === 'object') { ++ msg = type; ++ } else if (typeof type === 'string') { ++ msg = { type: type, message: val }; ++ } else { ++ throw new TypeError('invalid ui message: ' + type); ++ } ++ this.messageLines.push(msg); ++ while (this.messageLines.length > this.messageScrollBack) { ++ this.messageLines.shift(); ++ } ++ this.emit('ui-message-update'); // just trigger a sync, gets rate limited ++}; ++ ++Debugger.prototype.sendRequest = function (msg) { ++ var _this = this; ++ return new Promise(function (resolve, reject) { ++ var dvals = []; ++ var dval; ++ var data; ++ var i; ++ ++ if (!_this.attached || !_this.handshook || !_this.reqQueue || !_this.targetStream) { ++ throw new Error('invalid state for sendRequest'); ++ } ++ ++ for (i = 0; i < msg.length; i++) { ++ try { ++ dval = formatDebugValue(msg[i]); ++ } catch (e) { ++ console.log('Failed to format dvalue, dropping connection: ' + e); ++ console.log(e.stack || e); ++ _this.targetStream.destroy(); ++ throw new Error('failed to format dvalue'); ++ } ++ dvals.push(dval); ++ } ++ ++ data = Buffer.concat(dvals); ++ ++ _this.targetStream.write(data); ++ _this.outputPassThroughStream.write(data); // stats and dumping ++ ++ if (optLogMessages) { ++ console.log('Request ' + prettyDebugCommand(msg[1]) + ': ' + prettyDebugMessage(msg)); ++ } ++ ++ if (!_this.reqQueue) { ++ throw new Error('no reqQueue'); ++ } ++ ++ _this.reqQueue.push({ ++ reqMsg: msg, ++ reqCmd: msg[1], ++ resolveCb: resolve, ++ rejectCb: reject ++ }); ++ }); ++}; ++ ++Debugger.prototype.sendBasicInfoRequest = function () { ++ var _this = this; ++ return this.sendRequest([ DVAL_REQ, CMD_BASICINFO, DVAL_EOM ]).then(function (msg) { ++ _this.dukVersion = msg[1]; ++ _this.dukGitDescribe = msg[2]; ++ _this.targetInfo = msg[3]; ++ _this.endianness = { 1: 'little', 2: 'mixed', 3: 'big' }[msg[4]] || 'unknown'; ++ _this.emit('basic-info-update'); ++ return msg; ++ }); ++}; ++ ++Debugger.prototype.sendGetVarRequest = function (varname, level) { ++ var _this = this; ++ return this.sendRequest([ DVAL_REQ, CMD_GETVAR, (typeof level === 'number' ? level : -1), varname, DVAL_EOM ]).then(function (msg) { ++ return { found: msg[1] === 1, value: msg[2] }; ++ }); ++}; ++ ++Debugger.prototype.sendPutVarRequest = function (varname, varvalue, level) { ++ var _this = this; ++ return this.sendRequest([ DVAL_REQ, CMD_PUTVAR, (typeof level === 'number' ? level : -1), varname, varvalue, DVAL_EOM ]); ++}; ++ ++Debugger.prototype.sendInvalidCommandTestRequest = function () { ++ // Intentional invalid command ++ var _this = this; ++ return this.sendRequest([ DVAL_REQ, 0xdeadbeef, DVAL_EOM ]); ++}; ++ ++Debugger.prototype.sendStatusRequest = function () { ++ // Send a status request to trigger a status notify, result is ignored: ++ // target sends a status notify instead of a meaningful reply ++ var _this = this; ++ return this.sendRequest([ DVAL_REQ, CMD_TRIGGERSTATUS, DVAL_EOM ]); ++}; ++ ++Debugger.prototype.sendBreakpointListRequest = function () { ++ var _this = this; ++ return this.sendRequest([ DVAL_REQ, CMD_LISTBREAK, DVAL_EOM ]).then(function (msg) { ++ var i, n; ++ var breakpts = []; ++ ++ for (i = 1, n = msg.length - 1; i < n; i += 2) { ++ breakpts.push({ fileName: msg[i], lineNumber: msg[i + 1] }); ++ } ++ ++ _this.breakpoints = breakpts; ++ _this.emit('breakpoints-update'); ++ return msg; ++ }); ++}; ++ ++Debugger.prototype.sendGetLocalsRequest = function (level) { ++ var _this = this; ++ return this.sendRequest([ DVAL_REQ, CMD_GETLOCALS, (typeof level === 'number' ? level : -1), DVAL_EOM ]).then(function (msg) { ++ var i; ++ var locals = []; ++ ++ for (i = 1; i <= msg.length - 2; i += 2) { ++ // XXX: do pretty printing in debug client for now ++ locals.push({ key: msg[i], value: prettyUiDebugValue(msg[i + 1], LOCALS_CLIPLEN) }); ++ } ++ ++ _this.locals = locals; ++ _this.emit('locals-update'); ++ return msg; ++ }); ++}; ++ ++Debugger.prototype.sendGetCallStackRequest = function () { ++ var _this = this; ++ return this.sendRequest([ DVAL_REQ, CMD_GETCALLSTACK, DVAL_EOM ]).then(function (msg) { ++ var i; ++ var stack = []; ++ ++ for (i = 1; i + 3 <= msg.length - 1; i += 4) { ++ stack.push({ ++ fileName: msg[i], ++ funcName: msg[i + 1], ++ lineNumber: msg[i + 2], ++ pc: msg[i + 3] ++ }); ++ } ++ ++ _this.callstack = stack; ++ _this.emit('callstack-update'); ++ return msg; ++ }); ++}; ++ ++Debugger.prototype.sendStepIntoRequest = function () { ++ var _this = this; ++ return this.sendRequest([ DVAL_REQ, CMD_STEPINTO, DVAL_EOM ]); ++}; ++ ++Debugger.prototype.sendStepOverRequest = function () { ++ var _this = this; ++ return this.sendRequest([ DVAL_REQ, CMD_STEPOVER, DVAL_EOM ]); ++}; ++ ++Debugger.prototype.sendStepOutRequest = function () { ++ var _this = this; ++ return this.sendRequest([ DVAL_REQ, CMD_STEPOUT, DVAL_EOM ]); ++}; ++ ++Debugger.prototype.sendPauseRequest = function () { ++ var _this = this; ++ return this.sendRequest([ DVAL_REQ, CMD_PAUSE, DVAL_EOM ]); ++}; ++ ++Debugger.prototype.sendResumeRequest = function () { ++ var _this = this; ++ return this.sendRequest([ DVAL_REQ, CMD_RESUME, DVAL_EOM ]); ++}; ++ ++Debugger.prototype.sendEvalRequest = function (evalInput, level) { ++ var _this = this; ++ // Use explicit level if given. If no level is given, use null if the call ++ // stack is empty, -1 otherwise. This works well when we're paused and the ++ // callstack information is not liable to change before we do an Eval. ++ if (typeof level !== 'number') { ++ level = this.callstack && this.callstack.length > 0 ? -1 : null; ++ } ++ return this.sendRequest([ DVAL_REQ, CMD_EVAL, level, evalInput, DVAL_EOM ]).then(function (msg) { ++ return { error: msg[1] === 1 /*error*/, value: msg[2] }; ++ }); ++}; ++ ++Debugger.prototype.sendDetachRequest = function () { ++ var _this = this; ++ return this.sendRequest([ DVAL_REQ, CMD_DETACH, DVAL_EOM ]); ++}; ++ ++Debugger.prototype.sendDumpHeapRequest = function () { ++ var _this = this; ++ ++ return this.sendRequest([ DVAL_REQ, CMD_DUMPHEAP, DVAL_EOM ]).then(function (msg) { ++ var res = {}; ++ var objs = []; ++ var i, j, n, m, o, prop; ++ ++ res.type = 'heapDump'; ++ res.heapObjects = objs; ++ ++ for (i = 1, n = msg.length - 1; i < n; /*nop*/) { ++ o = {}; ++ o.ptr = msg[i++]; ++ o.type = msg[i++]; ++ o.flags = msg[i++] >>> 0; /* unsigned */ ++ o.refc = msg[i++]; ++ ++ if (o.type === DUK_HTYPE_STRING) { ++ o.blen = msg[i++]; ++ o.clen = msg[i++]; ++ o.hash = msg[i++] >>> 0; /* unsigned */ ++ o.data = msg[i++]; ++ } else if (o.type === DUK_HTYPE_BUFFER) { ++ o.len = msg[i++]; ++ o.data = msg[i++]; ++ } else if (o.type === DUK_HTYPE_OBJECT) { ++ o['class'] = msg[i++]; ++ o.proto = msg[i++]; ++ o.esize = msg[i++]; ++ o.enext = msg[i++]; ++ o.asize = msg[i++]; ++ o.hsize = msg[i++]; ++ o.props = []; ++ for (j = 0, m = o.enext; j < m; j++) { ++ prop = {}; ++ prop.flags = msg[i++]; ++ prop.key = msg[i++]; ++ prop.accessor = (msg[i++] == 1); ++ if (prop.accessor) { ++ prop.getter = msg[i++]; ++ prop.setter = msg[i++]; ++ } else { ++ prop.value = msg[i++]; ++ } ++ o.props.push(prop); ++ } ++ o.array = []; ++ for (j = 0, m = o.asize; j < m; j++) { ++ prop = {}; ++ prop.value = msg[i++]; ++ o.array.push(prop); ++ } ++ } else { ++ console.log('invalid htype: ' + o.type + ', disconnect'); ++ _this.disconnectDebugger(); ++ throw new Error('invalid htype'); ++ return; ++ } ++ ++ objs.push(o); ++ } ++ ++ return res; ++ }); ++}; ++ ++Debugger.prototype.sendGetBytecodeRequest = function () { ++ var _this = this; ++ ++ return this.sendRequest([ DVAL_REQ, CMD_GETBYTECODE, -1 /* level; could be other than -1 too */, DVAL_EOM ]).then(function (msg) { ++ var idx = 1; ++ var nconst; ++ var nfunc; ++ var val; ++ var buf; ++ var i, n; ++ var consts = []; ++ var funcs = []; ++ var bcode; ++ var preformatted; ++ var ret; ++ var idxPreformattedInstructions; ++ ++ //console.log(JSON.stringify(msg)); ++ ++ nconst = msg[idx++]; ++ for (i = 0; i < nconst; i++) { ++ val = msg[idx++]; ++ consts.push(val); ++ } ++ ++ nfunc = msg[idx++]; ++ for (i = 0; i < nfunc; i++) { ++ val = msg[idx++]; ++ funcs.push(val); ++ } ++ val = msg[idx++]; ++ ++ // Right now bytecode is a string containing a direct dump of the ++ // bytecode in target endianness. Decode here so that the web UI ++ // doesn't need to. ++ ++ buf = new Buffer(val.length); ++ writeDebugStringToBuffer(val, buf, 0); ++ bcode = _this.decodeBytecodeFromBuffer(buf, consts, funcs); ++ ++ preformatted = []; ++ consts.forEach(function (v, i) { ++ preformatted.push('; c' + i + ' ' + JSON.stringify(v)); ++ }); ++ preformatted.push(''); ++ idxPreformattedInstructions = preformatted.length; ++ bcode.forEach(function (v) { ++ preformatted.push(v.str); ++ }); ++ preformatted = preformatted.join('\n') + '\n'; ++ ++ ret = { ++ constants: consts, ++ functions: funcs, ++ bytecode: bcode, ++ preformatted: preformatted, ++ idxPreformattedInstructions: idxPreformattedInstructions ++ }; ++ ++ return ret; ++ }); ++}; ++ ++Debugger.prototype.changeBreakpoint = function (fileName, lineNumber, mode) { ++ var _this = this; ++ ++ return this.sendRequest([ DVAL_REQ, CMD_LISTBREAK, DVAL_EOM ]).then(function (msg) { ++ var i, n; ++ var breakpts = []; ++ var deleted = false; ++ ++ // Up-to-date list of breakpoints on target ++ for (i = 1, n = msg.length - 1; i < n; i += 2) { ++ breakpts.push({ fileName: msg[i], lineNumber: msg[i + 1] }); ++ } ++ ++ // Delete matching breakpoints in reverse order so that indices ++ // remain valid. We do this for all operations so that duplicates ++ // are eliminated if present. ++ for (i = breakpts.length - 1; i >= 0; i--) { ++ var bp = breakpts[i]; ++ if (mode === 'deleteall' || (bp.fileName === fileName && bp.lineNumber === lineNumber)) { ++ deleted = true; ++ _this.sendRequest([ DVAL_REQ, CMD_DELBREAK, i, DVAL_EOM ], function (msg) { ++ // nop ++ }, function (err) { ++ // nop ++ }); ++ } ++ } ++ ++ // Technically we should wait for each delbreak reply but because ++ // target processes the requests in order, it doesn't matter. ++ if ((mode === 'add') || (mode === 'toggle' && !deleted)) { ++ _this.sendRequest([ DVAL_REQ, CMD_ADDBREAK, fileName, lineNumber, DVAL_EOM ], function (msg) { ++ // nop ++ }, function (err) { ++ _this.uiMessage('debugger-info', 'Failed to add breakpoint: ' + err); ++ }); ++ } ++ ++ // Read final, effective breakpoints from the target ++ _this.sendBreakpointListRequest(); ++ }); ++}; ++ ++Debugger.prototype.disconnectDebugger = function () { ++ if (this.targetStream) { ++ // We require a destroy() method from the actual target stream ++ this.targetStream.destroy(); ++ this.targetStream = null; ++ } ++ if (this.inputParser) { ++ this.inputParser.close(); ++ this.inputParser = null; ++ } ++ if (this.outputPassThroughStream) { ++ // There is no close() or destroy() for a passthrough stream, so just ++ // close the outputParser which will cancel timers etc. ++ } ++ if (this.outputParser) { ++ this.outputParser.close(); ++ this.outputParser = null; ++ } ++ ++ this.attached = false; ++ this.handshook = false; ++ this.reqQueue = null; ++ this.execStatus = { ++ attached: false, ++ state: 'detached', ++ fileName: '', ++ funcName: '', ++ line: 0, ++ pc: 0 ++ }; ++}; ++ ++Debugger.prototype.connectDebugger = function () { ++ var _this = this; ++ ++ this.disconnectDebugger(); // close previous target connection ++ ++ // CUSTOMTRANSPORT: to use a custom transport, change this.targetStream to ++ // use your custom transport. ++ ++ console.log('Connecting to ' + optTargetHost + ':' + optTargetPort + '...'); ++ this.targetStream = new net.Socket(); ++ this.targetStream.connect(optTargetPort, optTargetHost, function () { ++ console.log('Debug transport connected'); ++ _this.attached = true; ++ _this.reqQueue = []; ++ _this.uiMessage('debugger-info', 'Debug transport connected'); ++ }); ++ ++ this.inputParser = new DebugProtocolParser( ++ this.targetStream, ++ null, ++ optDumpDebugRead, ++ optDumpDebugPretty, ++ optDumpDebugPretty ? 'Recv: ' : null, ++ null, ++ null // console logging is done at a higher level to match request/response ++ ); ++ ++ // Use a PassThrough stream to debug dump and get stats for output messages. ++ // Simply write outgoing data to both the targetStream and this passthrough ++ // separately. ++ this.outputPassThroughStream = stream.PassThrough(); ++ this.outputParser = new DebugProtocolParser( ++ this.outputPassThroughStream, ++ 1, ++ optDumpDebugWrite, ++ optDumpDebugPretty, ++ optDumpDebugPretty ? 'Send: ' : null, ++ null, ++ null // console logging is done at a higher level to match request/response ++ ); ++ ++ this.inputParser.on('transport-close', function () { ++ _this.uiMessage('debugger-info', 'Debug transport closed'); ++ _this.disconnectDebugger(); ++ _this.emit('exec-status-update'); ++ _this.emit('detached'); ++ }); ++ ++ this.inputParser.on('transport-error', function (err) { ++ _this.uiMessage('debugger-info', 'Debug transport error: ' + err); ++ _this.disconnectDebugger(); ++ }); ++ ++ this.inputParser.on('protocol-version', function (msg) { ++ var ver = msg.protocolVersion; ++ console.log('Debug version identification:', msg.versionIdentification); ++ _this.protocolVersion = ver; ++ _this.uiMessage('debugger-info', 'Debug version identification: ' + msg.versionIdentification); ++ if (ver !== SUPPORTED_DEBUG_PROTOCOL_VERSION) { ++ console.log('Unsupported debug protocol version (got ' + ver + ', support ' + SUPPORTED_DEBUG_PROTOCOL_VERSION + ')'); ++ _this.uiMessage('debugger-info', 'Protocol version ' + ver + ' unsupported, dropping connection'); ++ _this.targetStream.destroy(); ++ } else { ++ _this.uiMessage('debugger-info', 'Debug protocol version: ' + ver); ++ _this.handshook = true; ++ _this.execStatus = { ++ attached: true, ++ state: 'attached', ++ fileName: '', ++ funcName: '', ++ line: 0, ++ pc: 0 ++ }; ++ _this.emit('exec-status-update'); ++ _this.emit('attached'); // inform web UI ++ ++ // Fetch basic info right away ++ _this.sendBasicInfoRequest(); ++ } ++ }); ++ ++ this.inputParser.on('debug-message', function (msg) { ++ _this.processDebugMessage(msg); ++ }); ++ ++ this.inputParser.on('stats-update', function () { ++ _this.stats.rxBytes = this.bytes; ++ _this.stats.rxDvalues = this.dvalues; ++ _this.stats.rxMessages = this.messages; ++ _this.stats.rxBytesPerSec = this.bytesPerSec; ++ _this.emit('debug-stats-update'); ++ }); ++ ++ this.outputParser.on('stats-update', function () { ++ _this.stats.txBytes = this.bytes; ++ _this.stats.txDvalues = this.dvalues; ++ _this.stats.txMessages = this.messages; ++ _this.stats.txBytesPerSec = this.bytesPerSec; ++ _this.emit('debug-stats-update'); ++ }); ++}; ++ ++Debugger.prototype.processDebugMessage = function (msg) { ++ var req; ++ var prevState, newState; ++ var err; ++ ++ if (msg[0] === DVAL_REQ) { ++ // No actual requests sent by the target right now (just notifys). ++ console.log('Unsolicited reply message, dropping connection: ' + prettyDebugMessage(msg)); ++ } else if (msg[0] === DVAL_REP) { ++ if (this.reqQueue.length <= 0) { ++ console.log('Unsolicited reply message, dropping connection: ' + prettyDebugMessage(msg)); ++ this.targetStream.destroy(); ++ } ++ req = this.reqQueue.shift(); ++ ++ if (optLogMessages) { ++ console.log('Reply for ' + prettyDebugCommand(req.reqCmd) + ': ' + prettyDebugMessage(msg)); ++ } ++ ++ if (req.resolveCb) { ++ req.resolveCb(msg); ++ } else { ++ // nop: no callback ++ } ++ } else if (msg[0] === DVAL_ERR) { ++ if (this.reqQueue.length <= 0) { ++ console.log('Unsolicited error message, dropping connection: ' + prettyDebugMessage(msg)); ++ this.targetStream.destroy(); ++ } ++ err = new Error(String(msg[2]) + ' (code ' + String(msg[1]) + ')'); ++ err.errorCode = msg[1] || 0; ++ req = this.reqQueue.shift(); ++ ++ if (optLogMessages) { ++ console.log('Error for ' + prettyDebugCommand(req.reqCmd) + ': ' + prettyDebugMessage(msg)); ++ } ++ ++ if (req.rejectCb) { ++ req.rejectCb(err); ++ } else { ++ // nop: no callback ++ } ++ } else if (msg[0] === DVAL_NFY) { ++ if (optLogMessages) { ++ console.log('Notify ' + prettyDebugCommand(msg[1]) + ': ' + prettyDebugMessage(msg)); ++ } ++ ++ if (msg[1] === CMD_STATUS) { ++ prevState = this.execStatus.state; ++ newState = msg[2] === 0 ? 'running' : 'paused'; ++ this.execStatus = { ++ attached: true, ++ state: newState, ++ fileName: msg[3], ++ funcName: msg[4], ++ line: msg[5], ++ pc: msg[6] ++ }; ++ ++ if (prevState !== newState && newState === 'paused') { ++ // update run state now that we're paused ++ this.sendBreakpointListRequest(); ++ this.sendGetLocalsRequest(); ++ this.sendGetCallStackRequest(); ++ } ++ ++ this.emit('exec-status-update'); ++ } else if (msg[1] === CMD_THROW) { ++ this.uiMessage({ type: 'throw', fatal: msg[2], message: (msg[2] ? 'UNCAUGHT: ' : 'THROW: ') + prettyUiStringUnquoted(msg[3], UI_MESSAGE_CLIPLEN), fileName: msg[4], lineNumber: msg[5] }); ++ } else if (msg[1] === CMD_DETACHING) { ++ this.uiMessage({ type: 'detaching', reason: msg[2], message: 'DETACH: ' + (msg.length >= 5 ? prettyUiStringUnquoted(msg[3]) : 'detaching') }); ++ } else { ++ // Ignore unknown notify messages ++ console.log('Unknown notify, ignoring: ' + prettyDebugMessage(msg)); ++ ++ //this.targetStream.destroy(); ++ } ++ } else { ++ console.log('Invalid initial dvalue, dropping connection: ' + prettyDebugMessage(msg)); ++ this.targetStream.destroy(); ++ } ++}; ++ ++Debugger.prototype.run = function () { ++ var _this = this; ++ ++ // Initial debugger connection ++ ++ this.connectDebugger(); ++ ++ // Poll various state items when running ++ ++ var sendRound = 0; ++ var statusPending = false; ++ var bplistPending = false; ++ var localsPending = false; ++ var callStackPending = false; ++ ++ setInterval(function () { ++ if (_this.execStatus.state !== 'running') { ++ return; ++ } ++ ++ // Could also check for an empty request queue, but that's probably ++ // too strict? ++ ++ // Pending flags are used to avoid requesting the same thing twice ++ // while a previous request is pending. The flag-based approach is ++ // quite awkward. Rework to use promises. ++ ++ switch (sendRound) { ++ case 0: ++ if (!statusPending) { ++ statusPending = true; ++ _this.sendStatusRequest().finally(function () { statusPending = false; }); ++ } ++ break; ++ case 1: ++ if (!bplistPending) { ++ bplistPending = true; ++ _this.sendBreakpointListRequest().finally(function () { bplistPending = false; }); ++ } ++ break; ++ case 2: ++ if (!localsPending) { ++ localsPending = true; ++ _this.sendGetLocalsRequest().finally(function () { localsPending = false; }); ++ } ++ break; ++ case 3: ++ if (!callStackPending) { ++ callStackPending = true; ++ _this.sendGetCallStackRequest().finally(function () { callStackPending = false; }); ++ } ++ break; ++ } ++ sendRound = (sendRound + 1) % 4; ++ }, 500); ++}; ++ ++/* ++ * Express setup and socket.io ++ */ ++ ++function DebugWebServer() { ++ this.dbg = null; // debugger singleton ++ this.socket = null; // current socket (or null) ++ this.keepaliveTimer = null; ++ this.uiMessageLimiter = null; ++ this.cachedJson = {}; // cache to avoid resending identical data ++ this.sourceFileManager = new SourceFileManager(optSourceSearchDirs); ++ this.sourceFileManager.scan(); ++} ++ ++DebugWebServer.prototype.handleSourcePost = function (req, res) { ++ var fileName = req.body && req.body.fileName; ++ var fileData; ++ ++ console.log('Source request: ' + fileName); ++ ++ if (typeof fileName !== 'string') { ++ res.status(500).send('invalid request'); ++ return; ++ } ++ fileData = this.sourceFileManager.search(fileName, optSourceSearchDirs); ++ if (typeof fileData !== 'string') { ++ res.status(404).send('not found'); ++ return; ++ } ++ res.status(200).send(fileData); // UTF-8 ++}; ++ ++DebugWebServer.prototype.handleSourceListPost = function (req, res) { ++ console.log('Source list request'); ++ ++ var files = this.sourceFileManager.getFiles(); ++ res.header('Content-Type', 'application/json'); ++ res.status(200).json(files); ++}; ++ ++DebugWebServer.prototype.handleHeapDumpGet = function (req, res) { ++ console.log('Heap dump get'); ++ ++ this.dbg.sendDumpHeapRequest().then(function (val) { ++ res.header('Content-Type', 'application/json'); ++ //res.status(200).json(val); ++ res.status(200).send(JSON.stringify(val, null, 4)); ++ }).catch(function (err) { ++ res.status(500).send('Failed to get heap dump: ' + (err.stack || err)); ++ }); ++}; ++ ++DebugWebServer.prototype.run = function () { ++ var _this = this; ++ ++ var express = require('express'); ++ var bodyParser = require('body-parser'); ++ var app = express(); ++ var http = require('http').Server(app); ++ var io = require('socket.io')(http); ++ ++ app.use(bodyParser.json()); ++ app.post('/source', this.handleSourcePost.bind(this)); ++ app.post('/sourceList', this.handleSourceListPost.bind(this)); ++ app.get('/heapDump.json', this.handleHeapDumpGet.bind(this)); ++ app.use('/', express.static(__dirname + '/static')); ++ ++ http.listen(optHttpPort, function () { ++ console.log('Listening on *:' + optHttpPort); ++ }); ++ ++ io.on('connection', this.handleNewSocketIoConnection.bind(this)); ++ ++ this.dbg.on('attached', function () { ++ console.log('Debugger attached'); ++ }); ++ ++ this.dbg.on('detached', function () { ++ console.log('Debugger detached'); ++ }); ++ ++ this.dbg.on('debug-stats-update', function () { ++ _this.debugStatsLimiter.trigger(); ++ }); ++ ++ this.dbg.on('ui-message-update', function () { ++ // Explicit rate limiter because this is a source of a lot of traffic. ++ _this.uiMessageLimiter.trigger(); ++ }); ++ ++ this.dbg.on('basic-info-update', function () { ++ _this.emitBasicInfo(); ++ }); ++ ++ this.dbg.on('breakpoints-update', function () { ++ _this.emitBreakpoints(); ++ }); ++ ++ this.dbg.on('exec-status-update', function () { ++ // Explicit rate limiter because this is a source of a lot of traffic. ++ _this.execStatusLimiter.trigger(); ++ }); ++ ++ this.dbg.on('locals-update', function () { ++ _this.emitLocals(); ++ }); ++ ++ this.dbg.on('callstack-update', function () { ++ _this.emitCallStack(); ++ }); ++ ++ this.uiMessageLimiter = new RateLimited(10, 1000, this.uiMessageLimiterCallback.bind(this)); ++ this.execStatusLimiter = new RateLimited(50, 500, this.execStatusLimiterCallback.bind(this)); ++ this.debugStatsLimiter = new RateLimited(1, 2000, this.debugStatsLimiterCallback.bind(this)); ++ ++ this.keepaliveTimer = setInterval(this.emitKeepalive.bind(this), 30000); ++}; ++ ++DebugWebServer.prototype.handleNewSocketIoConnection = function (socket) { ++ var _this = this; ++ ++ console.log('Socket.io connected'); ++ if (this.socket) { ++ console.log('Closing previous socket.io socket'); ++ this.socket.emit('replaced'); ++ } ++ this.socket = socket; ++ ++ this.emitKeepalive(); ++ ++ socket.on('disconnect', function () { ++ console.log('Socket.io disconnected'); ++ if (_this.socket === socket) { ++ _this.socket = null; ++ } ++ }); ++ ++ socket.on('keepalive', function (msg) { ++ // nop ++ }); ++ ++ socket.on('attach', function (msg) { ++ if (_this.dbg.targetStream) { ++ console.log('Attach request when debugger already has a connection, ignoring'); ++ } else { ++ _this.dbg.connectDebugger(); ++ } ++ }); ++ ++ socket.on('detach', function (msg) { ++ // Try to detach cleanly, timeout if no response ++ Promise.any([ ++ _this.dbg.sendDetachRequest(), ++ Promise.delay(3000) ++ ]).finally(function () { ++ _this.dbg.disconnectDebugger(); ++ }); ++ }); ++ ++ socket.on('stepinto', function (msg) { ++ _this.dbg.sendStepIntoRequest(); ++ }); ++ ++ socket.on('stepover', function (msg) { ++ _this.dbg.sendStepOverRequest(); ++ }); ++ ++ socket.on('stepout', function (msg) { ++ _this.dbg.sendStepOutRequest(); ++ }); ++ ++ socket.on('pause', function (msg) { ++ _this.dbg.sendPauseRequest(); ++ }); ++ ++ socket.on('resume', function (msg) { ++ _this.dbg.sendResumeRequest(); ++ }); ++ ++ socket.on('eval', function (msg) { ++ // msg.input is a proper Unicode strings here, and needs to be ++ // converted into a protocol string (U+0000...U+00FF). ++ var input = stringToDebugString(msg.input); ++ _this.dbg.sendEvalRequest(input, msg.level).then(function (v) { ++ socket.emit('eval-result', { error: v.error, result: prettyUiDebugValue(v.value, EVAL_CLIPLEN) }); ++ }); ++ ++ // An eval call quite possibly changes the local variables so always ++ // re-read locals afterwards. We don't need to wait for Eval to ++ // complete here; the requests will pipeline automatically and be ++ // executed in order. ++ ++ // XXX: move this to the web UI so that the UI can control what ++ // locals are listed (or perhaps show locals for all levels with ++ // an expandable tree view). ++ _this.dbg.sendGetLocalsRequest(); ++ }); ++ ++ socket.on('getvar', function (msg) { ++ // msg.varname is a proper Unicode strings here, and needs to be ++ // converted into a protocol string (U+0000...U+00FF). ++ var varname = stringToDebugString(msg.varname); ++ _this.dbg.sendGetVarRequest(varname, msg.level) ++ .then(function (v) { ++ socket.emit('getvar-result', { found: v.found, result: prettyUiDebugValue(v.value, GETVAR_CLIPLEN) }); ++ }); ++ }); ++ ++ socket.on('putvar', function (msg) { ++ // msg.varname and msg.varvalue are proper Unicode strings here, they ++ // need to be converted into protocol strings (U+0000...U+00FF). ++ var varname = stringToDebugString(msg.varname); ++ var varvalue = msg.varvalue; ++ ++ // varvalue is JSON parsed by the web UI for now, need special string ++ // encoding here. ++ if (typeof varvalue === 'string') { ++ varvalue = stringToDebugString(msg.varvalue); ++ } ++ ++ _this.dbg.sendPutVarRequest(varname, varvalue, msg.level) ++ .then(function (v) { ++ console.log('putvar done'); // XXX: signal success to UI? ++ }); ++ ++ // A PutVar call quite possibly changes the local variables so always ++ // re-read locals afterwards. We don't need to wait for PutVar to ++ // complete here; the requests will pipeline automatically and be ++ // executed in order. ++ ++ // XXX: make the client do this? ++ _this.dbg.sendGetLocalsRequest(); ++ }); ++ ++ socket.on('add-breakpoint', function (msg) { ++ _this.dbg.changeBreakpoint(msg.fileName, msg.lineNumber, 'add'); ++ }); ++ ++ socket.on('delete-breakpoint', function (msg) { ++ _this.dbg.changeBreakpoint(msg.fileName, msg.lineNumber, 'delete'); ++ }); ++ ++ socket.on('toggle-breakpoint', function (msg) { ++ _this.dbg.changeBreakpoint(msg.fileName, msg.lineNumber, 'toggle'); ++ }); ++ ++ socket.on('delete-all-breakpoints', function (msg) { ++ _this.dbg.changeBreakpoint(null, null, 'deleteall'); ++ }); ++ ++ socket.on('get-bytecode', function (msg) { ++ _this.dbg.sendGetBytecodeRequest().then(function (res) { ++ socket.emit('bytecode', res); ++ }); ++ }); ++ ++ // Resend all debugger state for new client ++ this.cachedJson = {}; // clear client state cache ++ this.emitBasicInfo(); ++ this.emitStats(); ++ this.emitExecStatus(); ++ this.emitUiMessages(); ++ this.emitBreakpoints(); ++ this.emitCallStack(); ++ this.emitLocals(); ++}; ++ ++// Check if 'msg' would encode to the same JSON which was previously sent ++// to the web client. The caller then avoid resending unnecessary stuff. ++DebugWebServer.prototype.cachedJsonCheck = function (cacheKey, msg) { ++ var newJson = JSON.stringify(msg); ++ if (this.cachedJson[cacheKey] === newJson) { ++ return true; // cached ++ } ++ this.cachedJson[cacheKey] = newJson; ++ return false; // not cached, send (cache already updated) ++}; ++ ++DebugWebServer.prototype.uiMessageLimiterCallback = function () { ++ this.emitUiMessages(); ++}; ++ ++DebugWebServer.prototype.execStatusLimiterCallback = function () { ++ this.emitExecStatus(); ++}; ++ ++DebugWebServer.prototype.debugStatsLimiterCallback = function () { ++ this.emitStats(); ++}; ++ ++DebugWebServer.prototype.emitKeepalive = function () { ++ if (!this.socket) { return; } ++ ++ this.socket.emit('keepalive', { nodeVersion: process.version }); ++}; ++ ++DebugWebServer.prototype.emitBasicInfo = function () { ++ if (!this.socket) { return; } ++ ++ var newMsg = { ++ duk_version: this.dbg.dukVersion, ++ duk_git_describe: this.dbg.dukGitDescribe, ++ target_info: this.dbg.targetInfo, ++ endianness: this.dbg.endianness ++ }; ++ if (this.cachedJsonCheck('basic-info', newMsg)) { ++ return; ++ } ++ this.socket.emit('basic-info', newMsg); ++}; ++ ++DebugWebServer.prototype.emitStats = function () { ++ if (!this.socket) { return; } ++ ++ this.socket.emit('debug-stats', this.dbg.stats); ++}; ++ ++DebugWebServer.prototype.emitExecStatus = function () { ++ if (!this.socket) { return; } ++ ++ var newMsg = this.dbg.execStatus; ++ if (this.cachedJsonCheck('exec-status', newMsg)) { ++ return; ++ } ++ this.socket.emit('exec-status', newMsg); ++}; ++ ++DebugWebServer.prototype.emitUiMessages = function () { ++ if (!this.socket) { return; } ++ ++ var newMsg = this.dbg.messageLines; ++ if (this.cachedJsonCheck('output-lines', newMsg)) { ++ return; ++ } ++ this.socket.emit('output-lines', newMsg); ++}; ++ ++DebugWebServer.prototype.emitBreakpoints = function () { ++ if (!this.socket) { return; } ++ ++ var newMsg = { breakpoints: this.dbg.breakpoints }; ++ if (this.cachedJsonCheck('breakpoints', newMsg)) { ++ return; ++ } ++ this.socket.emit('breakpoints', newMsg); ++}; ++ ++DebugWebServer.prototype.emitCallStack = function () { ++ if (!this.socket) { return; } ++ ++ var newMsg = { callstack: this.dbg.callstack }; ++ if (this.cachedJsonCheck('callstack', newMsg)) { ++ return; ++ } ++ this.socket.emit('callstack', newMsg); ++}; ++ ++DebugWebServer.prototype.emitLocals = function () { ++ if (!this.socket) { return; } ++ ++ var newMsg = { locals: this.dbg.locals }; ++ if (this.cachedJsonCheck('locals', newMsg)) { ++ return; ++ } ++ this.socket.emit('locals', newMsg); ++}; ++ ++/* ++ * JSON debug proxy ++ */ ++ ++function DebugProxy(serverPort) { ++ this.serverPort = serverPort; ++ this.server = null; ++ this.socket = null; ++ this.targetStream = null; ++ this.inputParser = null; ++ ++ // preformatted dvalues ++ this.dval_eom = formatDebugValue(DVAL_EOM); ++ this.dval_req = formatDebugValue(DVAL_REQ); ++ this.dval_rep = formatDebugValue(DVAL_REP); ++ this.dval_nfy = formatDebugValue(DVAL_NFY); ++ this.dval_err = formatDebugValue(DVAL_ERR); ++} ++ ++DebugProxy.prototype.determineCommandNumber = function (cmdName, cmdNumber) { ++ var ret; ++ if (typeof cmdName === 'string') { ++ ret = debugCommandNumbers[cmdName]; ++ } else if (typeof cmdName === 'number') { ++ ret = cmdName; ++ } ++ ret = ret || cmdNumber; ++ if (typeof ret !== 'number') { ++ throw Error('cannot figure out command number for "' + cmdName + '" (' + cmdNumber + ')'); ++ } ++ return ret; ++}; ++ ++DebugProxy.prototype.commandNumberToString = function (id) { ++ return debugCommandNames[id] || String(id); ++}; ++ ++DebugProxy.prototype.formatDvalues = function (args) { ++ if (!args) { ++ return []; ++ } ++ return args.map(function (v) { ++ return formatDebugValue(v); ++ }); ++}; ++ ++DebugProxy.prototype.writeJson = function (val) { ++ this.socket.write(JSON.stringify(val) + '\n'); ++}; ++ ++DebugProxy.prototype.writeJsonSafe = function (val) { ++ try { ++ this.writeJson(val); ++ } catch (e) { ++ console.log('Failed to write JSON in writeJsonSafe, ignoring: ' + e); ++ } ++}; ++ ++DebugProxy.prototype.disconnectJsonClient = function () { ++ if (this.socket) { ++ this.socket.destroy(); ++ this.socket = null; ++ } ++}; ++ ++DebugProxy.prototype.disconnectTarget = function () { ++ if (this.inputParser) { ++ this.inputParser.close(); ++ this.inputParser = null; ++ } ++ if (this.targetStream) { ++ this.targetStream.destroy(); ++ this.targetStream = null; ++ } ++}; ++ ++DebugProxy.prototype.run = function () { ++ var _this = this; ++ ++ console.log('Waiting for client connections on port ' + this.serverPort); ++ this.server = net.createServer(function (socket) { ++ console.log('JSON proxy client connected'); ++ ++ _this.disconnectJsonClient(); ++ _this.disconnectTarget(); ++ ++ // A byline-parser is simple and good enough for now (assume ++ // compact JSON with no newlines). ++ var socketByline = byline(socket); ++ _this.socket = socket; ++ ++ socketByline.on('data', function (line) { ++ try { ++ // console.log('Received json proxy input line: ' + line.toString('utf8')); ++ var msg = JSON.parse(line.toString('utf8')); ++ var first_dval; ++ var args_dvalues = _this.formatDvalues(msg.args); ++ var last_dval = _this.dval_eom; ++ var cmd; ++ ++ if (msg.request) { ++ // "request" can be a string or "true" ++ first_dval = _this.dval_req; ++ cmd = _this.determineCommandNumber(msg.request, msg.command); ++ } else if (msg.reply) { ++ first_dval = _this.dval_rep; ++ } else if (msg.notify) { ++ // "notify" can be a string or "true" ++ first_dval = _this.dval_nfy; ++ cmd = _this.determineCommandNumber(msg.notify, msg.command); ++ } else if (msg.error) { ++ first_dval = _this.dval_err; ++ } else { ++ throw new Error('Invalid input JSON message: ' + JSON.stringify(msg)); ++ } ++ ++ _this.targetStream.write(first_dval); ++ if (cmd) { ++ _this.targetStream.write(formatDebugValue(cmd)); ++ } ++ args_dvalues.forEach(function (v) { ++ _this.targetStream.write(v); ++ }); ++ _this.targetStream.write(last_dval); ++ } catch (e) { ++ console.log(e); ++ ++ _this.writeJsonSafe({ ++ notify: '_Error', ++ args: [ 'Failed to handle input json message: ' + e ] ++ }); ++ ++ _this.disconnectJsonClient(); ++ _this.disconnectTarget(); ++ } ++ }); ++ ++ _this.connectToTarget(); ++ }).listen(this.serverPort); ++}; ++ ++DebugProxy.prototype.connectToTarget = function () { ++ var _this = this; ++ ++ console.log('Connecting to ' + optTargetHost + ':' + optTargetPort + '...'); ++ this.targetStream = new net.Socket(); ++ this.targetStream.connect(optTargetPort, optTargetHost, function () { ++ console.log('Debug transport connected'); ++ }); ++ ++ this.inputParser = new DebugProtocolParser( ++ this.targetStream, ++ null, ++ optDumpDebugRead, ++ optDumpDebugPretty, ++ optDumpDebugPretty ? 'Recv: ' : null, ++ null, ++ null // console logging is done at a higher level to match request/response ++ ); ++ ++ // Don't add a 'value' key to numbers. ++ this.inputParser.readableNumberValue = false; ++ ++ this.inputParser.on('transport-close', function () { ++ console.log('Debug transport closed'); ++ ++ _this.writeJsonSafe({ ++ notify: '_Disconnecting' ++ }); ++ ++ _this.disconnectJsonClient(); ++ _this.disconnectTarget(); ++ }); ++ ++ this.inputParser.on('transport-error', function (err) { ++ console.log('Debug transport error', err); ++ ++ _this.writeJsonSafe({ ++ notify: '_Error', ++ args: [ String(err) ] ++ }); ++ }); ++ ++ this.inputParser.on('protocol-version', function (msg) { ++ var ver = msg.protocolVersion; ++ console.log('Debug version identification:', msg.versionIdentification); ++ ++ _this.writeJson({ ++ notify: '_TargetConnected', ++ args: [ msg.versionIdentification ] // raw identification string ++ }); ++ ++ if (ver !== 1) { ++ console.log('Protocol version ' + ver + ' unsupported, dropping connection'); ++ } ++ }); ++ ++ this.inputParser.on('debug-message', function (msg) { ++ var t; ++ ++ //console.log(msg); ++ ++ if (typeof msg[0] !== 'object' || msg[0] === null) { ++ throw new Error('unexpected initial dvalue: ' + msg[0]); ++ } else if (msg[0].type === 'eom') { ++ throw new Error('unexpected initial dvalue: ' + msg[0]); ++ } else if (msg[0].type === 'req') { ++ if (typeof msg[1] !== 'number') { ++ throw new Error('unexpected request command number: ' + msg[1]); ++ } ++ t = { ++ request: _this.commandNumberToString(msg[1]), ++ command: msg[1], ++ args: msg.slice(2, msg.length - 1) ++ }; ++ _this.writeJson(t); ++ } else if (msg[0].type === 'rep') { ++ t = { ++ reply: true, ++ args: msg.slice(1, msg.length - 1) ++ }; ++ _this.writeJson(t); ++ } else if (msg[0].type === 'err') { ++ t = { ++ error: true, ++ args: msg.slice(1, msg.length - 1) ++ }; ++ _this.writeJson(t); ++ } else if (msg[0].type === 'nfy') { ++ if (typeof msg[1] !== 'number') { ++ throw new Error('unexpected notify command number: ' + msg[1]); ++ } ++ t = { ++ notify: _this.commandNumberToString(msg[1]), ++ command: msg[1], ++ args: msg.slice(2, msg.length - 1) ++ }; ++ _this.writeJson(t); ++ } else { ++ throw new Error('unexpected initial dvalue: ' + msg[0]); ++ } ++ }); ++ ++ this.inputParser.on('stats-update', function () { ++ }); ++}; ++ ++/* ++ * Command line parsing and initialization ++ */ ++ ++function main() { ++ console.log('((o) Duktape debugger'); ++ ++ // Parse arguments. ++ ++ var argv = require('minimist')(process.argv.slice(2)); ++ //console.dir(argv); ++ if (argv['target-host']) { ++ optTargetHost = argv['target-host']; ++ } ++ if (argv['target-port']) { ++ optTargetPort = argv['target-port']; ++ } ++ if (argv['http-port']) { ++ optHttpPort = argv['http-port']; ++ } ++ if (argv['json-proxy-port']) { ++ optJsonProxyPort = argv['json-proxy-port']; ++ } ++ if (argv['json-proxy']) { ++ optJsonProxy = argv['json-proxy']; ++ } ++ if (argv['source-dirs']) { ++ optSourceSearchDirs = argv['source-dirs'].split(path.delimiter); ++ } ++ if (argv['dump-debug-read']) { ++ optDumpDebugRead = argv['dump-debug-read']; ++ } ++ if (argv['dump-debug-write']) { ++ optDumpDebugWrite = argv['dump-debug-write']; ++ } ++ if (argv['dump-debug-pretty']) { ++ optDumpDebugPretty = argv['dump-debug-pretty']; ++ } ++ if (argv['log-messages']) { ++ optLogMessages = true; ++ } ++ ++ // Dump effective options. Also provides a list of option names. ++ ++ console.log(''); ++ console.log('Effective options:'); ++ console.log(' --target-host: ' + optTargetHost); ++ console.log(' --target-port: ' + optTargetPort); ++ console.log(' --http-port: ' + optHttpPort); ++ console.log(' --json-proxy-port: ' + optJsonProxyPort); ++ console.log(' --json-proxy: ' + optJsonProxy); ++ console.log(' --source-dirs: ' + optSourceSearchDirs.join(' ')); ++ console.log(' --dump-debug-read: ' + optDumpDebugRead); ++ console.log(' --dump-debug-write: ' + optDumpDebugWrite); ++ console.log(' --dump-debug-pretty: ' + optDumpDebugPretty); ++ console.log(' --log-messages: ' + optLogMessages); ++ console.log(''); ++ ++ // Create debugger and web UI singletons, tie them together and ++ // start them. ++ ++ if (optJsonProxy) { ++ console.log('Starting in JSON proxy mode, JSON port: ' + optJsonProxyPort); ++ ++ var prx = new DebugProxy(optJsonProxyPort); ++ prx.run(); ++ } else { ++ var dbg = new Debugger(); ++ var web = new DebugWebServer(); ++ dbg.web = web; ++ web.dbg = dbg; ++ dbg.run(); ++ web.run(); ++ } ++} ++ ++main(); +diff --git a/dist/source/debugger/duk_debug_meta.json b/dist/source/debugger/duk_debug_meta.json +new file mode 100644 +index 0000000..ed6bf40 +--- /dev/null ++++ b/dist/source/debugger/duk_debug_meta.json +@@ -0,0 +1,2189 @@ ++{ ++ "error_codes": [ ++ "Unknown", ++ "UnsupportedCommand", ++ "TooMany", ++ "NotFound", ++ "ApplicationError" ++ ], ++ "opcodes": [ ++ { ++ "args": [ ++ "A_R", ++ "BC_R" ++ ], ++ "name": "LDREG" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "BC_R" ++ ], ++ "name": "STREG" ++ }, ++ { ++ "args": [ ++ "ABC_JUMP" ++ ], ++ "name": "JUMP" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "BC_C" ++ ], ++ "name": "LDCONST" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "BC_LDINT" ++ ], ++ "name": "LDINT" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "BC_LDINTX" ++ ], ++ "name": "LDINTX" ++ }, ++ { ++ "args": [ ++ "BC_R" ++ ], ++ "name": "LDTHIS" ++ }, ++ { ++ "args": [ ++ "BC_R" ++ ], ++ "name": "LDUNDEF" ++ }, ++ { ++ "args": [ ++ "BC_R" ++ ], ++ "name": "LDNULL" ++ }, ++ { ++ "args": [ ++ "BC_R" ++ ], ++ "name": "LDTRUE" ++ }, ++ { ++ "args": [ ++ "BC_R" ++ ], ++ "name": "LDFALSE" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "BC_C" ++ ], ++ "name": "GETVAR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "BC_R" ++ ], ++ "name": "BNOT" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "BC_R" ++ ], ++ "name": "LNOT" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "BC_R" ++ ], ++ "name": "UNM" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "BC_R" ++ ], ++ "name": "UNP" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_R" ++ ], ++ "name": "EQ_RR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_R" ++ ], ++ "name": "EQ_CR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_C" ++ ], ++ "name": "EQ_RC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_C" ++ ], ++ "name": "EQ_CC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_R" ++ ], ++ "name": "NEQ_RR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_R" ++ ], ++ "name": "NEQ_CR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_C" ++ ], ++ "name": "NEQ_RC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_C" ++ ], ++ "name": "NEQ_CC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_R" ++ ], ++ "name": "SEQ_RR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_R" ++ ], ++ "name": "SEQ_CR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_C" ++ ], ++ "name": "SEQ_RC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_C" ++ ], ++ "name": "SEQ_CC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_R" ++ ], ++ "name": "SNEQ_RR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_R" ++ ], ++ "name": "SNEQ_CR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_C" ++ ], ++ "name": "SNEQ_RC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_C" ++ ], ++ "name": "SNEQ_CC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_R" ++ ], ++ "name": "GT_RR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_R" ++ ], ++ "name": "GT_CR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_C" ++ ], ++ "name": "GT_RC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_C" ++ ], ++ "name": "GT_CC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_R" ++ ], ++ "name": "GE_RR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_R" ++ ], ++ "name": "GE_CR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_C" ++ ], ++ "name": "GE_RC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_C" ++ ], ++ "name": "GE_CC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_R" ++ ], ++ "name": "LT_RR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_R" ++ ], ++ "name": "LT_CR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_C" ++ ], ++ "name": "LT_RC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_C" ++ ], ++ "name": "LT_CC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_R" ++ ], ++ "name": "LE_RR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_R" ++ ], ++ "name": "LE_CR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_C" ++ ], ++ "name": "LE_RC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_C" ++ ], ++ "name": "LE_CC" ++ }, ++ { ++ "args": [ ++ "BC_R" ++ ], ++ "name": "IFTRUE_R" ++ }, ++ { ++ "args": [ ++ "BC_C" ++ ], ++ "name": "IFTRUE_C" ++ }, ++ { ++ "args": [ ++ "BC_R" ++ ], ++ "name": "IFFALSE_R" ++ }, ++ { ++ "args": [ ++ "BC_C" ++ ], ++ "name": "IFFALSE_C" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_R" ++ ], ++ "name": "ADD_RR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_R" ++ ], ++ "name": "ADD_CR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_C" ++ ], ++ "name": "ADD_RC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_C" ++ ], ++ "name": "ADD_CC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_R" ++ ], ++ "name": "SUB_RR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_R" ++ ], ++ "name": "SUB_CR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_C" ++ ], ++ "name": "SUB_RC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_C" ++ ], ++ "name": "SUB_CC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_R" ++ ], ++ "name": "MUL_RR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_R" ++ ], ++ "name": "MUL_CR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_C" ++ ], ++ "name": "MUL_RC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_C" ++ ], ++ "name": "MUL_CC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_R" ++ ], ++ "name": "DIV_RR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_R" ++ ], ++ "name": "DIV_CR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_C" ++ ], ++ "name": "DIV_RC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_C" ++ ], ++ "name": "DIV_CC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_R" ++ ], ++ "name": "MOD_RR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_R" ++ ], ++ "name": "MOD_CR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_C" ++ ], ++ "name": "MOD_RC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_C" ++ ], ++ "name": "MOD_CC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_R" ++ ], ++ "name": "EXP_RR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_R" ++ ], ++ "name": "EXP_CR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_C" ++ ], ++ "name": "EXP_RC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_C" ++ ], ++ "name": "EXP_CC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_R" ++ ], ++ "name": "BAND_RR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_R" ++ ], ++ "name": "BAND_CR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_C" ++ ], ++ "name": "BAND_RC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_C" ++ ], ++ "name": "BAND_CC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_R" ++ ], ++ "name": "BOR_RR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_R" ++ ], ++ "name": "BOR_CR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_C" ++ ], ++ "name": "BOR_RC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_C" ++ ], ++ "name": "BOR_CC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_R" ++ ], ++ "name": "BXOR_RR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_R" ++ ], ++ "name": "BXOR_CR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_C" ++ ], ++ "name": "BXOR_RC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_C" ++ ], ++ "name": "BXOR_CC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_R" ++ ], ++ "name": "BASL_RR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_R" ++ ], ++ "name": "BASL_CR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_C" ++ ], ++ "name": "BASL_RC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_C" ++ ], ++ "name": "BASL_CC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_R" ++ ], ++ "name": "BLSR_RR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_R" ++ ], ++ "name": "BLSR_CR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_C" ++ ], ++ "name": "BLSR_RC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_C" ++ ], ++ "name": "BLSR_CC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_R" ++ ], ++ "name": "BASR_RR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_R" ++ ], ++ "name": "BASR_CR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_C" ++ ], ++ "name": "BASR_RC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_C" ++ ], ++ "name": "BASR_CC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_R" ++ ], ++ "name": "INSTOF_RR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_R" ++ ], ++ "name": "INSTOF_CR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_C" ++ ], ++ "name": "INSTOF_RC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_C" ++ ], ++ "name": "INSTOF_CC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_R" ++ ], ++ "name": "IN_RR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_R" ++ ], ++ "name": "IN_CR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_C" ++ ], ++ "name": "IN_RC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_C" ++ ], ++ "name": "IN_CC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_R" ++ ], ++ "name": "GETPROP_RR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_R" ++ ], ++ "name": "GETPROP_CR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_C" ++ ], ++ "name": "GETPROP_RC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_C" ++ ], ++ "name": "GETPROP_CC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_R" ++ ], ++ "name": "PUTPROP_RR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_R" ++ ], ++ "name": "PUTPROP_CR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_C" ++ ], ++ "name": "PUTPROP_RC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_C" ++ ], ++ "name": "PUTPROP_CC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_R" ++ ], ++ "name": "DELPROP_RR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_R" ++ ], ++ "name": "DELPROP_CR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_C" ++ ], ++ "name": "DELPROP_RC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_C" ++ ], ++ "name": "DELPROP_CC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "BC_R" ++ ], ++ "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_C" ++ ], ++ "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", ++ "B_R", ++ "C_R" ++ ], ++ "name": "PREINCP_RR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_R" ++ ], ++ "name": "PREINCP_CR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_C" ++ ], ++ "name": "PREINCP_RC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_C" ++ ], ++ "name": "PREINCP_CC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_R" ++ ], ++ "name": "PREDECP_RR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_R" ++ ], ++ "name": "PREDECP_CR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_C" ++ ], ++ "name": "PREDECP_RC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_C" ++ ], ++ "name": "PREDECP_CC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_R" ++ ], ++ "name": "POSTINCP_RR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_R" ++ ], ++ "name": "POSTINCP_CR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_C" ++ ], ++ "name": "POSTINCP_RC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_C" ++ ], ++ "name": "POSTINCP_CC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_R" ++ ], ++ "name": "POSTDECP_RR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_R" ++ ], ++ "name": "POSTDECP_CR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_C" ++ ], ++ "name": "POSTDECP_RC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_C" ++ ], ++ "name": "POSTDECP_CC" ++ }, ++ { ++ "args": [ ++ "A_H", ++ "B_R", ++ "C_R" ++ ], ++ "flags": [ ++ { ++ "mask": 256, ++ "name": "writable" ++ }, ++ { ++ "mask": 512, ++ "name": "enumerable" ++ }, ++ { ++ "mask": 1024, ++ "name": "configurable" ++ }, ++ { ++ "mask": 2048, ++ "name": "accessor" ++ }, ++ { ++ "mask": 4096, ++ "name": "func_decl" ++ } ++ ], ++ "name": "DECLVAR_RR" ++ }, ++ { ++ "args": [ ++ "A_H", ++ "B_C", ++ "C_R" ++ ], ++ "flags": [ ++ { ++ "mask": 256, ++ "name": "writable" ++ }, ++ { ++ "mask": 512, ++ "name": "enumerable" ++ }, ++ { ++ "mask": 1024, ++ "name": "configurable" ++ }, ++ { ++ "mask": 2048, ++ "name": "accessor" ++ }, ++ { ++ "mask": 4096, ++ "name": "func_decl" ++ } ++ ], ++ "name": "DECLVAR_CR" ++ }, ++ { ++ "args": [ ++ "A_H", ++ "B_R", ++ "C_C" ++ ], ++ "flags": [ ++ { ++ "mask": 256, ++ "name": "writable" ++ }, ++ { ++ "mask": 512, ++ "name": "enumerable" ++ }, ++ { ++ "mask": 1024, ++ "name": "configurable" ++ }, ++ { ++ "mask": 2048, ++ "name": "accessor" ++ }, ++ { ++ "mask": 4096, ++ "name": "func_decl" ++ } ++ ], ++ "name": "DECLVAR_RC" ++ }, ++ { ++ "args": [ ++ "A_H", ++ "B_C", ++ "C_C" ++ ], ++ "flags": [ ++ { ++ "mask": 256, ++ "name": "writable" ++ }, ++ { ++ "mask": 512, ++ "name": "enumerable" ++ }, ++ { ++ "mask": 1024, ++ "name": "configurable" ++ }, ++ { ++ "mask": 2048, ++ "name": "accessor" ++ }, ++ { ++ "mask": 4096, ++ "name": "func_decl" ++ } ++ ], ++ "name": "DECLVAR_CC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_R" ++ ], ++ "name": "REGEXP_RR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_R" ++ ], ++ "name": "REGEXP_CR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_C" ++ ], ++ "name": "REGEXP_RC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_C" ++ ], ++ "name": "REGEXP_CC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "BC_I" ++ ], ++ "name": "CLOSURE" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "BC_R" ++ ], ++ "name": "TYPEOF" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "BC_C" ++ ], ++ "name": "TYPEOFID" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "BC_C" ++ ], ++ "name": "PUTVAR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "BC_C" ++ ], ++ "name": "DELVAR" ++ }, ++ { ++ "args": [ ++ "BC_R" ++ ], ++ "name": "RETREG" ++ }, ++ { ++ "name": "RETUNDEF" ++ }, ++ { ++ "args": [ ++ "BC_C" ++ ], ++ "name": "RETCONST" ++ }, ++ { ++ "args": [ ++ "BC_C" ++ ], ++ "name": "RETCONSTN" ++ }, ++ { ++ "args": [ ++ "BC_I" ++ ], ++ "name": "LABEL" ++ }, ++ { ++ "args": [ ++ "BC_I" ++ ], ++ "name": "ENDLABEL" ++ }, ++ { ++ "args": [ ++ "BC_I" ++ ], ++ "name": "BREAK" ++ }, ++ { ++ "args": [ ++ "BC_I" ++ ], ++ "name": "CONTINUE" ++ }, ++ { ++ "args": [ ++ "A_H", ++ "BC_R" ++ ], ++ "flags": [ ++ { ++ "mask": 256, ++ "name": "have_catch" ++ }, ++ { ++ "mask": 512, ++ "name": "have_finally" ++ }, ++ { ++ "mask": 1024, ++ "name": "catch_binding" ++ }, ++ { ++ "mask": 2048, ++ "name": "with_binding" ++ } ++ ], ++ "name": "TRYCATCH" ++ }, ++ { ++ "name": "ENDTRY" ++ }, ++ { ++ "name": "ENDCATCH" ++ }, ++ { ++ "args": [ ++ "ABC_R" ++ ], ++ "name": "ENDFIN" ++ }, ++ { ++ "args": [ ++ "BC_R" ++ ], ++ "name": "THROW" ++ }, ++ { ++ "name": "INVLHS" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "BC_R" ++ ], ++ "name": "CSREG" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R" ++ ], ++ "name": "CSVAR_RR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C" ++ ], ++ "name": "CSVAR_CR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R" ++ ], ++ "name": "CSVAR_RC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C" ++ ], ++ "name": "CSVAR_CC" ++ }, ++ { ++ "args": [ ++ "A_I", ++ "BC_R" ++ ], ++ "flags": [ ++ { ++ "mask": 1, ++ "name": "tailcall" ++ }, ++ { ++ "mask": 2, ++ "name": "construct" ++ }, ++ { ++ "mask": 4, ++ "name": "called_as_eval" ++ }, ++ { ++ "mask": 8, ++ "name": "indirect" ++ } ++ ], ++ "name": "CALL0" ++ }, ++ { ++ "args": [ ++ "A_I", ++ "BC_R" ++ ], ++ "flags": [ ++ { ++ "mask": 1, ++ "name": "tailcall" ++ }, ++ { ++ "mask": 2, ++ "name": "construct" ++ }, ++ { ++ "mask": 4, ++ "name": "called_as_eval" ++ }, ++ { ++ "mask": 8, ++ "name": "indirect" ++ } ++ ], ++ "name": "CALL1" ++ }, ++ { ++ "args": [ ++ "A_I", ++ "BC_R" ++ ], ++ "flags": [ ++ { ++ "mask": 1, ++ "name": "tailcall" ++ }, ++ { ++ "mask": 2, ++ "name": "construct" ++ }, ++ { ++ "mask": 4, ++ "name": "called_as_eval" ++ }, ++ { ++ "mask": 8, ++ "name": "indirect" ++ } ++ ], ++ "name": "CALL2" ++ }, ++ { ++ "args": [ ++ "A_I", ++ "BC_R" ++ ], ++ "flags": [ ++ { ++ "mask": 1, ++ "name": "tailcall" ++ }, ++ { ++ "mask": 2, ++ "name": "construct" ++ }, ++ { ++ "mask": 4, ++ "name": "called_as_eval" ++ }, ++ { ++ "mask": 8, ++ "name": "indirect" ++ } ++ ], ++ "name": "CALL3" ++ }, ++ { ++ "args": [ ++ "A_I", ++ "BC_R" ++ ], ++ "flags": [ ++ { ++ "mask": 1, ++ "name": "tailcall" ++ }, ++ { ++ "mask": 2, ++ "name": "construct" ++ }, ++ { ++ "mask": 4, ++ "name": "called_as_eval" ++ }, ++ { ++ "mask": 8, ++ "name": "indirect" ++ } ++ ], ++ "name": "CALL4" ++ }, ++ { ++ "args": [ ++ "A_I", ++ "BC_R" ++ ], ++ "flags": [ ++ { ++ "mask": 1, ++ "name": "tailcall" ++ }, ++ { ++ "mask": 2, ++ "name": "construct" ++ }, ++ { ++ "mask": 4, ++ "name": "called_as_eval" ++ }, ++ { ++ "mask": 8, ++ "name": "indirect" ++ } ++ ], ++ "name": "CALL5" ++ }, ++ { ++ "args": [ ++ "A_I", ++ "BC_R" ++ ], ++ "flags": [ ++ { ++ "mask": 1, ++ "name": "tailcall" ++ }, ++ { ++ "mask": 2, ++ "name": "construct" ++ }, ++ { ++ "mask": 4, ++ "name": "called_as_eval" ++ }, ++ { ++ "mask": 8, ++ "name": "indirect" ++ } ++ ], ++ "name": "CALL6" ++ }, ++ { ++ "args": [ ++ "A_I", ++ "BC_R" ++ ], ++ "flags": [ ++ { ++ "mask": 1, ++ "name": "tailcall" ++ }, ++ { ++ "mask": 2, ++ "name": "construct" ++ }, ++ { ++ "mask": 4, ++ "name": "called_as_eval" ++ }, ++ { ++ "mask": 8, ++ "name": "indirect" ++ } ++ ], ++ "name": "CALL7" ++ }, ++ { ++ "args": [ ++ "A_I", ++ "BC_R" ++ ], ++ "flags": [ ++ { ++ "mask": 1, ++ "name": "tailcall" ++ }, ++ { ++ "mask": 2, ++ "name": "construct" ++ }, ++ { ++ "mask": 4, ++ "name": "called_as_eval" ++ }, ++ { ++ "mask": 8, ++ "name": "indirect" ++ } ++ ], ++ "name": "CALL8" ++ }, ++ { ++ "args": [ ++ "A_I", ++ "BC_R" ++ ], ++ "flags": [ ++ { ++ "mask": 1, ++ "name": "tailcall" ++ }, ++ { ++ "mask": 2, ++ "name": "construct" ++ }, ++ { ++ "mask": 4, ++ "name": "called_as_eval" ++ }, ++ { ++ "mask": 8, ++ "name": "indirect" ++ } ++ ], ++ "name": "CALL9" ++ }, ++ { ++ "args": [ ++ "A_I", ++ "BC_R" ++ ], ++ "flags": [ ++ { ++ "mask": 1, ++ "name": "tailcall" ++ }, ++ { ++ "mask": 2, ++ "name": "construct" ++ }, ++ { ++ "mask": 4, ++ "name": "called_as_eval" ++ }, ++ { ++ "mask": 8, ++ "name": "indirect" ++ } ++ ], ++ "name": "CALL10" ++ }, ++ { ++ "args": [ ++ "A_I", ++ "BC_R" ++ ], ++ "flags": [ ++ { ++ "mask": 1, ++ "name": "tailcall" ++ }, ++ { ++ "mask": 2, ++ "name": "construct" ++ }, ++ { ++ "mask": 4, ++ "name": "called_as_eval" ++ }, ++ { ++ "mask": 8, ++ "name": "indirect" ++ } ++ ], ++ "name": "CALL11" ++ }, ++ { ++ "args": [ ++ "A_I", ++ "BC_R" ++ ], ++ "flags": [ ++ { ++ "mask": 1, ++ "name": "tailcall" ++ }, ++ { ++ "mask": 2, ++ "name": "construct" ++ }, ++ { ++ "mask": 4, ++ "name": "called_as_eval" ++ }, ++ { ++ "mask": 8, ++ "name": "indirect" ++ } ++ ], ++ "name": "CALL12" ++ }, ++ { ++ "args": [ ++ "A_I", ++ "BC_R" ++ ], ++ "flags": [ ++ { ++ "mask": 1, ++ "name": "tailcall" ++ }, ++ { ++ "mask": 2, ++ "name": "construct" ++ }, ++ { ++ "mask": 4, ++ "name": "called_as_eval" ++ }, ++ { ++ "mask": 8, ++ "name": "indirect" ++ } ++ ], ++ "name": "CALL13" ++ }, ++ { ++ "args": [ ++ "A_I", ++ "BC_R" ++ ], ++ "flags": [ ++ { ++ "mask": 1, ++ "name": "tailcall" ++ }, ++ { ++ "mask": 2, ++ "name": "construct" ++ }, ++ { ++ "mask": 4, ++ "name": "called_as_eval" ++ }, ++ { ++ "mask": 8, ++ "name": "indirect" ++ } ++ ], ++ "name": "CALL14" ++ }, ++ { ++ "args": [ ++ "A_I", ++ "BC_R" ++ ], ++ "flags": [ ++ { ++ "mask": 1, ++ "name": "tailcall" ++ }, ++ { ++ "mask": 2, ++ "name": "construct" ++ }, ++ { ++ "mask": 4, ++ "name": "called_as_eval" ++ }, ++ { ++ "mask": 8, ++ "name": "indirect" ++ } ++ ], ++ "name": "CALL15" ++ }, ++ { ++ "args": [ ++ "A_I", ++ "BC_R" ++ ], ++ "name": "NEWOBJ" ++ }, ++ { ++ "args": [ ++ "A_I", ++ "BC_R" ++ ], ++ "name": "NEWARR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_I" ++ ], ++ "name": "MPUTOBJ" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_RI", ++ "C_I" ++ ], ++ "name": "MPUTOBJI" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "BC_R" ++ ], ++ "name": "INITSET" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "BC_R" ++ ], ++ "name": "INITGET" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_I" ++ ], ++ "name": "MPUTARR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_RI", ++ "C_I" ++ ], ++ "name": "MPUTARRI" ++ }, ++ { ++ "args": [ ++ "B_R", ++ "C_R" ++ ], ++ "name": "SETALEN" ++ }, ++ { ++ "args": [ ++ "B_R", ++ "C_R" ++ ], ++ "name": "INITENUM" ++ }, ++ { ++ "args": [ ++ "B_R", ++ "C_R" ++ ], ++ "name": "NEXTENUM" ++ }, ++ { ++ "args": [ ++ "BC_R" ++ ], ++ "name": "NEWTARGET" ++ }, ++ { ++ "name": "DEBUGGER" ++ }, ++ { ++ "args": [ ++ "ABC_I" ++ ], ++ "name": "NOP" ++ }, ++ { ++ "args": [ ++ "ABC_I" ++ ], ++ "name": "INVALID" ++ }, ++ { ++ "name": "UNUSED207" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_R" ++ ], ++ "name": "GETPROPC_RR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_R" ++ ], ++ "name": "GETPROPC_CR" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_R", ++ "C_C" ++ ], ++ "name": "GETPROPC_RC" ++ }, ++ { ++ "args": [ ++ "A_R", ++ "B_C", ++ "C_C" ++ ], ++ "name": "GETPROPC_CC" ++ }, ++ { ++ "name": "UNUSED212" ++ }, ++ { ++ "name": "UNUSED213" ++ }, ++ { ++ "name": "UNUSED214" ++ }, ++ { ++ "name": "UNUSED215" ++ }, ++ { ++ "name": "UNUSED216" ++ }, ++ { ++ "name": "UNUSED217" ++ }, ++ { ++ "name": "UNUSED218" ++ }, ++ { ++ "name": "UNUSED219" ++ }, ++ { ++ "name": "UNUSED220" ++ }, ++ { ++ "name": "UNUSED221" ++ }, ++ { ++ "name": "UNUSED222" ++ }, ++ { ++ "name": "UNUSED223" ++ }, ++ { ++ "name": "UNUSED224" ++ }, ++ { ++ "name": "UNUSED225" ++ }, ++ { ++ "name": "UNUSED226" ++ }, ++ { ++ "name": "UNUSED227" ++ }, ++ { ++ "name": "UNUSED228" ++ }, ++ { ++ "name": "UNUSED229" ++ }, ++ { ++ "name": "UNUSED230" ++ }, ++ { ++ "name": "UNUSED231" ++ }, ++ { ++ "name": "UNUSED232" ++ }, ++ { ++ "name": "UNUSED233" ++ }, ++ { ++ "name": "UNUSED234" ++ }, ++ { ++ "name": "UNUSED235" ++ }, ++ { ++ "name": "UNUSED236" ++ }, ++ { ++ "name": "UNUSED237" ++ }, ++ { ++ "name": "UNUSED238" ++ }, ++ { ++ "name": "UNUSED239" ++ }, ++ { ++ "name": "UNUSED240" ++ }, ++ { ++ "name": "UNUSED241" ++ }, ++ { ++ "name": "UNUSED242" ++ }, ++ { ++ "name": "UNUSED243" ++ }, ++ { ++ "name": "UNUSED244" ++ }, ++ { ++ "name": "UNUSED245" ++ }, ++ { ++ "name": "UNUSED246" ++ }, ++ { ++ "name": "UNUSED247" ++ }, ++ { ++ "name": "UNUSED248" ++ }, ++ { ++ "name": "UNUSED249" ++ }, ++ { ++ "name": "UNUSED250" ++ }, ++ { ++ "name": "UNUSED251" ++ }, ++ { ++ "name": "UNUSED252" ++ }, ++ { ++ "name": "UNUSED253" ++ }, ++ { ++ "name": "UNUSED254" ++ }, ++ { ++ "name": "UNUSED255" ++ } ++ ], ++ "client_commands": [ ++ "Reserved_0", ++ "Status", ++ "Reserved_2", ++ "Reserved_3", ++ "Reserved_4", ++ "Throw", ++ "Detaching", ++ "AppNotify" ++ ], ++ "class_names": [ ++ "none", ++ "Object", ++ "Array", ++ "Function", ++ "Arguments", ++ "Boolean", ++ "Date", ++ "Error", ++ "JSON", ++ "Math", ++ "Number", ++ "RegExp", ++ "String", ++ "global", ++ "Symbol", ++ "ObjEnv", ++ "DecEnv", ++ "Pointer", ++ "Thread", ++ "ArrayBuffer", ++ "DataView", ++ "Int8Array", ++ "Uint8Array", ++ "Uint8ClampedArray", ++ "Int16Array", ++ "Uint16Array", ++ "Int32Array", ++ "Uint32Array", ++ "Float32Array", ++ "Float64Array" ++ ], ++ "target_commands": [ ++ "Reserved_0", ++ "Reserved_1", ++ "Reserved_2", ++ "Reserved_3", ++ "Reserved_4", ++ "Reserved_5", ++ "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", ++ "AppRequest", ++ "GetHeapObjInfo", ++ "GetObjPropDesc", ++ "GetObjPropDescRange" ++ ] ++} +diff --git a/dist/source/debugger/duk_debug_proxy.js b/dist/source/debugger/duk_debug_proxy.js +new file mode 100644 +index 0000000..fd8ba91 +--- /dev/null ++++ b/dist/source/debugger/duk_debug_proxy.js +@@ -0,0 +1,1044 @@ ++/* ++ * JSON debug proxy written in DukLuv ++ * ++ * This single file JSON debug proxy implementation is an alternative to the ++ * Node.js-based proxy in duk_debug.js. DukLuv is a much smaller dependency ++ * than Node.js so embedding DukLuv in a debug client is easier. ++ */ ++ ++'use strict'; ++ ++// XXX: Code assumes uv.write() will write fully. This is not necessarily ++// true; should add support for partial writes (or at least failing when ++// a partial write occurs). ++ ++var log = new Duktape.Logger('Proxy'); // default logger ++//log.l = 0; // enable debug and trace logging ++ ++/* ++ * Config ++ */ ++ ++var serverHost = '0.0.0.0'; ++var serverPort = 9093; ++var targetHost = '127.0.0.1'; ++var targetPort = 9091; ++var singleConnection = false; ++var readableNumberValue = false; ++var lenientJsonParse = false; ++var jxParse = false; ++var metadataFile = null; ++var metadata = {}; ++var TORTURE = false; // for manual testing of binary/json parsing robustness ++ ++/* ++ * Duktape 1.x and 2.x buffer harmonization ++ */ ++ ++var allocPlain = (typeof Uint8Array.allocPlain === 'function' ? ++ Uint8Array.allocPlain : Duktape.Buffer); ++var plainOf = (typeof Uint8Array.plainOf === 'function' ? ++ Uint8Array.plainOf : Duktape.Buffer); ++var bufferToString = (typeof String.fromBuffer === 'function' ? ++ String.fromBuffer : String); ++ ++/* ++ * Detect missing 'var' declarations ++ */ ++ ++// Prevent new bindings on global object. This detects missing 'var' ++// declarations, e.g. "x = 123;" in a function without declaring it. ++var global = new Function('return this;')(); ++log.debug('Preventing extensions on global object'); ++log.debug('Global is extensible:', Object.isExtensible(global)); ++Object.preventExtensions(global); ++log.debug('Global is extensible:', Object.isExtensible(global)); ++ ++/* ++ * Misc helpers ++ */ ++ ++function jxEncode(v) { ++ return Duktape.enc('jx', v); ++} ++ ++function plainBufferCopy(typedarray) { ++ // This is still pretty awkward in Duktape 1.4.x. ++ // Argument may be a "slice" and we want a copy of the slice ++ // (not the full underlying buffer). ++ ++ var u8 = new Uint8Array(typedarray.length); ++ u8.set(typedarray); // make a copy, ensuring there's no slice offset ++ return plainOf(u8); // get underlying plain buffer ++} ++ ++function isObject(x) { ++ // Note that typeof null === 'object'. ++ return (typeof x === 'object' && x !== null); ++} ++ ++function readFully(filename, cb) { ++ uv.fs_open(metadataFile, 'r', 0, function (handle, err) { ++ var fileOff = 0; ++ var data = new Uint8Array(256); ++ var dataOff = 0; ++ ++ if (err) { ++ return cb(null, err); ++ } ++ function readCb(buf, err) { ++ var res; ++ var newData; ++ ++ log.debug('Read callback:', buf.length, err); ++ if (err) { ++ uv.fs_close(handle); ++ return cb(null, err); ++ } ++ if (buf.length == 0) { ++ uv.fs_close(handle); ++ res = new Uint8Array(dataOff); ++ res.set(data.subarray(0, dataOff)); ++ res = plainOf(res); // plain buffer ++ log.debug('Read', res.length, 'bytes from', filename); ++ return cb(res, null); ++ } ++ while (data.length - dataOff < buf.length) { ++ log.debug('Resize file read buffer:', data.length, '->', data.length * 2); ++ newData = new Uint8Array(data.length * 2); ++ newData.set(data); ++ data = newData; ++ } ++ data.set(new Uint8Array(buf), dataOff); ++ dataOff += buf.length; ++ fileOff += buf.length; ++ uv.fs_read(handle, 4096, fileOff, readCb); ++ } ++ uv.fs_read(handle, 4096, fileOff, readCb); ++ }); ++} ++ ++/* ++ * JSON proxy server ++ * ++ * Accepts an incoming JSON proxy client and connects to a debug target, ++ * tying the two connections together. Supports both a single connection ++ * and a persistent mode. ++ */ ++ ++function JsonProxyServer(host, port) { ++ this.name = 'JsonProxyServer'; ++ this.handle = uv.new_tcp(); ++ uv.tcp_bind(this.handle, host, port); ++ uv.listen(this.handle, 128, this.onConnection.bind(this)); ++} ++ ++JsonProxyServer.prototype.onConnection = function onConnection(err) { ++ if (err) { ++ log.error('JSON proxy onConnection error:', err); ++ return; ++ } ++ log.info('JSON proxy client connected'); // XXX: it'd be nice to log remote peer host:port ++ ++ var jsonSock = new JsonConnHandler(this); ++ var targSock = new TargetConnHandler(this); ++ jsonSock.targetHandler = targSock; ++ targSock.jsonHandler = jsonSock; ++ uv.accept(this.handle, jsonSock.handle); ++ ++ log.info('Connecting to debug target at', targetHost + ':' + targetPort); ++ jsonSock.writeJson({ notify: '_TargetConnecting', args: [ targetHost, targetPort ] }); ++ uv.tcp_connect(targSock.handle, targetHost, targetPort, targSock.onConnect.bind(targSock)); ++ ++ if (singleConnection) { ++ log.info('Single connection mode, stop listening for more connections'); ++ uv.shutdown(this.handle); ++ uv.read_stop(this.handle); // unnecessary but just in case ++ uv.close(this.handle); ++ this.handle = null; ++ } ++}; ++ ++JsonProxyServer.prototype.onProxyClientDisconnected = function onProxyClientDisconnected() { ++ // When this is invoked the proxy connection and the target connection ++ // have both been closed. ++ if (singleConnection) { ++ log.info('Proxy connection finished (single connection mode: we should be exiting now)'); ++ } else { ++ log.info('Proxy connection finished (persistent mode: wait for more connections)'); ++ } ++}; ++ ++/* ++ * JSON connection handler ++ */ ++ ++function JsonConnHandler(server) { ++ var i, n; ++ ++ this.name = 'JsonConnHandler'; ++ this.server = server; ++ this.handle = uv.new_tcp(); ++ this.incoming = new Uint8Array(4096); ++ this.incomingOffset = 0; ++ this.targetHandler = null; ++ ++ this.commandNumberLookup = {}; ++ if (metadata && metadata.target_commands) { ++ for (i = 0, n = metadata.target_commands.length; i < n; i++) { ++ this.commandNumberLookup[metadata.target_commands[i]] = i; ++ } ++ } ++} ++ ++JsonConnHandler.prototype.finish = function finish(msg) { ++ var args; ++ ++ if (!this.handle) { ++ log.info('JsonConnHandler already disconnected, ignore finish()'); ++ return; ++ } ++ log.info('JsonConnHandler finished:', msg); ++ try { ++ args = msg ? [ msg ] : void 0; ++ this.writeJson({ notify: '_Disconnecting', args: args }); ++ } catch (e) { ++ log.info('Failed to write _Disconnecting notify, ignoring:', e); ++ } ++ uv.shutdown(this.handle); ++ uv.read_stop(this.handle); ++ uv.close(this.handle); ++ this.handle = null; ++ ++ this.targetHandler.finish(msg); // disconnect target too (if not already disconnected) ++ ++ this.server.onProxyClientDisconnected(); ++}; ++ ++JsonConnHandler.prototype.onRead = function onRead(err, data) { ++ var newIncoming; ++ var msg; ++ var errmsg; ++ var tmpBuf; ++ ++ log.trace('Received data from JSON socket, err:', err, 'data length:', data ? data.length : 'null'); ++ ++ if (err) { ++ errmsg = 'Error reading data from JSON debug client: ' + err; ++ this.finish(errmsg); ++ return; ++ } ++ if (data) { ++ // Feed the data one byte at a time when torture testing. ++ if (TORTURE && data.length > 1) { ++ for (var i = 0; i < data.length; i++) { ++ tmpBuf = allocPlain(1); ++ tmpBuf[0] = data[i]; ++ this.onRead(null, tmpBuf); ++ } ++ return; ++ } ++ ++ // Receive data into 'incoming', resizing as necessary. ++ while (data.length > this.incoming.length - this.incomingOffset) { ++ newIncoming = new Uint8Array(this.incoming.length * 1.3 + 16); ++ newIncoming.set(this.incoming); ++ this.incoming = newIncoming; ++ log.debug('Resize incoming JSON buffer to ' + this.incoming.length); ++ } ++ this.incoming.set(new Uint8Array(data), this.incomingOffset); ++ this.incomingOffset += data.length; ++ ++ // Trial parse JSON message(s). ++ while (true) { ++ msg = this.trialParseJsonMessage(); ++ if (!msg) { ++ break; ++ } ++ try { ++ this.dispatchJsonMessage(msg); ++ } catch (e) { ++ errmsg = 'JSON message dispatch failed: ' + e; ++ this.writeJson({ notify: '_Error', args: [ errmsg ] }); ++ if (lenientJsonParse) { ++ log.warn('JSON message dispatch failed (lenient mode, ignoring):', e); ++ } else { ++ log.warn('JSON message dispatch failed (dropping connection):', e); ++ this.finish(errmsg); ++ } ++ } ++ } ++ } else { ++ this.finish('JSON proxy client disconnected'); ++ } ++}; ++ ++JsonConnHandler.prototype.writeJson = function writeJson(msg) { ++ log.info('PROXY --> CLIENT:', JSON.stringify(msg)); ++ if (this.handle) { ++ uv.write(this.handle, JSON.stringify(msg) + '\n'); ++ } ++}; ++ ++JsonConnHandler.prototype.handleDebugMessage = function handleDebugMessage(dvalues) { ++ var msg = {}; ++ var idx = 0; ++ var cmd; ++ ++ if (dvalues.length <= 0) { ++ throw new Error('invalid dvalues list: length <= 0'); ++ } ++ var x = dvalues[idx++]; ++ if (!isObject(x)) { ++ throw new Error('invalid initial dvalue: ' + Duktape.enc('jx', dvalues)); ++ } ++ if (x.type === 'req') { ++ cmd = dvalues[idx++]; ++ if (typeof cmd !== 'number') { ++ throw new Error('invalid command: ' + Duktape.enc('jx', cmd)); ++ } ++ msg.request = this.determineCommandName(cmd) || true; ++ msg.command = cmd; ++ } else if (x.type === 'rep') { ++ msg.reply = true; ++ } else if (x.type === 'err') { ++ msg.error = true; ++ } else if (x.type === 'nfy') { ++ cmd = dvalues[idx++]; ++ if (typeof cmd !== 'number') { ++ throw new Error('invalid command: ' + Duktape.enc('jx', cmd)); ++ } ++ msg.notify = this.determineCommandName(cmd) || true; ++ msg.command = cmd; ++ } else { ++ throw new Error('invalid initial dvalue: ' + Duktape.enc('jx', dvalues)); ++ } ++ ++ for (; idx < dvalues.length - 1; idx++) { ++ if (!msg.args) { ++ msg.args = []; ++ } ++ msg.args.push(dvalues[idx]); ++ } ++ ++ if (!isObject(dvalues[idx]) || dvalues[idx].type !== 'eom') { ++ throw new Error('invalid final dvalue: ' + Duktape.enc('jx', dvalues)); ++ } ++ ++ this.writeJson(msg); ++}; ++ ++JsonConnHandler.prototype.determineCommandName = function determineCommandName(cmd) { ++ if (!(metadata && metadata.client_commands)) { ++ return; ++ } ++ return metadata.client_commands[cmd]; ++}; ++ ++JsonConnHandler.prototype.trialParseJsonMessage = function trialParseJsonMessage() { ++ var buf = this.incoming; ++ var avail = this.incomingOffset; ++ var i; ++ var msg, str, errmsg; ++ ++ for (i = 0; i < avail; i++) { ++ if (buf[i] == 0x0a) { ++ str = bufferToString(plainBufferCopy(buf.subarray(0, i))); ++ try { ++ if (jxParse) { ++ msg = Duktape.dec('jx', str); ++ } else { ++ msg = JSON.parse(str); ++ } ++ } catch (e) { ++ // In lenient mode if JSON parse fails just send back an _Error ++ // and ignore the line (useful for initial development). ++ // ++ // In non-lenient mode drop the connection here; if the failed line ++ // was a request the client is expecting a reply/error message back ++ // (otherwise it may go out of sync) but we can't send a synthetic ++ // one (as we can't parse the request). ++ errmsg = 'JSON parse failed for: ' + jxEncode(str) + ': ' + e; ++ this.writeJson({ notify: '_Error', args: [ errmsg ] }); ++ if (lenientJsonParse) { ++ log.warn('JSON parse failed (lenient mode, ignoring):', e); ++ } else { ++ log.warn('JSON parse failed (dropping connection):', e); ++ this.finish(errmsg); ++ } ++ } ++ ++ this.incoming.set(this.incoming.subarray(i + 1)); ++ this.incomingOffset -= i + 1; ++ return msg; ++ } ++ } ++}; ++ ++JsonConnHandler.prototype.dispatchJsonMessage = function dispatchJsonMessage(msg) { ++ var cmd; ++ var dvalues = []; ++ var i, n; ++ ++ log.info('PROXY <-- CLIENT:', JSON.stringify(msg)); ++ ++ // Parse message type, determine initial marker for binary message. ++ if (msg.request) { ++ cmd = this.determineCommandNumber(msg.request, msg.command); ++ dvalues.push(new Uint8Array([ 0x01 ])); ++ dvalues.push(this.encodeJsonDvalue(cmd)); ++ } else if (msg.reply) { ++ dvalues.push(new Uint8Array([ 0x02 ])); ++ } else if (msg.notify) { ++ cmd = this.determineCommandNumber(msg.notify, msg.command); ++ dvalues.push(new Uint8Array([ 0x04 ])); ++ dvalues.push(this.encodeJsonDvalue(cmd)); ++ } else if (msg.error) { ++ dvalues.push(new Uint8Array([ 0x03 ])); ++ } else { ++ throw new Error('invalid input JSON message: ' + jxEncode(msg)); ++ } ++ ++ // Encode arguments into dvalues. ++ for (i = 0, n = (msg.args ? msg.args.length : 0); i < n; i++) { ++ dvalues.push(this.encodeJsonDvalue(msg.args[i])); ++ } ++ ++ // Add an EOM, and write out the dvalues to the debug target. ++ dvalues.push(new Uint8Array([ 0x00 ])); ++ for (i = 0, n = dvalues.length; i < n; i++) { ++ this.targetHandler.writeBinary(dvalues[i]); ++ } ++}; ++ ++JsonConnHandler.prototype.determineCommandNumber = function determineCommandNumber(name, val) { ++ var res; ++ ++ if (typeof name === 'string') { ++ res = this.commandNumberLookup[name]; ++ if (!res) { ++ log.info('Unknown command name: ' + name + ', command number: ' + val); ++ } ++ } else if (typeof name === 'number') { ++ res = name; ++ } else if (name !== true) { ++ throw new Error('invalid command name (must be string, number, or "true"): ' + name); ++ } ++ if (typeof res === 'undefined' && typeof val === 'undefined') { ++ throw new Error('cannot determine command number from name: ' + name); ++ } ++ if (typeof val !== 'number' && typeof val !== 'undefined') { ++ throw new Error('invalid command number: ' + val); ++ } ++ res = res || val; ++ return res; ++}; ++ ++JsonConnHandler.prototype.writeDebugStringToBuffer = function writeDebugStringToBuffer(v, buf, off) { ++ var i, n; ++ ++ for (i = 0, n = v.length; i < n; i++) { ++ buf[off + i] = v.charCodeAt(i) & 0xff; // truncate higher bits ++ } ++}; ++ ++JsonConnHandler.prototype.encodeJsonDvalue = function encodeJsonDvalue(v) { ++ var buf, dec, len, dv; ++ ++ if (isObject(v)) { ++ if (v.type === 'eom') { ++ return new Uint8Array([ 0x00 ]); ++ } else if (v.type === 'req') { ++ return new Uint8Array([ 0x01 ]); ++ } else if (v.type === 'rep') { ++ return new Uint8Array([ 0x02 ]); ++ } else if (v.type === 'err') { ++ return new Uint8Array([ 0x03 ]); ++ } else if (v.type === 'nfy') { ++ return new Uint8Array([ 0x04 ]); ++ } else if (v.type === 'unused') { ++ return new Uint8Array([ 0x15 ]); ++ } else if (v.type === 'undefined') { ++ return new Uint8Array([ 0x16 ]); ++ } else if (v.type === 'number') { ++ dec = Duktape.dec('hex', v.data); ++ len = dec.length; ++ if (len !== 8) { ++ throw new TypeError('value cannot be converted to dvalue: ' + jxEncode(v)); ++ } ++ buf = new Uint8Array(1 + len); ++ buf[0] = 0x1a; ++ buf.set(new Uint8Array(dec), 1); ++ return buf; ++ } else if (v.type === 'buffer') { ++ dec = Duktape.dec('hex', v.data); ++ len = dec.length; ++ if (len <= 0xffff) { ++ buf = new Uint8Array(3 + len); ++ buf[0] = 0x14; ++ buf[1] = (len >> 8) & 0xff; ++ buf[2] = (len >> 0) & 0xff; ++ buf.set(new Uint8Arrau(dec), 3); ++ return buf; ++ } else { ++ buf = new Uint8Array(5 + len); ++ buf[0] = 0x13; ++ buf[1] = (len >> 24) & 0xff; ++ buf[2] = (len >> 16) & 0xff; ++ buf[3] = (len >> 8) & 0xff; ++ buf[4] = (len >> 0) & 0xff; ++ buf.set(new Uint8Array(dec), 5); ++ return buf; ++ } ++ } else if (v.type === 'object') { ++ dec = Duktape.dec('hex', v.pointer); ++ len = dec.length; ++ buf = new Uint8Array(3 + len); ++ buf[0] = 0x1b; ++ buf[1] = v.class; ++ buf[2] = len; ++ buf.set(new Uint8Array(dec), 3); ++ return buf; ++ } else if (v.type === 'pointer') { ++ dec = Duktape.dec('hex', v.pointer); ++ len = dec.length; ++ buf = new Uint8Array(2 + len); ++ buf[0] = 0x1c; ++ buf[1] = len; ++ buf.set(new Uint8Array(dec), 2); ++ return buf; ++ } else if (v.type === 'lightfunc') { ++ dec = Duktape.dec('hex', v.pointer); ++ len = dec.length; ++ buf = new Uint8Array(4 + len); ++ buf[0] = 0x1d; ++ buf[1] = (v.flags >> 8) & 0xff; ++ buf[2] = v.flags & 0xff; ++ buf[3] = len; ++ buf.set(new Uint8Array(dec), 4); ++ return buf; ++ } else if (v.type === 'heapptr') { ++ dec = Duktape.dec('hex', v.pointer); ++ len = dec.length; ++ buf = new Uint8Array(2 + len); ++ buf[0] = 0x1e; ++ buf[1] = len; ++ buf.set(new Uint8Array(dec), 2); ++ return buf; ++ } ++ } else if (v === null) { ++ return new Uint8Array([ 0x17 ]); ++ } else if (typeof v === 'boolean') { ++ return new Uint8Array([ v ? 0x18 : 0x19 ]); ++ } else if (typeof v === 'number') { ++ if (Math.floor(v) === v && /* whole */ ++ (v !== 0 || 1 / v > 0) && /* not negative zero */ ++ v >= -0x80000000 && v <= 0x7fffffff) { ++ // Represented signed 32-bit integers as plain integers. ++ // Debugger code expects this for all fields that are not ++ // duk_tval representations (e.g. command numbers and such). ++ if (v >= 0x00 && v <= 0x3f) { ++ return new Uint8Array([ 0x80 + v ]); ++ } else if (v >= 0x0000 && v <= 0x3fff) { ++ return new Uint8Array([ 0xc0 + (v >> 8), v & 0xff ]); ++ } else if (v >= -0x80000000 && v <= 0x7fffffff) { ++ return new Uint8Array([ 0x10, ++ (v >> 24) & 0xff, ++ (v >> 16) & 0xff, ++ (v >> 8) & 0xff, ++ (v >> 0) & 0xff ]); ++ } else { ++ throw new Error('internal error when encoding integer to dvalue: ' + v); ++ } ++ } else { ++ // Represent non-integers as IEEE double dvalues. ++ buf = new Uint8Array(1 + 8); ++ buf[0] = 0x1a; ++ new DataView(buf).setFloat64(1, v, false); ++ return buf; ++ } ++ } else if (typeof v === 'string') { ++ if (v.length < 0 || v.length > 0xffffffff) { ++ // Not possible in practice. ++ throw new TypeError('cannot convert to dvalue, invalid string length: ' + v.length); ++ } ++ if (v.length <= 0x1f) { ++ buf = new Uint8Array(1 + v.length); ++ buf[0] = 0x60 + v.length; ++ this.writeDebugStringToBuffer(v, buf, 1); ++ return buf; ++ } else if (v.length <= 0xffff) { ++ buf = new Uint8Array(3 + v.length); ++ buf[0] = 0x12; ++ buf[1] = (v.length >> 8) & 0xff; ++ buf[2] = (v.length >> 0) & 0xff; ++ this.writeDebugStringToBuffer(v, buf, 3); ++ return buf; ++ } else { ++ buf = new Uint8Array(5 + v.length); ++ buf[0] = 0x11; ++ buf[1] = (v.length >> 24) & 0xff; ++ buf[2] = (v.length >> 16) & 0xff; ++ buf[3] = (v.length >> 8) & 0xff; ++ buf[4] = (v.length >> 0) & 0xff; ++ this.writeDebugStringToBuffer(v, buf, 5); ++ return buf; ++ } ++ } ++ ++ throw new TypeError('value cannot be converted to dvalue: ' + jxEncode(v)); ++}; ++ ++/* ++ * Target binary connection handler ++ */ ++ ++function TargetConnHandler(server) { ++ this.name = 'TargetConnHandler'; ++ this.server = server; ++ this.handle = uv.new_tcp(); ++ this.jsonHandler = null; ++ this.incoming = new Uint8Array(4096); ++ this.incomingOffset = 0; ++ this.dvalues = []; ++} ++ ++TargetConnHandler.prototype.finish = function finish(msg) { ++ if (!this.handle) { ++ log.info('TargetConnHandler already disconnected, ignore finish()'); ++ return; ++ } ++ log.info('TargetConnHandler finished:', msg); ++ ++ this.jsonHandler.writeJson({ notify: '_TargetDisconnected' }); ++ ++ // XXX: write a notify to target? ++ ++ uv.shutdown(this.handle); ++ uv.read_stop(this.handle); ++ uv.close(this.handle); ++ this.handle = null; ++ ++ this.jsonHandler.finish(msg); // disconnect JSON client too (if not already disconnected) ++}; ++ ++TargetConnHandler.prototype.onConnect = function onConnect(err) { ++ var errmsg; ++ ++ if (err) { ++ errmsg = 'Failed to connect to target: ' + err; ++ log.warn(errmsg); ++ this.jsonHandler.writeJson({ notify: '_Error', args: [ String(err) ] }); ++ this.finish(errmsg); ++ return; ++ } ++ ++ // Once we're connected to the target, start read both binary and JSON ++ // input. We don't want to read JSON input before this so that we can ++ // always translate incoming messages to dvalues and write them out ++ // without queueing. Any pending JSON messages will be queued by the ++ // OS instead. ++ ++ log.info('Connected to debug target at', targetHost + ':' + targetPort); ++ uv.read_start(this.jsonHandler.handle, this.jsonHandler.onRead.bind(this.jsonHandler)); ++ uv.read_start(this.handle, this.onRead.bind(this)); ++}; ++ ++TargetConnHandler.prototype.writeBinary = function writeBinary(buf) { ++ var plain = plainBufferCopy(buf); ++ log.info('PROXY --> TARGET:', Duktape.enc('jx', plain)); ++ if (this.handle) { ++ uv.write(this.handle, plain); ++ } ++}; ++ ++TargetConnHandler.prototype.onRead = function onRead(err, data) { ++ var res; ++ var errmsg; ++ var tmpBuf; ++ var newIncoming; ++ ++ log.trace('Received data from target socket, err:', err, 'data length:', data ? data.length : 'null'); ++ ++ if (err) { ++ errmsg = 'Error reading data from debug target: ' + err; ++ this.finish(errmsg); ++ return; ++ } ++ ++ if (data) { ++ // Feed the data one byte at a time when torture testing. ++ if (TORTURE && data.length > 1) { ++ for (var i = 0; i < data.length; i++) { ++ tmpBuf = allocPlain(1); ++ tmpBuf[0] = data[i]; ++ this.onRead(null, tmpBuf); ++ } ++ return; ++ } ++ ++ // Receive data into 'incoming', resizing as necessary. ++ while (data.length > this.incoming.length - this.incomingOffset) { ++ newIncoming = new Uint8Array(this.incoming.length * 1.3 + 16); ++ newIncoming.set(this.incoming); ++ this.incoming = newIncoming; ++ log.debug('Resize incoming binary buffer to ' + this.incoming.length); ++ } ++ this.incoming.set(new Uint8Array(data), this.incomingOffset); ++ this.incomingOffset += data.length; ++ ++ // Trial parse handshake unless done. ++ if (!this.handshake) { ++ this.trialParseHandshake(); ++ } ++ ++ // Trial parse dvalue(s) and debug messages. ++ if (this.handshake) { ++ for (;;) { ++ res = this.trialParseDvalue(); ++ if (!res) { ++ break; ++ } ++ log.trace('Got dvalue:', Duktape.enc('jx', res.dvalue)); ++ this.dvalues.push(res.dvalue); ++ if (isObject(res.dvalue) && res.dvalue.type === 'eom') { ++ try { ++ this.jsonHandler.handleDebugMessage(this.dvalues); ++ this.dvalues = []; ++ } catch (e) { ++ errmsg = 'JSON message handling failed: ' + e; ++ this.jsonHandler.writeJson({ notify: '_Error', args: [ errmsg ] }); ++ if (lenientJsonParse) { ++ log.warn('JSON message handling failed (lenient mode, ignoring):', e); ++ } else { ++ log.warn('JSON message handling failed (dropping connection):', e); ++ this.finish(errmsg); ++ } ++ } ++ } ++ } ++ } ++ } else { ++ log.info('Target disconnected'); ++ this.finish('Target disconnected'); ++ } ++}; ++ ++TargetConnHandler.prototype.trialParseHandshake = function trialParseHandshake() { ++ var buf = this.incoming; ++ var avail = this.incomingOffset; ++ var i; ++ var msg; ++ var m; ++ var protocolVersion; ++ ++ for (i = 0; i < avail; i++) { ++ if (buf[i] == 0x0a) { ++ msg = bufferToString(plainBufferCopy(buf.subarray(0, i))); ++ this.incoming.set(this.incoming.subarray(i + 1)); ++ this.incomingOffset -= i + 1; ++ ++ // Generic handshake format: only relies on initial version field. ++ m = /^(\d+) (.*)$/.exec(msg) || {}; ++ protocolVersion = +m[1]; ++ this.handshake = { ++ line: msg, ++ protocolVersion: protocolVersion, ++ text: m[2] ++ }; ++ ++ // More detailed v1 handshake line. ++ if (protocolVersion === 1) { ++ m = /^(\d+) (\d+) (.*?) (.*?) (.*)$/.exec(msg) || {}; ++ this.handshake.dukVersion = m[1]; ++ this.handshake.dukGitDescribe = m[2]; ++ this.handshake.targetString = m[3]; ++ } ++ ++ this.jsonHandler.writeJson({ notify: '_TargetConnected', args: [ msg ] }); ++ ++ log.info('Target handshake: ' + JSON.stringify(this.handshake)); ++ return; ++ } ++ } ++}; ++ ++TargetConnHandler.prototype.bufferToDebugString = function bufferToDebugString(buf) { ++ return String.fromCharCode.apply(null, buf); ++}; ++ ++TargetConnHandler.prototype.trialParseDvalue = function trialParseDvalue() { ++ var _this = this; ++ var buf = this.incoming; ++ var avail = this.incomingOffset; ++ var v; ++ var gotValue = false; // explicit flag for e.g. v === undefined ++ var dv = new DataView(buf); ++ var tmp; ++ var x; ++ var len; ++ ++ function consume(n) { ++ log.info('PROXY <-- TARGET:', Duktape.enc('jx', _this.incoming.subarray(0, n))); ++ _this.incoming.set(_this.incoming.subarray(n)); ++ _this.incomingOffset -= n; ++ } ++ ++ x = buf[0]; ++ if (avail <= 0) { ++ ; ++ } else if (x >= 0xc0) { ++ // 0xc0...0xff: integers 0-16383 ++ if (avail >= 2) { ++ v = ((x - 0xc0) << 8) + buf[1]; ++ consume(2); ++ } ++ } else if (x >= 0x80) { ++ // 0x80...0xbf: integers 0-63 ++ v = x - 0x80; ++ consume(1); ++ } else if (x >= 0x60) { ++ // 0x60...0x7f: strings with length 0-31 ++ len = x - 0x60; ++ if (avail >= 1 + len) { ++ v = new Uint8Array(len); ++ v.set(buf.subarray(1, 1 + len)); ++ v = this.bufferToDebugString(v); ++ consume(1 + len); ++ } ++ } else { ++ switch (x) { ++ case 0x00: consume(1); v = { type: 'eom' }; break; ++ case 0x01: consume(1); v = { type: 'req' }; break; ++ case 0x02: consume(1); v = { type: 'rep' }; break; ++ case 0x03: consume(1); v = { type: 'err' }; break; ++ case 0x04: consume(1); v = { type: 'nfy' }; break; ++ case 0x10: // 4-byte signed integer ++ if (avail >= 5) { ++ v = dv.getInt32(1, false); ++ consume(5); ++ } ++ break; ++ case 0x11: // 4-byte string ++ if (avail >= 5) { ++ len = dv.getUint32(1, false); ++ if (avail >= 5 + len) { ++ v = new Uint8Array(len); ++ v.set(buf.subarray(5, 5 + len)); ++ v = this.bufferToDebugString(v); ++ consume(5 + len); ++ } ++ } ++ break; ++ case 0x12: // 2-byte string ++ if (avail >= 3) { ++ len = dv.getUint16(1, false); ++ if (avail >= 3 + len) { ++ v = new Uint8Array(len); ++ v.set(buf.subarray(3, 3 + len)); ++ v = this.bufferToDebugString(v); ++ consume(3 + len); ++ } ++ } ++ break; ++ case 0x13: // 4-byte buffer ++ if (avail >= 5) { ++ len = dv.getUint32(1, false); ++ if (avail >= 5 + len) { ++ v = new Uint8Array(len); ++ v.set(buf.subarray(5, 5 + len)); ++ v = { type: 'buffer', data: Duktape.enc('hex', plainOf(v)) }; ++ consume(5 + len); ++ } ++ } ++ break; ++ case 0x14: // 2-byte buffer ++ if (avail >= 3) { ++ len = dv.getUint16(1, false); ++ if (avail >= 3 + len) { ++ v = new Uint8Array(len); ++ v.set(buf.subarray(3, 3 + len)); ++ v = { type: 'buffer', data: Duktape.enc('hex', plainOf(v)) }; ++ consume(3 + len); ++ } ++ } ++ break; ++ case 0x15: // unused/none ++ v = { type: 'unused' }; ++ consume(1); ++ break; ++ case 0x16: // undefined ++ v = { type: 'undefined' }; ++ gotValue = true; // indicate 'v' is actually set ++ consume(1); ++ break; ++ case 0x17: // null ++ v = null; ++ gotValue = true; // indicate 'v' is actually set ++ consume(1); ++ break; ++ case 0x18: // true ++ v = true; ++ consume(1); ++ break; ++ case 0x19: // false ++ v = false; ++ consume(1); ++ break; ++ case 0x1a: // number (IEEE double), big endian ++ if (avail >= 9) { ++ tmp = new Uint8Array(8); ++ tmp.set(buf.subarray(1, 9)); ++ v = { type: 'number', data: Duktape.enc('hex', plainOf(tmp)) }; ++ if (readableNumberValue) { ++ // The value key should not be used programmatically, ++ // it is just there to make the dumps more readable. ++ v.value = new DataView(tmp.buffer).getFloat64(0, false); ++ } ++ consume(9); ++ } ++ break; ++ case 0x1b: // object ++ if (avail >= 3) { ++ len = buf[2]; ++ if (avail >= 3 + len) { ++ v = new Uint8Array(len); ++ v.set(buf.subarray(3, 3 + len)); ++ v = { type: 'object', 'class': buf[1], pointer: Duktape.enc('hex', plainOf(v)) }; ++ consume(3 + len); ++ } ++ } ++ break; ++ case 0x1c: // pointer ++ if (avail >= 2) { ++ len = buf[1]; ++ if (avail >= 2 + len) { ++ v = new Uint8Array(len); ++ v.set(buf.subarray(2, 2 + len)); ++ v = { type: 'pointer', pointer: Duktape.enc('hex', plainOf(v)) }; ++ consume(2 + len); ++ } ++ } ++ break; ++ case 0x1d: // lightfunc ++ if (avail >= 4) { ++ len = buf[3]; ++ if (avail >= 4 + len) { ++ v = new Uint8Array(len); ++ v.set(buf.subarray(4, 4 + len)); ++ v = { type: 'lightfunc', flags: dv.getUint16(1, false), pointer: Duktape.enc('hex', plainOf(v)) }; ++ consume(4 + len); ++ } ++ } ++ break; ++ case 0x1e: // heapptr ++ if (avail >= 2) { ++ len = buf[1]; ++ if (avail >= 2 + len) { ++ v = new Uint8Array(len); ++ v.set(buf.subarray(2, 2 + len)); ++ v = { type: 'heapptr', pointer: Duktape.enc('hex', plainOf(v)) }; ++ consume(2 + len); ++ } ++ } ++ break; ++ default: ++ throw new Error('failed parse initial byte: ' + buf[0]); ++ } ++ } ++ ++ if (typeof v !== 'undefined' || gotValue) { ++ return { dvalue: v }; ++ } ++}; ++ ++/* ++ * Main ++ */ ++ ++function main() { ++ var argv = typeof uv.argv === 'function' ? uv.argv() : []; ++ var i; ++ for (i = 2; i < argv.length; i++) { // skip dukluv and script name ++ if (argv[i] == '--help') { ++ print('Usage: dukluv ' + argv[1] + ' [option]+'); ++ print(''); ++ print(' --server-host HOST JSON proxy server listen address'); ++ print(' --server-port PORT JSON proxy server listen port'); ++ print(' --target-host HOST Debug target address'); ++ print(' --target-port PORT Debug target port'); ++ print(' --metadata FILE Proxy metadata file (usually named duk_debug_meta.json)'); ++ print(' --log-level LEVEL Set log level, default is 2; 0=trace, 1=debug, 2=info, 3=warn, etc'); ++ print(' --single Run a single proxy connection and exit (default: persist for multiple connections)'); ++ print(' --readable-numbers Add a non-programmatic "value" key for IEEE doubles help readability'); ++ print(' --lenient Ignore (with warning) invalid JSON without dropping connection'); ++ print(' --jx-parse Parse JSON proxy input with JX, useful when testing manually'); ++ print(''); ++ return; // don't register any sockets/timers etc to exit ++ } else if (argv[i] == '--single') { ++ singleConnection = true; ++ continue; ++ } else if (argv[i] == '--readable-numbers') { ++ readableNumberValue = true; ++ continue; ++ } else if (argv[i] == '--lenient') { ++ lenientJsonParse = true; ++ continue; ++ } else if (argv[i] == '--jx-parse') { ++ jxParse = true; ++ continue; ++ } ++ if (i >= argv.length - 1) { ++ throw new Error('missing option value for ' + argv[i]); ++ } ++ if (argv[i] == '--server-host') { ++ serverHost = argv[i + 1]; ++ i++; ++ } else if (argv[i] == '--server-port') { ++ serverPort = Math.floor(+argv[i + 1]); ++ i++; ++ } else if (argv[i] == '--target-host') { ++ targetHost = argv[i + 1]; ++ i++; ++ } else if (argv[i] == '--target-port') { ++ targetPort = Math.floor(+argv[i + 1]); ++ i++; ++ } else if (argv[i] == '--metadata') { ++ metadataFile = argv[i + 1]; ++ i++; ++ } else if (argv[i] == '--log-level') { ++ log.l = Math.floor(+argv[i + 1]); ++ i++; ++ } else { ++ throw new Error('invalid option ' + argv[i]); ++ } ++ } ++ ++ function runServer() { ++ var serverSocket = new JsonProxyServer(serverHost, serverPort); ++ var connMode = singleConnection ? 'single connection mode' : 'persistent connection mode'; ++ log.info('Listening for incoming JSON debug connection on ' + serverHost + ':' + serverPort + ++ ', target is ' + targetHost + ':' + targetPort + ', ' + connMode); ++ } ++ ++ if (metadataFile) { ++ log.info('Read proxy metadata from', metadataFile); ++ readFully(metadataFile, function (data, err) { ++ if (err) { ++ log.error('Failed to load metadata:', err); ++ throw err; ++ } ++ try { ++ metadata = JSON.parse(bufferToString(data)); ++ } catch (e) { ++ log.error('Failed to parse JSON metadata from ' + metadataFile + ': ' + e); ++ throw e; ++ } ++ runServer(); ++ }); ++ } else { ++ runServer(); ++ } ++} ++ ++main(); +diff --git a/dist/source/debugger/duk_debugcommands.yaml b/dist/source/debugger/duk_debugcommands.yaml +new file mode 100644 +index 0000000..f253ba6 +--- /dev/null ++++ b/dist/source/debugger/duk_debugcommands.yaml +@@ -0,0 +1,52 @@ ++# Debug request/notify command names provided by the debug client. ++# These are concretely notify names now. ++client_commands: ++ - Reserved_0 ++ - Status ++ - Reserved_2 # Duktape 1.x: print notify ++ - Reserved_3 # Duktape 1.x: alert notify ++ - Reserved_4 # Duktape 1.x: log notify ++ - Throw ++ - Detaching ++ - AppNotify ++ ++# Debug request/notify command names provided by the debug target (Duktape). ++target_commands: ++ - Reserved_0 ++ - Reserved_1 ++ - Reserved_2 ++ - Reserved_3 ++ - Reserved_4 ++ - Reserved_5 ++ - 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 ++ - AppRequest ++ - GetHeapObjInfo ++ - GetObjPropDesc ++ - GetObjPropDescRange +diff --git a/dist/source/debugger/duk_debugerrors.yaml b/dist/source/debugger/duk_debugerrors.yaml +new file mode 100644 +index 0000000..500265f +--- /dev/null ++++ b/dist/source/debugger/duk_debugerrors.yaml +@@ -0,0 +1,6 @@ ++error_codes: ++ - Unknown ++ - UnsupportedCommand ++ - TooMany ++ - NotFound ++ - ApplicationError +diff --git a/dist/source/debugger/duk_opcodes.yaml b/dist/source/debugger/duk_opcodes.yaml +new file mode 100644 +index 0000000..d61bdba +--- /dev/null ++++ b/dist/source/debugger/duk_opcodes.yaml +@@ -0,0 +1,1193 @@ ++# 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: JUMP ++ args: ++ - ABC_JUMP ++ - name: LDCONST ++ args: ++ - A_R ++ - BC_C ++ - name: LDINT ++ args: ++ - A_R ++ - BC_LDINT ++ - name: LDINTX ++ args: ++ - A_R ++ - BC_LDINTX ++ - 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: GETVAR ++ args: ++ - A_R ++ - BC_C ++ - name: BNOT ++ args: ++ - A_R ++ - BC_R ++ - name: LNOT ++ args: ++ - A_R ++ - BC_R ++ - name: UNM ++ args: ++ - A_R ++ - BC_R ++ - name: UNP ++ args: ++ - A_R ++ - BC_R ++ - name: EQ_RR ++ args: ++ - A_R ++ - B_R ++ - C_R ++ - name: EQ_CR ++ args: ++ - A_R ++ - B_C ++ - C_R ++ - name: EQ_RC ++ args: ++ - A_R ++ - B_R ++ - C_C ++ - name: EQ_CC ++ args: ++ - A_R ++ - B_C ++ - C_C ++ - name: NEQ_RR ++ args: ++ - A_R ++ - B_R ++ - C_R ++ - name: NEQ_CR ++ args: ++ - A_R ++ - B_C ++ - C_R ++ - name: NEQ_RC ++ args: ++ - A_R ++ - B_R ++ - C_C ++ - name: NEQ_CC ++ args: ++ - A_R ++ - B_C ++ - C_C ++ - name: SEQ_RR ++ args: ++ - A_R ++ - B_R ++ - C_R ++ - name: SEQ_CR ++ args: ++ - A_R ++ - B_C ++ - C_R ++ - name: SEQ_RC ++ args: ++ - A_R ++ - B_R ++ - C_C ++ - name: SEQ_CC ++ args: ++ - A_R ++ - B_C ++ - C_C ++ - name: SNEQ_RR ++ args: ++ - A_R ++ - B_R ++ - C_R ++ - name: SNEQ_CR ++ args: ++ - A_R ++ - B_C ++ - C_R ++ - name: SNEQ_RC ++ args: ++ - A_R ++ - B_R ++ - C_C ++ - name: SNEQ_CC ++ args: ++ - A_R ++ - B_C ++ - C_C ++ - name: GT_RR ++ args: ++ - A_R ++ - B_R ++ - C_R ++ - name: GT_CR ++ args: ++ - A_R ++ - B_C ++ - C_R ++ - name: GT_RC ++ args: ++ - A_R ++ - B_R ++ - C_C ++ - name: GT_CC ++ args: ++ - A_R ++ - B_C ++ - C_C ++ - name: GE_RR ++ args: ++ - A_R ++ - B_R ++ - C_R ++ - name: GE_CR ++ args: ++ - A_R ++ - B_C ++ - C_R ++ - name: GE_RC ++ args: ++ - A_R ++ - B_R ++ - C_C ++ - name: GE_CC ++ args: ++ - A_R ++ - B_C ++ - C_C ++ - name: LT_RR ++ args: ++ - A_R ++ - B_R ++ - C_R ++ - name: LT_CR ++ args: ++ - A_R ++ - B_C ++ - C_R ++ - name: LT_RC ++ args: ++ - A_R ++ - B_R ++ - C_C ++ - name: LT_CC ++ args: ++ - A_R ++ - B_C ++ - C_C ++ - name: LE_RR ++ args: ++ - A_R ++ - B_R ++ - C_R ++ - name: LE_CR ++ args: ++ - A_R ++ - B_C ++ - C_R ++ - name: LE_RC ++ args: ++ - A_R ++ - B_R ++ - C_C ++ - name: LE_CC ++ args: ++ - A_R ++ - B_C ++ - C_C ++ - name: IFTRUE_R ++ args: ++ - BC_R ++ - name: IFTRUE_C ++ args: ++ - BC_C ++ - name: IFFALSE_R ++ args: ++ - BC_R ++ - name: IFFALSE_C ++ args: ++ - BC_C ++ - name: ADD_RR ++ args: ++ - A_R ++ - B_R ++ - C_R ++ - name: ADD_CR ++ args: ++ - A_R ++ - B_C ++ - C_R ++ - name: ADD_RC ++ args: ++ - A_R ++ - B_R ++ - C_C ++ - name: ADD_CC ++ args: ++ - A_R ++ - B_C ++ - C_C ++ - name: SUB_RR ++ args: ++ - A_R ++ - B_R ++ - C_R ++ - name: SUB_CR ++ args: ++ - A_R ++ - B_C ++ - C_R ++ - name: SUB_RC ++ args: ++ - A_R ++ - B_R ++ - C_C ++ - name: SUB_CC ++ args: ++ - A_R ++ - B_C ++ - C_C ++ - name: MUL_RR ++ args: ++ - A_R ++ - B_R ++ - C_R ++ - name: MUL_CR ++ args: ++ - A_R ++ - B_C ++ - C_R ++ - name: MUL_RC ++ args: ++ - A_R ++ - B_R ++ - C_C ++ - name: MUL_CC ++ args: ++ - A_R ++ - B_C ++ - C_C ++ - name: DIV_RR ++ args: ++ - A_R ++ - B_R ++ - C_R ++ - name: DIV_CR ++ args: ++ - A_R ++ - B_C ++ - C_R ++ - name: DIV_RC ++ args: ++ - A_R ++ - B_R ++ - C_C ++ - name: DIV_CC ++ args: ++ - A_R ++ - B_C ++ - C_C ++ - name: MOD_RR ++ args: ++ - A_R ++ - B_R ++ - C_R ++ - name: MOD_CR ++ args: ++ - A_R ++ - B_C ++ - C_R ++ - name: MOD_RC ++ args: ++ - A_R ++ - B_R ++ - C_C ++ - name: MOD_CC ++ args: ++ - A_R ++ - B_C ++ - C_C ++ - name: EXP_RR ++ args: ++ - A_R ++ - B_R ++ - C_R ++ - name: EXP_CR ++ args: ++ - A_R ++ - B_C ++ - C_R ++ - name: EXP_RC ++ args: ++ - A_R ++ - B_R ++ - C_C ++ - name: EXP_CC ++ args: ++ - A_R ++ - B_C ++ - C_C ++ - name: BAND_RR ++ args: ++ - A_R ++ - B_R ++ - C_R ++ - name: BAND_CR ++ args: ++ - A_R ++ - B_C ++ - C_R ++ - name: BAND_RC ++ args: ++ - A_R ++ - B_R ++ - C_C ++ - name: BAND_CC ++ args: ++ - A_R ++ - B_C ++ - C_C ++ - name: BOR_RR ++ args: ++ - A_R ++ - B_R ++ - C_R ++ - name: BOR_CR ++ args: ++ - A_R ++ - B_C ++ - C_R ++ - name: BOR_RC ++ args: ++ - A_R ++ - B_R ++ - C_C ++ - name: BOR_CC ++ args: ++ - A_R ++ - B_C ++ - C_C ++ - name: BXOR_RR ++ args: ++ - A_R ++ - B_R ++ - C_R ++ - name: BXOR_CR ++ args: ++ - A_R ++ - B_C ++ - C_R ++ - name: BXOR_RC ++ args: ++ - A_R ++ - B_R ++ - C_C ++ - name: BXOR_CC ++ args: ++ - A_R ++ - B_C ++ - C_C ++ - name: BASL_RR ++ args: ++ - A_R ++ - B_R ++ - C_R ++ - name: BASL_CR ++ args: ++ - A_R ++ - B_C ++ - C_R ++ - name: BASL_RC ++ args: ++ - A_R ++ - B_R ++ - C_C ++ - name: BASL_CC ++ args: ++ - A_R ++ - B_C ++ - C_C ++ - name: BLSR_RR ++ args: ++ - A_R ++ - B_R ++ - C_R ++ - name: BLSR_CR ++ args: ++ - A_R ++ - B_C ++ - C_R ++ - name: BLSR_RC ++ args: ++ - A_R ++ - B_R ++ - C_C ++ - name: BLSR_CC ++ args: ++ - A_R ++ - B_C ++ - C_C ++ - name: BASR_RR ++ args: ++ - A_R ++ - B_R ++ - C_R ++ - name: BASR_CR ++ args: ++ - A_R ++ - B_C ++ - C_R ++ - name: BASR_RC ++ args: ++ - A_R ++ - B_R ++ - C_C ++ - name: BASR_CC ++ args: ++ - A_R ++ - B_C ++ - C_C ++ - name: INSTOF_RR ++ args: ++ - A_R ++ - B_R ++ - C_R ++ - name: INSTOF_CR ++ args: ++ - A_R ++ - B_C ++ - C_R ++ - name: INSTOF_RC ++ args: ++ - A_R ++ - B_R ++ - C_C ++ - name: INSTOF_CC ++ args: ++ - A_R ++ - B_C ++ - C_C ++ - name: IN_RR ++ args: ++ - A_R ++ - B_R ++ - C_R ++ - name: IN_CR ++ args: ++ - A_R ++ - B_C ++ - C_R ++ - name: IN_RC ++ args: ++ - A_R ++ - B_R ++ - C_C ++ - name: IN_CC ++ args: ++ - A_R ++ - B_C ++ - C_C ++ - name: GETPROP_RR ++ args: ++ - A_R ++ - B_R ++ - C_R ++ - name: GETPROP_CR ++ args: ++ - A_R ++ - B_C ++ - C_R ++ - name: GETPROP_RC ++ args: ++ - A_R ++ - B_R ++ - C_C ++ - name: GETPROP_CC ++ args: ++ - A_R ++ - B_C ++ - C_C ++ - name: PUTPROP_RR ++ args: ++ - A_R ++ - B_R ++ - C_R ++ - name: PUTPROP_CR ++ args: ++ - A_R ++ - B_C ++ - C_R ++ - name: PUTPROP_RC ++ args: ++ - A_R ++ - B_R ++ - C_C ++ - name: PUTPROP_CC ++ args: ++ - A_R ++ - B_C ++ - C_C ++ - name: DELPROP_RR ++ args: ++ - A_R ++ - B_R ++ - C_R ++ - name: DELPROP_CR ++ args: ++ - A_R ++ - B_C ++ - C_R ++ - name: DELPROP_RC ++ args: ++ - A_R ++ - B_R ++ - C_C ++ - name: DELPROP_CC ++ args: ++ - A_R ++ - B_C ++ - C_C ++ - 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_RR ++ args: ++ - A_R ++ - B_R ++ - C_R ++ - name: PREINCP_CR ++ args: ++ - A_R ++ - B_C ++ - C_R ++ - name: PREINCP_RC ++ args: ++ - A_R ++ - B_R ++ - C_C ++ - name: PREINCP_CC ++ args: ++ - A_R ++ - B_C ++ - C_C ++ - name: PREDECP_RR ++ args: ++ - A_R ++ - B_R ++ - C_R ++ - name: PREDECP_CR ++ args: ++ - A_R ++ - B_C ++ - C_R ++ - name: PREDECP_RC ++ args: ++ - A_R ++ - B_R ++ - C_C ++ - name: PREDECP_CC ++ args: ++ - A_R ++ - B_C ++ - C_C ++ - name: POSTINCP_RR ++ args: ++ - A_R ++ - B_R ++ - C_R ++ - name: POSTINCP_CR ++ args: ++ - A_R ++ - B_C ++ - C_R ++ - name: POSTINCP_RC ++ args: ++ - A_R ++ - B_R ++ - C_C ++ - name: POSTINCP_CC ++ args: ++ - A_R ++ - B_C ++ - C_C ++ - name: POSTDECP_RR ++ args: ++ - A_R ++ - B_R ++ - C_R ++ - name: POSTDECP_CR ++ args: ++ - A_R ++ - B_C ++ - C_R ++ - name: POSTDECP_RC ++ args: ++ - A_R ++ - B_R ++ - C_C ++ - name: POSTDECP_CC ++ args: ++ - A_R ++ - B_C ++ - C_C ++ - name: DECLVAR_RR ++ args: ++ - A_H ++ - B_R ++ - C_R ++ flags: ++ - mask: 0x100 ++ name: writable ++ - mask: 0x200 ++ name: enumerable ++ - mask: 0x400 ++ name: configurable ++ - mask: 0x800 ++ name: accessor ++ - mask: 0x1000 ++ name: func_decl ++ - name: DECLVAR_CR ++ args: ++ - A_H ++ - B_C ++ - C_R ++ flags: ++ - mask: 0x100 ++ name: writable ++ - mask: 0x200 ++ name: enumerable ++ - mask: 0x400 ++ name: configurable ++ - mask: 0x800 ++ name: accessor ++ - mask: 0x1000 ++ name: func_decl ++ - name: DECLVAR_RC ++ args: ++ - A_H ++ - B_R ++ - C_C ++ flags: ++ - mask: 0x100 ++ name: writable ++ - mask: 0x200 ++ name: enumerable ++ - mask: 0x400 ++ name: configurable ++ - mask: 0x800 ++ name: accessor ++ - mask: 0x1000 ++ name: func_decl ++ - name: DECLVAR_CC ++ args: ++ - A_H ++ - B_C ++ - C_C ++ flags: ++ - mask: 0x100 ++ name: writable ++ - mask: 0x200 ++ name: enumerable ++ - mask: 0x400 ++ name: configurable ++ - mask: 0x800 ++ name: accessor ++ - mask: 0x1000 ++ name: func_decl ++ - name: REGEXP_RR ++ args: ++ - A_R ++ - B_R ++ - C_R ++ - name: REGEXP_CR ++ args: ++ - A_R ++ - B_C ++ - C_R ++ - name: REGEXP_RC ++ args: ++ - A_R ++ - B_R ++ - C_C ++ - name: REGEXP_CC ++ args: ++ - A_R ++ - B_C ++ - C_C ++ - name: CLOSURE ++ args: ++ - A_R ++ - BC_I ++ - name: TYPEOF ++ args: ++ - A_R ++ - BC_R ++ - name: TYPEOFID ++ args: ++ - A_R ++ - BC_C ++ - name: PUTVAR ++ args: ++ - A_R ++ - BC_C ++ - name: DELVAR ++ args: ++ - A_R ++ - BC_C ++ - name: RETREG ++ args: ++ - BC_R ++ - name: RETUNDEF ++ - name: RETCONST ++ args: ++ - BC_C ++ - name: RETCONSTN ++ args: ++ - BC_C ++ - name: LABEL ++ args: ++ - BC_I ++ - name: ENDLABEL ++ args: ++ - BC_I ++ - name: BREAK ++ args: ++ - BC_I ++ - name: CONTINUE ++ args: ++ - BC_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: 0x100 ++ name: have_catch ++ - mask: 0x200 ++ name: have_finally ++ - mask: 0x400 ++ name: catch_binding ++ - mask: 0x800 ++ name: with_binding ++ - name: ENDTRY ++ - name: ENDCATCH ++ - name: ENDFIN ++ args: ++ - ABC_R ++ - name: THROW ++ args: ++ - BC_R ++ - name: INVLHS ++ - name: CSREG ++ args: ++ - A_R ++ - BC_R ++ - name: CSVAR_RR ++ args: ++ - A_R ++ - B_R ++ - name: CSVAR_CR ++ args: ++ - A_R ++ - B_C ++ - name: CSVAR_RC ++ args: ++ - A_R ++ - B_R ++ - name: CSVAR_CC ++ args: ++ - A_R ++ - B_C ++ - name: CALL0 ++ args: ++ - A_I ++ - BC_R ++ flags: ++ - mask: 0x01 ++ name: tailcall ++ - mask: 0x02 ++ name: construct ++ - mask: 0x04 ++ name: called_as_eval ++ - mask: 0x08 ++ name: indirect ++ - name: CALL1 ++ args: ++ - A_I ++ - BC_R ++ flags: ++ - mask: 0x01 ++ name: tailcall ++ - mask: 0x02 ++ name: construct ++ - mask: 0x04 ++ name: called_as_eval ++ - mask: 0x08 ++ name: indirect ++ - name: CALL2 ++ args: ++ - A_I ++ - BC_R ++ flags: ++ - mask: 0x01 ++ name: tailcall ++ - mask: 0x02 ++ name: construct ++ - mask: 0x04 ++ name: called_as_eval ++ - mask: 0x08 ++ name: indirect ++ - name: CALL3 ++ args: ++ - A_I ++ - BC_R ++ flags: ++ - mask: 0x01 ++ name: tailcall ++ - mask: 0x02 ++ name: construct ++ - mask: 0x04 ++ name: called_as_eval ++ - mask: 0x08 ++ name: indirect ++ - name: CALL4 ++ args: ++ - A_I ++ - BC_R ++ flags: ++ - mask: 0x01 ++ name: tailcall ++ - mask: 0x02 ++ name: construct ++ - mask: 0x04 ++ name: called_as_eval ++ - mask: 0x08 ++ name: indirect ++ - name: CALL5 ++ args: ++ - A_I ++ - BC_R ++ flags: ++ - mask: 0x01 ++ name: tailcall ++ - mask: 0x02 ++ name: construct ++ - mask: 0x04 ++ name: called_as_eval ++ - mask: 0x08 ++ name: indirect ++ - name: CALL6 ++ args: ++ - A_I ++ - BC_R ++ flags: ++ - mask: 0x01 ++ name: tailcall ++ - mask: 0x02 ++ name: construct ++ - mask: 0x04 ++ name: called_as_eval ++ - mask: 0x08 ++ name: indirect ++ - name: CALL7 ++ args: ++ - A_I ++ - BC_R ++ flags: ++ - mask: 0x01 ++ name: tailcall ++ - mask: 0x02 ++ name: construct ++ - mask: 0x04 ++ name: called_as_eval ++ - mask: 0x08 ++ name: indirect ++ - name: CALL8 ++ args: ++ - A_I ++ - BC_R ++ flags: ++ - mask: 0x01 ++ name: tailcall ++ - mask: 0x02 ++ name: construct ++ - mask: 0x04 ++ name: called_as_eval ++ - mask: 0x08 ++ name: indirect ++ - name: CALL9 ++ args: ++ - A_I ++ - BC_R ++ flags: ++ - mask: 0x01 ++ name: tailcall ++ - mask: 0x02 ++ name: construct ++ - mask: 0x04 ++ name: called_as_eval ++ - mask: 0x08 ++ name: indirect ++ - name: CALL10 ++ args: ++ - A_I ++ - BC_R ++ flags: ++ - mask: 0x01 ++ name: tailcall ++ - mask: 0x02 ++ name: construct ++ - mask: 0x04 ++ name: called_as_eval ++ - mask: 0x08 ++ name: indirect ++ - name: CALL11 ++ args: ++ - A_I ++ - BC_R ++ flags: ++ - mask: 0x01 ++ name: tailcall ++ - mask: 0x02 ++ name: construct ++ - mask: 0x04 ++ name: called_as_eval ++ - mask: 0x08 ++ name: indirect ++ - name: CALL12 ++ args: ++ - A_I ++ - BC_R ++ flags: ++ - mask: 0x01 ++ name: tailcall ++ - mask: 0x02 ++ name: construct ++ - mask: 0x04 ++ name: called_as_eval ++ - mask: 0x08 ++ name: indirect ++ - name: CALL13 ++ args: ++ - A_I ++ - BC_R ++ flags: ++ - mask: 0x01 ++ name: tailcall ++ - mask: 0x02 ++ name: construct ++ - mask: 0x04 ++ name: called_as_eval ++ - mask: 0x08 ++ name: indirect ++ - name: CALL14 ++ args: ++ - A_I ++ - BC_R ++ flags: ++ - mask: 0x01 ++ name: tailcall ++ - mask: 0x02 ++ name: construct ++ - mask: 0x04 ++ name: called_as_eval ++ - mask: 0x08 ++ name: indirect ++ - name: CALL15 ++ args: ++ - A_I ++ - BC_R ++ flags: ++ - mask: 0x01 ++ name: tailcall ++ - mask: 0x02 ++ name: construct ++ - mask: 0x04 ++ name: called_as_eval ++ - mask: 0x08 ++ name: indirect ++ - name: NEWOBJ ++ args: ++ - A_I # property count init size ++ - BC_R ++ - name: NEWARR ++ args: ++ - A_I # array item count init size ++ - BC_R ++ - name: MPUTOBJ ++ args: ++ - A_R ++ - B_R ++ - C_I ++ - name: MPUTOBJI ++ args: ++ - A_R ++ - B_RI ++ - C_I ++ - name: INITSET ++ args: ++ - A_R ++ - BC_R ++ - name: INITGET ++ args: ++ - A_R ++ - BC_R ++ - name: MPUTARR ++ args: ++ - A_R ++ - B_R ++ - C_I ++ - name: MPUTARRI ++ args: ++ - A_R ++ - B_RI ++ - C_I ++ - name: SETALEN ++ args: ++ - B_R ++ - C_R ++ - name: INITENUM ++ args: ++ - B_R ++ - C_R ++ - name: NEXTENUM ++ args: ++ - B_R ++ - C_R ++ - name: NEWTARGET ++ args: ++ - BC_R ++ - name: DEBUGGER ++ - name: NOP ++ args: ++ - ABC_I ++ - name: INVALID ++ args: ++ - ABC_I ++ - name: UNUSED207 ++ - name: GETPROPC_RR ++ args: ++ - A_R ++ - B_R ++ - C_R ++ - name: GETPROPC_CR ++ args: ++ - A_R ++ - B_C ++ - C_R ++ - name: GETPROPC_RC ++ args: ++ - A_R ++ - B_R ++ - C_C ++ - name: GETPROPC_CC ++ args: ++ - A_R ++ - B_C ++ - C_C ++ - name: UNUSED212 ++ - name: UNUSED213 ++ - name: UNUSED214 ++ - name: UNUSED215 ++ - name: UNUSED216 ++ - name: UNUSED217 ++ - name: UNUSED218 ++ - name: UNUSED219 ++ - name: UNUSED220 ++ - name: UNUSED221 ++ - name: UNUSED222 ++ - name: UNUSED223 ++ - name: UNUSED224 ++ - name: UNUSED225 ++ - name: UNUSED226 ++ - name: UNUSED227 ++ - name: UNUSED228 ++ - name: UNUSED229 ++ - name: UNUSED230 ++ - name: UNUSED231 ++ - name: UNUSED232 ++ - name: UNUSED233 ++ - name: UNUSED234 ++ - name: UNUSED235 ++ - name: UNUSED236 ++ - name: UNUSED237 ++ - name: UNUSED238 ++ - name: UNUSED239 ++ - name: UNUSED240 ++ - name: UNUSED241 ++ - name: UNUSED242 ++ - name: UNUSED243 ++ - name: UNUSED244 ++ - name: UNUSED245 ++ - name: UNUSED246 ++ - name: UNUSED247 ++ - name: UNUSED248 ++ - name: UNUSED249 ++ - name: UNUSED250 ++ - name: UNUSED251 ++ - name: UNUSED252 ++ - name: UNUSED253 ++ - name: UNUSED254 ++ - name: UNUSED255 +diff --git a/dist/source/debugger/package.json b/dist/source/debugger/package.json +new file mode 100644 +index 0000000..40fad19 +--- /dev/null ++++ b/dist/source/debugger/package.json +@@ -0,0 +1,27 @@ ++{ ++ "name": "duk-debug", ++ "version": "0.1.0", ++ "description": "Duktape debugger", ++ "author": { ++ "name": "Sami Vaarala", ++ "email": "sami.vaarala@iki.fi" ++ }, ++ "dependencies": { ++ "bluebird": "~2.6.4", ++ "body-parser": "~1.9.3", ++ "byline": "~4.2.1", ++ "events": "~1.0.2", ++ "express": "~4.10.1", ++ "http": "0.0.0", ++ "minimist": "~1.2.5", ++ "readline": "0.0.5", ++ "recursive-readdir-sync": "^1.0.6", ++ "socket.io": "~1.2.1", ++ "sprintf": "~0.1.5", ++ "stream": "0.0.2", ++ "utf8": "~2.0.0", ++ "util": "~0.10.3", ++ "yamljs": "~0.2.1" ++ }, ++ "main": "duk_debug.js" ++} +diff --git a/dist/source/debugger/static/index.html b/dist/source/debugger/static/index.html +new file mode 100644 +index 0000000..f77af7a +--- /dev/null ++++ b/dist/source/debugger/static/index.html +@@ -0,0 +1,96 @@ ++ ++ ++ ++ ++ ++ ++ ++Duktape debugger ++ ++ ++ ++
++((o) Duktape debugger ++
++ ++
++ ++ ++ ++
++

++// No source loaded
++
++
++
++ ++
++
++
?
++
?
?
++
++
++
(output from script, print() and alert() calls)
++
++
++ ++
++
++
(callstack)
++
++
++
(locals)
++
++
++
(breakpoints)
++
++
++ watch (eval on pause) ++
++ ++
++
++
++ ++
++ ++ ++ ++
++

Duktape debugger is a web UI for debugging ECMAScript on a target device.

++

This web UI talks to a NodeJS debug server using socket.io. ++The debug server talks to the target device using the Duktape debug protocol ++(see debugger.rst).

++
++ ++
++

++
++ ++ ++ ++ ++ ++ ++ +diff --git a/dist/source/debugger/static/style.css b/dist/source/debugger/static/style.css +new file mode 100644 +index 0000000..1b6dd86 +--- /dev/null ++++ b/dist/source/debugger/static/style.css +@@ -0,0 +1,517 @@ ++// 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; ++ text-align: center; ++ width: 95%; ++ min-width: 6em; ++ 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: 10pt monospace; ++ color: #000000; ++} ++#bytecode-dialog div.highlight { ++ background: #888888; ++ color: #ffffff; ++} ++ ++#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; ++} +diff --git a/dist/source/debugger/static/webui.js b/dist/source/debugger/static/webui.js +new file mode 100644 +index 0000000..cb683f1 +--- /dev/null ++++ b/dist/source/debugger/static/webui.js +@@ -0,0 +1,808 @@ ++/* ++ * 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 ++var bytecodeDialogOpen = false; // bytecode dialog active ++var bytecodeIdxHighlight = null; // index of currently highlighted line (or null) ++var bytecodeIdxInstr = 0; // index to first line of bytecode instructions ++ ++// 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'); ++ } ++ } ++ ++ // Bytecode dialog highlight ++ if (loadedFileExecuting && bytecodeDialogOpen && bytecodeIdxHighlight !== bytecodeIdxInstr + currPc) { ++ if (typeof bytecodeIdxHighlight === 'number') { ++ $('#bytecode-preformatted div')[bytecodeIdxHighlight].classList.remove('highlight'); ++ } ++ bytecodeIdxHighlight = bytecodeIdxInstr + currPc; ++ $('#bytecode-preformatted div')[bytecodeIdxHighlight].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) { ++ // Not 100% reliable if callstack has several functions of the same name ++ if (bytecodeDialogOpen && (currFileName != msg.fileName || currFuncName != msg.funcName)) { ++ socket.emit('get-bytecode', {}); ++ } ++ ++ 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($('
').text(ent.message)); ++ } else if (ent.type === 'alert') { ++ elem.append($('
').text(ent.message)); ++ } else if (ent.type === 'log') { ++ elem.append($('
').text(ent.message)); ++ } else if (ent.type === 'debugger-info') { ++ elem.append($('
').text(ent.message)); ++ } else if (ent.type === 'debugger-debug') { ++ elem.append($('
').text(ent.message)); ++ } else { ++ elem.append($('
').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 = $('').text(e.fileName + ':' + e.lineNumber + ' (pc ' + e.pc + ')'); // float ++ s1.on('click', function () { ++ activeFileName = e.fileName; ++ activeLine = e.lineNumber || 1; ++ activeHighlight = activeLine; ++ requestSourceRefetch(); ++ }); ++ s2 = $('').text(e.funcName + '()'); ++ 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 = $('').text(e.value); // float ++ s2 = $('').text(e.key); ++ 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 = $('
'); ++ sub = $('').text('Delete all breakpoints'); ++ sub.on('click', function () { ++ socket.emit('delete-all-breakpoints'); ++ }); ++ div.append(sub); ++ sub = $('').val('file.js'); ++ div.append(sub); ++ sub = $('').text(':'); ++ div.append(sub); ++ sub = $('').val('123'); ++ div.append(sub); ++ sub = $('').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 = $('').text('or dblclick source'); ++ div.append(sub); ++ elem.append(div); ++ ++ // Active breakpoints follow ++ msg.breakpoints.forEach(function (bp) { ++ var div; ++ var sub; ++ ++ div = $('
'); ++ sub = $('').text('Delete'); ++ sub.on('click', function () { ++ socket.emit('delete-breakpoint', { ++ fileName: bp.fileName, ++ lineNumber: bp.lineNumber ++ }); ++ }); ++ div.append(sub); ++ sub = $('').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) { ++ var elem, div; ++ var div; ++ ++ elem = $('#bytecode-preformatted'); ++ elem.empty(); ++ ++ msg.preformatted.split('\n').forEach(function (line, idx) { ++ div = $('
'); ++ div.text(line); ++ elem.append(div); ++ }); ++ ++ bytecodeIdxHighlight = null; ++ bytecodeIdxInstr = msg.idxPreformattedInstructions; ++}); ++ ++$('#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 () { ++ bytecodeDialogOpen = true; ++ $('#bytecode-dialog').dialog('open'); ++ ++ elem = $('#bytecode-preformatted'); ++ elem.empty().text('Loading bytecode...'); ++ ++ 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.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 = $('').attr({ 'value': '__none__' }).text('No source file selected'); ++ elem.append(opt); ++ data.forEach(function (ent) { ++ var opt = $('').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: 700, ++ height: 600, ++ close: function () { ++ bytecodeDialogOpen = false; ++ bytecodeIdxHighlight = null; ++ bytecodeIdxInstr = 0; ++ } ++ }); ++ ++ // 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 ++ }); ++ ++ // Function keys ++ $('body').keydown(function(e){ ++ //alert("keydown: "+e.which); ++ switch (e.which) { ++ case 116: // F5: step into ++ socket.emit('stepinto', {}); ++ e.preventDefault(); ++ return; ++ case 117: // F6: step over ++ socket.emit('stepover', {}); ++ e.preventDefault(); ++ return; ++ case 118: // F7: step out (= step return) ++ socket.emit('stepout', {}); ++ e.preventDefault(); ++ return; ++ case 119: // F8: resume ++ socket.emit('resume', {}); ++ e.preventDefault(); ++ return; ++ } ++ }); ++ ++ forceButtonUpdate = true; ++ doUiUpdate(); ++}); +diff --git a/dist/source/duk_dist_meta.json b/dist/source/duk_dist_meta.json +new file mode 100644 +index 0000000..b25abab +--- /dev/null ++++ b/dist/source/duk_dist_meta.json +@@ -0,0 +1,9 @@ ++{ ++ "comment": "Metadata for Duktape distributable", ++ "duk_version_string": "2.99.99", ++ "type": "duk_dist_meta", ++ "duk_version": 29999, ++ "git_branch": "master", ++ "git_commit": "65f1e02e580796929e92e91ba4776d4b0131beb5", ++ "git_describe": "65f1e02" ++} +\ No newline at end of file +diff --git a/dist/source/examples/README.rst b/dist/source/examples/README.rst +new file mode 100644 +index 0000000..5b876de +--- /dev/null ++++ b/dist/source/examples/README.rst +@@ -0,0 +1,10 @@ ++================ ++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 necessarily fixed, unless the bug makes the example misleading as ++documentation. +diff --git a/dist/source/examples/alloc-hybrid/README.rst b/dist/source/examples/alloc-hybrid/README.rst +new file mode 100644 +index 0000000..ed63a13 +--- /dev/null ++++ b/dist/source/examples/alloc-hybrid/README.rst +@@ -0,0 +1,10 @@ ++===================== ++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. +diff --git a/dist/source/examples/alloc-hybrid/duk_alloc_hybrid.c b/dist/source/examples/alloc-hybrid/duk_alloc_hybrid.c +new file mode 100644 +index 0000000..3f63249 +--- /dev/null ++++ b/dist/source/examples/alloc-hybrid/duk_alloc_hybrid.c +@@ -0,0 +1,294 @@ ++/* ++ * 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 ++#include ++#include ++#include ++#include "duk_alloc_hybrid.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) ++ ++#if defined(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++) { ++#if defined(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 * (size_t) pool_sizes[i].count; ++ if (pool_sizes[i].size > max_size) { ++ max_size = pool_sizes[i].size; ++ } ++ } ++#if defined(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 * (size_t) 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) { ++#if defined(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 { ++#if defined(DUK_ALLOC_HYBRID_DEBUG) ++ printf("alloc out of pool entries: %ld -> pool size %ld\n", (long) size, (long) hdr->size); ++#endif ++ break; ++ } ++ } ++ ++#if defined(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) { ++#if defined(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; ++ } ++ } ++#if defined(DUK_ALLOC_HYBRID_DEBUG) ++ printf("NEVER HERE\n"); ++#endif ++ return NULL; ++ } else if (ptr != NULL) { ++ if (size == 0) { ++ free(ptr); ++ return NULL; ++ } else { ++#if defined(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; ++ } ++ } ++ ++#if defined(DUK_ALLOC_HYBRID_DEBUG) ++ printf("NEVER HERE\n"); ++#endif ++} +diff --git a/dist/source/examples/alloc-hybrid/duk_alloc_hybrid.h b/dist/source/examples/alloc-hybrid/duk_alloc_hybrid.h +new file mode 100644 +index 0000000..d8c09e9 +--- /dev/null ++++ b/dist/source/examples/alloc-hybrid/duk_alloc_hybrid.h +@@ -0,0 +1,11 @@ ++#if !defined(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 */ +diff --git a/dist/source/examples/alloc-logging/README.rst b/dist/source/examples/alloc-logging/README.rst +new file mode 100644 +index 0000000..97c1a32 +--- /dev/null ++++ b/dist/source/examples/alloc-logging/README.rst +@@ -0,0 +1,7 @@ ++====================== ++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. +diff --git a/dist/source/examples/alloc-logging/duk_alloc_logging.c b/dist/source/examples/alloc-logging/duk_alloc_logging.c +new file mode 100644 +index 0000000..b78fde4 +--- /dev/null ++++ b/dist/source/examples/alloc-logging/duk_alloc_logging.c +@@ -0,0 +1,139 @@ ++/* ++ * 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 ++#include ++#include ++#include ++#include "duk_alloc_logging.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) { ++ write_log("R %p %ld NULL 0\n", ptr, (long) old_size); ++ free((void *) hdr); ++ 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); ++} +diff --git a/dist/source/examples/alloc-logging/duk_alloc_logging.h b/dist/source/examples/alloc-logging/duk_alloc_logging.h +new file mode 100644 +index 0000000..a496c29 +--- /dev/null ++++ b/dist/source/examples/alloc-logging/duk_alloc_logging.h +@@ -0,0 +1,10 @@ ++#if !defined(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 */ +diff --git a/dist/source/examples/alloc-logging/log2gnuplot.py b/dist/source/examples/alloc-logging/log2gnuplot.py +new file mode 100644 +index 0000000..7a009f2 +--- /dev/null ++++ b/dist/source/examples/alloc-logging/log2gnuplot.py +@@ -0,0 +1,41 @@ ++#!/usr/bin/env python2 ++# ++# Analyze allocator logs and write total-bytes-in-use after every ++# operation to stdout. The output can be gnuplotted as: ++# ++# $ python log2gnuplot.py /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: duk-low 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() +diff --git a/dist/source/examples/alloc-torture/README.rst b/dist/source/examples/alloc-torture/README.rst +new file mode 100644 +index 0000000..f3278bb +--- /dev/null ++++ b/dist/source/examples/alloc-torture/README.rst +@@ -0,0 +1,10 @@ ++========================================== ++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. +diff --git a/dist/source/examples/alloc-torture/duk_alloc_torture.c b/dist/source/examples/alloc-torture/duk_alloc_torture.c +new file mode 100644 +index 0000000..fd1dbe3 +--- /dev/null ++++ b/dist/source/examples/alloc-torture/duk_alloc_torture.c +@@ -0,0 +1,183 @@ ++/* ++ * 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 ++#include ++#include ++#include ++#include "duk_alloc_torture.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); ++} +diff --git a/dist/source/examples/alloc-torture/duk_alloc_torture.h b/dist/source/examples/alloc-torture/duk_alloc_torture.h +new file mode 100644 +index 0000000..0b36b69 +--- /dev/null ++++ b/dist/source/examples/alloc-torture/duk_alloc_torture.h +@@ -0,0 +1,10 @@ ++#if !defined(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 */ +diff --git a/dist/source/examples/cmdline/README.rst b/dist/source/examples/cmdline/README.rst +new file mode 100644 +index 0000000..825eec9 +--- /dev/null ++++ b/dist/source/examples/cmdline/README.rst +@@ -0,0 +1,6 @@ ++==================== ++Duktape command line ++==================== ++ ++ECMAScript command line execution tool, useful for running ECMAScript code ++from a file, stdin, or interactively. Also used by automatic testing. +diff --git a/dist/source/examples/cmdline/duk_cmdline.c b/dist/source/examples/cmdline/duk_cmdline.c +new file mode 100644 +index 0000000..01a5794 +--- /dev/null ++++ b/dist/source/examples/cmdline/duk_cmdline.c +@@ -0,0 +1,1867 @@ ++/* ++ * Command line execution tool. Useful for test cases and manual testing. ++ * Also demonstrates some basic integration techniques. ++ * ++ * Optional features include: ++ * ++ * - To enable print()/alert() bindings, define DUK_CMDLINE_PRINTALERT_SUPPORT ++ * and add extras/print-alert/duk_print_alert.c to compilation. ++ * ++ * - To enable console.log() etc, define DUK_CMDLINE_CONSOLE_SUPPORT ++ * and add extras/console/duk_console.c to compilation. ++ * ++ * - To enable Duktape.Logger, define DUK_CMDLINE_LOGGING_SUPPORT ++ * and add extras/logging/duk_logging.c to compilation. ++ * ++ * - To enable Duktape 1.x module loading support (require(), ++ * Duktape.modSearch() etc), define DUK_CMDLINE_MODULE_SUPPORT and add ++ * extras/module-duktape/duk_module_duktape.c to compilation. ++ * ++ * - To enable linenoise 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_*. ++ */ ++ ++/* Helper define to enable a feature set; can also use separate defines. */ ++#if defined(DUK_CMDLINE_FANCY) ++#define DUK_CMDLINE_LINENOISE ++#define DUK_CMDLINE_LINENOISE_COMPLETION /* Enables completion and hints. */ ++#define DUK_CMDLINE_RLIMIT ++#define DUK_CMDLINE_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 ++#if defined(_MSC_VER) && (_MSC_VER < 1900) ++/* Workaround for snprintf() missing in older MSVC versions. ++ * Note that _snprintf() may not NUL terminate the string, but ++ * this difference does not matter here as a NUL terminator is ++ * always explicitly added. ++ */ ++#define snprintf _snprintf ++#endif ++#endif ++ ++#if defined(DUK_CMDLINE_PTHREAD_STACK_CHECK) ++#define _GNU_SOURCE ++#include ++#endif ++ ++#include ++#include ++#include ++#if defined(DUK_CMDLINE_SIGNAL) ++#include ++#endif ++#if defined(DUK_CMDLINE_RLIMIT) ++#include ++#endif ++#if defined(DUK_CMDLINE_LINENOISE) ++#include "linenoise.h" ++#include /* Assume C99/C++11 with linenoise. */ ++#endif ++#if defined(DUK_CMDLINE_PRINTALERT_SUPPORT) ++#include "duk_print_alert.h" ++#endif ++#if defined(DUK_CMDLINE_CONSOLE_SUPPORT) ++#include "duk_console.h" ++#endif ++#if defined(DUK_CMDLINE_LOGGING_SUPPORT) ++#include "duk_logging.h" ++#endif ++#if defined(DUK_CMDLINE_MODULE_SUPPORT) ++#include "duk_module_duktape.h" ++#endif ++#if defined(DUK_CMDLINE_FILEIO) ++#include ++#endif ++#if defined(EMSCRIPTEN) ++#include ++#endif ++#if defined(DUK_CMDLINE_ALLOC_LOGGING) ++#include "duk_alloc_logging.h" ++#endif ++#if defined(DUK_CMDLINE_ALLOC_TORTURE) ++#include "duk_alloc_torture.h" ++#endif ++#if defined(DUK_CMDLINE_ALLOC_HYBRID) ++#include "duk_alloc_hybrid.h" ++#endif ++#include "duktape.h" ++ ++#include "duk_cmdline.h" ++ ++#if defined(DUK_CMDLINE_LOWMEM) ++#include "duk_alloc_pool.h" ++#endif ++ ++#if defined(DUK_CMDLINE_DEBUGGER_SUPPORT) ++#include "duk_trans_socket.h" ++#endif ++ ++#if defined(DUK_USE_FUZZILLI) ++/* REPRL Pipe file descriptors for quicker fuzzing. */ ++#define REPRL_CRFD 100 ++#define REPRL_CWFD 101 ++#define REPRL_DRFD 102 ++#define REPRL_DWFD 103 ++/* Required includes for coverage tracking. */ ++#include ++#include ++#include ++#include ++#include ++#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 main_argc = 0; ++static const char **main_argv = NULL; ++static int interactive_mode = 0; ++static int allow_bytecode = 0; ++#if defined(DUK_CMDLINE_DEBUGGER_SUPPORT) ++static int debugger_reattach = 0; ++#endif ++#if defined(DUK_CMDLINE_LINENOISE_COMPLETION) ++static int no_auto_complete = 0; ++#endif ++ ++int duk_cmdline_stack_check(void); ++ ++/* ++ * Misc helpers ++ */ ++ ++static void print_greet_line(void) { ++ printf("((o) Duktape%s %d.%d.%d (%s)\n", ++#if defined(DUK_CMDLINE_LINENOISE) ++ " [linenoise]", ++#else ++ "", ++#endif ++ (int) (DUK_VERSION / 10000), ++ (int) ((DUK_VERSION / 100) % 100), ++ (int) (DUK_VERSION % 100), ++ DUK_GIT_DESCRIBE); ++} ++ ++#if defined(DUK_CMDLINE_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 /* DUK_CMDLINE_RLIMIT */ ++ ++#if defined(DUK_CMDLINE_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); ++ (void) signal(SIGPIPE, SIG_IGN); /* avoid SIGPIPE killing process */ ++} ++#endif /* DUK_CMDLINE_SIGNAL */ ++ ++static void cmdline_fatal_handler(void *udata, const char *msg) { ++ (void) udata; ++ fprintf(stderr, "*** FATAL ERROR: %s\n", msg ? msg : "no message"); ++ fprintf(stderr, "Causing intentional segfault...\n"); ++ fflush(stderr); ++ *((volatile unsigned int *) 0) = (unsigned int) 0xdeadbeefUL; ++ abort(); ++} ++ ++/* Print error to stderr and pop error. */ ++static void print_pop_error(duk_context *ctx, FILE *f) { ++ fprintf(f, "%s\n", duk_safe_to_stacktrace(ctx, -1)); ++ fflush(f); ++ duk_pop(ctx); ++} ++ ++static duk_ret_t wrapped_compile_execute(duk_context *ctx, void *udata) { ++ const char *src_data; ++ duk_size_t src_len; ++ duk_uint_t comp_flags; ++ ++ (void) udata; ++ ++ /* 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 >= 1 && src_data[0] == (char) 0xbf) { ++ /* Bytecode. */ ++ if (allow_bytecode) { ++ void *buf; ++ buf = duk_push_fixed_buffer(ctx, src_len); ++ memcpy(buf, (const void *) src_data, src_len); ++ duk_load_function(ctx); ++ } else { ++ (void) duk_type_error(ctx, "bytecode input rejected (use -b to allow bytecode inputs)"); ++ } ++ } else { ++ /* Source code. */ ++ comp_flags = DUK_COMPILE_SHEBANG; ++ 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; ++ char fnbuf[256]; ++ const char *filename; ++ ++ duk_dup_top(ctx); ++ duk_dump_function(ctx); ++ bc_ptr = duk_require_buffer_data(ctx, -1, &bc_len); ++ filename = duk_require_string(ctx, -5); ++#if defined(EMSCRIPTEN) ++ if (filename[0] == '/') { ++ snprintf(fnbuf, sizeof(fnbuf), "%s", filename); ++ } else { ++ snprintf(fnbuf, sizeof(fnbuf), "/working/%s", filename); ++ } ++#else ++ snprintf(fnbuf, sizeof(fnbuf), "%s", filename); ++#endif ++ fnbuf[sizeof(fnbuf) - 1] = (char) 0; ++ ++ f = fopen(fnbuf, "wb"); ++ if (!f) { ++ (void) duk_generic_error(ctx, "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) { ++ (void) duk_generic_error(ctx, "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 ecmatest" for a ++ * reasonably good coverage of different functions and programs. ++ */ ++ duk_dump_function(ctx); ++ duk_load_function(ctx); ++#endif ++ ++#if defined(DUK_CMDLINE_LOWMEM) ++ lowmem_start_exec_timeout(); ++#endif ++ ++ duk_push_global_object(ctx); /* 'this' binding */ ++ duk_call_method(ctx, 0); ++ ++#if defined(DUK_CMDLINE_LOWMEM) ++ lowmem_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: coercion to primitive failed ++ * 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(). ++ */ ++ ++ duk_push_global_stash(ctx); ++ duk_get_prop_string(ctx, -1, "dukFormat"); ++ duk_dup(ctx, -3); ++ duk_call(ctx, 1); /* -> [ ... res stash formatted ] */ ++ ++ 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 */ ++} ++ ++/* ++ * Minimal Linenoise completion support ++ */ ++ ++#if defined(DUK_CMDLINE_LINENOISE_COMPLETION) ++static duk_context *completion_ctx; ++ ++static const char *linenoise_completion_script = ++ "(function linenoiseCompletion(input, addCompletion) {\n" ++ " // Find maximal trailing string which looks like a property\n" ++ " // access. Look up all the components (starting from the global\n" ++ " // object now) except the last; treat the last component as a\n" ++ " // partial name and use it as a filter for possible properties.\n" ++ " var match, propseq, obj, i, partial, names, name, sanity;\n" ++ "\n" ++ " if (!input) { return; }\n" ++ " match = /^.*?((?:\\w+\\.)*\\w*)$/.exec(input);\n" ++ " if (!match || !match[1]) { return; }\n" ++ " var propseq = match[1].split('.');\n" ++ "\n" ++ " obj = Function('return this')();\n" ++ " for (i = 0; i < propseq.length - 1; i++) {\n" ++ " if (obj === void 0 || obj === null) { return; }\n" ++ " obj = obj[propseq[i]];\n" ++ " }\n" ++ " if (obj === void 0 || obj === null) { return; }\n" ++ "\n" ++ " partial = propseq[propseq.length - 1];\n" ++ " sanity = 1000;\n" ++ " while (obj != null) {\n" ++ " if (--sanity < 0) { throw new Error('sanity'); }\n" ++ " names = Object.getOwnPropertyNames(Object(obj));\n" ++ " for (i = 0; i < names.length; i++) {\n" ++ " if (--sanity < 0) { throw new Error('sanity'); }\n" ++ " name = names[i];\n" ++ " if (Number(name) >= 0) { continue; } // ignore array keys\n" ++ " if (name.substring(0, partial.length) !== partial) { continue; }\n" ++ " if (name === partial) { addCompletion(input + '.'); continue; }\n" ++ " addCompletion(input + name.substring(partial.length));\n" ++ " }\n" ++ " obj = Object.getPrototypeOf(Object(obj));\n" ++ " }\n" ++ "})"; ++ ++static const char *linenoise_hints_script = ++ "(function linenoiseHints(input) {\n" ++ " // Similar to completions but different handling for final results.\n" ++ " var match, propseq, obj, i, partial, names, name, res, found, first, sanity;\n" ++ "\n" ++ " if (!input) { return; }\n" ++ " match = /^.*?((?:\\w+\\.)*\\w*)$/.exec(input);\n" ++ " if (!match || !match[1]) { return; }\n" ++ " var propseq = match[1].split('.');\n" ++ "\n" ++ " obj = Function('return this')();\n" ++ " for (i = 0; i < propseq.length - 1; i++) {\n" ++ " if (obj === void 0 || obj === null) { return; }\n" ++ " obj = obj[propseq[i]];\n" ++ " }\n" ++ " if (obj === void 0 || obj === null) { return; }\n" ++ "\n" ++ " partial = propseq[propseq.length - 1];\n" ++ " res = [];\n" ++ " found = Object.create(null); // keys already handled\n" ++ " sanity = 1000;\n" ++ " while (obj != null) {\n" ++ " if (--sanity < 0) { throw new Error('sanity'); }\n" ++ " names = Object.getOwnPropertyNames(Object(obj));\n" ++ " first = true;\n" ++ " for (i = 0; i < names.length; i++) {\n" ++ " if (--sanity < 0) { throw new Error('sanity'); }\n" ++ " name = names[i];\n" ++ " if (Number(name) >= 0) { continue; } // ignore array keys\n" ++ " if (name.substring(0, partial.length) !== partial) { continue; }\n" ++ " if (name === partial) { continue; }\n" ++ " if (found[name]) { continue; }\n" ++ " found[name] = true;\n" ++ " res.push(res.length === 0 ? name.substring(partial.length) : (first ? ' || ' : ' | ') + name);\n" ++ " first = false;\n" ++ " }\n" ++ " obj = Object.getPrototypeOf(Object(obj));\n" ++ " }\n" ++ " return { hints: res.join(''), color: 35, bold: 1 };\n" ++ "})"; ++ ++static duk_ret_t linenoise_add_completion(duk_context *ctx) { ++ linenoiseCompletions *lc; ++ ++ duk_push_current_function(ctx); ++ duk_get_prop_string(ctx, -1, "lc"); ++ lc = duk_require_pointer(ctx, -1); ++ ++ linenoiseAddCompletion(lc, duk_require_string(ctx, 0)); ++ return 0; ++} ++ ++static char *linenoise_hints(const char *buf, int *color, int *bold) { ++ duk_context *ctx; ++ duk_int_t rc; ++ ++ ctx = completion_ctx; ++ if (!ctx) { ++ return NULL; ++ } ++ ++ duk_push_global_stash(ctx); ++ duk_get_prop_string(ctx, -1, "linenoiseHints"); ++ if (!buf) { ++ duk_push_undefined(ctx); ++ } else { ++ duk_push_string(ctx, buf); ++ } ++ ++ rc = duk_pcall(ctx, 1 /*nargs*/); /* [ stash func ] -> [ stash result ] */ ++ if (rc != 0) { ++ const char *res; ++ res = strdup(duk_safe_to_string(ctx, -1)); ++ *color = 31; /* red */ ++ *bold = 1; ++ duk_pop_2(ctx); ++ return (char *) (uintptr_t) res; /* uintptr_t cast to avoid const discard warning. */ ++ } ++ ++ if (duk_is_object(ctx, -1)) { ++ const char *tmp; ++ const char *res = NULL; ++ ++ duk_get_prop_string(ctx, -1, "hints"); ++ tmp = duk_get_string(ctx, -1); ++ if (tmp) { ++ res = strdup(tmp); ++ } ++ duk_pop(ctx); ++ ++ duk_get_prop_string(ctx, -1, "color"); ++ *color = duk_to_int(ctx, -1); ++ duk_pop(ctx); ++ ++ duk_get_prop_string(ctx, -1, "bold"); ++ *bold = duk_to_int(ctx, -1); ++ duk_pop(ctx); ++ ++ duk_pop_2(ctx); ++ return (char *) (uintptr_t) res; /* uintptr_t cast to avoid const discard warning. */ ++ } ++ ++ duk_pop_2(ctx); ++ return NULL; ++} ++ ++static void linenoise_freehints(void *ptr) { ++#if 0 ++ printf("free hint: %p\n", (void *) ptr); ++#endif ++ free(ptr); ++} ++ ++static void linenoise_completion(const char *buf, linenoiseCompletions *lc) { ++ duk_context *ctx; ++ duk_int_t rc; ++ ++ ctx = completion_ctx; ++ if (!ctx) { ++ return; ++ } ++ ++ duk_push_global_stash(ctx); ++ duk_get_prop_string(ctx, -1, "linenoiseCompletion"); ++ ++ if (!buf) { ++ duk_push_undefined(ctx); ++ } else { ++ duk_push_string(ctx, buf); ++ } ++ duk_push_c_function(ctx, linenoise_add_completion, 2 /*nargs*/); ++ duk_push_pointer(ctx, (void *) lc); ++ duk_put_prop_string(ctx, -2, "lc"); ++ ++ rc = duk_pcall(ctx, 2 /*nargs*/); /* [ stash func callback ] -> [ stash result ] */ ++ if (rc != 0) { ++ linenoiseAddCompletion(lc, duk_safe_to_string(ctx, -1)); ++ } ++ duk_pop_2(ctx); ++} ++#endif /* DUK_CMDLINE_LINENOISE_COMPLETION */ ++ ++/* ++ * Execute from file handle etc ++ */ ++ ++static int handle_fh(duk_context *ctx, FILE *f, const char *filename, const char *bytecode_filename) { ++ char *buf = NULL; ++ size_t bufsz; ++ size_t bufoff; ++ size_t got; ++ int rc; ++ int retval = -1; ++ ++ buf = (char *) malloc(1024); ++ if (!buf) { ++ goto error; ++ } ++ bufsz = 1024; ++ bufoff = 0; ++ ++ /* Read until EOF, avoid fseek/stat because it won't work with stdin. */ ++ for (;;) { ++ size_t avail; ++ ++ avail = bufsz - bufoff; ++ if (avail < 1024) { ++ size_t newsz; ++ char *buf_new; ++#if 0 ++ fprintf(stderr, "resizing read buffer: %ld -> %ld\n", (long) bufsz, (long) (bufsz * 2)); ++#endif ++ newsz = bufsz + (bufsz >> 2) + 1024; /* +25% and some extra */ ++ buf_new = (char *) realloc(buf, newsz); ++ if (!buf_new) { ++ goto error; ++ } ++ buf = buf_new; ++ bufsz = newsz; ++ } ++ ++ avail = bufsz - bufoff; ++#if 0 ++ fprintf(stderr, "reading input: buf=%p bufsz=%ld bufoff=%ld avail=%ld\n", ++ (void *) buf, (long) bufsz, (long) bufoff, (long) avail); ++#endif ++ ++ got = fread((void *) (buf + bufoff), (size_t) 1, avail, f); ++#if 0 ++ fprintf(stderr, "got=%ld\n", (long) got); ++#endif ++ if (got == 0) { ++ break; ++ } ++ bufoff += got; ++ ++ /* Emscripten specific: stdin EOF doesn't work as expected. ++ * Instead, when 'emduk' is executed using Node.js, a file ++ * piped to stdin repeats (!). Detect that repeat and cut off ++ * the stdin read. Ensure the loop repeats enough times to ++ * avoid detecting spurious loops. ++ * ++ * This only seems to work for inputs up to 256 bytes long. ++ */ ++#if defined(EMSCRIPTEN) ++ if (bufoff >= 16384) { ++ size_t i, j, nloops; ++ int looped = 0; ++ ++ for (i = 16; i < bufoff / 8; i++) { ++ int ok; ++ ++ nloops = bufoff / i; ++ ok = 1; ++ for (j = 1; j < nloops; j++) { ++ if (memcmp((void *) buf, (void *) (buf + i * j), i) != 0) { ++ ok = 0; ++ break; ++ } ++ } ++ if (ok) { ++ fprintf(stderr, "emscripten workaround: detect looping at index %ld, verified with %ld loops\n", (long) i, (long) (nloops - 1)); ++ bufoff = i; ++ looped = 1; ++ break; ++ } ++ } ++ ++ if (looped) { ++ break; ++ } ++ } ++#endif ++ } ++ ++ duk_push_string(ctx, bytecode_filename); ++ duk_push_pointer(ctx, (void *) buf); ++ duk_push_uint(ctx, (duk_uint_t) bufoff); ++ duk_push_string(ctx, filename); ++ ++ interactive_mode = 0; /* global */ ++ ++ rc = duk_safe_call(ctx, wrapped_compile_execute, NULL /*udata*/, 4 /*nargs*/, 1 /*nret*/); ++ ++#if defined(DUK_CMDLINE_LOWMEM) ++ lowmem_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); ++ buf = NULL; ++ } ++ 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; ++ char fnbuf[256]; ++ ++ /* Example of sending an application specific debugger notification. */ ++ duk_push_string(ctx, "DebuggerHandleFile"); ++ duk_push_string(ctx, filename); ++ duk_debugger_notify(ctx, 2); ++ ++#if defined(EMSCRIPTEN) ++ if (filename[0] == '/') { ++ snprintf(fnbuf, sizeof(fnbuf), "%s", filename); ++ } else { ++ snprintf(fnbuf, sizeof(fnbuf), "/working/%s", filename); ++ } ++#else ++ snprintf(fnbuf, sizeof(fnbuf), "%s", filename); ++#endif ++ fnbuf[sizeof(fnbuf) - 1] = (char) 0; ++ ++ f = fopen(fnbuf, "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; ++ union { ++ void *ptr; ++ const void *constptr; ++ } u; ++ ++ u.constptr = code; /* Lose 'const' without warning. */ ++ duk_push_pointer(ctx, u.ptr); ++ 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, NULL /*udata*/, 3 /*nargs*/, 1 /*nret*/); ++ ++#if defined(DUK_CMDLINE_LOWMEM) ++ lowmem_clear_exec_timeout(); ++#endif ++ ++ if (rc != DUK_EXEC_SUCCESS) { ++ print_pop_error(ctx, stderr); ++ } else { ++ duk_pop(ctx); ++ retval = 0; ++ } ++ ++ return retval; ++} ++ ++#if defined(DUK_CMDLINE_LINENOISE) ++static int handle_interactive(duk_context *ctx) { ++ const char *prompt = "duk> "; ++ char *buffer = NULL; ++ int retval = 0; ++ int rc; ++ ++ linenoiseSetMultiLine(1); ++ linenoiseHistorySetMaxLen(64); ++#if defined(DUK_CMDLINE_LINENOISE_COMPLETION) ++ if (!no_auto_complete) { ++ linenoiseSetCompletionCallback(linenoise_completion); ++ linenoiseSetHintsCallback(linenoise_hints); ++ linenoiseSetFreeHintsCallback(linenoise_freehints); ++ duk_push_global_stash(ctx); ++ duk_eval_string(ctx, linenoise_completion_script); ++ duk_put_prop_string(ctx, -2, "linenoiseCompletion"); ++ duk_eval_string(ctx, linenoise_hints_script); ++ duk_put_prop_string(ctx, -2, "linenoiseHints"); ++ duk_pop(ctx); ++ } ++#endif ++ ++ for (;;) { ++ if (buffer) { ++ linenoiseFree(buffer); ++ buffer = NULL; ++ } ++ ++#if defined(DUK_CMDLINE_LINENOISE_COMPLETION) ++ completion_ctx = ctx; ++#endif ++ buffer = linenoise(prompt); ++#if defined(DUK_CMDLINE_LINENOISE_COMPLETION) ++ completion_ctx = NULL; ++#endif ++ ++ if (!buffer) { ++ break; ++ } ++ ++ if (buffer && buffer[0] != (char) 0) { ++ linenoiseHistoryAdd(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, NULL /*udata*/, 3 /*nargs*/, 1 /*nret*/); ++ ++#if defined(DUK_CMDLINE_LOWMEM) ++ lowmem_clear_exec_timeout(); ++#endif ++ ++ if (buffer) { ++ linenoiseFree(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) { ++ linenoiseFree(buffer); ++ buffer = NULL; ++ } ++ ++ return retval; ++} ++#else /* DUK_CMDLINE_LINENOISE */ ++static int handle_interactive(duk_context *ctx) { ++ const char *prompt = "duk> "; ++ char *buffer = NULL; ++ int retval = 0; ++ int rc; ++ int got_eof = 0; ++ ++ 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, NULL /*udata*/, 3 /*nargs*/, 1 /*nret*/); ++ ++#if defined(DUK_CMDLINE_LOWMEM) ++ lowmem_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; ++} ++#endif /* DUK_CMDLINE_LINENOISE */ ++ ++/* ++ * Simple file read/write bindings ++ */ ++ ++#if defined(DUK_CMDLINE_FILEIO) ++static duk_ret_t fileio_read_file(duk_context *ctx) { ++ const char *fn; ++ char *buf; ++ size_t len; ++ size_t off; ++ int rc; ++ FILE *f; ++ ++ fn = duk_require_string(ctx, 0); ++ f = fopen(fn, "rb"); ++ if (!f) { ++ (void) duk_type_error(ctx, "cannot open file %s for reading, errno %ld: %s", ++ fn, (long) errno, strerror(errno)); ++ } ++ ++ rc = fseek(f, 0, SEEK_END); ++ if (rc < 0) { ++ (void) fclose(f); ++ (void) duk_type_error(ctx, "fseek() failed for %s, errno %ld: %s", ++ fn, (long) errno, strerror(errno)); ++ } ++ len = (size_t) ftell(f); ++ rc = fseek(f, 0, SEEK_SET); ++ if (rc < 0) { ++ (void) fclose(f); ++ (void) duk_type_error(ctx, "fseek() failed for %s, errno %ld: %s", ++ fn, (long) errno, strerror(errno)); ++ } ++ ++ buf = (char *) duk_push_fixed_buffer(ctx, (duk_size_t) len); ++ for (off = 0; off < len;) { ++ size_t got; ++ got = fread((void *) (buf + off), 1, len - off, f); ++ if (ferror(f)) { ++ (void) fclose(f); ++ (void) duk_type_error(ctx, "error while reading %s", fn); ++ } ++ if (got == 0) { ++ if (feof(f)) { ++ break; ++ } else { ++ (void) fclose(f); ++ (void) duk_type_error(ctx, "error while reading %s", fn); ++ } ++ } ++ off += got; ++ } ++ ++ if (f) { ++ (void) fclose(f); ++ } ++ ++ return 1; ++} ++ ++static duk_ret_t fileio_write_file(duk_context *ctx) { ++ const char *fn; ++ const char *buf; ++ size_t len; ++ size_t off; ++ FILE *f; ++ ++ fn = duk_require_string(ctx, 0); ++ f = fopen(fn, "wb"); ++ if (!f) { ++ (void) duk_type_error(ctx, "cannot open file %s for writing, errno %ld: %s", ++ fn, (long) errno, strerror(errno)); ++ } ++ ++ len = 0; ++ buf = (char *) duk_require_buffer_data(ctx, 1, &len); ++ for (off = 0; off < len;) { ++ size_t got; ++ got = fwrite((const void *) (buf + off), 1, len - off, f); ++ if (ferror(f)) { ++ (void) fclose(f); ++ (void) duk_type_error(ctx, "error while writing %s", fn); ++ } ++ if (got == 0) { ++ (void) fclose(f); ++ (void) duk_type_error(ctx, "error while writing %s", fn); ++ } ++ off += got; ++ } ++ ++ if (f) { ++ (void) fclose(f); ++ } ++ ++ return 0; ++} ++ ++static duk_ret_t sys_execute(duk_context *ctx) { ++ const char *command = duk_require_string(ctx, 0); ++ int rc; ++ ++ rc = system(command); ++ if (rc != 0) { ++ duk_push_error_object(ctx, DUK_ERR_TYPE_ERROR, "system() failed with code %d", rc); ++ duk_push_int(ctx, rc); ++ duk_put_prop_string(ctx, -2, "exitCode"); ++ return duk_throw(ctx); ++ } ++ ++ return 0; ++} ++#endif ++ ++#if defined(DUK_USE_FUZZILLI) ++/* Custom builtin for Fuzzilli, used to ensure the fuzzer is properly catching ++ * crashes and assert failures. ++ */ ++static duk_ret_t fuzzilli(duk_context *ctx) { ++ const char *first_arg = duk_require_string(ctx, 0); ++ ++ if (strcmp(first_arg, "FUZZILLI_CRASH") == 0) { ++ duk_int_t second_arg = duk_get_int(ctx, 1); ++ switch (second_arg) { ++ case 0: ++ *((duk_int_t *)0x41414141) = 0x1337; /* intentionally segfault */ ++ break; ++ default: ++ duk_assert_wrapper(0); /* Intentionally fail assertion */ ++ break; ++ } ++ } else if (strcmp(first_arg, "FUZZILLI_PRINT") == 0) { ++ const char *string = duk_require_string(ctx, 1); ++ ++ FILE *fzliout = fdopen(REPRL_DWFD, "w"); ++ if (!fzliout) { ++ fprintf(stderr, "Fuzzer output channel not available, printing to stdout instead\n"); ++ fzliout = stdout; ++ } ++ fprintf(fzliout, "%s\n", string); ++ fflush(fzliout); ++ } ++ ++ return 0; ++} ++#endif /* DUK_USE_FUZZILLI */ ++ ++/* ++ * String.fromBufferRaw() ++ */ ++ ++static duk_ret_t string_frombufferraw(duk_context *ctx) { ++ duk_buffer_to_string(ctx, 0); ++ return 1; ++} ++ ++/* ++ * Duktape heap lifecycle ++ */ ++ ++#if defined(DUK_CMDLINE_DEBUGGER_SUPPORT) ++static duk_idx_t debugger_request(duk_context *ctx, void *udata, duk_idx_t nvalues) { ++ const char *cmd; ++ int i; ++ ++ (void) udata; ++ ++ if (nvalues < 1) { ++ duk_push_string(ctx, "missing AppRequest argument(s)"); ++ return -1; ++ } ++ ++ cmd = duk_get_string(ctx, -nvalues + 0); ++ ++ if (cmd && strcmp(cmd, "CommandLine") == 0) { ++ if (!duk_check_stack(ctx, main_argc)) { ++ /* Callback should avoid errors for now, so use ++ * duk_check_stack() rather than duk_require_stack(). ++ */ ++ duk_push_string(ctx, "failed to extend stack"); ++ return -1; ++ } ++ for (i = 0; i < main_argc; i++) { ++ duk_push_string(ctx, main_argv[i]); ++ } ++ return main_argc; ++ } ++ duk_push_sprintf(ctx, "command not supported"); ++ return -1; ++} ++ ++static void debugger_detached(duk_context *ctx, void *udata) { ++ fprintf(stderr, "Debugger detached, udata: %p\n", (void *) udata); ++ fflush(stderr); ++ ++ /* Ensure socket is closed even when detach is initiated by Duktape ++ * rather than debug client. ++ */ ++ duk_trans_socket_finish(); ++ ++ if (debugger_reattach) { ++ /* For automatic reattach testing. */ ++ duk_trans_socket_init(); ++ duk_trans_socket_waitconn(); ++ fprintf(stderr, "Debugger reconnected, call duk_debugger_attach()\n"); ++ fflush(stderr); ++#if 0 ++ /* This is not necessary but should be harmless. */ ++ duk_debugger_detach(ctx); ++#endif ++ 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_request, ++ debugger_detached, ++ NULL); ++ } ++} ++#endif ++ ++#define ALLOC_DEFAULT 0 ++#define ALLOC_LOGGING 1 ++#define ALLOC_TORTURE 2 ++#define ALLOC_HYBRID 3 ++#define ALLOC_LOWMEM 4 ++ ++static duk_context *create_duktape_heap(int alloc_provider, int debugger, int lowmem_log) { ++ duk_context *ctx; ++#if defined(DUK_CMDLINE_FILEIO) ++ int i; ++#endif ++ ++ (void) lowmem_log; /* suppress warning */ ++ ++ ctx = NULL; ++ if (!ctx && alloc_provider == ALLOC_LOGGING) { ++#if defined(DUK_CMDLINE_ALLOC_LOGGING) ++ ctx = duk_create_heap(duk_alloc_logging, ++ duk_realloc_logging, ++ duk_free_logging, ++ (void *) 0xdeadbeef, ++ cmdline_fatal_handler); ++#else ++ fprintf(stderr, "Warning: option --alloc-logging ignored, no logging allocator support\n"); ++ fflush(stderr); ++#endif ++ } ++ if (!ctx && alloc_provider == ALLOC_TORTURE) { ++#if defined(DUK_CMDLINE_ALLOC_TORTURE) ++ ctx = duk_create_heap(duk_alloc_torture, ++ duk_realloc_torture, ++ duk_free_torture, ++ (void *) 0xdeadbeef, ++ cmdline_fatal_handler); ++#else ++ fprintf(stderr, "Warning: option --alloc-torture ignored, no torture allocator support\n"); ++ fflush(stderr); ++#endif ++ } ++ if (!ctx && alloc_provider == ALLOC_HYBRID) { ++#if defined(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, ++ cmdline_fatal_handler); ++ } ++#else ++ fprintf(stderr, "Warning: option --alloc-hybrid ignored, no hybrid allocator support\n"); ++ fflush(stderr); ++#endif ++ } ++ if (!ctx && alloc_provider == ALLOC_LOWMEM) { ++#if defined(DUK_CMDLINE_LOWMEM) ++ lowmem_init(); ++ ++ ctx = duk_create_heap( ++ lowmem_log ? lowmem_alloc_wrapped : duk_alloc_pool, ++ lowmem_log ? lowmem_realloc_wrapped : duk_realloc_pool, ++ lowmem_log ? lowmem_free_wrapped : duk_free_pool, ++ (void *) lowmem_pool_ptr, ++ cmdline_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(NULL, NULL, NULL, NULL, cmdline_fatal_handler); ++ } ++ ++ if (!ctx) { ++ fprintf(stderr, "Failed to create Duktape heap\n"); ++ fflush(stderr); ++ exit(1); ++ } ++ ++#if defined(DUK_CMDLINE_LOWMEM) ++ if (alloc_provider == ALLOC_LOWMEM) { ++ fprintf(stderr, "*** pool dump after heap creation ***\n"); ++ lowmem_dump(); ++ } ++#endif ++ ++#if defined(DUK_CMDLINE_LOWMEM) ++ if (alloc_provider == ALLOC_LOWMEM) { ++ lowmem_register(ctx); ++ } ++#endif ++ ++ /* Register print() and alert() (removed in Duktape 2.x). */ ++#if defined(DUK_CMDLINE_PRINTALERT_SUPPORT) ++ duk_print_alert_init(ctx, 0 /*flags*/); ++#endif ++ ++ /* Register String.fromBufferRaw() which does a 1:1 buffer-to-string ++ * coercion needed by testcases. String.fromBufferRaw() is -not- a ++ * default built-in! For stripped builds the 'String' built-in ++ * doesn't exist and we create it here; for ROM builds it may be ++ * present but unwritable (which is ignored). ++ */ ++ duk_eval_string(ctx, ++ "(function(v){" ++ "if (typeof String === 'undefined') { String = {}; }" ++ "Object.defineProperty(String, 'fromBufferRaw', {value:v, configurable:true});" ++ "})"); ++ duk_push_c_function(ctx, string_frombufferraw, 1 /*nargs*/); ++ (void) duk_pcall(ctx, 1); ++ duk_pop(ctx); ++ ++ /* Register console object. */ ++#if defined(DUK_CMDLINE_CONSOLE_SUPPORT) ++ duk_console_init(ctx, DUK_CONSOLE_FLUSH /*flags*/); ++#endif ++ ++ /* Register Duktape.Logger (removed in Duktape 2.x). */ ++#if defined(DUK_CMDLINE_LOGGING_SUPPORT) ++ duk_logging_init(ctx, 0 /*flags*/); ++#endif ++ ++ /* Register require() (removed in Duktape 2.x). */ ++#if defined(DUK_CMDLINE_MODULE_SUPPORT) ++ duk_module_duktape_init(ctx); ++#endif ++ ++ /* Trivial file I/O bindings, sysExecute(), and sysArgv for testing. */ ++#if defined(DUK_CMDLINE_FILEIO) ++ duk_push_c_function(ctx, fileio_read_file, 1 /*nargs*/); ++ duk_put_global_string(ctx, "readFile"); ++ duk_push_c_function(ctx, fileio_write_file, 2 /*nargs*/); ++ duk_put_global_string(ctx, "writeFile"); ++ duk_push_c_function(ctx, sys_execute, 1 /*nargs*/); ++ duk_put_global_string(ctx, "sysExecute"); ++ duk_push_array(ctx); ++ for (i = 0; i < main_argc; i++) { ++ duk_push_string(ctx, main_argv[i]); ++ duk_put_prop_index(ctx, -2, (duk_uarridx_t) i); ++ } ++ duk_put_global_string(ctx, "sysArgv"); /* raw argv */ ++#endif ++ ++ /* Stash a formatting function for evaluation results. */ ++ duk_push_global_stash(ctx); ++ duk_eval_string(ctx, ++ "(function (E) {" ++ "return function format(v){" ++ "try{" ++ "return E('jx',v);" ++ "}catch(e){" ++ "return ''+v;" ++ "}" ++ "};" ++ "})(Duktape.enc)"); ++ duk_put_prop_string(ctx, -2, "dukFormat"); ++ duk_pop(ctx); ++ ++ if (debugger) { ++#if defined(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_request, ++ debugger_detached, ++ NULL); ++#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 ++ ++#if defined(DUK_USE_FUZZILLI) ++ /* Add the custom function for Fuzzilli integration. */ ++ ++ duk_push_c_function(ctx, fuzzilli, 2 /*nargs*/); ++ duk_put_global_string(ctx, "fuzzilli"); ++#endif /* DUK_USE_FUZZILLI */ ++ ++ return ctx; ++} ++ ++static void destroy_duktape_heap(duk_context *ctx, int alloc_provider) { ++ (void) alloc_provider; ++ ++#if defined(DUK_CMDLINE_LOWMEM) ++ if (alloc_provider == ALLOC_LOWMEM) { ++ fprintf(stderr, "*** pool dump before duk_destroy_heap(), before forced gc ***\n"); ++ lowmem_dump(); ++ ++ duk_gc(ctx, 0); ++ ++ fprintf(stderr, "*** pool dump before duk_destroy_heap(), after forced gc ***\n"); ++ lowmem_dump(); ++ } ++#endif ++ ++ if (ctx) { ++ duk_destroy_heap(ctx); ++ } ++ ++#if defined(DUK_CMDLINE_LOWMEM) ++ if (alloc_provider == ALLOC_LOWMEM) { ++ fprintf(stderr, "*** pool dump after duk_destroy_heap() (should have zero allocs) ***\n"); ++ lowmem_dump(); ++ } ++ lowmem_free(); ++#endif ++} ++ ++#if defined(DUK_USE_FUZZILLI) ++/* Enables coverage tracking for fuzzing. */ ++ ++void __sanitizer_cov_reset_edgeguards(void); ++ ++#define SHM_SIZE 0x100000 ++#define MAX_EDGES ((SHM_SIZE - 4) * 8) ++ ++#define CHECK(cond) if (!(cond)) { fprintf(stderr, "\"" #cond "\" failed\n"); _exit(-1); } ++ ++struct shmem_data { ++ duk_uint32_t num_edges; ++ unsigned char edges[]; ++}; ++ ++struct shmem_data *__shmem; ++duk_uint32_t *__edges_start, *__edges_stop; ++ ++void __sanitizer_cov_reset_edgeguards(void) { ++ duk_uint64_t N = 0; ++ duk_uint32_t *x; ++ for (x = __edges_start; x < __edges_stop && N < MAX_EDGES; x++) { ++ *x = ++N; ++ } ++} ++ ++ ++void __sanitizer_cov_trace_pc_guard_init(duk_uint32_t *start, duk_uint32_t *stop) { ++ /* Avoid duplicate initialization. */ ++ if (start == stop || *start) { ++ return; ++ } ++ ++ if (__edges_start != NULL || __edges_stop != NULL) { ++ fprintf(stderr, "Coverage instrumentation is only supported for a single module\n"); ++ _exit(-1); ++ } ++ ++ __edges_start = start; ++ __edges_stop = stop; ++ ++ /* Map the shared memory region. */ ++ const char *shm_key = getenv("SHM_ID"); ++ if (!shm_key) { ++ puts("[COV] no shared memory bitmap available, skipping"); ++ __shmem = (struct shmem_data *) malloc(SHM_SIZE); ++ } else { ++ int fd = shm_open(shm_key, O_RDWR, S_IREAD | S_IWRITE); ++ if (fd <= -1) { ++ fprintf(stderr, "Failed to open shared memory region: %s\n", strerror(errno)); ++ _exit(-1); ++ } ++ ++ __shmem = (struct shmem_data *) mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); ++ if (__shmem == MAP_FAILED) { ++ fprintf(stderr, "Failed to mmap shared memory region\n"); ++ _exit(-1); ++ } ++ } ++ ++ __sanitizer_cov_reset_edgeguards(); ++ ++ __shmem->num_edges = stop - start; ++ printf("[COV] edge counters initialized. Shared memory: %s with %u edges\n", shm_key, __shmem->num_edges); ++} ++ ++void __sanitizer_cov_trace_pc_guard(duk_uint32_t *guard) { ++ /* There's a small race condition here: if this function executes in two threads for the same ++ * edge at the same time, the first thread might disable the edge (by setting the guard to zero) ++ * before the second thread fetches the guard value (and thus the index). However, our ++ * instrumentation ignores the first edge (see libcoverage.c) and so the race is unproblematic. ++ */ ++ duk_uint32_t index = *guard; ++ ++ /* If this function is called before coverage instrumentation is properly initialized we want ++ * to return early. ++ */ ++ if (!index) { ++ return; ++ } ++ ++ __shmem->edges[index / 8] |= 1 << (index % 8); ++ *guard = 0; ++} ++#endif /* DUK_USE_FUZZILLI */ ++ ++/* ++ * Main ++ */ ++ ++int main(int argc, const 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 lowmem_log = 0; ++ int debugger = 0; ++ int recreate_heap = 0; ++ int no_heap_destroy = 0; ++ int verbose = 0; ++ int run_stdin = 0; ++ const char *compile_filename = NULL; ++ int i; ++#if defined(DUK_USE_FUZZILLI) ++ int reprl_mode = 0; ++#endif /* DUK_USE_FUZZILLI */ ++ ++ main_argc = argc; ++ main_argv = (const char **) argv; ++ ++#if defined(EMSCRIPTEN) ++ /* Try to use NODEFS to provide access to local files. Mount the ++ * CWD as /working, and then prepend "/working/" to relative native ++ * paths in file calls to get something that works reasonably for ++ * relative paths. Emscripten doesn't support replacing virtual ++ * "/" with host "/" (the default MEMFS at "/" can't be unmounted) ++ * but we can mount "/tmp" as host "/tmp" to allow testcase runs. ++ * ++ * https://kripken.github.io/emscripten-site/docs/api_reference/Filesystem-API.html#filesystem-api-nodefs ++ * https://github.com/kripken/emscripten/blob/master/tests/fs/test_nodefs_rw.c ++ */ ++ EM_ASM( ++ /* At the moment it's not possible to replace the default MEMFS mounted at '/': ++ * https://github.com/kripken/emscripten/issues/2040 ++ * https://github.com/kripken/emscripten/blob/incoming/src/library_fs.js#L1341-L1358 ++ */ ++ /* ++ try { ++ FS.unmount("/"); ++ } catch (e) { ++ console.log("Failed to unmount default '/' MEMFS mount: " + e); ++ } ++ */ ++ try { ++ FS.mkdir("/working"); ++ FS.mount(NODEFS, { root: "." }, "/working"); ++ } catch (e) { ++ console.log("Failed to mount NODEFS /working: " + e); ++ } ++ /* A virtual '/tmp' exists by default: ++ * https://gist.github.com/evanw/e6be28094f34451bd5bd#file-temp-js-L3806-L3809 ++ */ ++ /* ++ try { ++ FS.mkdir("/tmp"); ++ } catch (e) { ++ console.log("Failed to create virtual /tmp: " + e); ++ } ++ */ ++ try { ++ FS.mount(NODEFS, { root: "/tmp" }, "/tmp"); ++ } catch (e) { ++ console.log("Failed to mount NODEFS /tmp: " + e); ++ } ++ ); ++#endif /* EMSCRIPTEN */ ++ ++#if defined(DUK_CMDLINE_LOWMEM) ++ alloc_provider = ALLOC_LOWMEM; ++#endif ++ (void) lowmem_log; ++ ++ /* ++ * Signal handling setup ++ */ ++ ++#if defined(DUK_CMDLINE_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++) { ++ const char *arg = argv[i]; ++ if (!arg) { ++ goto usage; ++ } else if (strcmp(arg, "--") == 0) { ++ /* Ignore anything after '--'. */ ++ break; ++ } else if (strcmp(arg, "--restrict-memory") == 0) { ++ memlimit_high = 0; ++ } else if (strcmp(arg, "-i") == 0) { ++ interactive = 1; ++ } else if (strcmp(arg, "-b") == 0) { ++ allow_bytecode = 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-lowmem") == 0) { ++ alloc_provider = ALLOC_LOWMEM; ++ } else if (strcmp(arg, "--lowmem-log") == 0) { ++ lowmem_log = 1; ++ } else if (strcmp(arg, "--debugger") == 0) { ++ debugger = 1; ++#if defined(DUK_CMDLINE_DEBUGGER_SUPPORT) ++ } else if (strcmp(arg, "--reattach") == 0) { ++ debugger_reattach = 1; ++#endif ++#if defined(DUK_USE_FUZZILLI) ++ } else if (strcmp(arg, "--reprl") == 0) { ++ reprl_mode = 1; ++#endif ++ } else if (strcmp(arg, "--recreate-heap") == 0) { ++ recreate_heap = 1; ++ } else if (strcmp(arg, "--no-heap-destroy") == 0) { ++ no_heap_destroy = 1; ++ } else if (strcmp(arg, "--no-auto-complete") == 0) { ++#if defined(DUK_CMDLINE_LINENOISE_COMPLETION) ++ no_auto_complete = 1; ++#endif ++ } else if (strcmp(arg, "--verbose") == 0) { ++ verbose = 1; ++ } else if (strcmp(arg, "--run-stdin") == 0) { ++ run_stdin = 1; ++ } else if (strlen(arg) >= 1 && arg[0] == '-') { ++ goto usage; ++ } else { ++ have_files = 1; ++ } ++ } ++ if (!have_files && !have_eval && !run_stdin) { ++ interactive = 1; ++ } ++ ++ /* ++ * Memory limit ++ */ ++ ++#if defined(DUK_CMDLINE_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, lowmem_log); ++ ++ /* ++ * Execute any argument file(s) ++ */ ++ ++ for (i = 1; i < argc; i++) { ++ const char *arg = argv[i]; ++ if (!arg) { ++ continue; ++ } else if (strcmp(arg, "--") == 0) { ++ /* Ignore anything after '--'. */ ++ break; ++ } 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, lowmem_log); ++ } ++ } ++ ++#if defined(DUK_USE_FUZZILLI) ++ /* Run the fuzzilli run-eval-print-repeat loop, and exit at the end. */ ++ if (reprl_mode) { ++ /* REPRL: let parent know we are ready */ ++ char helo[4] = "HELO"; ++ if (write(REPRL_CWFD, helo, 4) != 4 || ++ read(REPRL_CRFD, helo, 4) != 4) { ++ reprl_mode = 0; ++ } ++ ++ if (memcmp(helo, "HELO", 4) != 0) { ++ fprintf(stderr, "Invalid response from parent\n"); ++ _exit(-1); ++ } ++ ++ while (reprl_mode) { ++ unsigned int action = 0; ++ duk_size_t script_size; ++ char static_buff[4096]; ++ char *buffer; ++ char *ptr; ++ duk_size_t remaining; ++ duk_int_t rc; ++ ++ duk_int64_t nread = read(REPRL_CRFD, &action, 4); ++ if (nread != 4 || action != 'cexe') { ++ fprintf(stderr, "Unknown action: %u\n", action); ++ duk_assert_wrapper(0); ++ _exit(-1); ++ } ++ ++ duk_assert_wrapper(read(REPRL_CRFD, &script_size, 8) == 8); ++ /* In practice, we're never going to get to 4k long input scripts (21 core days got to ~80 bytes). */ ++ if (script_size > 4095) { ++ buffer = (char *) malloc((sizeof(char) * (script_size + 1))); ++ } else { ++ buffer = static_buff; ++ } ++ ++ ptr = buffer; ++ remaining = script_size; ++ while (remaining > 0) { ++ duk_int64_t rv = read(REPRL_DRFD, ptr, remaining); ++ duk_assert_wrapper(rv >= 0); ++ remaining -= rv; ++ ptr += rv; ++ } ++ buffer[script_size] = 0; ++ ++ /* Actually execute. */ ++ duk_push_string(ctx, buffer); ++ rc = duk_peval(ctx); ++ ++ /* Return result to parent. */ ++ rc <<= 8; ++ duk_assert_wrapper(write(REPRL_CWFD, &rc, 4) == 4); ++ ++ /* Clean up this round. */ ++ if (script_size > 4095) { ++ free(buffer); ++ } ++ duk_destroy_heap(ctx); ++ ctx = create_duktape_heap(alloc_provider, debugger, lowmem_log); ++ __sanitizer_cov_reset_edgeguards(); ++ } ++ return 0; ++ } /* reprl_mode */ ++#endif ++ ++ if (run_stdin) { ++ /* Running stdin like a full file (reading all lines before ++ * compiling) is useful with emduk: ++ * cat test.js | ./emduk --run-stdin ++ */ ++ if (handle_fh(ctx, stdin, "stdin", 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, lowmem_log); ++ } ++ } ++ ++ /* ++ * Enter interactive mode if options indicate it ++ */ ++ ++ if (interactive) { ++ print_greet_line(); ++ if (handle_interactive(ctx) != 0) { ++ retval = 1; ++ goto cleanup; ++ } ++ } ++ ++ /* ++ * Cleanup and exit ++ */ ++ ++ cleanup: ++ if (interactive) { ++ fprintf(stderr, "Cleaning up...\n"); ++ fflush(stderr); ++ } ++ ++ if (ctx && no_heap_destroy) { ++ duk_gc(ctx, 0); ++ } ++ if (ctx && !no_heap_destroy) { ++ destroy_duktape_heap(ctx, alloc_provider); ++ } ++ ctx = NULL; ++ ++ return retval; ++ ++ /* ++ * Usage ++ */ ++ ++ usage: ++ fprintf(stderr, "Usage: duk [options] []\n" ++ "\n" ++ " -i enter interactive mode after executing argument file(s) / eval code\n" ++ " -e CODE evaluate code\n" ++ " -c FILE compile into bytecode and write to FILE (use with only one file argument)\n" ++ " -b allow bytecode input files (memory unsafe for invalid bytecode)\n" ++ " --run-stdin treat stdin like a file, i.e. compile full input (not line by line)\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" ++#if defined(DUK_CMDLINE_ALLOC_LOGGING) ++ " --alloc-logging use logging allocator, write alloc log to /tmp/duk-alloc-log.txt\n" ++#endif ++#if defined(DUK_CMDLINE_ALLOC_TORTURE) ++ " --alloc-torture use torture allocator\n" ++#endif ++#if defined(DUK_CMDLINE_ALLOC_HYBRID) ++ " --alloc-hybrid use hybrid allocator\n" ++#endif ++#if defined(DUK_CMDLINE_LOWMEM) ++ " --alloc-lowmem use pooled allocator (enabled by default for duk-low)\n" ++ " --lowmem-log write alloc log to /tmp/lowmem-alloc-log.txt\n" ++#endif ++#if defined(DUK_CMDLINE_DEBUGGER_SUPPORT) ++ " --debugger start example debugger\n" ++ " --reattach automatically reattach debugger on detach\n" ++#endif ++ " --recreate-heap recreate heap after every file\n" ++ " --no-heap-destroy force GC, but don't destroy heap at end (leak testing)\n" ++#if defined(DUK_CMDLINE_LINENOISE_COMPLETION) ++ " --no-auto-complete disable linenoise auto completion\n" ++#else ++ " --no-auto-complete disable linenoise auto completion [ignored, not supported]\n" ++#endif ++ "\n" ++ "If is omitted, interactive mode is started automatically.\n" ++ "\n" ++ "Input files can be either ECMAScript source files or bytecode files\n" ++ "(if -b is given). Bytecode files are not validated prior to loading,\n" ++ "so that incompatible or crafted files can cause memory unsafe behavior.\n" ++ "See discussion in\n" ++ "https://github.com/svaarala/duktape/blob/master/doc/bytecode.rst#memory-safety-and-bytecode-validation.\n"); ++ fflush(stderr); ++ exit(1); ++} ++ ++/* Example of how a native stack check can be implemented in a platform ++ * specific manner for DUK_USE_NATIVE_STACK_CHECK(). This example is for ++ * (Linux) pthreads, and rejects further native recursion if less than ++ * 16kB stack is left (conservative). ++ */ ++#if defined(DUK_CMDLINE_PTHREAD_STACK_CHECK) ++int duk_cmdline_stack_check(void) { ++ pthread_attr_t attr; ++ void *stackaddr; ++ size_t stacksize; ++ char *ptr; ++ char *ptr_base; ++ ptrdiff_t remain; ++ ++ (void) pthread_getattr_np(pthread_self(), &attr); ++ (void) pthread_attr_getstack(&attr, &stackaddr, &stacksize); ++ ptr = (char *) &stacksize; /* Rough estimate of current stack pointer. */ ++ ptr_base = (char *) stackaddr; ++ remain = ptr - ptr_base; ++ ++ /* HIGH ADDR ----- stackaddr + stacksize ++ * | ++ * | stack growth direction ++ * v -- ptr, approximate used size ++ * ++ * LOW ADDR ----- ptr_base, end of stack (lowest address) ++ */ ++ ++#if 0 ++ fprintf(stderr, "STACK CHECK: stackaddr=%p, stacksize=%ld, ptr=%p, remain=%ld\n", ++ stackaddr, (long) stacksize, (void *) ptr, (long) remain); ++ fflush(stderr); ++#endif ++ if (remain < 16384) { ++ return 1; ++ } ++ return 0; /* 0: no error, != 0: throw RangeError */ ++} ++#else ++int duk_cmdline_stack_check(void) { ++ return 0; ++} ++#endif +diff --git a/dist/source/examples/cmdline/duk_cmdline.h b/dist/source/examples/cmdline/duk_cmdline.h +new file mode 100644 +index 0000000..0c5c03d +--- /dev/null ++++ b/dist/source/examples/cmdline/duk_cmdline.h +@@ -0,0 +1,16 @@ ++#if !defined(DUK_CMDLINE_H_INCLUDED) ++#define DUK_CMDLINE_H_INCLUDED ++ ++/* Defined in duk_cmdline_lowmem.c. */ ++extern void *lowmem_pool_ptr; ++void lowmem_init(void); ++void lowmem_free(void); ++void lowmem_dump(void); ++void lowmem_register(duk_context *ctx); ++void lowmem_start_exec_timeout(void); ++void lowmem_clear_exec_timeout(void); ++void *lowmem_alloc_wrapped(void *udata, duk_size_t size); ++void *lowmem_realloc_wrapped(void *udata, void *ptr, duk_size_t size); ++void lowmem_free_wrapped(void *udata, void *ptr); ++ ++#endif /* DUK_CMDLINE_H_INCLUDED */ +diff --git a/dist/source/examples/cmdline/duk_cmdline_lowmem.c b/dist/source/examples/cmdline/duk_cmdline_lowmem.c +new file mode 100644 +index 0000000..3f3dea1 +--- /dev/null ++++ b/dist/source/examples/cmdline/duk_cmdline_lowmem.c +@@ -0,0 +1,1016 @@ ++/* ++ * Examples for low memory techniques ++ */ ++ ++#if defined(DUK_CMDLINE_LOWMEM) ++ ++#include ++#include ++#include ++#include ++#include "duktape.h" ++#include "duk_cmdline.h" ++#include "duk_alloc_pool.h" ++ ++#if defined(DUK_USE_ROM_OBJECTS) && defined(DUK_USE_HEAPPTR16) ++/* Pointer compression with ROM strings/objects: ++ * ++ * For now, use DUK_USE_ROM_OBJECTS to signal the need for compressed ROM ++ * pointers. DUK_USE_ROM_PTRCOMP_FIRST is provided for the ROM pointer ++ * compression range minimum to avoid duplication in user code. ++ */ ++#if 0 /* This extern declaration is provided by duktape.h, array provided by duktape.c. */ ++extern const void * const duk_rom_compressed_pointers[]; ++#endif ++static const void *duk__romptr_low = NULL; ++static const void *duk__romptr_high = NULL; ++#define DUK__ROMPTR_COMPRESSION ++#define DUK__ROMPTR_FIRST ((duk_uint_t) DUK_USE_ROM_PTRCOMP_FIRST) ++#endif ++ ++#define LOWMEM_NUM_POOLS 28 ++ ++#define LOWMEM_HEAP_SIZE (255 * 1024) ++ ++static const duk_pool_config lowmem_config[LOWMEM_NUM_POOLS] = { ++ { 8, 10 * 8, 0 }, ++ { 12, 600 * 12, 0 }, ++ { 16, 300 * 16, 0 }, ++ { 20, 300 * 20, 0 }, ++ { 24, 300 * 24, 0 }, ++ { 28, 250 * 28, 0 }, ++ { 32, 150 * 32, 0 }, ++ { 40, 150 * 40, 0 }, ++ { 48, 50 * 48, 0 }, ++ { 52, 50 * 52, 0 }, ++ { 56, 50 * 56, 0 }, ++ { 60, 50 * 60, 0 }, ++ { 64, 50 * 64, 0 }, ++ { 96, 50 * 96, 0 }, ++ { 196, 0, 196 }, /* duk_heap, with heap ptr compression, ROM strings+objects */ ++ { 232, 0, 232 }, /* duk_hthread, with heap ptr compression, ROM strings+objects */ ++ { 256, 16 * 256, 0 }, ++ { 288, 1 * 288, 0 }, ++ { 320, 1 * 320, 0 }, ++ { 400, 0, 400 }, /* duk_hthread, with heap ptr compression, RAM strings+objects */ ++ { 520, 0, 520 }, /* duk_heap, with heap ptr compression, RAM strings+objects */ ++ { 512, 16 * 512, 0 }, ++ { 768, 0, 768 }, /* initial value stack for packed duk_tval */ ++ { 1024, 6 * 1024, 0 }, ++ { 2048, 5 * 2048, 0 }, ++ { 4096, 3 * 4096, 0 }, ++ { 8192, 3 * 8192, 0 }, ++ { 16384, 1 * 16384, 0 }, ++}; ++ ++static duk_pool_state lowmem_state[LOWMEM_NUM_POOLS]; ++ ++static duk_pool_global lowmem_global; ++ ++void *lowmem_pool_ptr = NULL; ++ ++uint8_t *lowmem_ram = NULL; ++ ++static void *duk__lose_const(const void *ptr) { ++ /* Somewhat portable way of losing a const without warnings. ++ * Another approach is to cast through intptr_t, but that ++ * type is not always available. ++ */ ++ union { ++ const void *p; ++ void *q; ++ } u; ++ u.p = ptr; ++ return u.q; ++} ++ ++static void duk__safe_print_chars(const char *p, duk_size_t len, int until_nul) { ++ duk_size_t i; ++ ++ fprintf(stderr, "\""); ++ 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 == '\\') { ++ fprintf(stderr, "\\x%02x", (int) x); ++ } else { ++ fprintf(stderr, "%c", (char) x); ++ } ++ } ++ fprintf(stderr, "\""); ++} ++ ++ ++void lowmem_init(void) { ++ void *ptr; ++ ++ lowmem_ram = (uint8_t *) malloc(LOWMEM_HEAP_SIZE); ++ if (lowmem_ram == NULL) { ++ fprintf(stderr, "Failed to allocate lowmem heap\n"); ++ fflush(stderr); ++ exit(1); ++ } ++ ++ ptr = duk_alloc_pool_init((char *) lowmem_ram, ++ LOWMEM_HEAP_SIZE, ++ lowmem_config, ++ lowmem_state, ++ LOWMEM_NUM_POOLS, ++ &lowmem_global); ++ if (ptr == NULL) { ++ free(lowmem_ram); ++ lowmem_ram = NULL; ++ fprintf(stderr, "Failed to init lowmem pool\n"); ++ fflush(stderr); ++ exit(1); ++ } ++ ++ lowmem_pool_ptr = ptr; ++ ++#if defined(DUK__ROMPTR_COMPRESSION) ++ /* Scan ROM pointer range for faster detection of "is 'p' a ROM pointer" ++ * later on. ++ */ ++ if (1) { ++ const void * const * ptrs = (const void * const *) duk_rom_compressed_pointers; ++ duk__romptr_low = duk__romptr_high = (const void *) *ptrs; ++ while (*ptrs) { ++ if (*ptrs > duk__romptr_high) { ++ duk__romptr_high = (const void *) *ptrs; ++ } ++ if (*ptrs < duk__romptr_low) { ++ duk__romptr_low = (const void *) *ptrs; ++ } ++ ptrs++; ++ } ++ fprintf(stderr, "romptrs: low=%p high=%p\n", ++ (const void *) duk__romptr_low, (const void *) duk__romptr_high); ++ fflush(stderr); ++ } ++#endif ++} ++ ++void lowmem_free(void) { ++ if (lowmem_ram != NULL) { ++ free(lowmem_ram); ++ lowmem_ram = NULL; ++ } ++ lowmem_pool_ptr = NULL; ++} ++ ++static duk_ret_t lowmem__dump_binding(duk_context *ctx) { ++ lowmem_dump(); ++ return 0; ++} ++ ++void lowmem_dump(void) { ++ int i; ++ duk_pool_global_stats global_stats; ++ ++ for (i = 0; i < LOWMEM_NUM_POOLS; i++) { ++ duk_pool_state *s = &lowmem_state[i]; ++ duk_pool_stats stats; ++ ++ duk_alloc_pool_get_pool_stats(s, &stats); ++ ++ fprintf(stderr, " %2ld: %4ld %5ldB | free %4ld %5ldB | used %4ld %5ldB | waste %5ldB | hwm %4ld (%3ld%%)%s\n", ++ (long) i, (long) s->count, (long) s->size, ++ (long) stats.free_count, (long) stats.free_bytes, ++ (long) stats.used_count, (long) stats.used_bytes, ++ (long) stats.waste_bytes, (long) stats.hwm_used_count, ++ (long) ((double) stats.hwm_used_count / (double) s->count * 100.0), ++ (stats.hwm_used_count == s->count ? " !" : "")); ++ } ++ ++ /* This causes another walk over the individual pools which is a bit ++ * inelegant, but we want the highwater mark stats too. ++ */ ++ duk_alloc_pool_get_global_stats(&lowmem_global, &global_stats); ++ ++ fprintf(stderr, " TOTAL: %ld bytes used, %ld bytes waste, %ld bytes free, %ld bytes total; highwater %ld used, %ld waste\n", ++ (long) global_stats.used_bytes, (long) global_stats.waste_bytes, ++ (long) global_stats.free_bytes, (long) (global_stats.used_bytes + global_stats.free_bytes), ++ (long) global_stats.hwm_used_bytes, (long) global_stats.hwm_waste_bytes); ++ fflush(stderr); ++} ++ ++void lowmem_register(duk_context *ctx) { ++ duk_push_global_object(ctx); ++ duk_push_string(ctx, "dumpHeap"); ++ duk_push_c_function(ctx, lowmem__dump_binding, 0); ++ duk_def_prop(ctx, -3, DUK_DEFPROP_SET_WRITABLE | ++ DUK_DEFPROP_CLEAR_ENUMERABLE | ++ DUK_DEFPROP_SET_CONFIGURABLE | ++ DUK_DEFPROP_HAVE_VALUE); ++ duk_pop(ctx); ++} ++ ++/* ++ * Wrapped alloc functions ++ * ++ * Used to write an alloc log. ++ */ ++ ++static FILE *lowmem_alloc_log = NULL; ++ ++static void lowmem_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 (lowmem_alloc_log == NULL) { ++ lowmem_alloc_log = fopen("/tmp/lowmem-alloc-log.txt", "wb"); ++ if (lowmem_alloc_log == NULL) { ++ fprintf(stderr, "WARNING: failed to write alloc log, ignoring\n"); ++ fflush(stderr); ++ return; ++ } ++ } ++ ++ (void) fwrite((const void *) buf, 1, strlen(buf), lowmem_alloc_log); ++ (void) fflush(lowmem_alloc_log); ++} ++ ++void *lowmem_alloc_wrapped(void *udata, duk_size_t size) { ++ void *ret = duk_alloc_pool(udata, size); ++ if (size > 0 && ret == NULL) { ++ lowmem_write_alloc_log("A FAIL %ld\n", (long) size); ++ } else if (ret == NULL) { ++ lowmem_write_alloc_log("A NULL %ld\n", (long) size); ++ } else { ++ lowmem_write_alloc_log("A %p %ld\n", ret, (long) size); ++ } ++ return ret; ++} ++ ++void *lowmem_realloc_wrapped(void *udata, void *ptr, duk_size_t size) { ++ void *ret = duk_realloc_pool(udata, ptr, size); ++ if (size > 0 && ret == NULL) { ++ if (ptr == NULL) { ++ lowmem_write_alloc_log("R NULL -1 FAIL %ld\n", (long) size); ++ } else { ++ lowmem_write_alloc_log("R %p -1 FAIL %ld\n", ptr, (long) size); ++ } ++ } else if (ret == NULL) { ++ if (ptr == NULL) { ++ lowmem_write_alloc_log("R NULL -1 NULL %ld\n", (long) size); ++ } else { ++ lowmem_write_alloc_log("R %p -1 NULL %ld\n", ptr, (long) size); ++ } ++ } else { ++ if (ptr == NULL) { ++ lowmem_write_alloc_log("R NULL -1 %p %ld\n", ret, (long) size); ++ } else { ++ lowmem_write_alloc_log("R %p -1 %p %ld\n", ptr, ret, (long) size); ++ } ++ } ++ return ret; ++} ++ ++void lowmem_free_wrapped(void *udata, void *ptr) { ++ duk_free_pool(udata, ptr); ++ if (ptr == NULL) { ++ /* Ignore. */ ++ } else { ++ lowmem_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 lowmem_enc16(void *ud, void *p) { ++ duk_uint32_t ret; ++ char *base = (char *) lowmem_ram - 4; ++ ++#if defined(DUK__ROMPTR_COMPRESSION) ++ if (p >= duk__romptr_low && p <= duk__romptr_high) { ++ /* The if-condition should be the fastest possible check ++ * for "is 'p' in ROM?". If pointer is in ROM, we'd like ++ * to compress it quickly. Here we just scan a ~1K array ++ * which is very bad for performance and for illustration ++ * only. ++ */ ++ const void * const * ptrs = duk_rom_compressed_pointers; ++ while (*ptrs) { ++ if (*ptrs == p) { ++ ret = (duk_uint32_t) DUK__ROMPTR_FIRST + (duk_uint32_t) (ptrs - duk_rom_compressed_pointers); ++#if 0 ++ fprintf(stderr, "lowmem_enc16: rom pointer: %p -> 0x%04lx\n", (void *) p, (long) ret); ++ fflush(stderr); ++#endif ++ return (duk_uint16_t) ret; ++ } ++ ptrs++; ++ } ++ ++ /* We should really never be here: Duktape should only be ++ * compressing pointers which are in the ROM compressed ++ * pointers list, which are known when configuring sources. ++ * We go on, causing a pointer compression error. ++ */ ++ fprintf(stderr, "lowmem_enc16: rom pointer: %p could not be compressed, should never happen\n", (void *) p); ++ fflush(stderr); ++ } ++#endif ++ ++ /* 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 *) lowmem_pool_ptr) { ++ fprintf(stderr, "invalid udata for lowmem_enc16: %p\n", ud); ++ fflush(stderr); ++ } ++#endif ++ ++ if (p == NULL) { ++ ret = 0; ++ } else { ++ ret = (duk_uint32_t) (((char *) p - base) >> 2); ++ } ++#if 0 ++ fprintf(stderr, "lowmem_enc16: %p -> %u\n", p, (unsigned int) ret); ++#endif ++ if (ret > 0xffffUL) { ++ fprintf(stderr, "Failed to compress pointer: %p (ret was %ld)\n", (void *) p, (long) ret); ++ fflush(stderr); ++ abort(); ++ } ++#if defined(DUK__ROMPTR_COMPRESSION) ++ if (ret >= (duk_uint32_t) DUK__ROMPTR_FIRST) { ++ fprintf(stderr, "Failed to compress pointer, in 16-bit range but matches romptr range: %p (ret was %ld)\n", (void *) p, (long) ret); ++ fflush(stderr); ++ abort(); ++ } ++#endif ++ return (duk_uint16_t) ret; ++} ++ ++void *lowmem_dec16(void *ud, duk_uint16_t x) { ++ void *ret; ++ char *base = (char *) lowmem_ram - 4; ++ ++#if defined(DUK__ROMPTR_COMPRESSION) ++ if (x >= (duk_uint16_t) DUK__ROMPTR_FIRST) { ++ /* This is a blind lookup, could check index validity. ++ * Duktape should never decompress a pointer which would ++ * be out-of-bounds here. ++ */ ++ ret = (void *) duk__lose_const(duk_rom_compressed_pointers[x - (duk_uint16_t) DUK__ROMPTR_FIRST]); ++#if 0 ++ fprintf(stderr, "lowmem_dec16: rom pointer: 0x%04lx -> %p\n", (long) x, ret); ++ fflush(stderr); ++#endif ++ return ret; ++ } ++#endif ++ ++ /* See userdata discussion in lowmem_enc16(). */ ++ (void) ud; ++#if 1 ++ /* Ensure that we always get the heap_udata given in heap creation. */ ++ if (ud != (void *) lowmem_pool_ptr) { ++ fprintf(stderr, "invalid udata for lowmem_dec16: %p\n", ud); ++ fflush(stderr); ++ } ++#endif ++ ++ if (x == 0) { ++ ret = NULL; ++ } else { ++ ret = (void *) (base + (((duk_uint32_t) x) << 2)); ++ } ++#if 0 ++ fprintf(stderr, "lowmem_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 lowmem_strdata[65536]; ++static size_t lowmem_strdata_used = 0; ++ ++const void *lowmem_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) duk__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 = lowmem_strdata, p_end = p + lowmem_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 ++ fprintf(stderr, "lowmem_extstr_check_1: ptr=%p, len=%ld ", ++ (void *) ptr, (long) len); ++ duk__safe_print_chars((const char *) ptr, len, 0 /*until_nul*/); ++ fprintf(stderr, " -> existing %p (used=%ld)\n", ++ (void *) ret, (long) lowmem_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 (lowmem_strdata_used + len + 1 > sizeof(lowmem_strdata)) { ++#if 0 ++ fprintf(stderr, "lowmem_extstr_check_1: ptr=%p, len=%ld ", (void *) ptr, (long) len); ++ duk__safe_print_chars((const char *) ptr, len, 0 /*until_nul*/); ++ fprintf(stderr, " -> no space (used=%ld)\n", (long) lowmem_strdata_used); ++#endif ++ return NULL; ++ } ++ ++ /* ++ * There is space, add the string to our collection, being careful ++ * to append the NUL. ++ */ ++ ++ ret = lowmem_strdata + lowmem_strdata_used; ++ memcpy(ret, ptr, len); ++ ret[len] = (uint8_t) 0; ++ lowmem_strdata_used += len + 1; ++ ++#if 0 ++ fprintf(stderr, "lowmem_extstr_check_1: ptr=%p, len=%ld -> ", (void *) ptr, (long) len); ++ duk__safe_print_chars((const char *) ptr, len, 0 /*until_nul*/); ++ fprintf(stderr, " -> %p (used=%ld)\n", (void *) ret, (long) lowmem_strdata_used); ++#endif ++ return (const void *) ret; ++} ++ ++void lowmem_extstr_free_1(const void *ptr) { ++ (void) ptr; ++#if 0 ++ fprintf(stderr, "lowmem_extstr_free_1: freeing extstr %p -> ", ptr); ++ duk__safe_print_chars((const char *) ptr, DUK_SIZE_MAX, 1 /*until_nul*/); ++ fprintf(stderr, "\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_source_meta.json in a ++ * prepared source directory, see tools/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. tools/scan_strings.py. ++ */ ++ ++static const char *strdata_duk_builtin_strings[] = { ++ /* ++ * These strings are from tools/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", ++ "\x82" "Regbase", ++ "\x82" "Thread", ++ "\x82" "Handler", ++ "\x82" "Finalizer", ++ "\x82" "Callee", ++ "\x82" "Map", ++ "\x82" "Args", ++ "\x82" "This", ++ "\x82" "Pc2line", ++ "\x82" "Source", ++ "\x82" "Varenv", ++ "\x82" "Lexenv", ++ "\x82" "Varmap", ++ "\x82" "Formals", ++ "\x82" "Bytecode", ++ "\x82" "Next", ++ "\x82" "Target", ++ "\x82" "Value", ++ "pointer", ++ "buffer", ++ "\x82" "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 *lowmem_extstr_check_2(const void *ptr, duk_size_t len) { ++ int i, n; ++ ++ (void) duk__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 ++ fprintf(stderr, "lowmem_extstr_check_2: ptr=%p, len=%ld ", ++ (void *) ptr, (long) len); ++ duk__safe_print_chars((const char *) ptr, len, 0 /*until_nul*/); ++ fprintf(stderr, " -> constant string index %ld\n", (long) i); ++#endif ++ return (void *) duk__lose_const(strdata_duk_builtin_strings[i]); ++ } ++ } ++ ++#if 0 ++ fprintf(stderr, "lowmem_extstr_check_2: ptr=%p, len=%ld ", ++ (void *) ptr, (long) len); ++ duk__safe_print_chars((const char *) ptr, len, 0 /*until_nul*/); ++ fprintf(stderr, " -> not found\n"); ++#endif ++ return NULL; ++} ++ ++void lowmem_extstr_free_2(const void *ptr) { ++ (void) ptr; ++#if 0 ++ fprintf(stderr, "lowmem_extstr_free_2: freeing extstr %p -> ", ptr); ++ duk__safe_print_chars((const char *) ptr, DUK_SIZE_MAX, 1 /*until_nul*/); ++ fprintf(stderr, "\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 *lowmem_extstr_check_3(const void *ptr, duk_size_t len) { ++ duk_uint8_t *ret; ++ ++ (void) duk__safe_print_chars; /* potentially unused */ ++ ++ ret = malloc((size_t) len + 1); ++ if (ret == NULL) { ++#if 0 ++ fprintf(stderr, "lowmem_extstr_check_3: ptr=%p, len=%ld ", ++ (void *) ptr, (long) len); ++ duk__safe_print_chars((const char *) ptr, len, 0 /*until_nul*/); ++ fprintf(stderr, " -> 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 ++ fprintf(stderr, "lowmem_extstr_check_3: ptr=%p, len=%ld ", ++ (void *) ptr, (long) len); ++ duk__safe_print_chars((const char *) ptr, len, 0 /*until_nul*/); ++ fprintf(stderr, " -> %p\n", (void *) ret); ++#endif ++ return (const void *) ret; ++} ++ ++void lowmem_extstr_free_3(const void *ptr) { ++ (void) ptr; ++#if 0 ++ fprintf(stderr, "lowmem_extstr_free_3: freeing extstr %p -> ", ptr); ++ duk__safe_print_chars((const char *) ptr, DUK_SIZE_MAX, 1 /*until_nul*/); ++ fprintf(stderr, "\n"); ++#endif ++ free((void *) duk__lose_const(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 lowmem_start_exec_timeout(void) { ++ curr_pcall_start = time(NULL); ++} ++ ++void lowmem_clear_exec_timeout(void) { ++ curr_pcall_start = 0; ++} ++ ++duk_bool_t lowmem_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 ++ fprintf(stderr, "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(stderr); ++#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_LOWMEM */ ++ ++int duk_lowmem_dummy = 0; /* to avoid empty source file */ ++ ++#endif /* DUK_CMDLINE_LOWMEM */ +diff --git a/dist/source/examples/codepage-conv/README.rst b/dist/source/examples/codepage-conv/README.rst +new file mode 100644 +index 0000000..98b53d2 +--- /dev/null ++++ b/dist/source/examples/codepage-conv/README.rst +@@ -0,0 +1,8 @@ ++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. +diff --git a/dist/source/examples/codepage-conv/duk_codepage_conv.c b/dist/source/examples/codepage-conv/duk_codepage_conv.c +new file mode 100644 +index 0000000..315f042 +--- /dev/null ++++ b/dist/source/examples/codepage-conv/duk_codepage_conv.c +@@ -0,0 +1,54 @@ ++/* ++ * 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. */ ++ (void) 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); ++} +diff --git a/dist/source/examples/codepage-conv/duk_codepage_conv.h b/dist/source/examples/codepage-conv/duk_codepage_conv.h +new file mode 100644 +index 0000000..052d27f +--- /dev/null ++++ b/dist/source/examples/codepage-conv/duk_codepage_conv.h +@@ -0,0 +1,8 @@ ++#if !defined(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 */ +diff --git a/dist/source/examples/codepage-conv/test.c b/dist/source/examples/codepage-conv/test.c +new file mode 100644 +index 0000000..7a89dd4 +--- /dev/null ++++ b/dist/source/examples/codepage-conv/test.c +@@ -0,0 +1,298 @@ ++#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');"; ++ ++static duk_ret_t duk__print(duk_context *ctx) { ++ duk_push_string(ctx, " "); ++ duk_insert(ctx, 0); ++ duk_join(ctx, duk_get_top(ctx) - 1); ++ printf("%s\n", duk_safe_to_string(ctx, -1)); ++ return 0; ++} ++ ++/* 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; ++ } ++ ++ /* Minimal print() provider. */ ++ duk_push_c_function(ctx, duk__print, DUK_VARARGS); ++ duk_put_global_string(ctx, "print"); ++ ++ duk_decode_string_codepage(ctx, example_source, strlen(example_source), cp1252); ++ duk_eval_noresult(ctx); ++ ++ duk_destroy_heap(ctx); ++ return 0; ++} +diff --git a/dist/source/examples/coffee/README.rst b/dist/source/examples/coffee/README.rst +new file mode 100644 +index 0000000..f147522 +--- /dev/null ++++ b/dist/source/examples/coffee/README.rst +@@ -0,0 +1,10 @@ ++===================== ++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 +diff --git a/dist/source/examples/coffee/globals.coffee b/dist/source/examples/coffee/globals.coffee +new file mode 100644 +index 0000000..25773cd +--- /dev/null ++++ b/dist/source/examples/coffee/globals.coffee +@@ -0,0 +1,7 @@ ++ ++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 ++ +diff --git a/dist/source/examples/coffee/hello.coffee b/dist/source/examples/coffee/hello.coffee +new file mode 100644 +index 0000000..088ed8d +--- /dev/null ++++ b/dist/source/examples/coffee/hello.coffee +@@ -0,0 +1,2 @@ ++print 'Hello world!' ++print 'version: ' + Duktape.version +diff --git a/dist/source/examples/coffee/mandel.coffee b/dist/source/examples/coffee/mandel.coffee +new file mode 100644 +index 0000000..8e3e170 +--- /dev/null ++++ b/dist/source/examples/coffee/mandel.coffee +@@ -0,0 +1,28 @@ ++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) ++ +diff --git a/dist/source/examples/cpp-exceptions/README.rst b/dist/source/examples/cpp-exceptions/README.rst +new file mode 100644 +index 0000000..fb969da +--- /dev/null ++++ b/dist/source/examples/cpp-exceptions/README.rst +@@ -0,0 +1,29 @@ ++========================================= ++C++ exceptions for long control transfers ++========================================= ++ ++Normally Duktape uses ``setjmp()`` / ``longjmp()`` or their variants for ++internal long control transfers. One downside of these functions is that ++C++ automatic destructors (scope-based resource management, SBRM, a special ++case of RAII) in Duktape/C functions won't be executed which is awkward for ++C++ programmers. ++ ++When ``DUK_USE_CPP_EXCEPTIONS`` is defined, and both Duktape and application ++code is compiled using a C++ compiler, Duktape uses C++ ``try-catch`` and ++``throw`` for internal long control transfers. This allows automatic ++destructors to run as expected. The config option is not enabled by default ++because C++ exceptions are sometimes disabled even when a C++ compiler is ++used (e.g. for performance reasons). ++ ++The ``cpp_exceptions.cpp`` example illustrates how C++ exceptions can be ++used in Duktape/C functions at the moment: ++ ++* Duktape uses C++ try/catch/throw internally; this is not visible to user ++ code directly. ++ ++* Automatic destructors (scope-based resource management) work as expected. ++ ++* C++ exceptions can be used in Duktape/C functions normally, but user ++ exceptions must be caught before they reach Duktape. If this is not ++ done, such exceptions are caught by Duktape and converted to API errors ++ (in other words, they won't propagate "through" Duktape at the moment). +diff --git a/dist/source/examples/cpp-exceptions/cpp_exceptions.cpp b/dist/source/examples/cpp-exceptions/cpp_exceptions.cpp +new file mode 100644 +index 0000000..67faf90 +--- /dev/null ++++ b/dist/source/examples/cpp-exceptions/cpp_exceptions.cpp +@@ -0,0 +1,311 @@ ++/* ++ * Example of how to use DUK_USE_CPP_EXCEPTIONS to support automatic ++ * variables (e.g. destructor calls) in Duktape/C functions. ++ * ++ * Configure and compile with -DDUK_USE_CPP_EXCEPTIONS: ++ * ++ * $ python2 tools/configure.py \ ++ * --source-directory src-input \ ++ * --output-directory /tmp/output \ ++ * --config-metadata config \ ++ * -DDUK_USE_CPP_EXCEPTIONS ++ * ++ * $ g++ -otest -I/tmp/output \ ++ * /tmp/output/duktape.c cpp_exceptions.cpp -lm ++ * ++ * When executed you should see something like: ++ * ++ * $ ./test ++ * my_class instance created ++ * my_class instance destroyed <== destructor gets called ++ * --> rc=1 (SyntaxError: parse error (line 1)) ++ * [...] ++ * ++ * Duktape uses a custom exception class (duk_internal_exception) which ++ * doesn't inherit from any base class, so that catching any base classes ++ * in user code won't accidentally catch exceptions thrown by Duktape. ++ */ ++ ++#if !defined(__cplusplus) ++#error compile using a c++ compiler ++#endif ++ ++#include ++#include ++#include "duktape.h" ++ ++#if defined(__cplusplus) && (__cplusplus >= 201103L) ++#define NOEXCEPT noexcept ++#else ++#define NOEXCEPT throw() ++#endif ++ ++/* ++ * Example class with a destructor ++ */ ++ ++class my_class { ++ public: ++ my_class(); ++ ~my_class(); ++}; ++ ++my_class::my_class() { ++ printf("my_class instance created\n"); ++} ++ ++my_class::~my_class() { ++ printf("my_class instance destroyed\n"); ++} ++ ++/* ++ * SyntaxError caused by eval exits Duktape/C function but destructors ++ * are executed. ++ */ ++ ++duk_ret_t test1(duk_context *ctx) { ++ my_class myclass; ++ ++ duk_eval_string(ctx, "aiee="); ++ ++ return 0; ++} ++duk_ret_t test1_safecall(duk_context *ctx, void *udata) { ++ (void) udata; ++ return test1(ctx); ++} ++ ++/* ++ * You can use C++ exceptions inside Duktape/C functions for your own ++ * purposes but you should catch them before they propagate to Duktape. ++ */ ++ ++duk_ret_t test2(duk_context *ctx) { ++ my_class myclass; ++ ++ try { ++ throw 123; ++ } catch (int myvalue) { ++ printf("Caught: %d\n", myvalue); ++ } ++ ++ return 0; ++} ++duk_ret_t test2_safecall(duk_context *ctx, void *udata) { ++ (void) udata; ++ return test2(ctx); ++} ++ ++/* ++ * If you let your own C++ exceptions propagate out of a Duktape/C function ++ * it will be caught by Duktape and considered a programming error. Duktape ++ * will catch the exception and convert it to a Duktape error. ++ * ++ * This may be allowed in a later version once all the implications have been ++ * worked out. ++ */ ++ ++duk_ret_t test3(duk_context *ctx) { ++ my_class myclass; ++ ++ throw 123; /* ERROR: exception propagated to Duktape */ ++ ++ return 0; ++} ++duk_ret_t test3_safecall(duk_context *ctx, void *udata) { ++ (void) udata; ++ return test3(ctx); ++} ++ ++/* ++ * Same as above, but if the exception inherits from std::exception, it's ++ * "what()" will be included in the error message. ++ */ ++ ++class my_exception : public std::exception { ++ virtual const char *what() const NOEXCEPT { ++ return "my_exception"; ++ } ++}; ++ ++duk_ret_t test4(duk_context *ctx) { ++ my_class myclass; ++ my_exception myexc; ++ ++ throw myexc; /* ERROR: exception propagated to Duktape */ ++ ++ return 0; ++} ++duk_ret_t test4_safecall(duk_context *ctx, void *udata) { ++ (void) udata; ++ return test4(ctx); ++} ++ ++/* ++ * Same as above, but if the exception inherits from std::exception with ++ * a NULL what(). Duktape will describe the error as 'unknown' if so. ++ */ ++ ++class my_exception2 : public std::exception { ++ virtual const char *what() const NOEXCEPT { ++ return NULL; ++ } ++}; ++ ++duk_ret_t test5(duk_context *ctx) { ++ my_class myclass; ++ my_exception2 myexc; ++ ++ throw myexc; /* ERROR: exception propagated to Duktape */ ++ ++ return 0; ++} ++duk_ret_t test5_safecall(duk_context *ctx, void *udata) { ++ (void) udata; ++ return test5(ctx); ++} ++ ++static duk_ret_t duk__print(duk_context *ctx) { ++ duk_push_string(ctx, " "); ++ duk_insert(ctx, 0); ++ duk_join(ctx, duk_get_top(ctx) - 1); ++ printf("%s\n", duk_safe_to_string(ctx, -1)); ++ return 0; ++} ++ ++int main(int argc, char *argv[]) { ++ duk_context *ctx = duk_create_heap_default(); ++ duk_int_t rc; ++ ++ (void) argc; (void) argv; /* suppress warning */ ++ ++ /* Minimal print() provider. */ ++ duk_push_c_function(ctx, duk__print, DUK_VARARGS); ++ duk_put_global_string(ctx, "print"); ++ ++ printf("*** test1 - duk_pcall()\n"); ++ duk_push_c_function(ctx, test1, 0); ++ rc = duk_pcall(ctx, 0); ++ printf("--> rc=%ld (%s)\n", (long) rc, duk_safe_to_string(ctx, -1)); ++ duk_pop(ctx); ++ printf("\n"); ++ ++ printf("*** test1 - duk_safe_call()\n"); ++ rc = duk_safe_call(ctx, test1_safecall, NULL, 0, 1); ++ printf("--> rc=%ld (%s)\n", (long) rc, duk_safe_to_string(ctx, -1)); ++ duk_pop(ctx); ++ printf("\n"); ++ ++ printf("*** test1 - ecmascript try-catch\n"); ++ duk_push_c_function(ctx, test1, 0); ++ duk_put_global_string(ctx, "test1"); ++ duk_eval_string_noresult(ctx, ++ "try {\n" ++ " test1();\n" ++ "} catch (e) {\n" ++ " print(e.stack || e);\n" ++ "}\n"); ++ printf("\n"); ++ ++ printf("*** test2 - duk_pcall()\n"); ++ duk_push_c_function(ctx, test2, 0); ++ rc = duk_pcall(ctx, 0); ++ printf("--> rc=%ld (%s)\n", (long) rc, duk_safe_to_string(ctx, -1)); ++ duk_pop(ctx); ++ printf("\n"); ++ ++ printf("*** test2 - duk_safe_call()\n"); ++ rc = duk_safe_call(ctx, test2_safecall, NULL, 0, 1); ++ printf("--> rc=%ld (%s)\n", (long) rc, duk_safe_to_string(ctx, -1)); ++ duk_pop(ctx); ++ printf("\n"); ++ ++ printf("*** test2 - ecmascript try-catch\n"); ++ duk_push_c_function(ctx, test2, 0); ++ duk_put_global_string(ctx, "test2"); ++ duk_eval_string_noresult(ctx, ++ "try {\n" ++ " test2();\n" ++ "} catch (e) {\n" ++ " print(e.stack || e);\n" ++ "}\n"); ++ printf("\n"); ++ ++ printf("*** test3 - duk_pcall()\n"); ++ duk_push_c_function(ctx, test3, 0); ++ rc = duk_pcall(ctx, 0); ++ printf("--> rc=%ld (%s)\n", (long) rc, duk_safe_to_string(ctx, -1)); ++ duk_pop(ctx); ++ printf("\n"); ++ ++ printf("*** test3 - duk_safe_call()\n"); ++ rc = duk_safe_call(ctx, test3_safecall, NULL, 0, 1); ++ printf("--> rc=%ld (%s)\n", (long) rc, duk_safe_to_string(ctx, -1)); ++ duk_pop(ctx); ++ printf("\n"); ++ ++ printf("*** test3 - ecmascript try-catch\n"); ++ duk_push_c_function(ctx, test3, 0); ++ duk_put_global_string(ctx, "test3"); ++ duk_eval_string_noresult(ctx, ++ "try {\n" ++ " test3();\n" ++ "} catch (e) {\n" ++ " print(e.stack || e);\n" ++ "}\n"); ++ printf("\n"); ++ ++ printf("*** test4 - duk_pcall()\n"); ++ duk_push_c_function(ctx, test4, 0); ++ rc = duk_pcall(ctx, 0); ++ printf("--> rc=%ld (%s)\n", (long) rc, duk_safe_to_string(ctx, -1)); ++ duk_pop(ctx); ++ printf("\n"); ++ ++ printf("*** test4 - duk_safe_call()\n"); ++ rc = duk_safe_call(ctx, test4_safecall, NULL, 0, 1); ++ printf("--> rc=%ld (%s)\n", (long) rc, duk_safe_to_string(ctx, -1)); ++ duk_pop(ctx); ++ printf("\n"); ++ ++ printf("*** test4 - ecmascript try-catch\n"); ++ duk_push_c_function(ctx, test4, 0); ++ duk_put_global_string(ctx, "test4"); ++ duk_eval_string_noresult(ctx, ++ "try {\n" ++ " test4();\n" ++ "} catch (e) {\n" ++ " print(e.stack || e);\n" ++ "}\n"); ++ printf("\n"); ++ ++ printf("*** test5 - duk_pcall()\n"); ++ duk_push_c_function(ctx, test5, 0); ++ rc = duk_pcall(ctx, 0); ++ printf("--> rc=%ld (%s)\n", (long) rc, duk_safe_to_string(ctx, -1)); ++ duk_pop(ctx); ++ printf("\n"); ++ ++ printf("*** test5 - duk_safe_call()\n"); ++ rc = duk_safe_call(ctx, test5_safecall, NULL, 0, 1); ++ printf("--> rc=%ld (%s)\n", (long) rc, duk_safe_to_string(ctx, -1)); ++ duk_pop(ctx); ++ printf("\n"); ++ ++ printf("*** test5 - ecmascript try-catch\n"); ++ duk_push_c_function(ctx, test5, 0); ++ duk_put_global_string(ctx, "test5"); ++ duk_eval_string_noresult(ctx, ++ "try {\n" ++ " test5();\n" ++ "} catch (e) {\n" ++ " print(e.stack || e);\n" ++ "}\n"); ++ printf("\n"); ++ ++ printf("*** done\n"); ++ ++ duk_destroy_heap(ctx); ++ ++ return 0; ++} +diff --git a/dist/source/examples/debug-trans-dvalue/Makefile b/dist/source/examples/debug-trans-dvalue/Makefile +new file mode 100644 +index 0000000..79b7317 +--- /dev/null ++++ b/dist/source/examples/debug-trans-dvalue/Makefile +@@ -0,0 +1,16 @@ ++DUKTAPE_OPTS= ++DUKTAPE_OPTS+=-DDUK_USE_ASSERTIONS ++DUKTAPE_OPTS+=-DDUK_USE_DEBUGGER_SUPPORT -DDUK_USE_INTERRUPT_COUNTER ++DUKTAPE_OPTS+=-DDUK_USE_DEBUGGER_DUMPHEAP ++#DUKTAPE_OPTS+=-DDUK_USE_DEBUGGER_TRANSPORT_TORTURE ++TRANS_OPTS= ++#TRANS_OPTS+=-DDEBUG_PRINTS ++ ++test: test.c duk_trans_dvalue.c duk_trans_dvalue.h ++ rm -rf ./prep ++ python2 ../../tools/configure.py \ ++ --output-directory ./prep \ ++ $(DUKTAPE_OPTS) ++ gcc -O0 -g -ggdb -Wall -Wextra -std=c99 -o test -I./prep -I. \ ++ $(TRANS_OPTS) \ ++ ./prep/duktape.c duk_trans_dvalue.c test.c -lm +diff --git a/dist/source/examples/debug-trans-dvalue/README.rst b/dist/source/examples/debug-trans-dvalue/README.rst +new file mode 100644 +index 0000000..86b2bb5 +--- /dev/null ++++ b/dist/source/examples/debug-trans-dvalue/README.rst +@@ -0,0 +1,8 @@ ++=========================================================== ++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. +diff --git a/dist/source/examples/debug-trans-dvalue/duk_trans_dvalue.c b/dist/source/examples/debug-trans-dvalue/duk_trans_dvalue.c +new file mode 100644 +index 0000000..84cf70b +--- /dev/null ++++ b/dist/source/examples/debug-trans-dvalue/duk_trans_dvalue.c +@@ -0,0 +1,1241 @@ ++/* ++ * Example debug transport with a local debug message encoder/decoder. ++ * ++ * Provides a "received dvalue" callback for a fully parsed dvalue (user ++ * code frees dvalue) and a "cooperate" callback for e.g. UI integration. ++ * There are a few other callbacks. See test.c for usage examples. ++ * ++ * This transport implementation is not multithreaded which means that: ++ * ++ * - Callbacks to "received dvalue" callback come from the Duktape thread, ++ * either during normal execution or from duk_debugger_cooperate(). ++ * ++ * - Calls into duk_trans_dvalue_send() must be made from the callbacks ++ * provided (e.g. "received dvalue" or "cooperate") which use the active ++ * Duktape thread. ++ * ++ * - The only exception to this is when Duktape is idle: you can then call ++ * duk_trans_dvalue_send() from any thread (only one thread at a time). ++ * When you next call into Duktape or call duk_debugger_cooperate(), the ++ * queued data will be read and processed by Duktape. ++ * ++ * There are functions for creating and freeing values; internally they use ++ * malloc() and free() for memory management. Duktape heap alloc functions ++ * are not used to minimize disturbances to the Duktape heap under debugging. ++ * ++ * Doesn't depend on C99 types; assumes "int" is at least 32 bits, and makes ++ * a few assumptions about format specifiers. ++ */ ++ ++#include ++#include ++#include ++ ++#include "duktape.h" ++#include "duk_trans_dvalue.h" ++ ++/* Define to enable debug prints to stderr. */ ++#if 0 ++#define DEBUG_PRINTS ++#endif ++ ++/* Define to enable error prints to stderr. */ ++#if 1 ++#define ERROR_PRINTS ++#endif ++ ++/* ++ * Dvalue handling ++ */ ++ ++duk_dvalue *duk_dvalue_alloc(void) { ++ duk_dvalue *dv = (duk_dvalue *) malloc(sizeof(duk_dvalue)); ++ if (dv) { ++ memset((void *) dv, 0, sizeof(duk_dvalue)); ++ dv->buf = NULL; ++ } ++ return dv; ++} ++ ++void duk_dvalue_free(duk_dvalue *dv) { ++ if (dv) { ++ free(dv->buf); /* tolerates NULL */ ++ dv->buf = NULL; ++ free(dv); ++ } ++} ++ ++static void duk__dvalue_bufesc(duk_dvalue *dv, char *buf, size_t maxbytes, int stresc) { ++ size_t i, limit; ++ ++ *buf = (char) 0; ++ limit = dv->len > maxbytes ? maxbytes : dv->len; ++ for (i = 0; i < limit; i++) { ++ unsigned char c = dv->buf[i]; ++ if (stresc) { ++ if (c >= 0x20 && c <= 0x7e && c != (char) '"' && c != (char) '\'') { ++ sprintf(buf, "%c", c); ++ buf++; ++ } else { ++ sprintf(buf, "\\x%02x", (unsigned int) c); ++ buf += 4; ++ } ++ } else { ++ sprintf(buf, "%02x", (unsigned int) c); ++ buf += 2; ++ } ++ } ++ if (dv->len > maxbytes) { ++ sprintf(buf, "..."); ++ buf += 3; ++ } ++} ++ ++/* Caller must provide a buffer at least DUK_DVALUE_TOSTRING_BUFLEN in size. */ ++void duk_dvalue_to_string(duk_dvalue *dv, char *buf) { ++ char hexbuf[32 * 4 + 4]; /* 32 hex encoded or \xXX escaped bytes, possible "...", NUL */ ++ ++ if (!dv) { ++ sprintf(buf, "NULL"); ++ return; ++ } ++ ++ switch (dv->tag) { ++ case DUK_DVALUE_EOM: ++ sprintf(buf, "EOM"); ++ break; ++ case DUK_DVALUE_REQ: ++ sprintf(buf, "REQ"); ++ break; ++ case DUK_DVALUE_REP: ++ sprintf(buf, "REP"); ++ break; ++ case DUK_DVALUE_ERR: ++ sprintf(buf, "ERR"); ++ break; ++ case DUK_DVALUE_NFY: ++ sprintf(buf, "NFY"); ++ break; ++ case DUK_DVALUE_INTEGER: ++ sprintf(buf, "%d", dv->i); ++ break; ++ case DUK_DVALUE_STRING: ++ duk__dvalue_bufesc(dv, hexbuf, 32, 1); ++ sprintf(buf, "str:%ld:\"%s\"", (long) dv->len, hexbuf); ++ break; ++ case DUK_DVALUE_BUFFER: ++ duk__dvalue_bufesc(dv, hexbuf, 32, 0); ++ sprintf(buf, "buf:%ld:%s", (long) dv->len, hexbuf); ++ break; ++ case DUK_DVALUE_UNUSED: ++ sprintf(buf, "undefined"); ++ break; ++ case DUK_DVALUE_UNDEFINED: ++ sprintf(buf, "undefined"); ++ break; ++ case DUK_DVALUE_NULL: ++ sprintf(buf, "null"); ++ break; ++ case DUK_DVALUE_TRUE: ++ sprintf(buf, "true"); ++ break; ++ case DUK_DVALUE_FALSE: ++ sprintf(buf, "false"); ++ break; ++ case DUK_DVALUE_NUMBER: ++ if (fpclassify(dv->d) == FP_ZERO) { ++ if (signbit(dv->d)) { ++ sprintf(buf, "-0"); ++ } else { ++ sprintf(buf, "0"); ++ } ++ } else { ++ sprintf(buf, "%lg", dv->d); ++ } ++ break; ++ case DUK_DVALUE_OBJECT: ++ duk__dvalue_bufesc(dv, hexbuf, 32, 0); ++ sprintf(buf, "obj:%d:%s", (int) dv->i, hexbuf); ++ break; ++ case DUK_DVALUE_POINTER: ++ duk__dvalue_bufesc(dv, hexbuf, 32, 0); ++ sprintf(buf, "ptr:%s", hexbuf); ++ break; ++ case DUK_DVALUE_LIGHTFUNC: ++ duk__dvalue_bufesc(dv, hexbuf, 32, 0); ++ sprintf(buf, "lfunc:%04x:%s", (unsigned int) dv->i, hexbuf); ++ break; ++ case DUK_DVALUE_HEAPPTR: ++ duk__dvalue_bufesc(dv, hexbuf, 32, 0); ++ sprintf(buf, "heapptr:%s", hexbuf); ++ break; ++ default: ++ sprintf(buf, "unknown:%d", (int) dv->tag); ++ } ++} ++ ++duk_dvalue *duk_dvalue_make_tag(int tag) { ++ duk_dvalue *dv = duk_dvalue_alloc(); ++ if (!dv) { return NULL; } ++ dv->tag = tag; ++ return dv; ++} ++ ++duk_dvalue *duk_dvalue_make_tag_int(int tag, int intval) { ++ duk_dvalue *dv = duk_dvalue_alloc(); ++ if (!dv) { return NULL; } ++ dv->tag = tag; ++ dv->i = intval; ++ return dv; ++} ++ ++duk_dvalue *duk_dvalue_make_tag_double(int tag, double dblval) { ++ duk_dvalue *dv = duk_dvalue_alloc(); ++ if (!dv) { return NULL; } ++ dv->tag = tag; ++ dv->d = dblval; ++ return dv; ++} ++ ++duk_dvalue *duk_dvalue_make_tag_data(int tag, const char *buf, size_t len) { ++ unsigned char *p; ++ duk_dvalue *dv = duk_dvalue_alloc(); ++ if (!dv) { return NULL; } ++ /* Alloc size is len + 1 so that a NUL terminator is always ++ * guaranteed which is convenient, e.g. you can printf() the ++ * value safely. ++ */ ++ p = (unsigned char *) malloc(len + 1); ++ if (!p) { ++ free(dv); ++ return NULL; ++ } ++ memcpy((void *) p, (const void *) buf, len); ++ p[len] = (unsigned char) 0; ++ dv->tag = tag; ++ dv->buf = p; ++ dv->len = len; ++ return dv; ++} ++ ++duk_dvalue *duk_dvalue_make_tag_int_data(int tag, int intval, const char *buf, size_t len) { ++ duk_dvalue *dv = duk_dvalue_make_tag_data(tag, buf, len); ++ if (!dv) { return NULL; } ++ dv->i = intval; ++ return dv; ++} ++ ++/* ++ * Dvalue transport handling ++ */ ++ ++static void duk__trans_dvalue_double_byteswap(duk_trans_dvalue_ctx *ctx, volatile unsigned char *p) { ++ unsigned char t; ++ ++ /* Portable IEEE double byteswap. Relies on runtime detection of ++ * host endianness. ++ */ ++ ++ if (ctx->double_byteorder == 0) { ++ /* little endian */ ++ t = p[0]; p[0] = p[7]; p[7] = t; ++ t = p[1]; p[1] = p[6]; p[6] = t; ++ t = p[2]; p[2] = p[5]; p[5] = t; ++ t = p[3]; p[3] = p[4]; p[4] = t; ++ } else if (ctx->double_byteorder == 1) { ++ /* big endian: ok as is */ ++ ; ++ } else { ++ /* mixed endian */ ++ t = p[0]; p[0] = p[3]; p[3] = t; ++ t = p[1]; p[1] = p[2]; p[2] = t; ++ t = p[4]; p[4] = p[7]; p[7] = t; ++ t = p[5]; p[5] = p[6]; p[6] = t; ++ } ++} ++ ++static unsigned int duk__trans_dvalue_parse_u32(duk_trans_dvalue_ctx *ctx, unsigned char *p) { ++ /* Integers are network endian, read back into host format in ++ * a portable manner. ++ */ ++ (void) ctx; ++ return (((unsigned int) p[0]) << 24) + ++ (((unsigned int) p[1]) << 16) + ++ (((unsigned int) p[2]) << 8) + ++ (((unsigned int) p[3]) << 0); ++} ++ ++static int duk__trans_dvalue_parse_i32(duk_trans_dvalue_ctx *ctx, unsigned char *p) { ++ /* Portable sign handling, doesn't assume 'int' is exactly 32 bits ++ * like a direct cast would. ++ */ ++ unsigned int tmp = duk__trans_dvalue_parse_u32(ctx, p); ++ if (tmp & 0x80000000UL) { ++ return -((int) ((tmp ^ 0xffffffffUL) + 1UL)); ++ } else { ++ return tmp; ++ } ++} ++ ++static unsigned int duk__trans_dvalue_parse_u16(duk_trans_dvalue_ctx *ctx, unsigned char *p) { ++ /* Integers are network endian, read back into host format. */ ++ (void) ctx; ++ return (((unsigned int) p[0]) << 8) + ++ (((unsigned int) p[1]) << 0); ++} ++ ++static double duk__trans_dvalue_parse_double(duk_trans_dvalue_ctx *ctx, unsigned char *p) { ++ /* IEEE doubles are network endian, read back into host format. */ ++ volatile union { ++ double d; ++ unsigned char b[8]; ++ } u; ++ memcpy((void *) u.b, (const void *) p, 8); ++ duk__trans_dvalue_double_byteswap(ctx, u.b); ++ return u.d; ++} ++ ++static unsigned char *duk__trans_dvalue_encode_u32(duk_trans_dvalue_ctx *ctx, unsigned char *p, unsigned int val) { ++ /* Integers are written in network endian format. */ ++ (void) ctx; ++ *p++ = (unsigned char) ((val >> 24) & 0xff); ++ *p++ = (unsigned char) ((val >> 16) & 0xff); ++ *p++ = (unsigned char) ((val >> 8) & 0xff); ++ *p++ = (unsigned char) (val & 0xff); ++ return p; ++} ++ ++static unsigned char *duk__trans_dvalue_encode_i32(duk_trans_dvalue_ctx *ctx, unsigned char *p, int val) { ++ return duk__trans_dvalue_encode_u32(ctx, p, (unsigned int) val & 0xffffffffUL); ++} ++ ++static unsigned char *duk__trans_dvalue_encode_u16(duk_trans_dvalue_ctx *ctx, unsigned char *p, unsigned int val) { ++ /* Integers are written in network endian format. */ ++ (void) ctx; ++ *p++ = (unsigned char) ((val >> 8) & 0xff); ++ *p++ = (unsigned char) (val & 0xff); ++ return p; ++} ++ ++static unsigned char *duk__trans_dvalue_encode_double(duk_trans_dvalue_ctx *ctx, unsigned char *p, double val) { ++ /* IEEE doubles are written in network endian format. */ ++ volatile union { ++ double d; ++ unsigned char b[8]; ++ } u; ++ u.d = val; ++ duk__trans_dvalue_double_byteswap(ctx, u.b); ++ memcpy((void *) p, (const void *) u.b, 8); ++ p += 8; ++ return p; ++} ++ ++static unsigned char *duk__trans_buffer_ensure(duk_trans_buffer *dbuf, size_t space) { ++ size_t avail; ++ size_t used; ++ size_t new_size; ++ void *new_alloc; ++ ++ used = dbuf->write_offset; ++ avail = dbuf->alloc_size - dbuf->write_offset; ++ ++ if (avail >= space) { ++ if (avail - space > 256) { ++ /* Too big, resize so that we reclaim memory if we have just ++ * received a large string/buffer value. ++ */ ++ goto do_realloc; ++ } ++ } else { ++ /* Too small, resize. */ ++ goto do_realloc; ++ } ++ ++ return dbuf->base + dbuf->write_offset; ++ ++ do_realloc: ++ new_size = used + space + 256; /* some extra to reduce resizes */ ++ new_alloc = realloc(dbuf->base, new_size); ++ if (new_alloc) { ++ dbuf->base = (unsigned char *) new_alloc; ++ dbuf->alloc_size = new_size; ++#if defined(DEBUG_PRINTS) ++ fprintf(stderr, "%s: resized buffer %p to %ld bytes, read_offset=%ld, write_offset=%ld\n", ++ __func__, (void *) dbuf, (long) new_size, (long) dbuf->read_offset, (long) dbuf->write_offset); ++ fflush(stderr); ++#endif ++ return dbuf->base + dbuf->write_offset; ++ } else { ++ return NULL; ++ } ++} ++ ++/* When read_offset is large enough, "rebase" buffer by deleting already ++ * read data and updating offsets. ++ */ ++static void duk__trans_buffer_rebase(duk_trans_buffer *dbuf) { ++ if (dbuf->read_offset > 64) { ++#if defined(DEBUG_PRINTS) ++ fprintf(stderr, "%s: rebasing buffer %p, read_offset=%ld, write_offset=%ld\n", ++ __func__, (void *) dbuf, (long) dbuf->read_offset, (long) dbuf->write_offset); ++ fflush(stderr); ++#endif ++ if (dbuf->write_offset > dbuf->read_offset) { ++ memmove((void *) dbuf->base, (const void *) (dbuf->base + dbuf->read_offset), dbuf->write_offset - dbuf->read_offset); ++ } ++ dbuf->write_offset -= dbuf->read_offset; ++ dbuf->read_offset = 0; ++ } ++} ++ ++duk_trans_dvalue_ctx *duk_trans_dvalue_init(void) { ++ volatile union { ++ double d; ++ unsigned char b[8]; ++ } u; ++ duk_trans_dvalue_ctx *ctx = NULL; ++ ++ ctx = (duk_trans_dvalue_ctx *) malloc(sizeof(duk_trans_dvalue_ctx)); ++ if (!ctx) { goto fail; } ++ memset((void *) ctx, 0, sizeof(duk_trans_dvalue_ctx)); ++ ctx->received = NULL; ++ ctx->cooperate = NULL; ++ ctx->handshake = NULL; ++ ctx->detached = NULL; ++ ctx->send_buf.base = NULL; ++ ctx->recv_buf.base = NULL; ++ ++ ctx->send_buf.base = malloc(256); ++ if (!ctx->send_buf.base) { goto fail; } ++ ctx->send_buf.alloc_size = 256; ++ ++ ctx->recv_buf.base = malloc(256); ++ if (!ctx->recv_buf.base) { goto fail; } ++ ctx->recv_buf.alloc_size = 256; ++ ++ /* IEEE double byte order, detect at run time (could also use ++ * preprocessor defines but that's verbose to make portable). ++ * ++ * >>> struct.unpack('>d', '1122334455667788'.decode('hex')) ++ * (3.841412024471731e-226,) ++ * >>> struct.unpack('>d', '8877665544332211'.decode('hex')) ++ * (-7.086876636573014e-268,) ++ * >>> struct.unpack('>d', '4433221188776655'.decode('hex')) ++ * (3.5294303071877444e+20,) ++ */ ++ u.b[0] = 0x11; u.b[1] = 0x22; u.b[2] = 0x33; u.b[3] = 0x44; ++ u.b[4] = 0x55; u.b[5] = 0x66; u.b[6] = 0x77; u.b[7] = 0x88; ++ if (u.d < 0.0) { ++ ctx->double_byteorder = 0; /* little endian */ ++ } else if (u.d < 1.0) { ++ ctx->double_byteorder = 1; /* big endian */ ++ } else { ++ ctx->double_byteorder = 2; /* mixed endian (arm) */ ++ } ++#if defined(DEBUG_PRINTS) ++ fprintf(stderr, "double endianness test value is %lg -> byteorder %d\n", ++ u.d, ctx->double_byteorder); ++ fflush(stderr); ++#endif ++ ++ return ctx; ++ ++ fail: ++ if (ctx) { ++ free(ctx->recv_buf.base); /* tolerates NULL */ ++ free(ctx->send_buf.base); /* tolerates NULL */ ++ free(ctx); ++ } ++ return NULL; ++} ++ ++void duk_trans_dvalue_free(duk_trans_dvalue_ctx *ctx) { ++ if (ctx) { ++ free(ctx->send_buf.base); /* tolerates NULL */ ++ free(ctx->recv_buf.base); /* tolerates NULL */ ++ free(ctx); ++ } ++} ++ ++void duk_trans_dvalue_send(duk_trans_dvalue_ctx *ctx, duk_dvalue *dv) { ++ unsigned char *p; ++ ++ /* Convert argument dvalue into Duktape debug protocol format. ++ * Literal constants are used here for the debug protocol, ++ * e.g. initial byte 0x02 is REP, see doc/debugger.rst. ++ */ ++ ++#if defined(DEBUG_PRINTS) ++ { ++ char buf[DUK_DVALUE_TOSTRING_BUFLEN]; ++ duk_dvalue_to_string(dv, buf); ++ fprintf(stderr, "%s: sending dvalue: %s\n", __func__, buf); ++ fflush(stderr); ++ } ++#endif ++ ++ switch (dv->tag) { ++ case DUK_DVALUE_EOM: { ++ p = duk__trans_buffer_ensure(&ctx->send_buf, 1); ++ if (!p) { goto alloc_error; } ++ *p++ = 0x00; ++ ctx->send_buf.write_offset += 1; ++ break; ++ } ++ case DUK_DVALUE_REQ: { ++ p = duk__trans_buffer_ensure(&ctx->send_buf, 1); ++ if (!p) { goto alloc_error; } ++ *p++ = 0x01; ++ ctx->send_buf.write_offset += 1; ++ break; ++ } ++ case DUK_DVALUE_REP: { ++ p = duk__trans_buffer_ensure(&ctx->send_buf, 1); ++ if (!p) { goto alloc_error; } ++ *p++ = 0x02; ++ ctx->send_buf.write_offset += 1; ++ break; ++ } ++ case DUK_DVALUE_ERR: { ++ p = duk__trans_buffer_ensure(&ctx->send_buf, 1); ++ if (!p) { goto alloc_error; } ++ *p++ = 0x03; ++ ctx->send_buf.write_offset += 1; ++ break; ++ } ++ case DUK_DVALUE_NFY: { ++ p = duk__trans_buffer_ensure(&ctx->send_buf, 1); ++ if (!p) { goto alloc_error; } ++ *p++ = 0x04; ++ ctx->send_buf.write_offset += 1; ++ break; ++ } ++ case DUK_DVALUE_INTEGER: { ++ int i = dv->i; ++ if (i >= 0 && i <= 63) { ++ p = duk__trans_buffer_ensure(&ctx->send_buf, 1); ++ if (!p) { goto alloc_error; } ++ *p++ = (unsigned char) (0x80 + i); ++ ctx->send_buf.write_offset += 1; ++ } else if (i >= 0 && i <= 16383L) { ++ p = duk__trans_buffer_ensure(&ctx->send_buf, 2); ++ if (!p) { goto alloc_error; } ++ *p++ = (unsigned char) (0xc0 + (i >> 8)); ++ *p++ = (unsigned char) (i & 0xff); ++ ctx->send_buf.write_offset += 2; ++ } else if (i >= -0x80000000L && i <= 0x7fffffffL) { /* Harmless warning on some platforms (re: range) */ ++ p = duk__trans_buffer_ensure(&ctx->send_buf, 5); ++ if (!p) { goto alloc_error; } ++ *p++ = 0x10; ++ p = duk__trans_dvalue_encode_i32(ctx, p, i); ++ ctx->send_buf.write_offset += 5; ++ } else { ++ goto dvalue_error; ++ } ++ break; ++ } ++ case DUK_DVALUE_STRING: { ++ size_t i = dv->len; ++ if (i <= 0x1fUL) { ++ p = duk__trans_buffer_ensure(&ctx->send_buf, 1 + i); ++ if (!p) { goto alloc_error; } ++ *p++ = (unsigned char) (0x60 + i); ++ memcpy((void *) p, (const void *) dv->buf, i); ++ p += i; ++ ctx->send_buf.write_offset += 1 + i; ++ } else if (i <= 0xffffUL) { ++ p = duk__trans_buffer_ensure(&ctx->send_buf, 3 + i); ++ if (!p) { goto alloc_error; } ++ *p++ = 0x12; ++ p = duk__trans_dvalue_encode_u16(ctx, p, (unsigned int) i); ++ memcpy((void *) p, (const void *) dv->buf, i); ++ p += i; ++ ctx->send_buf.write_offset += 3 + i; ++ } else if (i <= 0xffffffffUL) { ++ p = duk__trans_buffer_ensure(&ctx->send_buf, 5 + i); ++ if (!p) { goto alloc_error; } ++ *p++ = 0x11; ++ p = duk__trans_dvalue_encode_u32(ctx, p, (unsigned int) i); ++ memcpy((void *) p, (const void *) dv->buf, i); ++ p += i; ++ ctx->send_buf.write_offset += 5 + i; ++ } else { ++ goto dvalue_error; ++ } ++ break; ++ } ++ case DUK_DVALUE_BUFFER: { ++ size_t i = dv->len; ++ if (i <= 0xffffUL) { ++ p = duk__trans_buffer_ensure(&ctx->send_buf, 3 + i); ++ if (!p) { goto alloc_error; } ++ *p++ = 0x14; ++ p = duk__trans_dvalue_encode_u16(ctx, p, (unsigned int) i); ++ memcpy((void *) p, (const void *) dv->buf, i); ++ p += i; ++ ctx->send_buf.write_offset += 3 + i; ++ } else if (i <= 0xffffffffUL) { ++ p = duk__trans_buffer_ensure(&ctx->send_buf, 5 + i); ++ if (!p) { goto alloc_error; } ++ *p++ = 0x13; ++ p = duk__trans_dvalue_encode_u32(ctx, p, (unsigned int) i); ++ memcpy((void *) p, (const void *) dv->buf, i); ++ p += i; ++ ctx->send_buf.write_offset += 5 + i; ++ } else { ++ goto dvalue_error; ++ } ++ break; ++ } ++ case DUK_DVALUE_UNUSED: { ++ p = duk__trans_buffer_ensure(&ctx->send_buf, 1); ++ if (!p) { goto alloc_error; } ++ *p++ = 0x15; ++ ctx->send_buf.write_offset += 1; ++ break; ++ } ++ case DUK_DVALUE_UNDEFINED: { ++ p = duk__trans_buffer_ensure(&ctx->send_buf, 1); ++ if (!p) { goto alloc_error; } ++ *p++ = 0x16; ++ ctx->send_buf.write_offset += 1; ++ break; ++ } ++ case DUK_DVALUE_NULL: { ++ p = duk__trans_buffer_ensure(&ctx->send_buf, 1); ++ if (!p) { goto alloc_error; } ++ *p++ = 0x17; ++ ctx->send_buf.write_offset += 1; ++ break; ++ } ++ case DUK_DVALUE_TRUE: { ++ p = duk__trans_buffer_ensure(&ctx->send_buf, 1); ++ if (!p) { goto alloc_error; } ++ *p++ = 0x18; ++ ctx->send_buf.write_offset += 1; ++ break; ++ } ++ case DUK_DVALUE_FALSE: { ++ p = duk__trans_buffer_ensure(&ctx->send_buf, 1); ++ if (!p) { goto alloc_error; } ++ *p++ = 0x19; ++ ctx->send_buf.write_offset += 1; ++ break; ++ } ++ case DUK_DVALUE_NUMBER: { ++ p = duk__trans_buffer_ensure(&ctx->send_buf, 9); ++ if (!p) { goto alloc_error; } ++ *p++ = 0x1a; ++ p = duk__trans_dvalue_encode_double(ctx, p, dv->d); ++ ctx->send_buf.write_offset += 9; ++ break; ++ } ++ case DUK_DVALUE_OBJECT: { ++ size_t i = dv->len; ++ if (i <= 0xffUL && dv->i >= 0 && dv->i <= 0xffL) { ++ p = duk__trans_buffer_ensure(&ctx->send_buf, 3 + i); ++ if (!p) { goto alloc_error; } ++ *p++ = 0x1b; ++ *p++ = (unsigned char) dv->i; ++ *p++ = (unsigned char) i; ++ memcpy((void *) p, (const void *) dv->buf, i); ++ ctx->send_buf.write_offset += 3 + i; ++ } else { ++ goto dvalue_error; ++ } ++ break; ++ } ++ case DUK_DVALUE_POINTER: { ++ size_t i = dv->len; ++ if (i <= 0xffUL) { ++ p = duk__trans_buffer_ensure(&ctx->send_buf, 2 + i); ++ if (!p) { goto alloc_error; } ++ *p++ = 0x1c; ++ *p++ = (unsigned char) i; ++ memcpy((void *) p, (const void *) dv->buf, i); ++ ctx->send_buf.write_offset += 2 + i; ++ } else { ++ goto dvalue_error; ++ } ++ break; ++ } ++ case DUK_DVALUE_LIGHTFUNC: { ++ size_t i = dv->len; ++ if (i <= 0xffUL && dv->i >= 0 && dv->i <= 0xffffL) { ++ p = duk__trans_buffer_ensure(&ctx->send_buf, 4 + i); ++ if (!p) { goto alloc_error; } ++ *p++ = 0x1d; ++ p = duk__trans_dvalue_encode_u16(ctx, p, (unsigned int) dv->i); ++ *p++ = (unsigned char) i; ++ memcpy((void *) p, (const void *) dv->buf, i); ++ ctx->send_buf.write_offset += 4 + i; ++ } else { ++ goto dvalue_error; ++ } ++ break; ++ } ++ case DUK_DVALUE_HEAPPTR: { ++ size_t i = dv->len; ++ if (i <= 0xffUL) { ++ p = duk__trans_buffer_ensure(&ctx->send_buf, 2 + i); ++ if (!p) { goto alloc_error; } ++ *p++ = 0x1e; ++ *p++ = (unsigned char) i; ++ memcpy((void *) p, (const void *) dv->buf, i); ++ ctx->send_buf.write_offset += 2 + i; ++ } else { ++ goto dvalue_error; ++ } ++ break; ++ } ++ default: { ++ goto dvalue_error; ++ } ++ } /* end switch */ ++ ++ return; ++ ++ dvalue_error: ++#if defined(ERROR_PRINTS) ++ fprintf(stderr, "%s: internal error, argument dvalue is invalid\n", __func__); ++ fflush(stdout); ++#endif ++ return; ++ ++ alloc_error: ++#if defined(ERROR_PRINTS) ++ fprintf(stderr, "%s: internal error, failed to allocate space for write\n", __func__); ++ fflush(stdout); ++#endif ++ return; ++} ++ ++static void duk__trans_dvalue_send_and_free(duk_trans_dvalue_ctx *ctx, duk_dvalue *dv) { ++ if (!dv) { return; } ++ duk_trans_dvalue_send(ctx, dv); ++ duk_dvalue_free(dv); ++} ++ ++void duk_trans_dvalue_send_eom(duk_trans_dvalue_ctx *ctx) { ++ duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag(DUK_DVALUE_EOM)); ++} ++ ++void duk_trans_dvalue_send_req(duk_trans_dvalue_ctx *ctx) { ++ duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag(DUK_DVALUE_REQ)); ++} ++ ++void duk_trans_dvalue_send_rep(duk_trans_dvalue_ctx *ctx) { ++ duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag(DUK_DVALUE_REP)); ++} ++ ++void duk_trans_dvalue_send_err(duk_trans_dvalue_ctx *ctx) { ++ duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag(DUK_DVALUE_ERR)); ++} ++ ++void duk_trans_dvalue_send_nfy(duk_trans_dvalue_ctx *ctx) { ++ duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag(DUK_DVALUE_NFY)); ++} ++ ++void duk_trans_dvalue_send_integer(duk_trans_dvalue_ctx *ctx, int val) { ++ duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag_int(DUK_DVALUE_INTEGER, val)); ++} ++ ++void duk_trans_dvalue_send_string(duk_trans_dvalue_ctx *ctx, const char *str) { ++ duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag_data(DUK_DVALUE_STRING, str, strlen(str))); ++} ++ ++void duk_trans_dvalue_send_lstring(duk_trans_dvalue_ctx *ctx, const char *str, size_t len) { ++ duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag_data(DUK_DVALUE_STRING, str, len)); ++} ++ ++void duk_trans_dvalue_send_buffer(duk_trans_dvalue_ctx *ctx, const char *buf, size_t len) { ++ duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag_data(DUK_DVALUE_BUFFER, buf, len)); ++} ++ ++void duk_trans_dvalue_send_unused(duk_trans_dvalue_ctx *ctx) { ++ duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag(DUK_DVALUE_UNUSED)); ++} ++ ++void duk_trans_dvalue_send_undefined(duk_trans_dvalue_ctx *ctx) { ++ duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag(DUK_DVALUE_UNDEFINED)); ++} ++ ++void duk_trans_dvalue_send_null(duk_trans_dvalue_ctx *ctx) { ++ duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag(DUK_DVALUE_NULL)); ++} ++ ++void duk_trans_dvalue_send_true(duk_trans_dvalue_ctx *ctx) { ++ duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag(DUK_DVALUE_TRUE)); ++} ++ ++void duk_trans_dvalue_send_false(duk_trans_dvalue_ctx *ctx) { ++ duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag(DUK_DVALUE_FALSE)); ++} ++ ++void duk_trans_dvalue_send_number(duk_trans_dvalue_ctx *ctx, double val) { ++ duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag_double(DUK_DVALUE_NUMBER, val)); ++} ++ ++void duk_trans_dvalue_send_object(duk_trans_dvalue_ctx *ctx, int classnum, const char *ptr_data, size_t ptr_len) { ++ duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag_int_data(DUK_DVALUE_OBJECT, classnum, ptr_data, ptr_len)); ++} ++ ++void duk_trans_dvalue_send_pointer(duk_trans_dvalue_ctx *ctx, const char *ptr_data, size_t ptr_len) { ++ duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag_data(DUK_DVALUE_POINTER, ptr_data, ptr_len)); ++} ++ ++void duk_trans_dvalue_send_lightfunc(duk_trans_dvalue_ctx *ctx, int lf_flags, const char *ptr_data, size_t ptr_len) { ++ duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag_int_data(DUK_DVALUE_LIGHTFUNC, lf_flags, ptr_data, ptr_len)); ++} ++ ++void duk_trans_dvalue_send_heapptr(duk_trans_dvalue_ctx *ctx, const char *ptr_data, size_t ptr_len) { ++ duk__trans_dvalue_send_and_free(ctx, duk_dvalue_make_tag_data(DUK_DVALUE_HEAPPTR, ptr_data, ptr_len)); ++} ++ ++void duk_trans_dvalue_send_req_cmd(duk_trans_dvalue_ctx *ctx, int cmd) { ++ duk_trans_dvalue_send_req(ctx); ++ duk_trans_dvalue_send_integer(ctx, cmd); ++} ++ ++static duk_dvalue *duk__trans_trial_parse_dvalue(duk_trans_dvalue_ctx *ctx) { ++ unsigned char *p; ++ size_t len; ++ unsigned char ib; ++ duk_dvalue *dv; ++ size_t datalen; ++ ++ p = ctx->recv_buf.base + ctx->recv_buf.read_offset; ++ len = ctx->recv_buf.write_offset - ctx->recv_buf.read_offset; ++ ++ if (len == 0) { ++ return NULL; ++ } ++ ib = p[0]; ++ ++#if defined(DEBUG_PRINTS) ++ { ++ size_t i; ++ fprintf(stderr, "%s: parsing dvalue, window:", __func__); ++ for (i = 0; i < 16; i++) { ++ if (i < len) { ++ fprintf(stderr, " %02x", (unsigned int) p[i]); ++ } else { ++ fprintf(stderr, " ??"); ++ } ++ } ++ fprintf(stderr, " (length %ld, read_offset %ld, write_offset %ld, alloc_size %ld)\n", ++ (long) len, (long) ctx->recv_buf.read_offset, (long) ctx->recv_buf.write_offset, ++ (long) ctx->recv_buf.alloc_size); ++ fflush(stderr); ++ } ++#endif ++ ++ if (ib <= 0x1fU) { ++ /* 0x00 ... 0x1f */ ++ switch (ib) { ++ case 0x00: { ++ ctx->recv_buf.read_offset += 1; ++ dv = duk_dvalue_make_tag(DUK_DVALUE_EOM); ++ if (!dv) { goto alloc_error; } ++ return dv; ++ } ++ case 0x01: { ++ ctx->recv_buf.read_offset += 1; ++ dv = duk_dvalue_make_tag(DUK_DVALUE_REQ); ++ if (!dv) { goto alloc_error; } ++ return dv; ++ } ++ case 0x02: { ++ ctx->recv_buf.read_offset += 1; ++ dv = duk_dvalue_make_tag(DUK_DVALUE_REP); ++ if (!dv) { goto alloc_error; } ++ return dv; ++ } ++ case 0x03: { ++ ctx->recv_buf.read_offset += 1; ++ dv = duk_dvalue_make_tag(DUK_DVALUE_ERR); ++ if (!dv) { goto alloc_error; } ++ return dv; ++ } ++ case 0x04: { ++ ctx->recv_buf.read_offset += 1; ++ dv = duk_dvalue_make_tag(DUK_DVALUE_NFY); ++ if (!dv) { goto alloc_error; } ++ return dv; ++ } ++ case 0x10: { ++ int intval; ++ if (len < 5) { goto partial; } ++ intval = duk__trans_dvalue_parse_i32(ctx, p + 1); ++ ctx->recv_buf.read_offset += 5; ++ dv = duk_dvalue_make_tag_int(DUK_DVALUE_INTEGER, intval); ++ if (!dv) { goto alloc_error; } ++ return dv; ++ } ++ case 0x11: { ++ if (len < 5) { goto partial; } ++ datalen = (size_t) duk__trans_dvalue_parse_u32(ctx, p + 1); ++ if (len < 5 + datalen) { goto partial; } ++ ctx->recv_buf.read_offset += 5 + datalen; ++ dv = duk_dvalue_make_tag_data(DUK_DVALUE_STRING, (const char *) (p + 5), datalen); ++ if (!dv) { goto alloc_error; } ++ return dv; ++ } ++ case 0x12: { ++ if (len < 3) { goto partial; } ++ datalen = (size_t) duk__trans_dvalue_parse_u16(ctx, p + 1); ++ if (len < 3 + datalen) { goto partial; } ++ ctx->recv_buf.read_offset += 3 + datalen; ++ dv = duk_dvalue_make_tag_data(DUK_DVALUE_STRING, (const char *) (p + 3), datalen); ++ if (!dv) { goto alloc_error; } ++ return dv; ++ } ++ case 0x13: { ++ if (len < 5) { goto partial; } ++ datalen = (size_t) duk__trans_dvalue_parse_u32(ctx, p + 1); ++ if (len < 5 + datalen) { goto partial; } ++ ctx->recv_buf.read_offset += 5 + datalen; ++ dv = duk_dvalue_make_tag_data(DUK_DVALUE_BUFFER, (const char *) (p + 5), datalen); ++ if (!dv) { goto alloc_error; } ++ return dv; ++ } ++ case 0x14: { ++ if (len < 3) { goto partial; } ++ datalen = (size_t) duk__trans_dvalue_parse_u16(ctx, p + 1); ++ if (len < 3 + datalen) { goto partial; } ++ ctx->recv_buf.read_offset += 3 + datalen; ++ dv = duk_dvalue_make_tag_data(DUK_DVALUE_BUFFER, (const char *) (p + 3), datalen); ++ if (!dv) { goto alloc_error; } ++ return dv; ++ } ++ case 0x15: { ++ ctx->recv_buf.read_offset += 1; ++ dv = duk_dvalue_make_tag(DUK_DVALUE_UNUSED); ++ if (!dv) { goto alloc_error; } ++ return dv; ++ } ++ case 0x16: { ++ ctx->recv_buf.read_offset += 1; ++ dv = duk_dvalue_make_tag(DUK_DVALUE_UNDEFINED); ++ if (!dv) { goto alloc_error; } ++ return dv; ++ } ++ case 0x17: { ++ ctx->recv_buf.read_offset += 1; ++ dv = duk_dvalue_make_tag(DUK_DVALUE_NULL); ++ if (!dv) { goto alloc_error; } ++ return dv; ++ } ++ case 0x18: { ++ ctx->recv_buf.read_offset += 1; ++ dv = duk_dvalue_make_tag(DUK_DVALUE_TRUE); ++ if (!dv) { goto alloc_error; } ++ return dv; ++ } ++ case 0x19: { ++ ctx->recv_buf.read_offset += 1; ++ dv = duk_dvalue_make_tag(DUK_DVALUE_FALSE); ++ if (!dv) { goto alloc_error; } ++ return dv; ++ } ++ case 0x1a: { ++ double dblval; ++ if (len < 9) { goto partial; } ++ dblval = duk__trans_dvalue_parse_double(ctx, p + 1); ++ ctx->recv_buf.read_offset += 9; ++ dv = duk_dvalue_make_tag_double(DUK_DVALUE_NUMBER, dblval); ++ if (!dv) { goto alloc_error; } ++ return dv; ++ } ++ case 0x1b: { ++ int classnum; ++ if (len < 3) { goto partial; } ++ datalen = (size_t) p[2]; ++ if (len < 3 + datalen) { goto partial; } ++ classnum = (int) p[1]; ++ ctx->recv_buf.read_offset += 3 + datalen; ++ dv = duk_dvalue_make_tag_int_data(DUK_DVALUE_OBJECT, classnum, (const char *) (p + 3), datalen); ++ if (!dv) { goto alloc_error; } ++ return dv; ++ } ++ case 0x1c: { ++ if (len < 2) { goto partial; } ++ datalen = (size_t) p[1]; ++ if (len < 2 + datalen) { goto partial; } ++ ctx->recv_buf.read_offset += 2 + datalen; ++ dv = duk_dvalue_make_tag_data(DUK_DVALUE_POINTER, (const char *) (p + 2), datalen); ++ if (!dv) { goto alloc_error; } ++ return dv; ++ } ++ case 0x1d: { ++ int lf_flags; ++ if (len < 4) { goto partial; } ++ datalen = (size_t) p[3]; ++ if (len < 4 + datalen) { goto partial; } ++ lf_flags = (int) duk__trans_dvalue_parse_u16(ctx, p + 1); ++ ctx->recv_buf.read_offset += 4 + datalen; ++ dv = duk_dvalue_make_tag_int_data(DUK_DVALUE_LIGHTFUNC, lf_flags, (const char *) (p + 4), datalen); ++ if (!dv) { goto alloc_error; } ++ return dv; ++ } ++ case 0x1e: { ++ if (len < 2) { goto partial; } ++ datalen = (size_t) p[1]; ++ if (len < 2 + datalen) { goto partial; } ++ ctx->recv_buf.read_offset += 2 + datalen; ++ dv = duk_dvalue_make_tag_data(DUK_DVALUE_HEAPPTR, (const char *) (p + 2), datalen); ++ if (!dv) { goto alloc_error; } ++ return dv; ++ } ++ default: { ++ goto format_error; ++ } ++ } /* end switch */ ++ } else if (ib <= 0x5fU) { ++ /* 0x20 ... 0x5f */ ++ goto format_error; ++ } else if (ib <= 0x7fU) { ++ /* 0x60 ... 0x7f */ ++ datalen = (size_t) (ib - 0x60U); ++ if (len < 1 + datalen) { goto partial; } ++ ctx->recv_buf.read_offset += 1 + datalen; ++ dv = duk_dvalue_make_tag_data(DUK_DVALUE_STRING, (const char *) (p + 1), datalen); ++ if (!dv) { goto alloc_error; } ++ return dv; ++ } else if (ib <= 0xbfU) { ++ /* 0x80 ... 0xbf */ ++ int intval; ++ intval = (int) (ib - 0x80U); ++ ctx->recv_buf.read_offset += 1; ++ dv = duk_dvalue_make_tag_int(DUK_DVALUE_INTEGER, intval); ++ if (!dv) { goto alloc_error; } ++ return dv; ++ } else { ++ /* 0xc0 ... 0xff */ ++ int intval; ++ if (len < 2) { goto partial; } ++ intval = (((int) (ib - 0xc0U)) << 8) + (int) p[1]; ++ ctx->recv_buf.read_offset += 2; ++ dv = duk_dvalue_make_tag_int(DUK_DVALUE_INTEGER, intval); ++ if (!dv) { goto alloc_error; } ++ return dv; ++ } ++ ++ /* never here */ ++ ++ partial: ++ return NULL; ++ ++ alloc_error: ++#if defined(ERROR_PRINTS) ++ fprintf(stderr, "%s: internal error, cannot allocate space for dvalue\n", __func__); ++ fflush(stdout); ++#endif ++ return NULL; ++ ++ format_error: ++#if defined(ERROR_PRINTS) ++ fprintf(stderr, "%s: internal error, dvalue format error\n", __func__); ++ fflush(stdout); ++#endif ++ return NULL; ++} ++ ++static duk_dvalue *duk__trans_trial_parse_handshake(duk_trans_dvalue_ctx *ctx) { ++ unsigned char *p; ++ size_t len; ++ duk_dvalue *dv; ++ size_t i; ++ ++ p = ctx->recv_buf.base + ctx->recv_buf.read_offset; ++ len = ctx->recv_buf.write_offset - ctx->recv_buf.read_offset; ++ ++ for (i = 0; i < len; i++) { ++ if (p[i] == 0x0a) { ++ /* Handshake line is returned as a dvalue for convenience; it's ++ * not actually a part of the dvalue phase of the protocol. ++ */ ++ ctx->recv_buf.read_offset += i + 1; ++ dv = duk_dvalue_make_tag_data(DUK_DVALUE_STRING, (const char *) p, i); ++ if (!dv) { goto alloc_error; } ++ return dv; ++ } ++ } ++ ++ return NULL; ++ ++ alloc_error: ++#if defined(ERROR_PRINTS) ++ fprintf(stderr, "%s: internal error, cannot allocate space for handshake line\n", __func__); ++ fflush(stdout); ++#endif ++ return NULL; ++} ++ ++static void duk__trans_call_cooperate(duk_trans_dvalue_ctx *ctx, int block) { ++ if (ctx->cooperate) { ++ ctx->cooperate(ctx, block); ++ } ++} ++ ++static void duk__trans_call_received(duk_trans_dvalue_ctx *ctx, duk_dvalue *dv) { ++ if (ctx->received) { ++ ctx->received(ctx, dv); ++ } ++} ++ ++static void duk__trans_call_handshake(duk_trans_dvalue_ctx *ctx, const char *line) { ++ if (ctx->handshake) { ++ ctx->handshake(ctx, line); ++ } ++} ++ ++static void duk__trans_call_detached(duk_trans_dvalue_ctx *ctx) { ++ if (ctx->detached) { ++ ctx->detached(ctx); ++ } ++} ++ ++/* ++ * Duktape callbacks ++ */ ++ ++duk_size_t duk_trans_dvalue_read_cb(void *udata, char *buffer, duk_size_t length) { ++ duk_trans_dvalue_ctx *ctx = (duk_trans_dvalue_ctx *) udata; ++ ++#if defined(DEBUG_PRINTS) ++ fprintf(stderr, "%s: %p %p %ld\n", __func__, udata, (void *) buffer, (long) length); ++ fflush(stderr); ++#endif ++ ++ duk__trans_call_cooperate(ctx, 0); ++ ++ for (;;) { ++ size_t avail, now; ++ ++ avail = (size_t) (ctx->send_buf.write_offset - ctx->send_buf.read_offset); ++ if (avail == 0) { ++ /* Must cooperate until user callback provides data. From ++ * Duktape's perspective we MUST block until data is received. ++ */ ++ duk__trans_call_cooperate(ctx, 1); ++ } else { ++ now = avail; ++ if (now > length) { ++ now = length; ++ } ++ memcpy((void *) buffer, (const void *) (ctx->send_buf.base + ctx->send_buf.read_offset), now); ++ duk__trans_buffer_rebase(&ctx->send_buf); ++ ctx->send_buf.read_offset += now; ++ return now; ++ } ++ } ++} ++ ++duk_size_t duk_trans_dvalue_write_cb(void *udata, const char *buffer, duk_size_t length) { ++ duk_trans_dvalue_ctx *ctx = (duk_trans_dvalue_ctx *) udata; ++ unsigned char *p; ++ ++#if defined(DEBUG_PRINTS) ++ fprintf(stderr, "%s: %p %p %ld\n", __func__, udata, (void *) buffer, (long) length); ++ fflush(stderr); ++#endif ++ ++ duk__trans_call_cooperate(ctx, 0); ++ ++ /* Append data. */ ++ duk__trans_buffer_rebase(&ctx->recv_buf); ++ p = duk__trans_buffer_ensure(&ctx->recv_buf, length); ++ memcpy((void *) p, (const void *) buffer, (size_t) length); ++ ctx->recv_buf.write_offset += length; ++ ++ /* Trial parse handshake line or dvalue(s). */ ++ if (!ctx->handshake_done) { ++ duk_dvalue *dv = duk__trans_trial_parse_handshake(ctx); ++ if (dv) { ++ /* Handshake line is available for caller for the ++ * duration of the callback, and must not be freed ++ * by the caller. ++ */ ++ duk__trans_call_handshake(ctx, (const char *) dv->buf); ++#if defined(DEBUG_PRINTS) ++ fprintf(stderr, "%s: handshake ok\n", __func__); ++ fflush(stderr); ++#endif ++ duk_dvalue_free(dv); ++ ctx->handshake_done = 1; ++ } ++ } ++ if (ctx->handshake_done) { ++ for (;;) { ++ duk_dvalue *dv = duk__trans_trial_parse_dvalue(ctx); ++ if (dv) { ++#if defined(DEBUG_PRINTS) ++ { ++ char buf[DUK_DVALUE_TOSTRING_BUFLEN]; ++ duk_dvalue_to_string(dv, buf); ++ fprintf(stderr, "%s: received dvalue: %s\n", __func__, buf); ++ fflush(stderr); ++ } ++#endif ++ ++ duk__trans_call_received(ctx, dv); ++ } else { ++ break; ++ } ++ } ++ } ++ ++ duk__trans_call_cooperate(ctx, 0); /* just in case, if dvalues changed something */ ++ ++ return length; ++} ++ ++duk_size_t duk_trans_dvalue_peek_cb(void *udata) { ++ duk_trans_dvalue_ctx *ctx = (duk_trans_dvalue_ctx *) udata; ++ size_t avail; ++ ++#if defined(DEBUG_PRINTS) ++ fprintf(stderr, "%s: %p\n", __func__, udata); ++ fflush(stderr); ++#endif ++ ++ duk__trans_call_cooperate(ctx, 0); ++ avail = (size_t) (ctx->send_buf.write_offset - ctx->send_buf.read_offset); ++ return (duk_size_t) avail; ++} ++ ++void duk_trans_dvalue_read_flush_cb(void *udata) { ++ duk_trans_dvalue_ctx *ctx = (duk_trans_dvalue_ctx *) udata; ++ ++#if defined(DEBUG_PRINTS) ++ fprintf(stderr, "%s: %p\n", __func__, udata); ++ fflush(stderr); ++#endif ++ ++ duk__trans_call_cooperate(ctx, 0); ++} ++ ++void duk_trans_dvalue_write_flush_cb(void *udata) { ++ duk_trans_dvalue_ctx *ctx = (duk_trans_dvalue_ctx *) udata; ++ ++#if defined(DEBUG_PRINTS) ++ fprintf(stderr, "%s: %p\n", __func__, udata); ++ fflush(stderr); ++#endif ++ ++ duk__trans_call_cooperate(ctx, 0); ++} ++ ++void duk_trans_dvalue_detached_cb(duk_context *duk_ctx, void *udata) { ++ duk_trans_dvalue_ctx *ctx = (duk_trans_dvalue_ctx *) udata; ++ ++ (void) duk_ctx; ++ ++#if defined(DEBUG_PRINTS) ++ fprintf(stderr, "%s: %p\n", __func__, udata); ++ fflush(stderr); ++#endif ++ ++ duk__trans_call_detached(ctx); ++} +diff --git a/dist/source/examples/debug-trans-dvalue/duk_trans_dvalue.h b/dist/source/examples/debug-trans-dvalue/duk_trans_dvalue.h +new file mode 100644 +index 0000000..a9f44ed +--- /dev/null ++++ b/dist/source/examples/debug-trans-dvalue/duk_trans_dvalue.h +@@ -0,0 +1,113 @@ ++#if !defined(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(duk_context *ctx, void *udata); ++ ++#endif /* DUK_TRANS_DVALUE_H_INCLUDED */ +diff --git a/dist/source/examples/debug-trans-dvalue/test.c b/dist/source/examples/debug-trans-dvalue/test.c +new file mode 100644 +index 0000000..80b35d7 +--- /dev/null ++++ b/dist/source/examples/debug-trans-dvalue/test.c +@@ -0,0 +1,248 @@ ++/* ++ * Example program using the dvalue debug transport. ++ */ ++ ++#include ++#include ++ ++#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); ++} ++ ++static duk_ret_t native_print(duk_context *ctx) { ++ duk_push_string(ctx, " "); ++ duk_insert(ctx, 0); ++ duk_join(ctx, duk_get_top(ctx) - 1); ++ printf("%s\n", duk_to_string(ctx, -1)); ++ return 0; ++} ++ ++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; ++ } ++ ++ duk_push_c_function(ctx, native_print, DUK_VARARGS); ++ duk_put_global_string(ctx, "print"); ++ ++ 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, ++ NULL, /* app request 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', Uint8Array.allocPlain('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 = Uint8Array.allocPlain(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; ++} +diff --git a/dist/source/examples/debug-trans-socket/README.rst b/dist/source/examples/debug-trans-socket/README.rst +new file mode 100644 +index 0000000..78522cd +--- /dev/null ++++ b/dist/source/examples/debug-trans-socket/README.rst +@@ -0,0 +1,17 @@ ++================================================ ++Debug transport using a simple socket connection ++================================================ ++ ++This example implements an example debug transport which uses a Linux or ++Windows TCP server socket on the debug target. ++ ++Files: ++ ++* ``duk_trans_socket.h``: header file for the transport, used for both Linux ++ and Windows socket variants. ++ ++* ``duk_trans_socket_unix.c``: implementation for Linux/Unix. ++ ++* ``duk_trans_socket_windows.c``: implementation for Windows. ++ ++Compile either Unix or Windows source file only. +diff --git a/dist/source/examples/debug-trans-socket/duk_trans_socket.h b/dist/source/examples/debug-trans-socket/duk_trans_socket.h +new file mode 100644 +index 0000000..4671a1d +--- /dev/null ++++ b/dist/source/examples/debug-trans-socket/duk_trans_socket.h +@@ -0,0 +1,23 @@ ++#if !defined(DUK_TRANS_SOCKET_H_INCLUDED) ++#define DUK_TRANS_SOCKET_H_INCLUDED ++ ++#include "duktape.h" ++ ++#if defined(__cplusplus) ++extern "C" { ++#endif ++ ++void duk_trans_socket_init(void); ++void duk_trans_socket_finish(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); ++ ++#if defined(__cplusplus) ++} ++#endif /* end 'extern "C"' wrapper */ ++ ++#endif /* DUK_TRANS_SOCKET_H_INCLUDED */ +diff --git a/dist/source/examples/debug-trans-socket/duk_trans_socket_unix.c b/dist/source/examples/debug-trans-socket/duk_trans_socket_unix.c +new file mode 100644 +index 0000000..0633eff +--- /dev/null ++++ b/dist/source/examples/debug-trans-socket/duk_trans_socket_unix.c +@@ -0,0 +1,364 @@ ++/* ++ * Example debug transport using a Linux/Unix TCP socket ++ * ++ * Provides a TCP server socket which a debug client can connect to. ++ * After that data is just passed through. ++ * ++ * On some UNIX systems poll() may not be available but select() is. ++ * The default is to use poll(), but you can switch to select() by ++ * defining USE_SELECT. See https://daniel.haxx.se/docs/poll-vs-select.html. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#if !defined(USE_SELECT) ++#include ++#endif /* !USE_SELECT */ ++#include ++#include "duktape.h" ++#include "duk_trans_socket.h" ++ ++#if !defined(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 and finish ++ */ ++ ++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_finish(void) { ++ if (client_sock >= 0) { ++ (void) close(client_sock); ++ client_sock = -1; ++ } ++ 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: (possibly 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, closing connection: %s\n", ++ __FILE__, 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: (possibly 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, (const 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) { ++#if defined(USE_SELECT) ++ struct timeval tm; ++ fd_set rfds; ++ int select_rc; ++#else ++ struct pollfd fds[1]; ++ int poll_rc; ++#endif ++ ++ (void) udata; /* not needed by the example */ ++ ++#if defined(DEBUG_PRINTS) ++ fprintf(stderr, "%s: udata=%p\n", __func__, (void *) udata); ++ fflush(stderr); ++#endif ++ ++ if (client_sock < 0) { ++ return 0; ++ } ++#if defined(USE_SELECT) ++ FD_ZERO(&rfds); ++ FD_SET(client_sock, &rfds); ++ tm.tv_sec = tm.tv_usec = 0; ++ select_rc = select(client_sock + 1, &rfds, NULL, NULL, &tm); ++ if (select_rc == 0) { ++ return 0; ++ } else if (select_rc == 1) { ++ return 1; ++ } ++ goto fail; ++#else /* USE_SELECT */ ++ 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 */ ++ } ++#endif /* USE_SELECT */ ++ fail: ++ if (client_sock >= 0) { ++ (void) close(client_sock); ++ client_sock = -1; ++ } ++ return 0; ++} ++ ++void duk_trans_socket_read_flush_cb(void *udata) { ++ (void) udata; /* not needed by the example */ ++ ++#if defined(DEBUG_PRINTS) ++ fprintf(stderr, "%s: udata=%p\n", __func__, (void *) udata); ++ fflush(stderr); ++#endif ++ ++ /* 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) { ++ (void) udata; /* not needed by the example */ ++ ++#if defined(DEBUG_PRINTS) ++ fprintf(stderr, "%s: udata=%p\n", __func__, (void *) udata); ++ fflush(stderr); ++#endif ++ ++ /* 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; ++} +diff --git a/dist/source/examples/debug-trans-socket/duk_trans_socket_windows.c b/dist/source/examples/debug-trans-socket/duk_trans_socket_windows.c +new file mode 100644 +index 0000000..59bee04 +--- /dev/null ++++ b/dist/source/examples/debug-trans-socket/duk_trans_socket_windows.c +@@ -0,0 +1,419 @@ ++/* ++ * Example debug transport using a Windows TCP socket ++ * ++ * Provides a TCP server socket which a debug client can connect to. ++ * After that data is just passed through. ++ * ++ * https://msdn.microsoft.com/en-us/library/windows/desktop/ms737593(v=vs.85).aspx ++ * ++ * Compiling 'duk' with debugger support using MSVC (Visual Studio): ++ * ++ * > python2 tools\configure.py \ ++ * --output-directory prep ++ * -DDUK_USE_DEBUGGER_SUPPORT -DDUK_USE_INTERRUPT_COUNTER ++ * > cl /W3 /O2 /Feduk.exe \ ++ * /DDUK_CMDLINE_DEBUGGER_SUPPORT ++ * /Iexamples\debug-trans-socket /Iprep ++ * examples\cmdline\duk_cmdline.c ++ * examples\debug-trans-socket\duk_trans_socket_windows.c ++ * prep\duktape.c ++ * ++ * With MinGW: ++ * ++ * $ python2 tools\configure.py \ ++ * --output-directory prep ++ * -DDUK_USE_DEBUGGER_SUPPORT -DDUK_USE_INTERRUPT_COUNTER ++ * $ gcc -oduk.exe -Wall -O2 \ ++ * -DDUK_CMDLINE_DEBUGGER_SUPPORT \ ++ * -Iexamples/debug-trans-socket -Iprep \ ++ * examples/cmdline/duk_cmdline.c \ ++ * examples/debug-trans-socket/duk_trans_socket_windows.c \ ++ * prep/duktape.c -lm -lws2_32 ++ */ ++ ++#undef UNICODE ++#if !defined(WIN32_LEAN_AND_MEAN) ++#define WIN32_LEAN_AND_MEAN ++#endif ++ ++/* MinGW workaround for missing getaddrinfo() etc: ++ * http://programmingrants.blogspot.fi/2009/09/tips-on-undefined-reference-to.html ++ */ ++#if defined(__MINGW32__) || defined(__MINGW64__) ++#if !defined(_WIN32_WINNT) ++#define _WIN32_WINNT 0x0501 ++#endif ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include "duktape.h" ++#include "duk_trans_socket.h" ++ ++#if defined(_MSC_VER) ++#pragma comment (lib, "Ws2_32.lib") ++#endif ++ ++#if !defined(DUK_DEBUG_PORT) ++#define DUK_DEBUG_PORT 9091 ++#endif ++#if !defined(DUK_DEBUG_ADDRESS) ++#define DUK_DEBUG_ADDRESS "0.0.0.0" ++#endif ++#define DUK__STRINGIFY_HELPER(x) #x ++#define DUK__STRINGIFY(x) DUK__STRINGIFY_HELPER(x) ++ ++#if 0 ++#define DEBUG_PRINTS ++#endif ++ ++static SOCKET server_sock = INVALID_SOCKET; ++static SOCKET client_sock = INVALID_SOCKET; ++static int wsa_inited = 0; ++ ++/* ++ * Transport init and finish ++ */ ++ ++void duk_trans_socket_init(void) { ++ WSADATA wsa_data; ++ struct addrinfo hints; ++ struct addrinfo *result = NULL; ++ int rc; ++ ++ memset((void *) &wsa_data, 0, sizeof(wsa_data)); ++ memset((void *) &hints, 0, sizeof(hints)); ++ ++ rc = WSAStartup(MAKEWORD(2, 2), &wsa_data); ++ if (rc != 0) { ++ fprintf(stderr, "%s: WSAStartup() failed: %d\n", __FILE__, rc); ++ fflush(stderr); ++ goto fail; ++ } ++ wsa_inited = 1; ++ ++ hints.ai_family = AF_UNSPEC; ++ hints.ai_socktype = SOCK_STREAM; ++ hints.ai_protocol = IPPROTO_TCP; ++ hints.ai_flags = AI_PASSIVE; ++ ++ rc = getaddrinfo(DUK_DEBUG_ADDRESS, DUK__STRINGIFY(DUK_DEBUG_PORT), &hints, &result); ++ if (rc != 0) { ++ fprintf(stderr, "%s: getaddrinfo() failed: %d\n", __FILE__, rc); ++ fflush(stderr); ++ goto fail; ++ } ++ ++ server_sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol); ++ if (server_sock == INVALID_SOCKET) { ++ fprintf(stderr, "%s: socket() failed with error: %ld\n", ++ __FILE__, (long) WSAGetLastError()); ++ fflush(stderr); ++ goto fail; ++ } ++ ++ rc = bind(server_sock, result->ai_addr, (int) result->ai_addrlen); ++ if (rc == SOCKET_ERROR) { ++ fprintf(stderr, "%s: bind() failed with error: %ld\n", ++ __FILE__, (long) WSAGetLastError()); ++ fflush(stderr); ++ goto fail; ++ } ++ ++ rc = listen(server_sock, SOMAXCONN); ++ if (rc == SOCKET_ERROR) { ++ fprintf(stderr, "%s: listen() failed with error: %ld\n", ++ __FILE__, (long) WSAGetLastError()); ++ fflush(stderr); ++ goto fail; ++ } ++ ++ if (result != NULL) { ++ freeaddrinfo(result); ++ result = NULL; ++ } ++ return; ++ ++ fail: ++ if (result != NULL) { ++ freeaddrinfo(result); ++ result = NULL; ++ } ++ if (server_sock != INVALID_SOCKET) { ++ (void) closesocket(server_sock); ++ server_sock = INVALID_SOCKET; ++ } ++ if (wsa_inited) { ++ WSACleanup(); ++ wsa_inited = 0; ++ } ++} ++ ++void duk_trans_socket_finish(void) { ++ if (client_sock != INVALID_SOCKET) { ++ (void) closesocket(client_sock); ++ client_sock = INVALID_SOCKET; ++ } ++ if (server_sock != INVALID_SOCKET) { ++ (void) closesocket(server_sock); ++ server_sock = INVALID_SOCKET; ++ } ++ if (wsa_inited) { ++ WSACleanup(); ++ wsa_inited = 0; ++ } ++} ++ ++void duk_trans_socket_waitconn(void) { ++ if (server_sock == INVALID_SOCKET) { ++ fprintf(stderr, "%s: no server socket, skip waiting for connection\n", ++ __FILE__); ++ fflush(stderr); ++ return; ++ } ++ if (client_sock != INVALID_SOCKET) { ++ (void) closesocket(client_sock); ++ client_sock = INVALID_SOCKET; ++ } ++ ++ fprintf(stderr, "Waiting for debug connection on port %d\n", (int) DUK_DEBUG_PORT); ++ fflush(stderr); ++ ++ client_sock = accept(server_sock, NULL, NULL); ++ if (client_sock == INVALID_SOCKET) { ++ fprintf(stderr, "%s: accept() failed with error %ld, skip waiting for connection\n", ++ __FILE__, (long) WSAGetLastError()); ++ 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 != INVALID_SOCKET) { ++ (void) closesocket(server_sock); ++ server_sock = INVALID_SOCKET; ++ } ++ return; ++ ++ fail: ++ if (client_sock != INVALID_SOCKET) { ++ (void) closesocket(client_sock); ++ client_sock = INVALID_SOCKET; ++ } ++} ++ ++/* ++ * Duktape callbacks ++ */ ++ ++/* Duktape debug transport callback: (possibly partial) read. */ ++duk_size_t duk_trans_socket_read_cb(void *udata, char *buffer, duk_size_t length) { ++ int ret; ++ ++ (void) udata; /* not needed by the example */ ++ ++#if defined(DEBUG_PRINTS) ++ fprintf(stderr, "%s: udata=%p, buffer=%p, length=%ld\n", ++ __FUNCTION__, (void *) udata, (void *) buffer, (long) length); ++ fflush(stderr); ++#endif ++ ++ if (client_sock == INVALID_SOCKET) { ++ 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 = recv(client_sock, buffer, (int) length, 0); ++ if (ret < 0) { ++ fprintf(stderr, "%s: debug read failed, error %d, closing connection\n", ++ __FILE__, ret); ++ 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 > (int) 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 != INVALID_SOCKET) { ++ (void) closesocket(client_sock); ++ client_sock = INVALID_SOCKET; ++ } ++ return 0; ++} ++ ++/* Duktape debug transport callback: (possibly partial) write. */ ++duk_size_t duk_trans_socket_write_cb(void *udata, const char *buffer, duk_size_t length) { ++ int ret; ++ ++ (void) udata; /* not needed by the example */ ++ ++#if defined(DEBUG_PRINTS) ++ fprintf(stderr, "%s: udata=%p, buffer=%p, length=%ld\n", ++ __FUNCTION__, (void *) udata, (const void *) buffer, (long) length); ++ fflush(stderr); ++#endif ++ ++ if (client_sock == INVALID_SOCKET) { ++ 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 = send(client_sock, buffer, (int) length, 0); ++ if (ret <= 0 || ret > (int) length) { ++ fprintf(stderr, "%s: debug write failed, ret %d, closing connection\n", ++ __FILE__, ret); ++ fflush(stderr); ++ goto fail; ++ } ++ ++ return (duk_size_t) ret; ++ ++ fail: ++ if (client_sock != INVALID_SOCKET) { ++ (void) closesocket(INVALID_SOCKET); ++ client_sock = INVALID_SOCKET; ++ } ++ return 0; ++} ++ ++duk_size_t duk_trans_socket_peek_cb(void *udata) { ++ u_long avail; ++ int rc; ++ ++ (void) udata; /* not needed by the example */ ++ ++#if defined(DEBUG_PRINTS) ++ fprintf(stderr, "%s: udata=%p\n", __FUNCTION__, (void *) udata); ++ fflush(stderr); ++#endif ++ ++ if (client_sock == INVALID_SOCKET) { ++ return 0; ++ } ++ ++ avail = 0; ++ rc = ioctlsocket(client_sock, FIONREAD, &avail); ++ if (rc != 0) { ++ fprintf(stderr, "%s: ioctlsocket() returned %d, closing connection\n", ++ __FILE__, rc); ++ fflush(stderr); ++ goto fail; /* also returns 0, which is correct */ ++ } else { ++ if (avail == 0) { ++ return 0; /* nothing to read */ ++ } else { ++ return 1; /* something to read */ ++ } ++ } ++ /* never here */ ++ ++ fail: ++ if (client_sock != INVALID_SOCKET) { ++ (void) closesocket(client_sock); ++ client_sock = INVALID_SOCKET; ++ } ++ return 0; ++} ++ ++void duk_trans_socket_read_flush_cb(void *udata) { ++ (void) udata; /* not needed by the example */ ++ ++#if defined(DEBUG_PRINTS) ++ fprintf(stderr, "%s: udata=%p\n", __FUNCTION__, (void *) udata); ++ fflush(stderr); ++#endif ++ ++ /* 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) { ++ (void) udata; /* not needed by the example */ ++ ++#if defined(DEBUG_PRINTS) ++ fprintf(stderr, "%s: udata=%p\n", __FUNCTION__, (void *) udata); ++ fflush(stderr); ++#endif ++ ++ /* 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; ++} ++ ++#undef DUK__STRINGIFY_HELPER ++#undef DUK__STRINGIFY +diff --git a/dist/source/examples/dummy-date-provider/README.rst b/dist/source/examples/dummy-date-provider/README.rst +new file mode 100644 +index 0000000..bc62779 +--- /dev/null ++++ b/dist/source/examples/dummy-date-provider/README.rst +@@ -0,0 +1,5 @@ ++==================================== ++Dummy external Date provider example ++==================================== ++ ++This example implements a dummy, minimal external Date provider. +diff --git a/dist/source/examples/dummy-date-provider/dummy_date_provider.c b/dist/source/examples/dummy-date-provider/dummy_date_provider.c +new file mode 100644 +index 0000000..9c9e716 +--- /dev/null ++++ b/dist/source/examples/dummy-date-provider/dummy_date_provider.c +@@ -0,0 +1,27 @@ ++/* ++ * 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; ++} +diff --git a/dist/source/examples/eval/README.rst b/dist/source/examples/eval/README.rst +new file mode 100644 +index 0000000..eed1806 +--- /dev/null ++++ b/dist/source/examples/eval/README.rst +@@ -0,0 +1,5 @@ ++============ ++Eval example ++============ ++ ++Evaluate expressions from command line. +diff --git a/dist/source/examples/eval/eval.c b/dist/source/examples/eval/eval.c +new file mode 100644 +index 0000000..731f451 +--- /dev/null ++++ b/dist/source/examples/eval/eval.c +@@ -0,0 +1,55 @@ ++/* ++ * Very simple example program for evaluating expressions from ++ * command line ++ */ ++ ++#include "duktape.h" ++#include ++ ++static duk_ret_t native_print(duk_context *ctx) { ++ duk_push_string(ctx, " "); ++ duk_insert(ctx, 0); ++ duk_join(ctx, duk_get_top(ctx) - 1); ++ printf("%s\n", duk_to_string(ctx, -1)); ++ return 0; ++} ++ ++static void usage_exit(void) { ++ fprintf(stderr, "Usage: eval [] ...\n"); ++ fflush(stderr); ++ exit(1); ++} ++ ++int main(int argc, char *argv[]) { ++ duk_context *ctx; ++ int i; ++ const char *res; ++ duk_int_t rc; ++ ++ if (argc < 2) { ++ usage_exit(); ++ } ++ ++ ctx = duk_create_heap_default(); ++ ++ duk_push_c_function(ctx, native_print, DUK_VARARGS); ++ duk_put_global_string(ctx, "print"); ++ ++ for (i = 1; i < argc; i++) { ++ printf("=== eval: '%s' ===\n", argv[i]); ++ duk_push_string(ctx, argv[i]); ++ rc = duk_peval(ctx); ++ if (rc != 0) { ++ duk_safe_to_stacktrace(ctx, -1); ++ } else { ++ duk_safe_to_string(ctx, -1); ++ } ++ res = duk_get_string(ctx, -1); ++ printf("%s\n", res ? res : "null"); ++ duk_pop(ctx); ++ } ++ ++ duk_destroy_heap(ctx); ++ ++ return 0; ++} +diff --git a/dist/source/examples/eventloop/README.rst b/dist/source/examples/eventloop/README.rst +new file mode 100644 +index 0000000..c67e4f5 +--- /dev/null ++++ b/dist/source/examples/eventloop/README.rst +@@ -0,0 +1,72 @@ ++================== ++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):: ++ ++ $ ./evloop timer-test.js # run with ECMAScript eventloop ++ $ ./evloop -c timer-test.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 ECMAScript. 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 ++ ++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. +diff --git a/dist/source/examples/eventloop/basic-test.js b/dist/source/examples/eventloop/basic-test.js +new file mode 100644 +index 0000000..77605c0 +--- /dev/null ++++ b/dist/source/examples/eventloop/basic-test.js +@@ -0,0 +1,16 @@ ++/* ++ * 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'); +diff --git a/dist/source/examples/eventloop/c_eventloop.c b/dist/source/examples/eventloop/c_eventloop.c +new file mode 100644 +index 0000000..24cebbb +--- /dev/null ++++ b/dist/source/examples/eventloop/c_eventloop.c +@@ -0,0 +1,626 @@ ++/* ++ * 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 ++#include ++#include ++#include ++#include ++#include ++ ++#include "duktape.h" ++#include "c_eventloop.h" ++ ++#if !defined(DUKTAPE_EVENTLOOP_DEBUG) ++#define DUKTAPE_EVENTLOOP_DEBUG 0 /* set to 1 to debug with printf */ ++#endif ++ ++#define TIMERS_SLOT_NAME "eventTimers" ++#define MIN_DELAY 1.0 ++#define MIN_WAIT 1.0 ++#define MAX_WAIT 60000.0 ++#define MAX_EXPIRIES 10 ++ ++#define MAX_FDS 256 ++#define MAX_TIMERS 4096 /* this is quite excessive for embedded use, but good for testing */ ++ ++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 .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_EXPIRIES; ++ 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, TIMERS_SLOT_NAME); ++ ++ /* [ ... stash eventTimers ] */ ++ ++ now = get_now(); ++ while (sanity-- > 0) { ++ /* ++ * If exit has been requested, exit without running further ++ * callbacks. ++ */ ++ ++ if (exit_requested) { ++#if DUKTAPE_EVENTLOOP_DEBUG > 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 DUKTAPE_EVENTLOOP_DEBUG > 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 DUKTAPE_EVENTLOOP_DEBUG > 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 DUKTAPE_EVENTLOOP_DEBUG > 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 DUKTAPE_EVENTLOOP_DEBUG > 0 ++ fprintf(stderr, "queueing timer %d back into active list\n", (int) t->id); ++ fflush(stderr); ++#endif ++ if (timer_count >= MAX_TIMERS) { ++ (void) 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 DUKTAPE_EVENTLOOP_DEBUG > 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 DUKTAPE_EVENTLOOP_DEBUG > 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; ++} ++ ++duk_ret_t eventloop_run(duk_context *ctx, void *udata) { ++ ev_timer *t; ++ double now; ++ double diff; ++ int timeout; ++ int rc; ++ int i, n; ++ int idx_eventloop; ++ int idx_fd_handler; ++ ++ (void) udata; ++ ++ /* 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 DUKTAPE_EVENTLOOP_DEBUG > 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 DUKTAPE_EVENTLOOP_DEBUG > 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 DUKTAPE_EVENTLOOP_DEBUG > 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 DUKTAPE_EVENTLOOP_DEBUG > 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 DUKTAPE_EVENTLOOP_DEBUG > 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 DUKTAPE_EVENTLOOP_DEBUG > 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) { ++ (void) 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, TIMERS_SLOT_NAME); /* -> [ 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 DUKTAPE_EVENTLOOP_DEBUG > 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 DUKTAPE_EVENTLOOP_DEBUG > 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, TIMERS_SLOT_NAME); /* -> [ timer_id stash eventTimers ] */ ++ duk_push_number(ctx, (double) timer_id); ++ duk_del_prop(ctx, -2); /* delete eventTimers[timer_id] */ ++ ++#if DUKTAPE_EVENTLOOP_DEBUG > 0 ++ fprintf(stderr, "deleted timer id: %d\n", (int) timer_id); ++ fflush(stderr); ++#endif ++ break; ++ } ++ } ++ ++#if DUKTAPE_EVENTLOOP_DEBUG > 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 DUKTAPE_EVENTLOOP_DEBUG > 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 DUKTAPE_EVENTLOOP_DEBUG > 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 DUKTAPE_EVENTLOOP_DEBUG > 0 ++ fprintf(stderr, "listen_fd: fd not found on list, add new entry\n"); ++ fflush(stderr); ++#endif ++ ++ if (poll_count >= MAX_FDS) { ++ (void) 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, TIMERS_SLOT_NAME); ++ duk_pop(ctx); ++} +diff --git a/dist/source/examples/eventloop/c_eventloop.h b/dist/source/examples/eventloop/c_eventloop.h +new file mode 100644 +index 0000000..719205f +--- /dev/null ++++ b/dist/source/examples/eventloop/c_eventloop.h +@@ -0,0 +1,7 @@ ++#if !defined(C_EVENTLOOP_H) ++#define C_EVENTLOOP_H ++ ++void eventloop_register(duk_context *ctx); ++duk_ret_t eventloop_run(duk_context *ctx, void *udata); ++ ++#endif +diff --git a/dist/source/examples/eventloop/c_eventloop.js b/dist/source/examples/eventloop/c_eventloop.js +new file mode 100644 +index 0000000..5b3c7c9 +--- /dev/null ++++ b/dist/source/examples/eventloop/c_eventloop.js +@@ -0,0 +1,192 @@ ++/* ++ * 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; ++ ++ // Delay can be optional at least in some contexts, so tolerate that. ++ // https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout ++ if (typeof delay !== 'number') { ++ if (typeof delay === 'undefined') { ++ delay = 0; ++ } else { ++ throw new TypeError('invalid 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 = 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') { ++ if (typeof delay === 'undefined') { ++ delay = 0; ++ } else { ++ throw new TypeError('invalid 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 = 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 ++ if (typeof data === 'string') { ++ data = new TextEncoder().encode(data); ++ } ++ var rc = Socket.write(fd, data); ++} +diff --git a/dist/source/examples/eventloop/client-socket-test.js b/dist/source/examples/eventloop/client-socket-test.js +new file mode 100644 +index 0000000..d08b875 +--- /dev/null ++++ b/dist/source/examples/eventloop/client-socket-test.js +@@ -0,0 +1,16 @@ ++var HOST = 'localhost'; ++var PORT = 80; ++ ++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(new TextDecoder().decode(data)); ++ // Read until completion, socket is closed by server. ++ }); ++ EventLoop.write(fd, "GET / HTTP/1.1\r\n" + ++ "Host: " + HOST + "\r\n" + ++ "User-Agent: client-socket-test.js\r\n" + ++ "Connection: close\r\n" + ++ "\r\n"); ++}); +diff --git a/dist/source/examples/eventloop/ecma_eventloop.js b/dist/source/examples/eventloop/ecma_eventloop.js +new file mode 100644 +index 0000000..9ddd39d +--- /dev/null ++++ b/dist/source/examples/eventloop/ecma_eventloop.js +@@ -0,0 +1,478 @@ ++/* ++ * 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. ++ } ++ ++ /* ++ * 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 ++ if (typeof data === 'string') { ++ data = new TextEncoder().encode(data); ++ } ++ var rc = Socket.write(fd, 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; ++ ++ // Delay can be optional at least in some contexts, so tolerate that. ++ // https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout ++ if (typeof delay !== 'number') { ++ if (typeof delay === 'undefined') { ++ delay = 0; ++ } else { ++ throw new TypeError('invalid delay'); ++ } ++ } ++ 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') { ++ if (typeof delay === 'undefined') { ++ delay = 0; ++ } else { ++ throw new TypeError('invalid delay'); ++ } ++ } ++ 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(); ++} +diff --git a/dist/source/examples/eventloop/fileio.c b/dist/source/examples/eventloop/fileio.c +new file mode 100644 +index 0000000..974e2d7 +--- /dev/null ++++ b/dist/source/examples/eventloop/fileio.c +@@ -0,0 +1,84 @@ ++/* ++ * File I/O binding example. ++ */ ++ ++#include ++#include ++#include ++ ++#include "duktape.h" ++ ++/* Push file as a buffer. */ ++void fileio_push_file_buffer(duk_context *ctx, const char *filename) { ++ 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) { ++ duk_pop(ctx); ++ goto error; ++ } ++ ++ fclose(f); ++ return; ++ ++ error: ++ if (f) { ++ fclose(f); ++ } ++ duk_push_undefined(ctx); ++} ++ ++/* Push file as a string. */ ++void fileio_push_file_string(duk_context *ctx, const char *filename) { ++ fileio_push_file_buffer(ctx, filename); ++ if (duk_is_buffer_data(ctx, -1)) { ++ duk_buffer_to_string(ctx, -1); ++ } ++} ++ ++static int fileio_readfile(duk_context *ctx) { ++ const char *filename = duk_to_string(ctx, 0); ++ fileio_push_file_buffer(ctx, filename); ++ if (!duk_is_buffer_data(ctx, -1)) { ++ return DUK_RET_ERROR; ++ } ++ return 1; ++} ++ ++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); ++} +diff --git a/dist/source/examples/eventloop/main.c b/dist/source/examples/eventloop/main.c +new file mode 100644 +index 0000000..d09828f +--- /dev/null ++++ b/dist/source/examples/eventloop/main.c +@@ -0,0 +1,286 @@ ++/* ++ * 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 ++#include ++#include ++#if !defined(NO_SIGNAL) ++#include ++#endif ++ ++#include "duktape.h" ++ ++extern void poll_register(duk_context *ctx); ++extern void socket_register(duk_context *ctx); ++extern void fileio_register(duk_context *ctx); ++extern void fileio_push_file_buffer(duk_context *ctx, const char *filename); ++extern void fileio_push_file_string(duk_context *ctx, const char *filename); ++extern void eventloop_register(duk_context *ctx); ++extern duk_ret_t eventloop_run(duk_context *ctx, void *udata); ++ ++static int c_evloop = 0; ++ ++#if !defined(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); ++} ++ ++static duk_ret_t native_print(duk_context *ctx) { ++ duk_push_string(ctx, " "); ++ duk_insert(ctx, 0); ++ duk_join(ctx, duk_get_top(ctx) - 1); ++ printf("%s\n", duk_safe_to_string(ctx, -1)); ++ return 0; ++} ++ ++static void print_register(duk_context *ctx) { ++ duk_push_c_function(ctx, native_print, DUK_VARARGS); ++ duk_put_global_string(ctx, "print"); ++} ++ ++static duk_ret_t wrapped_compile_execute(duk_context *ctx, void *udata) { ++ int comp_flags = 0; ++ int rc; ++ ++ (void) udata; ++ ++ /* 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, NULL, 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; ++} ++ ++static 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, NULL, 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; ++} ++ ++static 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; ++} ++ ++static int handle_stdin(duk_context *ctx) { ++ int retval; ++ ++ retval = handle_fh(ctx, stdin, "stdin"); ++ ++ return retval; ++} ++ ++static duk_ret_t init_duktape_context(duk_context *ctx, void *udata) { ++ (void) udata; ++ ++ print_register(ctx); ++ poll_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); ++ fileio_push_file_string(ctx, "c_eventloop.js"); ++ duk_eval(ctx); ++ } else { ++ fprintf(stderr, "Using ECMAScript based eventloop (give -c to use C based eventloop)\n"); ++ fflush(stderr); ++ ++ fileio_push_file_string(ctx, "ecma_eventloop.js"); ++ duk_eval(ctx); ++ } ++ ++ return 0; ++} ++ ++int main(int argc, char *argv[]) { ++ duk_context *ctx = NULL; ++ int retval = 1; ++ const char *filename = NULL; ++ int i; ++ ++#if !defined(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(); ++ ++ if (duk_safe_call(ctx, init_duktape_context, NULL, 0 /*nargs*/, 1 /*nrets*/) != 0) { ++ fprintf(stderr, "Failed to initialize: %s\n", duk_safe_to_string(ctx, -1)); ++ goto cleanup; ++ } ++ duk_pop(ctx); ++ ++ fprintf(stderr, "Executing code from: '%s'\n", filename); ++ fflush(stderr); ++ ++ if (strcmp(filename, "-") == 0) { ++ if (handle_stdin(ctx) != 0) { ++ goto cleanup; ++ } ++ } else { ++ if (handle_file(ctx, filename) != 0) { ++ goto cleanup; ++ } ++ } ++ ++ retval = 0; ++ /* fall through */ ++ cleanup: ++ if (ctx) { ++ duk_destroy_heap(ctx); ++ } ++ ++ return retval; ++ ++ usage: ++ fprintf(stderr, "Usage: evloop [-c] \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 is '-', the entire STDIN executed.\n"); ++ fflush(stderr); ++ exit(1); ++} +diff --git a/dist/source/examples/eventloop/poll.c b/dist/source/examples/eventloop/poll.c +new file mode 100644 +index 0000000..330bb6f +--- /dev/null ++++ b/dist/source/examples/eventloop/poll.c +@@ -0,0 +1,119 @@ ++/* ++ * C wrapper for poll(). ++ */ ++ ++#define _GNU_SOURCE ++#include ++#include ++#include ++ ++#if defined(_WIN32) ++#include ++#define poll WSAPoll ++#pragma comment(lib, "ws2_32") ++#else ++#include ++#endif ++ ++#include ++ ++#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) { ++ (void) 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); ++} +diff --git a/dist/source/examples/eventloop/server-socket-test.js b/dist/source/examples/eventloop/server-socket-test.js +new file mode 100644 +index 0000000..e3c0086 +--- /dev/null ++++ b/dist/source/examples/eventloop/server-socket-test.js +@@ -0,0 +1,33 @@ ++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); ++ }); ++}); +diff --git a/dist/source/examples/eventloop/socket.c b/dist/source/examples/eventloop/socket.c +new file mode 100644 +index 0000000..9c391fa +--- /dev/null ++++ b/dist/source/examples/eventloop/socket.c +@@ -0,0 +1,286 @@ ++/* ++ * TCP sockets binding example. ++ */ ++ ++#define _GNU_SOURCE ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "duktape.h" ++ ++#define ERROR_FROM_ERRNO(ctx) do { \ ++ (void) 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); ++ } ++} ++ ++#if defined(__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); ++#if defined(__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) { ++ (void) 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); ++#if defined(__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); ++#if defined(__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) { ++ (void) 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_require_buffer_data(ctx, 1, &len); ++ ++ /* MSG_NOSIGNAL: avoid SIGPIPE */ ++#if defined(__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); ++} +diff --git a/dist/source/examples/eventloop/timer-test.js b/dist/source/examples/eventloop/timer-test.js +new file mode 100644 +index 0000000..b5cf116 +--- /dev/null ++++ b/dist/source/examples/eventloop/timer-test.js +@@ -0,0 +1,40 @@ ++/* ++ * Test using timers and intervals. ++ */ ++ ++function main() { ++ var i; ++ var counters = []; ++ var ntimers = 0; ++ ++ print('set interval timer'); ++ var intervalTimer = setInterval(function () { ++ print(new Date().toISOString() + ': timers pending: ' + ntimers); ++ if (ntimers === 0) { ++ clearInterval(intervalTimer); ++ } ++ }, 500); ++ ++ function addTimer(interval) { ++ ntimers++; ++ setTimeout(function () { ++ ntimers--; ++ }, interval); ++ } ++ ++ /* Here the inserts take a lot of time because the underlying timer manager ++ * data structure has O(n) insertion performance. ++ */ ++ print('launch timers'); ++ for (i = 0; i < 4000; i++) { ++ // Math.exp(0)...Math.exp(8) is an uneven distribution between 1...~2980. ++ addTimer(16000 - Math.exp(Math.random() * 8) * 5); ++ } ++ print('timers launched'); ++} ++ ++try { ++ main(); ++} catch (e) { ++ print(e.stack || e); ++} +diff --git a/dist/source/examples/guide/README.rst b/dist/source/examples/guide/README.rst +new file mode 100644 +index 0000000..1933094 +--- /dev/null ++++ b/dist/source/examples/guide/README.rst +@@ -0,0 +1,5 @@ ++=========================== ++Duktape guide example files ++=========================== ++ ++Examples used in the Duktape guide. +diff --git a/dist/source/examples/guide/fib.js b/dist/source/examples/guide/fib.js +new file mode 100644 +index 0000000..2b2982f +--- /dev/null ++++ b/dist/source/examples/guide/fib.js +@@ -0,0 +1,16 @@ ++// 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(); +diff --git a/dist/source/examples/guide/prime.js b/dist/source/examples/guide/prime.js +new file mode 100644 +index 0000000..df97d50 +--- /dev/null ++++ b/dist/source/examples/guide/prime.js +@@ -0,0 +1,32 @@ ++// 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(' ')); ++} ++ +diff --git a/dist/source/examples/guide/primecheck.c b/dist/source/examples/guide/primecheck.c +new file mode 100644 +index 0000000..addcc56 +--- /dev/null ++++ b/dist/source/examples/guide/primecheck.c +@@ -0,0 +1,81 @@ ++/* primecheck.c */ ++#include ++#include ++#include ++#include "duktape.h" ++ ++/* For brevity assumes a maximum file length of 16kB. */ ++static void push_file_as_string(duk_context *ctx, const char *filename) { ++ FILE *f; ++ size_t len; ++ char buf[16384]; ++ ++ f = fopen(filename, "rb"); ++ if (f) { ++ len = fread((void *) buf, 1, sizeof(buf), f); ++ fclose(f); ++ duk_push_lstring(ctx, (const char *) buf, (duk_size_t) len); ++ } else { ++ duk_push_undefined(ctx); ++ } ++} ++ ++static duk_ret_t native_print(duk_context *ctx) { ++ duk_push_string(ctx, " "); ++ duk_insert(ctx, 0); ++ duk_join(ctx, duk_get_top(ctx) - 1); ++ printf("%s\n", duk_to_string(ctx, -1)); ++ return 0; ++} ++ ++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; ++ ++ (void) argc; (void) argv; ++ ++ 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_print, DUK_VARARGS); ++ duk_put_prop_string(ctx, -2, "print"); ++ duk_push_c_function(ctx, native_prime_check, 2 /*nargs*/); ++ duk_put_prop_string(ctx, -2, "primeCheckNative"); ++ ++ push_file_as_string(ctx, "prime.js"); ++ if (duk_peval(ctx) != 0) { ++ printf("Error running: %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); ++} +diff --git a/dist/source/examples/guide/process.js b/dist/source/examples/guide/process.js +new file mode 100644 +index 0000000..62b5011 +--- /dev/null ++++ b/dist/source/examples/guide/process.js +@@ -0,0 +1,12 @@ ++// 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 '' + m + ''; ++ }); ++} +diff --git a/dist/source/examples/guide/processlines.c b/dist/source/examples/guide/processlines.c +new file mode 100644 +index 0000000..244e651 +--- /dev/null ++++ b/dist/source/examples/guide/processlines.c +@@ -0,0 +1,78 @@ ++/* processlines.c */ ++#include ++#include ++#include ++#include "duktape.h" ++ ++/* For brevity assumes a maximum file length of 16kB. */ ++static void push_file_as_string(duk_context *ctx, const char *filename) { ++ FILE *f; ++ size_t len; ++ char buf[16384]; ++ ++ f = fopen(filename, "rb"); ++ if (f) { ++ len = fread((void *) buf, 1, sizeof(buf), f); ++ fclose(f); ++ duk_push_lstring(ctx, (const char *) buf, (duk_size_t) len); ++ } else { ++ duk_push_undefined(ctx); ++ } ++} ++ ++int main(int argc, const char *argv[]) { ++ duk_context *ctx = NULL; ++ char line[4096]; ++ size_t idx; ++ int ch; ++ ++ (void) argc; (void) argv; ++ ++ ctx = duk_create_heap_default(); ++ if (!ctx) { ++ printf("Failed to create a Duktape heap.\n"); ++ exit(1); ++ } ++ ++ push_file_as_string(ctx, "process.js"); ++ if (duk_peval(ctx) != 0) { ++ printf("Error: %s\n", duk_safe_to_string(ctx, -1)); ++ goto finished; ++ } ++ duk_pop(ctx); /* ignore result */ ++ ++ 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); ++} +diff --git a/dist/source/examples/guide/uppercase.c b/dist/source/examples/guide/uppercase.c +new file mode 100644 +index 0000000..03d0869 +--- /dev/null ++++ b/dist/source/examples/guide/uppercase.c +@@ -0,0 +1,42 @@ ++/* uppercase.c */ ++#include ++#include ++#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; ++} +diff --git a/dist/source/examples/hello/README.rst b/dist/source/examples/hello/README.rst +new file mode 100644 +index 0000000..7afef53 +--- /dev/null ++++ b/dist/source/examples/hello/README.rst +@@ -0,0 +1,5 @@ ++=================== ++Hello world example ++=================== ++ ++Very simple example, most useful for compilation tests. +diff --git a/dist/source/examples/hello/hello.c b/dist/source/examples/hello/hello.c +new file mode 100644 +index 0000000..1c5faff +--- /dev/null ++++ b/dist/source/examples/hello/hello.c +@@ -0,0 +1,46 @@ ++/* ++ * Very simple example program ++ */ ++ ++#include "duktape.h" ++ ++static duk_ret_t native_print(duk_context *ctx) { ++ duk_push_string(ctx, " "); ++ duk_insert(ctx, 0); ++ duk_join(ctx, duk_get_top(ctx) - 1); ++ printf("%s\n", duk_safe_to_string(ctx, -1)); ++ return 0; ++} ++ ++static duk_ret_t native_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_push_c_function(ctx, native_print, DUK_VARARGS); ++ duk_put_global_string(ctx, "print"); ++ duk_push_c_function(ctx, native_adder, DUK_VARARGS); ++ duk_put_global_string(ctx, "adder"); ++ ++ duk_eval_string(ctx, "print('Hello world!');"); ++ ++ duk_eval_string(ctx, "print('2+3=' + adder(2, 3));"); ++ duk_pop(ctx); /* pop eval result */ ++ ++ duk_destroy_heap(ctx); ++ ++ return 0; ++} +diff --git a/dist/source/examples/jxpretty/README.rst b/dist/source/examples/jxpretty/README.rst +new file mode 100644 +index 0000000..5ab43a8 +--- /dev/null ++++ b/dist/source/examples/jxpretty/README.rst +@@ -0,0 +1,5 @@ ++================ ++Jxpretty example ++================ ++ ++Simple command line utility to pretty print JSON in the JX format. +diff --git a/dist/source/examples/jxpretty/jxpretty.c b/dist/source/examples/jxpretty/jxpretty.c +new file mode 100644 +index 0000000..c5134b3 +--- /dev/null ++++ b/dist/source/examples/jxpretty/jxpretty.c +@@ -0,0 +1,76 @@ ++/* ++ * Pretty print JSON from stdin into indented JX. ++ */ ++ ++#include ++#include ++#include "duktape.h" ++ ++static duk_ret_t duk__print(duk_context *ctx) { ++ duk_push_string(ctx, " "); ++ duk_insert(ctx, 0); ++ duk_join(ctx, duk_get_top(ctx) - 1); ++ printf("%s\n", duk_safe_to_string(ctx, -1)); ++ return 0; ++} ++ ++static duk_ret_t do_jxpretty(duk_context *ctx, void *udata) { ++ FILE *f = stdin; ++ char buf[4096]; ++ size_t ret; ++ ++ (void) udata; ++ ++ for (;;) { ++ if (ferror(f)) { ++ (void) 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(); ++ ++ duk_push_c_function(ctx, duk__print, DUK_VARARGS); ++ duk_put_global_string(ctx, "print"); ++ ++ rc = duk_safe_call(ctx, do_jxpretty, NULL, 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; ++} +diff --git a/dist/source/examples/sandbox/README.rst b/dist/source/examples/sandbox/README.rst +new file mode 100644 +index 0000000..24df0a2 +--- /dev/null ++++ b/dist/source/examples/sandbox/README.rst +@@ -0,0 +1,5 @@ ++=============== ++Sandbox example ++=============== ++ ++Very simple, minimal sandboxing example. +diff --git a/dist/source/examples/sandbox/sandbox.c b/dist/source/examples/sandbox/sandbox.c +new file mode 100644 +index 0000000..c8ad483 +--- /dev/null ++++ b/dist/source/examples/sandbox/sandbox.c +@@ -0,0 +1,266 @@ ++/* ++ * Sandboxing example ++ * ++ * Uses custom memory allocation functions which keep track of total amount ++ * of memory allocated, imposing a maximum total allocation size. ++ */ ++ ++#include ++#include ++#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 duk__print(duk_context *ctx) { ++ duk_push_string(ctx, " "); ++ duk_insert(ctx, 0); ++ duk_join(ctx, duk_get_top(ctx) - 1); ++ printf("%s\n", duk_safe_to_string(ctx, -1)); ++ return 0; ++} ++ ++static duk_ret_t do_sandbox_test(duk_context *ctx, void *udata) { ++ FILE *f; ++ char buf[4096]; ++ size_t ret; ++ const char *globobj; ++ ++ (void) udata; ++ ++ /* ++ * Setup sandbox ++ */ ++ ++ /* Minimal print() provider. */ ++ duk_push_c_function(ctx, duk__print, DUK_VARARGS); ++ duk_put_global_string(ctx, "print"); ++ ++ 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(void *udata, const char *msg) { ++ (void) udata; /* Suppress warning. */ ++ fprintf(stderr, "FATAL: %s\n", (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 \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, NULL, 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; ++} +diff --git a/dist/source/extras/README.rst b/dist/source/extras/README.rst +new file mode 100644 +index 0000000..1b70160 +--- /dev/null ++++ b/dist/source/extras/README.rst +@@ -0,0 +1,13 @@ ++============== ++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. +diff --git a/dist/source/extras/alloc-pool/Makefile b/dist/source/extras/alloc-pool/Makefile +new file mode 100644 +index 0000000..6bd6ee5 +--- /dev/null ++++ b/dist/source/extras/alloc-pool/Makefile +@@ -0,0 +1,42 @@ ++# For manual testing; say 'make' in extras/alloc-pool and run ./test. ++ ++CC = gcc ++DEFS = ++#DEFS += '-DDUK_ALLOC_POOL_DEBUG' ++ ++.PHONY: test ++test: ++ rm -rf ./prep ++ echo 'DUK_USE_FATAL_HANDLER:' > opts.yaml ++ echo ' verbatim: "#define DUK_USE_FATAL_HANDLER(udata,msg) my_fatal((msg))"' >> opts.yaml ++ python2 ../../tools/configure.py \ ++ --output-directory ./prep \ ++ --option-file ./opts.yaml \ ++ --fixup-line 'extern void my_fatal(const char *msg);' ++ $(CC) -std=c99 -Wall -Wextra -m32 -Os -otest \ ++ -I./prep ./prep/duktape.c \ ++ $(DEFS) \ ++ duk_alloc_pool.c test.c \ ++ -lm ++ ./test 'print("foo", "bar", 1, 2, 3)' ++ ./test 'alert("foo", "bar", 1, 2, 3)' ++ ++.PHONY: ptrcomptest ++ptrcomptest: ++ rm -rf ./prep ++ echo 'DUK_USE_FATAL_HANDLER:' > opts.yaml ++ echo ' verbatim: "#define DUK_USE_FATAL_HANDLER(udata,msg) my_fatal((msg))"' >> opts.yaml ++ python2 ../../tools/configure.py \ ++ --output-directory ./prep \ ++ --option-file ./opts.yaml \ ++ --fixup-line 'extern void my_fatal(const char *msg);' \ ++ --option-file ../../config/examples/low_memory.yaml \ ++ --option-file ptrcomp.yaml \ ++ --fixup-file ptrcomp_fixup.h ++ $(CC) -std=c99 -Wall -Wextra -m32 -Os -optrcomptest \ ++ -I. -I./prep ./prep/duktape.c \ ++ $(DEFS) \ ++ duk_alloc_pool.c test.c \ ++ -lm ++ ./ptrcomptest 'print("foo", "bar", 1, 2, 3)' ++ ./ptrcomptest 'alert("foo", "bar", 1, 2, 3)' +diff --git a/dist/source/extras/alloc-pool/README.rst b/dist/source/extras/alloc-pool/README.rst +new file mode 100644 +index 0000000..9e579a9 +--- /dev/null ++++ b/dist/source/extras/alloc-pool/README.rst +@@ -0,0 +1,38 @@ ++===================================== ++Pool allocator for low memory targets ++===================================== ++ ++A simple pool allocator which satisfies allocations from preallocated pools ++containing blocks of a certain size. The caller provides a continuous memory ++region and a pool configuration when initializing the allocator. ++ ++The pool configuration specifies the block sizes used, and parameters to ++control how many entries are allocated for each block size. The parameters ++are specified with respect to an arbitrary floating point scaling parameter ++``t`` as follows:: ++ ++ bytes = A*t + B ++ count = floor(bytes / block_size) ++ = floor((A*t + B) / block_size) ++ ++ A: constant which indicates how quickly more bytes are assigned for this ++ block size as the total allocation grows ++ ++ B: constant which indicates the base allocation for this block size, i.e. ++ the allocated needed by Duktape initialization ++ ++Pool initialization finds the largest floating point ``t`` which still fits in ++the memory region provided. Any leftover bytes are sprinkled to the pools to ++minimize wasted space. ++ ++A pool configuration can be written manually (by trial and error) or using ++some automatic tooling such as ``pool_simulator.py``. ++ ++When using pointer compression only a single global pool is supported. This ++reduces code footprint and is usually sufficient in low memory targets. ++ ++Pointer compression functions are defined as inline functions in ++``duk_alloc_pool.h`` to allow the compiler to inline pointer compression when ++appropriate. As a side effect ``duk_config.h`` must include ++``duk_alloc_pool.h`` so that the declarations are visible when compiling ++Duktape. +diff --git a/dist/source/extras/alloc-pool/duk_alloc_pool.c b/dist/source/extras/alloc-pool/duk_alloc_pool.c +new file mode 100644 +index 0000000..240ebf4 +--- /dev/null ++++ b/dist/source/extras/alloc-pool/duk_alloc_pool.c +@@ -0,0 +1,612 @@ ++/* ++ * Pool allocator for low memory targets. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include "duktape.h" ++#include "duk_alloc_pool.h" ++ ++/* Define to enable some debug printfs. */ ++/* #define DUK_ALLOC_POOL_DEBUG */ ++ ++/* Define to enable approximate waste tracking. */ ++/* #define DUK_ALLOC_POOL_TRACK_WASTE */ ++ ++/* Define to track global highwater for used and waste bytes. VERY SLOW, only ++ * useful for manual testing. ++ */ ++/* #define DUK_ALLOC_POOL_TRACK_HIGHWATER */ ++ ++#if defined(DUK_ALLOC_POOL_ROMPTR_COMPRESSION) ++#if 0 /* This extern declaration is provided by duktape.h, array provided by duktape.c. */ ++extern const void * const duk_rom_compressed_pointers[]; ++#endif ++const void *duk_alloc_pool_romptr_low = NULL; ++const void *duk_alloc_pool_romptr_high = NULL; ++static void duk__alloc_pool_romptr_init(void); ++#endif ++ ++#if defined(DUK_USE_HEAPPTR16) ++void *duk_alloc_pool_ptrcomp_base = NULL; ++#endif ++ ++#if defined(DUK_ALLOC_POOL_DEBUG) ++static void duk__alloc_pool_dprintf(const char *fmt, ...) { ++ va_list ap; ++ va_start(ap, fmt); ++ vfprintf(stderr, fmt, ap); ++ va_end(ap); ++} ++#endif ++ ++/* ++ * Pool initialization ++ */ ++ ++void *duk_alloc_pool_init(char *buffer, ++ size_t size, ++ const duk_pool_config *configs, ++ duk_pool_state *states, ++ int num_pools, ++ duk_pool_global *global) { ++ double t_min, t_max, t_curr, x; ++ int step, i, j, n; ++ size_t total; ++ char *p; ++ ++ /* XXX: check that 'size' is not too large when using pointer ++ * compression. ++ */ ++ ++ /* To optimize pool counts first come up with a 't' which still allows ++ * total pool size to fit within user provided region. After that ++ * sprinkle any remaining bytes to the counts. Binary search with a ++ * fixed step count; last round uses 't_min' as 't_curr' to ensure it ++ * succeeds. ++ */ ++ ++ t_min = 0.0; /* Unless config is insane, this should always be "good". */ ++ t_max = 1e6; ++ ++ for (step = 0; ; step++) { ++ if (step >= 100) { ++ /* Force "known good", rerun config, and break out. ++ * Deals with rounding corner cases where t_curr is ++ * persistently "bad" even though t_min is a valid ++ * solution. ++ */ ++ t_curr = t_min; ++ } else { ++ t_curr = (t_min + t_max) / 2.0; ++ } ++ ++ for (i = 0, total = 0; i < num_pools; i++) { ++ states[i].size = configs[i].size; ++ ++ /* Target bytes = A*t + B ==> target count = (A*t + B) / block_size. ++ * Rely on A and B being small enough so that 'x' won't wrap. ++ */ ++ x = ((double) configs[i].a * t_curr + (double) configs[i].b) / (double) configs[i].size; ++ ++ states[i].count = (unsigned int) x; ++ total += (size_t) states[i].size * (size_t) states[i].count; ++ if (total > size) { ++ goto bad; ++ } ++ } ++ ++ /* t_curr is good. */ ++#if defined(DUK_ALLOC_POOL_DEBUG) ++ duk__alloc_pool_dprintf("duk_alloc_pool_init: step=%d, t=[%lf %lf %lf] -> total %ld/%ld (good)\n", ++ step, t_min, t_curr, t_max, (long) total, (long) size); ++#endif ++ if (step >= 100) { ++ /* Keep state[] initialization state. The state was ++ * created using the highest 't_min'. ++ */ ++ break; ++ } ++ t_min = t_curr; ++ continue; ++ ++ bad: ++ /* t_curr is bad. */ ++#if defined(DUK_ALLOC_POOL_DEBUG) ++ duk__alloc_pool_dprintf("duk_alloc_pool_init: step=%d, t=[%lf %lf %lf] -> total %ld/%ld (bad)\n", ++ step, t_min, t_curr, t_max, (long) total, (long) size); ++#endif ++ ++ if (step >= 1000) { ++ /* Cannot find any good solution; shouldn't happen ++ * unless config is bad or 'size' is so small that ++ * even a baseline allocation won't fit. ++ */ ++ return NULL; ++ } ++ t_max = t_curr; ++ /* continue */ ++ } ++ ++ /* The base configuration is now good; sprinkle any leftovers to ++ * pools in descending order. Note that for good t_curr, 'total' ++ * indicates allocated bytes so far and 'size - total' indicates ++ * leftovers. ++ */ ++ for (i = num_pools - 1; i >= 0; i--) { ++ while (size - total >= states[i].size) { ++ /* Ignore potential wrapping of states[i].count as the count ++ * is 32 bits and shouldn't wrap in practice. ++ */ ++ states[i].count++; ++ total += states[i].size; ++#if defined(DUK_ALLOC_POOL_DEBUG) ++ duk__alloc_pool_dprintf("duk_alloc_pool_init: sprinkle %ld bytes (%ld left after) to pool index %ld, new count %ld\n", ++ (long) states[i].size, (long) (size - total), (long) i, (long) states[i].count); ++#endif ++ } ++ } ++ ++ /* Pool counts are final. Allocate the user supplied region based ++ * on the final counts, initialize free lists for each block size, ++ * and otherwise finalize 'state' for use. ++ */ ++ p = buffer; ++ global->num_pools = num_pools; ++ global->states = states; ++#if defined(DUK_ALLOC_POOL_TRACK_HIGHWATER) ++#if defined(DUK_ALLOC_POOL_DEBUG) ++ duk__alloc_pool_dprintf("duk_alloc_pool_init: global highwater mark tracking enabled, THIS IS VERY SLOW!\n"); ++#endif ++ global->hwm_used_bytes = 0U; ++ global->hwm_waste_bytes = 0U; ++#endif ++#if defined(DUK_ALLOC_POOL_TRACK_WASTE) ++#if defined(DUK_ALLOC_POOL_DEBUG) ++ duk__alloc_pool_dprintf("duk_alloc_pool_init: approximate waste tracking enabled\n"); ++#endif ++#endif ++ ++#if defined(DUK_USE_HEAPPTR16) ++ /* Register global base value for pointer compression, assumes ++ * a single active pool -4 allows a single subtract to be used and ++ * still ensures no non-NULL pointer encodes to zero. ++ */ ++ duk_alloc_pool_ptrcomp_base = (void *) (p - 4); ++#endif ++ ++ for (i = 0; i < num_pools; i++) { ++ n = (int) states[i].count; ++ if (n > 0) { ++ states[i].first = (duk_pool_free *) p; ++ for (j = 0; j < n; j++) { ++ char *p_next = p + states[i].size; ++ ((duk_pool_free *) p)->next = ++ (j == n - 1) ? (duk_pool_free *) NULL : (duk_pool_free *) p_next; ++ p = p_next; ++ } ++ } else { ++ states[i].first = (duk_pool_free *) NULL; ++ } ++ states[i].alloc_end = p; ++#if defined(DUK_ALLOC_POOL_TRACK_HIGHWATER) ++ states[i].hwm_used_count = 0; ++#endif ++ /* All members of 'state' now initialized. */ ++ ++#if defined(DUK_ALLOC_POOL_DEBUG) ++ duk__alloc_pool_dprintf("duk_alloc_pool_init: block size %5ld, count %5ld, %8ld total bytes, " ++ "end %p\n", ++ (long) states[i].size, (long) states[i].count, ++ (long) states[i].size * (long) states[i].count, ++ (void *) states[i].alloc_end); ++#endif ++ } ++ ++#if defined(DUK_ALLOC_POOL_ROMPTR_COMPRESSION) ++ /* ROM pointer compression precomputation. Assumes a single active ++ * pool. ++ */ ++ duk__alloc_pool_romptr_init(); ++#endif ++ ++ /* Use 'global' as udata. */ ++ return (void *) global; ++} ++ ++/* ++ * Misc helpers ++ */ ++ ++#if defined(DUK_ALLOC_POOL_TRACK_WASTE) ++static void duk__alloc_pool_set_waste_marker(void *ptr, size_t used, size_t size) { ++ /* Rely on the base pointer and size being divisible by 4 and thus ++ * aligned. Use 32-bit markers: a 4-byte resolution is good enough, ++ * and comparing 32 bits at a time makes false waste estimates less ++ * likely than when comparing as bytes. ++ */ ++ duk_uint32_t *p, *p_start, *p_end; ++ size_t used_round; ++ ++ used_round = (used + 3U) & ~0x03U; /* round up to 4 */ ++ p_end = (duk_uint32_t *) ((duk_uint8_t *) ptr + size); ++ p_start = (duk_uint32_t *) ((duk_uint8_t *) ptr + used_round); ++ p = (duk_uint32_t *) p_start; ++ while (p != p_end) { ++ *p++ = DUK_ALLOC_POOL_WASTE_MARKER; ++ } ++} ++#else /* DUK_ALLOC_POOL_TRACK_WASTE */ ++static void duk__alloc_pool_set_waste_marker(void *ptr, size_t used, size_t size) { ++ (void) ptr; (void) used; (void) size; ++} ++#endif /* DUK_ALLOC_POOL_TRACK_WASTE */ ++ ++#if defined(DUK_ALLOC_POOL_TRACK_WASTE) ++static size_t duk__alloc_pool_get_waste_estimate(void *ptr, size_t size) { ++ duk_uint32_t *p, *p_end, *p_start; ++ ++ /* Assumes size is >= 4. */ ++ p_start = (duk_uint32_t *) ptr; ++ p_end = (duk_uint32_t *) ((duk_uint8_t *) ptr + size); ++ p = p_end; ++ ++ /* This scan may cause harmless valgrind complaints: there may be ++ * uninitialized bytes within the legitimate allocation or between ++ * the start of the waste marker and the end of the allocation. ++ */ ++ do { ++ p--; ++ if (*p == DUK_ALLOC_POOL_WASTE_MARKER) { ++ ; ++ } else { ++ return (size_t) (p_end - p - 1) * 4U; ++ } ++ } while (p != p_start); ++ ++ return size; ++} ++#else /* DUK_ALLOC_POOL_TRACK_WASTE */ ++static size_t duk__alloc_pool_get_waste_estimate(void *ptr, size_t size) { ++ (void) ptr; (void) size; ++ return 0; ++} ++#endif /* DUK_ALLOC_POOL_TRACK_WASTE */ ++ ++static int duk__alloc_pool_ptr_in_freelist(duk_pool_state *s, void *ptr) { ++ duk_pool_free *curr; ++ ++ for (curr = s->first; curr != NULL; curr = curr->next) { ++ if ((void *) curr == ptr) { ++ return 1; ++ } ++ } ++ return 0; ++} ++ ++void duk_alloc_pool_get_pool_stats(duk_pool_state *s, duk_pool_stats *res) { ++ void *curr; ++ size_t free_count; ++ size_t used_count; ++ size_t waste_bytes; ++ ++ curr = s->alloc_end - (s->size * s->count); ++ free_count = 0U; ++ waste_bytes = 0U; ++ while (curr != s->alloc_end) { ++ if (duk__alloc_pool_ptr_in_freelist(s, curr)) { ++ free_count++; ++ } else { ++ waste_bytes += duk__alloc_pool_get_waste_estimate(curr, s->size); ++ } ++ curr = curr + s->size; ++ } ++ used_count = (size_t) (s->count - free_count); ++ ++ res->used_count = used_count; ++ res->used_bytes = (size_t) (used_count * s->size); ++ res->free_count = free_count; ++ res->free_bytes = (size_t) (free_count * s->size); ++ res->waste_bytes = waste_bytes; ++#if defined(DUK_ALLOC_POOL_TRACK_HIGHWATER) ++ res->hwm_used_count = s->hwm_used_count; ++#else ++ res->hwm_used_count = 0U; ++#endif ++} ++ ++void duk_alloc_pool_get_global_stats(duk_pool_global *g, duk_pool_global_stats *res) { ++ int i; ++ size_t total_used = 0U; ++ size_t total_free = 0U; ++ size_t total_waste = 0U; ++ ++ for (i = 0; i < g->num_pools; i++) { ++ duk_pool_state *s = &g->states[i]; ++ duk_pool_stats stats; ++ ++ duk_alloc_pool_get_pool_stats(s, &stats); ++ ++ total_used += stats.used_bytes; ++ total_free += stats.free_bytes; ++ total_waste += stats.waste_bytes; ++ } ++ ++ res->used_bytes = total_used; ++ res->free_bytes = total_free; ++ res->waste_bytes = total_waste; ++#if defined(DUK_ALLOC_POOL_TRACK_HIGHWATER) ++ res->hwm_used_bytes = g->hwm_used_bytes; ++ res->hwm_waste_bytes = g->hwm_waste_bytes; ++#else ++ res->hwm_used_bytes = 0U; ++ res->hwm_waste_bytes = 0U; ++#endif ++} ++ ++#if defined(DUK_ALLOC_POOL_TRACK_HIGHWATER) ++static void duk__alloc_pool_update_highwater(duk_pool_global *g) { ++ int i; ++ size_t total_used = 0U; ++ size_t total_free = 0U; ++ size_t total_waste = 0U; ++ ++ /* Per pool highwater used count, useful to checking if a pool is ++ * too small. ++ */ ++ for (i = 0; i < g->num_pools; i++) { ++ duk_pool_state *s = &g->states[i]; ++ duk_pool_stats stats; ++ ++ duk_alloc_pool_get_pool_stats(s, &stats); ++ if (stats.used_count > s->hwm_used_count) { ++#if defined(DUK_ALLOC_POOL_DEBUG) ++ duk__alloc_pool_dprintf("duk__alloc_pool_update_highwater: pool %ld (%ld bytes) highwater updated: count %ld -> %ld\n", ++ (long) i, (long) s->size, ++ (long) s->hwm_used_count, (long) stats.used_count); ++#endif ++ s->hwm_used_count = stats.used_count; ++ } ++ ++ total_used += stats.used_bytes; ++ total_free += stats.free_bytes; ++ total_waste += stats.waste_bytes; ++ } ++ ++ /* Global highwater mark for used and waste bytes. Both fields are ++ * updated from the same snapshot based on highest used count. ++ * This is VERY, VERY slow and only useful for development. ++ * (Note that updating HWM states for pools individually and then ++ * summing them won't create a consistent global snapshot. There ++ * are still easy ways to make this much, much faster.) ++ */ ++ if (total_used > g->hwm_used_bytes) { ++#if defined(DUK_ALLOC_POOL_DEBUG) ++ duk__alloc_pool_dprintf("duk__alloc_pool_update_highwater: global highwater updated: used=%ld, bytes=%ld -> " ++ "used=%ld, bytes=%ld\n", ++ (long) g->hwm_used_bytes, (long) g->hwm_waste_bytes, ++ (long) total_used, (long) total_waste); ++#endif ++ g->hwm_used_bytes = total_used; ++ g->hwm_waste_bytes = total_waste; ++ } ++} ++#else /* DUK_ALLOC_POOL_TRACK_HIGHWATER */ ++static void duk__alloc_pool_update_highwater(duk_pool_global *g) { ++ (void) g; ++} ++#endif /* DUK_ALLOC_POOL_TRACK_HIGHWATER */ ++ ++/* ++ * Allocation providers ++ */ ++ ++void *duk_alloc_pool(void *udata, duk_size_t size) { ++ duk_pool_global *g = (duk_pool_global *) udata; ++ int i, n; ++ ++#if defined(DUK_ALLOC_POOL_DEBUG) ++ duk__alloc_pool_dprintf("duk_alloc_pool: %p %ld\n", udata, (long) size); ++#endif ++ ++ if (size == 0) { ++ return NULL; ++ } ++ ++ for (i = 0, n = g->num_pools; i < n; i++) { ++ duk_pool_state *st = g->states + i; ++ ++ if (size <= st->size) { ++ duk_pool_free *res = st->first; ++ if (res != NULL) { ++ st->first = res->next; ++ duk__alloc_pool_set_waste_marker((void *) res, size, st->size); ++ duk__alloc_pool_update_highwater(g); ++ return (void *) res; ++ } ++ } ++ ++ /* Allocation doesn't fit or no free entries, try to borrow ++ * from the next block size. There's no support for preventing ++ * a borrow at present. ++ */ ++ } ++ ++ return NULL; ++} ++ ++void *duk_realloc_pool(void *udata, void *ptr, duk_size_t size) { ++ duk_pool_global *g = (duk_pool_global *) udata; ++ int i, j, n; ++ ++#if defined(DUK_ALLOC_POOL_DEBUG) ++ duk__alloc_pool_dprintf("duk_realloc_pool: %p %p %ld\n", udata, ptr, (long) size); ++#endif ++ ++ if (ptr == NULL) { ++ return duk_alloc_pool(udata, size); ++ } ++ if (size == 0) { ++ duk_free_pool(udata, ptr); ++ return NULL; ++ } ++ ++ /* Non-NULL pointers are necessarily from the pool so we should ++ * always be able to find the allocation. ++ */ ++ ++ for (i = 0, n = g->num_pools; i < n; i++) { ++ duk_pool_state *st = g->states + i; ++ char *new_ptr; ++ ++ /* Because 'ptr' is assumed to be in the pool and pools are ++ * allocated in sequence, it suffices to check for end pointer ++ * only. ++ */ ++ if ((char *) ptr >= st->alloc_end) { ++ continue; ++ } ++ ++ if (size <= st->size) { ++ /* Allocation still fits existing allocation. Check if ++ * we can shrink the allocation to a smaller block size ++ * (smallest possible). ++ */ ++ for (j = 0; j < i; j++) { ++ duk_pool_state *st2 = g->states + j; ++ ++ if (size <= st2->size) { ++ new_ptr = (char *) st2->first; ++ if (new_ptr != NULL) { ++#if defined(DUK_ALLOC_POOL_DEBUG) ++ duk__alloc_pool_dprintf("duk_realloc_pool: shrink, block size %ld -> %ld\n", ++ (long) st->size, (long) st2->size); ++#endif ++ st2->first = ((duk_pool_free *) new_ptr)->next; ++ memcpy((void *) new_ptr, (const void *) ptr, (size_t) size); ++ ((duk_pool_free *) ptr)->next = st->first; ++ st->first = (duk_pool_free *) ptr; ++ duk__alloc_pool_set_waste_marker((void *) new_ptr, size, st2->size); ++ duk__alloc_pool_update_highwater(g); ++ return (void *) new_ptr; ++ } ++ } ++ } ++ ++ /* Failed to shrink; return existing pointer. */ ++ duk__alloc_pool_set_waste_marker((void *) ptr, size, st->size); ++ return ptr; ++ } ++ ++ /* Find first free larger block. */ ++ for (j = i + 1; j < n; j++) { ++ duk_pool_state *st2 = g->states + j; ++ ++ if (size <= st2->size) { ++ new_ptr = (char *) st2->first; ++ if (new_ptr != NULL) { ++ st2->first = ((duk_pool_free *) new_ptr)->next; ++ memcpy((void *) new_ptr, (const void *) ptr, (size_t) st->size); ++ ((duk_pool_free *) ptr)->next = st->first; ++ st->first = (duk_pool_free *) ptr; ++ duk__alloc_pool_set_waste_marker((void *) new_ptr, size, st2->size); ++ duk__alloc_pool_update_highwater(g); ++ return (void *) new_ptr; ++ } ++ } ++ } ++ ++ /* Failed to resize. */ ++ return NULL; ++ } ++ ++ /* We should never be here because 'ptr' should be a valid pool ++ * entry and thus always found above. ++ */ ++ return NULL; ++} ++ ++void duk_free_pool(void *udata, void *ptr) { ++ duk_pool_global *g = (duk_pool_global *) udata; ++ int i, n; ++ ++#if defined(DUK_ALLOC_POOL_DEBUG) ++ duk__alloc_pool_dprintf("duk_free_pool: %p %p\n", udata, ptr); ++#endif ++ ++ if (ptr == NULL) { ++ return; ++ } ++ ++ for (i = 0, n = g->num_pools; i < n; i++) { ++ duk_pool_state *st = g->states + i; ++ ++ /* Enough to check end address only. */ ++ if ((char *) ptr >= st->alloc_end) { ++ continue; ++ } ++ ++ ((duk_pool_free *) ptr)->next = st->first; ++ st->first = (duk_pool_free *) ptr; ++#if 0 /* never necessary when freeing */ ++ duk__alloc_pool_update_highwater(g); ++#endif ++ return; ++ } ++ ++ /* We should never be here because 'ptr' should be a valid pool ++ * entry and thus always found above. ++ */ ++} ++ ++/* ++ * Pointer compression ++ */ ++ ++#if defined(DUK_ALLOC_POOL_ROMPTR_COMPRESSION) ++static void duk__alloc_pool_romptr_init(void) { ++ /* Scan ROM pointer range for faster detection of "is 'p' a ROM pointer" ++ * later on. ++ */ ++ const void * const * ptrs = (const void * const *) duk_rom_compressed_pointers; ++ duk_alloc_pool_romptr_low = duk_alloc_pool_romptr_high = (const void *) *ptrs; ++ while (*ptrs) { ++ if (*ptrs > duk_alloc_pool_romptr_high) { ++ duk_alloc_pool_romptr_high = (const void *) *ptrs; ++ } ++ if (*ptrs < duk_alloc_pool_romptr_low) { ++ duk_alloc_pool_romptr_low = (const void *) *ptrs; ++ } ++ ptrs++; ++ } ++} ++#endif ++ ++/* Encode/decode functions are defined in the header to allow inlining. */ ++ ++#if defined(DUK_ALLOC_POOL_ROMPTR_COMPRESSION) ++duk_uint16_t duk_alloc_pool_enc16_rom(void *ptr) { ++ /* The if-condition should be the fastest possible check ++ * for "is 'ptr' in ROM?". If pointer is in ROM, we'd like ++ * to compress it quickly. Here we just scan a ~1K array ++ * which is very bad for performance. ++ */ ++ const void * const * ptrs = duk_rom_compressed_pointers; ++ while (*ptrs) { ++ if (*ptrs == ptr) { ++ return DUK_ALLOC_POOL_ROMPTR_FIRST + (duk_uint16_t) (ptrs - duk_rom_compressed_pointers); ++ } ++ ptrs++; ++ } ++ ++ /* We should really never be here: Duktape should only be ++ * compressing pointers which are in the ROM compressed ++ * pointers list, which are known when configuring sources. ++ * We go on, causing a pointer compression error. ++ */ ++ return 0; ++} ++#endif +diff --git a/dist/source/extras/alloc-pool/duk_alloc_pool.h b/dist/source/extras/alloc-pool/duk_alloc_pool.h +new file mode 100644 +index 0000000..671f761 +--- /dev/null ++++ b/dist/source/extras/alloc-pool/duk_alloc_pool.h +@@ -0,0 +1,231 @@ ++#if !defined(DUK_ALLOC_POOL_H_INCLUDED) ++#define DUK_ALLOC_POOL_H_INCLUDED ++ ++#include "duktape.h" ++ ++#if defined(__cplusplus) ++extern "C" { ++#endif ++ ++/* 32-bit (big endian) marker used at the end of pool entries so that wasted ++ * space can be detected. Waste tracking must be enabled explicitly. ++ */ ++#if defined(DUK_ALLOC_POOL_TRACK_WASTE) ++#define DUK_ALLOC_POOL_WASTE_MARKER 0xedcb2345UL ++#endif ++ ++/* Pointer compression with ROM strings/objects: ++ * ++ * For now, use DUK_USE_ROM_OBJECTS to signal the need for compressed ROM ++ * pointers. DUK_USE_ROM_PTRCOMP_FIRST is provided for the ROM pointer ++ * compression range minimum to avoid duplication in user code. ++ */ ++#if defined(DUK_USE_ROM_OBJECTS) && defined(DUK_USE_HEAPPTR16) ++#define DUK_ALLOC_POOL_ROMPTR_COMPRESSION ++#define DUK_ALLOC_POOL_ROMPTR_FIRST DUK_USE_ROM_PTRCOMP_FIRST ++ ++/* This extern declaration is provided by duktape.h, array provided by duktape.c. ++ * Because duk_config.h may include this file (to get the inline functions) we ++ * need to forward declare this also here. ++ */ ++extern const void * const duk_rom_compressed_pointers[]; ++#endif ++ ++/* Pool configuration for a certain block size. */ ++typedef struct { ++ unsigned int size; /* must be divisible by 4 and >= sizeof(void *) */ ++ unsigned int a; /* bytes (not count) to allocate: a*t + b, t is an arbitrary scale parameter */ ++ unsigned int b; ++} duk_pool_config; ++ ++/* Freelist entry, must fit into the smallest block size. */ ++struct duk_pool_free; ++typedef struct duk_pool_free duk_pool_free; ++struct duk_pool_free { ++ duk_pool_free *next; ++}; ++ ++/* Pool state for a certain block size. */ ++typedef struct { ++ duk_pool_free *first; ++ char *alloc_end; ++ unsigned int size; ++ unsigned int count; ++#if defined(DUK_ALLOC_POOL_TRACK_HIGHWATER) ++ unsigned int hwm_used_count; ++#endif ++} duk_pool_state; ++ ++/* Statistics for a certain pool. */ ++typedef struct { ++ size_t used_count; ++ size_t used_bytes; ++ size_t free_count; ++ size_t free_bytes; ++ size_t waste_bytes; ++ size_t hwm_used_count; ++} duk_pool_stats; ++ ++/* Top level state for all pools. Pointer to this struct is used as the allocator ++ * userdata pointer. ++ */ ++typedef struct { ++ int num_pools; ++ duk_pool_state *states; ++#if defined(DUK_ALLOC_POOL_TRACK_HIGHWATER) ++ size_t hwm_used_bytes; ++ size_t hwm_waste_bytes; ++#endif ++} duk_pool_global; ++ ++/* Statistics for the entire set of pools. */ ++typedef struct { ++ size_t used_bytes; ++ size_t free_bytes; ++ size_t waste_bytes; ++ size_t hwm_used_bytes; ++ size_t hwm_waste_bytes; ++} duk_pool_global_stats; ++ ++/* Initialize a pool allocator, arguments: ++ * - buffer and size: continuous region to use for pool, must align to 4 ++ * - config: configuration for pools in ascending block size ++ * - state: state for pools, matches config order ++ * - num_pools: number of entries in 'config' and 'state' ++ * - global: global state structure ++ * ++ * The 'config', 'state', and 'global' pointers must be valid beyond the init ++ * call, as long as the pool is used. ++ * ++ * Returns a void pointer to be used as userdata for the allocator functions. ++ * Concretely the return value will be "(void *) global", i.e. the global ++ * state struct. If pool init fails, the return value will be NULL. ++ */ ++void *duk_alloc_pool_init(char *buffer, ++ size_t size, ++ const duk_pool_config *configs, ++ duk_pool_state *states, ++ int num_pools, ++ duk_pool_global *global); ++ ++/* Duktape allocation providers. Typing matches Duktape requirements. */ ++void *duk_alloc_pool(void *udata, duk_size_t size); ++void *duk_realloc_pool(void *udata, void *ptr, duk_size_t size); ++void duk_free_pool(void *udata, void *ptr); ++ ++/* Stats. */ ++void duk_alloc_pool_get_pool_stats(duk_pool_state *s, duk_pool_stats *res); ++void duk_alloc_pool_get_global_stats(duk_pool_global *g, duk_pool_global_stats *res); ++ ++/* Duktape pointer compression global state (assumes single pool). */ ++#if defined(DUK_USE_ROM_OBJECTS) && defined(DUK_USE_HEAPPTR16) ++extern const void *duk_alloc_pool_romptr_low; ++extern const void *duk_alloc_pool_romptr_high; ++duk_uint16_t duk_alloc_pool_enc16_rom(void *ptr); ++#endif ++#if defined(DUK_USE_HEAPPTR16) ++extern void *duk_alloc_pool_ptrcomp_base; ++#endif ++ ++#if 0 ++duk_uint16_t duk_alloc_pool_enc16(void *ptr); ++void *duk_alloc_pool_dec16(duk_uint16_t val); ++#endif ++ ++/* Inlined pointer compression functions. Gcc and clang -Os won't in ++ * practice inline these without an "always inline" attribute because it's ++ * more size efficient (by a few kB) to use explicit calls instead. Having ++ * these defined inline here allows performance optimized builds to inline ++ * pointer compression operations. ++ * ++ * Pointer compression assumes there's a single globally registered memory ++ * pool which makes pointer compression more efficient. This would be easy ++ * to fix by adding a userdata pointer to the compression functions and ++ * plumbing the heap userdata from the compression/decompression macros. ++ */ ++ ++/* DUK_ALWAYS_INLINE is not a public API symbol so it may go away in even a ++ * minor update. But it's pragmatic for this extra because it handles many ++ * compilers via duk_config.h detection. Check that the macro exists so that ++ * if it's gone, we can still compile. ++ */ ++#if defined(DUK_ALWAYS_INLINE) ++#define DUK__ALLOC_POOL_ALWAYS_INLINE DUK_ALWAYS_INLINE ++#else ++#define DUK__ALLOC_POOL_ALWAYS_INLINE /* nop */ ++#endif ++ ++#if defined(DUK_USE_HEAPPTR16) ++static DUK__ALLOC_POOL_ALWAYS_INLINE duk_uint16_t duk_alloc_pool_enc16(void *ptr) { ++ if (ptr == NULL) { ++ /* With 'return 0' gcc and clang -Os generate inefficient code. ++ * For example, gcc -Os generates: ++ * ++ * 0804911d : ++ * 804911d: 55 push %ebp ++ * 804911e: 85 c0 test %eax,%eax ++ * 8049120: 89 e5 mov %esp,%ebp ++ * 8049122: 74 0b je 804912f ++ * 8049124: 2b 05 e4 90 07 08 sub 0x80790e4,%eax ++ * 804912a: c1 e8 02 shr $0x2,%eax ++ * 804912d: eb 02 jmp 8049131 ++ * 804912f: 31 c0 xor %eax,%eax ++ * 8049131: 5d pop %ebp ++ * 8049132: c3 ret ++ * ++ * The NULL path checks %eax for zero; if it is zero, a zero ++ * is unnecessarily loaded into %eax again. The non-zero path ++ * has an unnecessary jump as a side effect of this. ++ * ++ * Using 'return (duk_uint16_t) (intptr_t) ptr;' generates similarly ++ * inefficient code; not sure how to make the result better. ++ */ ++ return 0; ++ } ++#if defined(DUK_ALLOC_POOL_ROMPTR_COMPRESSION) ++ if (ptr >= duk_alloc_pool_romptr_low && ptr <= duk_alloc_pool_romptr_high) { ++ /* This is complex enough now to need a separate function. */ ++ return duk_alloc_pool_enc16_rom(ptr); ++ } ++#endif ++ return (duk_uint16_t) (((size_t) ((char *) ptr - (char *) duk_alloc_pool_ptrcomp_base)) >> 2); ++} ++ ++static DUK__ALLOC_POOL_ALWAYS_INLINE void *duk_alloc_pool_dec16(duk_uint16_t val) { ++ if (val == 0) { ++ /* As with enc16 the gcc and clang -Os output is inefficient, ++ * e.g. gcc -Os: ++ * ++ * 08049133 : ++ * 8049133: 55 push %ebp ++ * 8049134: 66 85 c0 test %ax,%ax ++ * 8049137: 89 e5 mov %esp,%ebp ++ * 8049139: 74 0e je 8049149 ++ * 804913b: 8b 15 e4 90 07 08 mov 0x80790e4,%edx ++ * 8049141: 0f b7 c0 movzwl %ax,%eax ++ * 8049144: 8d 04 82 lea (%edx,%eax,4),%eax ++ * 8049147: eb 02 jmp 804914b ++ * 8049149: 31 c0 xor %eax,%eax ++ * 804914b: 5d pop %ebp ++ * 804914c: c3 ret ++ */ ++ return NULL; ++ } ++#if defined(DUK_ALLOC_POOL_ROMPTR_COMPRESSION) ++ if (val >= DUK_ALLOC_POOL_ROMPTR_FIRST) { ++ /* This is a blind lookup, could check index validity. ++ * Duktape should never decompress a pointer which would ++ * be out-of-bounds here. ++ */ ++ return (void *) (intptr_t) (duk_rom_compressed_pointers[val - DUK_ALLOC_POOL_ROMPTR_FIRST]); ++ } ++#endif ++ return (void *) ((char *) duk_alloc_pool_ptrcomp_base + (((size_t) val) << 2)); ++} ++#endif ++ ++#if defined(__cplusplus) ++} ++#endif /* end 'extern "C"' wrapper */ ++ ++#endif /* DUK_ALLOC_POOL_H_INCLUDED */ +diff --git a/dist/source/extras/alloc-pool/ptrcomp.yaml b/dist/source/extras/alloc-pool/ptrcomp.yaml +new file mode 100644 +index 0000000..9ce58f4 +--- /dev/null ++++ b/dist/source/extras/alloc-pool/ptrcomp.yaml +@@ -0,0 +1,16 @@ ++DUK_USE_REFCOUNT16: true ++DUK_USE_STRHASH16: true ++DUK_USE_STRLEN16: true ++DUK_USE_BUFLEN16: true ++DUK_USE_OBJSIZES16: true ++DUK_USE_HSTRING_CLEN: false ++DUK_USE_HOBJECT_HASH_PART: false ++DUK_USE_HEAPPTR16: true ++DUK_USE_HEAPPTR_ENC16: ++ verbatim: "#define DUK_USE_HEAPPTR_ENC16(ud,p) duk_alloc_pool_enc16((p))" ++DUK_USE_HEAPPTR_DEC16: ++ verbatim: "#define DUK_USE_HEAPPTR_DEC16(ud,p) duk_alloc_pool_dec16((p))" ++ ++#DUK_USE_ROM_OBJECTS: true ++#DUK_USE_ROM_STRINGS: true ++#DUK_USE_ROM_GLOBAL_INHERIT: true +diff --git a/dist/source/extras/alloc-pool/ptrcomp_fixup.h b/dist/source/extras/alloc-pool/ptrcomp_fixup.h +new file mode 100644 +index 0000000..fec0ff5 +--- /dev/null ++++ b/dist/source/extras/alloc-pool/ptrcomp_fixup.h +@@ -0,0 +1,2 @@ ++/* To provide declarations for inline pointer compression functions. */ ++#include "duk_alloc_pool.h" +diff --git a/dist/source/extras/alloc-pool/test.c b/dist/source/extras/alloc-pool/test.c +new file mode 100644 +index 0000000..f272244 +--- /dev/null ++++ b/dist/source/extras/alloc-pool/test.c +@@ -0,0 +1,114 @@ ++#include ++#include ++#include "duktape.h" ++#include "duk_alloc_pool.h" ++ ++void my_fatal(const char *msg) { ++ fprintf(stderr, "*** FATAL: %s\n", msg ? msg : "no message"); ++ fflush(stderr); ++ abort(); ++} ++ ++static duk_ret_t my_print(duk_context *ctx) { ++ duk_push_string(ctx, " "); ++ duk_insert(ctx, 0); ++ duk_join(ctx, duk_get_top(ctx) - 1); ++ printf("%s\n", duk_safe_to_string(ctx, -1)); ++ return 1; ++} ++ ++static void dump_pool_state(duk_pool_global *g) { ++ int i; ++ long total_size = 0; ++ long total_used = 0; ++ ++ for (i = 0; i < g->num_pools; i++) { ++ duk_pool_state *st = g->states + i; ++ int free, used; ++ duk_pool_free *f; ++ ++ for (free = 0, f = st->first; f; f = f->next) { ++ free++; ++ } ++ used = st->count - free; ++ printf("Pool %2d: block size %5d, count %4d/%4d, bytes %6d/%6d\n", ++ i, (int) st->size, used, (int) st->count, ++ (int) st->size * used, (int) st->size * (int) st->count); ++ ++ total_size += (long) st->size * (long) st->count; ++ total_used += (long) st->size * (long) used; ++ } ++ printf("=== Total: %ld/%ld, free %ld\n", ++ (long) total_used, (long) total_size, (long) (total_size - total_used)); ++} ++ ++int main(int argc, char *argv[]) { ++ duk_context *ctx; ++ int i; ++ int exitcode = 0; ++ ++ /* NOTE! This pool configuration is NOT a good pool configuration ++ * for practical use (and is not intended to be one). A production ++ * pool configuration should be created using measurements. ++ */ ++ const duk_pool_config pool_configs[15] = { ++ { 16, 20, 200 }, ++ { 20, 40, 100 }, ++ { 24, 40, 100 }, ++ { 32, 60, 50 }, ++ { 40, 60, 50 }, ++ { 48, 60, 50 }, ++ { 56, 60, 50 }, ++ { 64, 60, 50 }, ++ { 80, 60, 50 }, ++ { 256, 100, 10 }, ++ { 1024, 20, 2 }, ++ { 2048, 20, 2 }, ++ { 4096, 100, 2 }, ++ { 6144, 60, 2 }, ++ { 8192, 100, 2 }, ++ }; ++ duk_pool_state pool_states[15]; /* Count must match pool_configs[]. */ ++ duk_pool_global pool_global; ++ ++ char buffer[200000]; ++ void *pool_udata; ++ ++ pool_udata = duk_alloc_pool_init(buffer, sizeof(buffer), pool_configs, pool_states, sizeof(pool_configs) / sizeof(duk_pool_config), &pool_global); ++ if (!pool_udata) { ++ return 1; ++ } ++ ++ printf("Pool after pool init:\n"); ++ dump_pool_state(&pool_global); ++ ++ ctx = duk_create_heap(duk_alloc_pool, duk_realloc_pool, duk_free_pool, pool_udata, NULL); ++ if (!ctx) { ++ return 1; ++ } ++ ++ printf("Pool after Duktape heap creation:\n"); ++ dump_pool_state(&pool_global); ++ ++ duk_push_c_function(ctx, my_print, DUK_VARARGS); ++ duk_put_global_string(ctx, "print"); ++ duk_push_c_function(ctx, my_print, DUK_VARARGS); ++ duk_put_global_string(ctx, "alert"); ++ printf("top after init: %ld\n", (long) duk_get_top(ctx)); ++ ++ for (i = 1; i < argc; i++) { ++ printf("Evaling: %s\n", argv[i]); ++ if (duk_peval_string(ctx, argv[i]) != 0) { ++ exitcode = 1; ++ } ++ printf("--> %s\n", duk_safe_to_string(ctx, -1)); ++ duk_pop(ctx); ++ } ++ ++ printf("Pool after evaling code:\n"); ++ dump_pool_state(&pool_global); ++ ++ printf("Done\n"); ++ duk_destroy_heap(ctx); ++ return exitcode; ++} +diff --git a/dist/source/extras/cbor/Makefile b/dist/source/extras/cbor/Makefile +new file mode 100644 +index 0000000..1e8a6d4 +--- /dev/null ++++ b/dist/source/extras/cbor/Makefile +@@ -0,0 +1,261 @@ ++# For manual testing; say 'make' in extras/cbor. ++ ++VALGRIND = valgrind ++CC = gcc ++CCOPTS = -std=c99 -Wall -Wextra -O2 ++#CCOPTS += -fsanitize=undefined ++ ++.PHONY: all ++all: clean jsoncbor test-runs ++ ++.PHONY: clean ++clean: ++ -rm -rf ./prep ++ -rm -f jsoncbor ++ -rm -f appendix_a.json ++ ++jsoncbor: jsoncbor.c duk_cbor.c duk_cbor.h ++ -rm -rf ./prep ++ python2 ../../tools/configure.py --quiet --output-directory ./prep \ ++ -DDUK_USE_JSON_DECNUMBER_FASTPATH \ ++ -DDUK_USE_JSON_DECSTRING_FASTPATH \ ++ -DDUK_USE_JSON_EATWHITE_FASTPATH \ ++ -DDUK_USE_JSON_QUOTESTRING_FASTPATH \ ++ -DDUK_USE_JSON_STRINGIFY_FASTPATH ++ $(CC) $(CCOPTS) -o $@ -I./prep -I. ./prep/duktape.c duk_cbor.c jsoncbor.c -lm ++ ++appendix_a.json: ++ wget -O $@ https://raw.githubusercontent.com/cbor/test-vectors/master/appendix_a.json ++ ++.PHONY: test-runs ++test-runs: jsoncbor \ ++ enc-primitives enc-number enc-buffer enc-string enc-array enc-object enc-half-float enc-float enc-misc \ ++ dec-half-float dec-half-float dec-array dec-object ++ ++.PHONY: test-vectors ++test-vectors: appendix_a.json ++ echo "Expects 'duk' to exist in local directory for now..." ++ ./duk run_testvectors.js ++ ++.PHONY: dec-half-float ++dec-half-float: jsoncbor ++ @echo "half-float zero" ++ python -c 'import sys; sys.stdout.write("\xf9\x00\x00")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\xf9\x00\x00")'| $(VALGRIND) -q ./jsoncbor -d ++ python -c 'import sys; sys.stdout.write("\xf9\x80\x00")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\xf9\x80\x00")'| $(VALGRIND) -q ./jsoncbor -d ++ @echo "half-float denormal" ++ python -c 'import sys; sys.stdout.write("\xf9\x00\x01")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\xf9\x00\x01")'| $(VALGRIND) -q ./jsoncbor -d ++ python -c 'import sys; sys.stdout.write("\xf9\x80\x01")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\xf9\x80\x01")'| $(VALGRIND) -q ./jsoncbor -d ++ python -c 'import sys; sys.stdout.write("\xf9\x00\x02")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\xf9\x00\x02")'| $(VALGRIND) -q ./jsoncbor -d ++ python -c 'import sys; sys.stdout.write("\xf9\x80\x02")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\xf9\x80\x02")'| $(VALGRIND) -q ./jsoncbor -d ++ python -c 'import sys; sys.stdout.write("\xf9\x03\xff")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\xf9\x03\xff")'| $(VALGRIND) -q ./jsoncbor -d ++ python -c 'import sys; sys.stdout.write("\xf9\x83\xff")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\xf9\x83\xff")'| $(VALGRIND) -q ./jsoncbor -d ++ @echo "half-float normal" ++ python -c 'import sys; sys.stdout.write("\xf9\x04\x00")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\xf9\x04\x00")'| $(VALGRIND) -q ./jsoncbor -d ++ python -c 'import sys; sys.stdout.write("\xf9\x84\x00")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\xf9\x84\x00")'| $(VALGRIND) -q ./jsoncbor -d ++ python -c 'import sys; sys.stdout.write("\xf9\x07\xfe")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\xf9\x07\xfe")'| $(VALGRIND) -q ./jsoncbor -d ++ python -c 'import sys; sys.stdout.write("\xf9\x87\xfe")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\xf9\x87\xfe")'| $(VALGRIND) -q ./jsoncbor -d ++ python -c 'import sys; sys.stdout.write("\xf9\x08\x00")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\xf9\x08\x00")'| $(VALGRIND) -q ./jsoncbor -d ++ python -c 'import sys; sys.stdout.write("\xf9\x88\x00")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\xf9\x88\x00")'| $(VALGRIND) -q ./jsoncbor -d ++ python -c 'import sys; sys.stdout.write("\xf9\x78\x00")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\xf9\x78\x00")'| $(VALGRIND) -q ./jsoncbor -d ++ python -c 'import sys; sys.stdout.write("\xf9\xf8\x00")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\xf9\xf8\x00")'| $(VALGRIND) -q ./jsoncbor -d ++ python -c 'import sys; sys.stdout.write("\xf9\x7b\xff")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\xf9\x7b\xff")'| $(VALGRIND) -q ./jsoncbor -d ++ python -c 'import sys; sys.stdout.write("\xf9\xfb\xff")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\xf9\xfb\xff")'| $(VALGRIND) -q ./jsoncbor -d ++ @echo "half-float inf" ++ python -c 'import sys; sys.stdout.write("\xf9\x7c\x00")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\xf9\x7c\x00")'| $(VALGRIND) -q ./jsoncbor -d ++ python -c 'import sys; sys.stdout.write("\xf9\xfc\x00")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\xf9\xfc\x00")'| $(VALGRIND) -q ./jsoncbor -d ++ @echo "half-float nan" ++ python -c 'import sys; sys.stdout.write("\xf9\x7c\x01")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\xf9\x7c\x01")'| $(VALGRIND) -q ./jsoncbor -d ++ python -c 'import sys; sys.stdout.write("\xf9\xfc\x01")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\xf9\xfc\x01")'| $(VALGRIND) -q ./jsoncbor -d ++ python -c 'import sys; sys.stdout.write("\xf9\x7e\x00")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\xf9\x7e\x00")'| $(VALGRIND) -q ./jsoncbor -d ++ python -c 'import sys; sys.stdout.write("\xf9\xfe\x00")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\xf9\xfe\x00")'| $(VALGRIND) -q ./jsoncbor -d ++ python -c 'import sys; sys.stdout.write("\xf9\x7f\x12")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\xf9\x7f\x12")'| $(VALGRIND) -q ./jsoncbor -d ++ python -c 'import sys; sys.stdout.write("\xf9\xff\x12")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\xf9\xff\x12")'| $(VALGRIND) -q ./jsoncbor -d ++ ++.PHONY: dec-array ++dec-array: jsoncbor ++ python -c 'import sys; sys.stdout.write("\x9f\x01\x02\x03\x9f\xff\x04\x80\x80\x9f\x10\x11\xff\x84\x04\x03\x02\x01\xff")' | python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\x9f\x01\x02\x03\x9f\xff\x04\x80\x80\x9f\x10\x11\xff\x84\x04\x03\x02\x01\xff")' | $(VALGRIND) -q ./jsoncbor -d ++ ++.PHONY: dec-object ++dec-object: jsoncbor ++ python -c 'import sys; sys.stdout.write("\xbf\x63foo\x63bar\x63bar\xa0\x64quux\xbf\xff\x63baz\xa2\x61a\x61b\x61c\x61d\x65quuux\xbf\x61x\x61y\x61z\x61w\xff\xff")' | python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\xbf\x63foo\x63bar\x63bar\xa0\x64quux\xbf\xff\x63baz\xa2\x61a\x61b\x61c\x61d\x65quuux\xbf\x61x\x61y\x61z\x61w\xff\xff")' | $(VALGRIND) -q ./jsoncbor -d ++ ++.PHONY: dec-misc ++dec-misc: jsoncbor ++ python -c 'import sys; sys.stdout.write("\xa1cfoocbar")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\xa1cfoocbar")'| $(VALGRIND) -q ./jsoncbor -d ++ python -c 'import sys; sys.stdout.write("\xa1\xa0\xa0")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\xa1\xa0\xa0")'| $(VALGRIND) -q ./jsoncbor -d # object key, gets string coerced in JSON output ++ python -c 'import sys; sys.stdout.write("\x7f\xff")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\x7f\xff")'| $(VALGRIND) -q ./jsoncbor -d ++ python -c 'import sys; sys.stdout.write("\x7f`````````````````````````````````````````````````````````````````````````````````````````````````````````\xff")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\x7f`````````````````````````````````````````````````````````````````````````````````````````````````````````\xff")'| $(VALGRIND) -q ./jsoncbor -d ++ python -c 'import sys; sys.stdout.write("\x7fcfoo\xff")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\x7fcfoo\xff")'| $(VALGRIND) -q ./jsoncbor -d ++ python -c 'import sys; sys.stdout.write("\x7fcfoocbaraqau`auax\xff")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\x7fcfoocbaraqau`auax\xff")'| $(VALGRIND) -q ./jsoncbor -d ++ python -c 'import sys; sys.stdout.write("\x5f\xff")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\x5f\xff")'| $(VALGRIND) -q ./jsoncbor -d ++ python -c 'import sys; sys.stdout.write("\x5f@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\xff")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\x5f@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\xff")'| $(VALGRIND) -q ./jsoncbor -d ++ python -c 'import sys; sys.stdout.write("\x5fCfoo\xff")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\x5fCfoo\xff")'| $(VALGRIND) -q ./jsoncbor -d ++ python -c 'import sys; sys.stdout.write("\x5fCfooCbarAqAu@AuAx\xff")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\x5fCfooCbarAqAu@AuAx\xff")'| $(VALGRIND) -q ./jsoncbor -d ++ python -c 'import sys; sys.stdout.write("\xc0cfoo")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\xc0cfoo")'| $(VALGRIND) -q ./jsoncbor -d ++ python -c 'import sys; sys.stdout.write("\xc0\xc1cfoo")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\xc0\xc1cfoo")'| $(VALGRIND) -q ./jsoncbor -d ++ python -c 'import sys; sys.stdout.write("\xc0\xd7cfoo")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\xc0\xd7cfoo")'| $(VALGRIND) -q ./jsoncbor -d ++ python -c 'import sys; sys.stdout.write("\xc0\xd8\xffcfoo")'| python cbordecode.py ++ python -c 'import sys; sys.stdout.write("\xc0\xd8\xffcfoo")'| $(VALGRIND) -q ./jsoncbor -d ++ ++ ++.PHONY: enc-primitives ++enc-primitives: jsoncbor ++ echo 'void 0' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo 'null' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo 'true' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo 'false' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ ++.PHONY: enc-number ++enc-number: jsoncbor ++ echo '0' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '0.1' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '1' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '23' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '24' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '25' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '255' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '256' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '65535' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '65536' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '65536.9' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '1048576' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '4294967295' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '4294967296' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py # shortest encoding: ieee single ++ echo '4294967297' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '0xdeadbeef' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '3.141592653589793' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '-0' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '-0.1' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '-1' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '-23' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '-24' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '-25' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '-255' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '-256' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '-257' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '-65535' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '-65536' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '-65536.9' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '-65537' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '-1048576' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '-4294967295' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '-4294967296' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '-4294967297' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '-0xdeadbeef' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '-3.141592653589793' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '1/0' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py # shortest encoding: half-float ++ echo '0/0' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py # shortest encoding: half-float ++ echo '-1/0' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py # shortest encoding: half-float ++ ++.PHONY: enc-half-float ++enc-half-float: ++ echo '1.5' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '1/16384' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '1/32768' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '-1.5' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '-1/16384' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '-1/32768' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ ++.PHONY: enc-float ++enc-float: ++ echo '0xffffffff' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '0x100000000' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '0x1fedcbe0000000000' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '0x1fedcbf0000000000' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ # Unlike for positive integers, 0x100000000 encodes to a cbor integer (sint). ++ # Then -0x100000001 doesn't encode to a float because the mantissa is too long. ++ echo '-0x100000000' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '-0x100000001' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '-0x100010000' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '-0x1fedcbe0000000000' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '-0x1fedcbf0000000000' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ ++.PHONY: enc-buffer ++enc-buffer: jsoncbor ++ echo '(function () { var p = Uint8Array.allocPlain(0); return p; })()' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '(function () { var p = Uint8Array.allocPlain(1); p[0] = 0xfe; return p; })()' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '(function () { var p = Uint8Array.allocPlain(2); p[0] = 0xc0; p[1] = 0x80; return p; })()' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '(function () { var p = Uint8Array.allocPlain(3); p[0] = 0x31; p[1] = 0x4a; p[2] = 0x7a; return p; })()' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '(function () { var p = Uint8Array.allocPlain(23); p[22] = 0xfe; return p; })()' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '(function () { var p = Uint8Array.allocPlain(24); p[23] = 0xfe; return p; })()' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '(function () { var p = Uint8Array.allocPlain(255); p[254] = 0xfe; return p; })()' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '(function () { var p = Uint8Array.allocPlain(256); p[255] = 0xfe; return p; })()' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '(function () { var p = Uint8Array.allocPlain(256); p[255] = 0xfe; return p; })()' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '(function () { var p = new Uint16Array([ 1, 2, 3, 4 ]); return p; })()' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ ++.PHONY: enc-string ++enc-string: jsoncbor ++ echo '""' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '"foo"' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '"foo\u20acbar"' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '"foo\ud800bar"' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py # unpaired surrogate ++ echo '"\u4321\u4321\u4321\u4321\u4321\u4321\u4321xy"' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '"\u4321\u4321\u4321\u4321\u4321\u4321\u4321xyz"' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '"\u4321\u4321\u4321\u4321\u4321\u4321\u4321xyzw"' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '"\u4321\u4321\u4321\u4321\u4321\u4321\u4321xyzw......................................................................................................................................................................................................................................"' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '"\u4321\u4321\u4321\u4321\u4321\u4321\u4321xyzw......................................................................................................................................................................................................................................!"' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ ++.PHONY: enc-array ++enc-array: jsoncbor ++ echo '[]' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '[ "foo", "bar", "quux" ]' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ # XXX: other lengths ++ echo '([[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[{ foo: 123 }]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]])' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ ++.PHONY: enc-object ++enc-object: jsoncbor ++ echo '({})' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '({ foo: 123 })' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '({ foo: 123, bar: 234, quux: 345 })' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '({ foo: 123, bar: [ "foo", "bar", { baz: true } ], quux: 345 })' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '({ a:1, b:2, c:3, d:4, e:5, f:6, g:7, h:8, i:9, j:10, k:11, l:12, m:13, n:14, o:15, p:16, q:17, r:18, s:19, t:20, u:21, v:22 })' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '({ a:1, b:2, c:3, d:4, e:5, f:6, g:7, h:8, i:9, j:10, k:11, l:12, m:13, n:14, o:15, p:16, q:17, r:18, s:19, t:20, u:21, v:22, w:23 })' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '({ a:1, b:2, c:3, d:4, e:5, f:6, g:7, h:8, i:9, j:10, k:11, l:12, m:13, n:14, o:15, p:16, q:17, r:18, s:19, t:20, u:21, v:22, w:23, x:24 })' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '({ a:1, b:2, c:3, d:4, e:5, f:6, g:7, h:8, i:9, j:10, k:11, l:12, m:13, n:14, o:15, p:16, q:17, r:18, s:19, t:20, u:21, v:22, w:23, x:24, y:25 })' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '({ a:1, b:2, c:3, d:4, e:5, f:6, g:7, h:8, i:9, j:10, k:11, l:12, m:13, n:14, o:15, p:16, q:17, r:18, s:19, t:20, u:21, v:22, w:23, x:24, y:25, z:26 })' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ echo '({foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:{foo:123}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}})' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py ++ ++.PHONY: enc-misc ++enc-misc: jsoncbor ++ echo '({ jsonrpc: "2.0", id: "foo-1", method: "Add", params: { a: 123, b: 234 }})' | $(VALGRIND) -q ./jsoncbor -r js -w cbor | python2 cbordecode.py +diff --git a/dist/source/extras/cbor/README.rst b/dist/source/extras/cbor/README.rst +new file mode 100644 +index 0000000..1352b96 +--- /dev/null ++++ b/dist/source/extras/cbor/README.rst +@@ -0,0 +1,150 @@ ++============ ++CBOR binding ++============ ++ ++Overview ++======== ++ ++C functions to encode/decode values in CBOR format, and a simple command ++line utility to convert between JSON and CBOR. ++ ++To integrate CBOR into your application: ++ ++* Call ``duk_cbor_encode()`` and ``duk_cbor_decode()`` directly if a C API ++ is enough. ++ ++* Call ``duk_cbor_init()`` to register a global ``CBOR`` object with ++ ECMAScript bindings ``CBOR.encode()`` and ``CBOR.decode()``, roughly ++ matching https://github.com/paroga/cbor-js. ++ ++Basic usage of the ``jsoncbor`` conversion tool:: ++ ++ $ make jsoncbor ++ [...] ++ $ cat test.json | ./jsoncbor -e # writes CBOR to stdout ++ $ cat test.cbor | ./jsoncbor -d # writes JSON to stdout ++ ++CBOR objects are decoded into ECMAScript objects, with non-string keys ++coerced into strings. ++ ++Direct support for CBOR is likely to be included in the Duktape API in the ++future. This extra will then become unnecessary. ++ ++CBOR ++==== ++ ++CBOR is a standard format for JSON-like binary interchange. It is ++faster and smaller, and can encode more data types than JSON. In particular, ++binary data can be serialized without encoding e.g. in base-64. These ++properties make it useful for storing state files, IPC, etc. ++ ++Some CBOR shortcomings for preserving information: ++ ++* No property attribute or inheritance support. ++ ++* No DAGs or looped graphs. ++ ++* Array objects with properties lose their non-index properties. ++ ++* Array objects with gaps lose their gaps as they read back as undefined. ++ ++* Buffer objects and views lose much of their detail besides the raw data. ++ ++* ECMAScript strings cannot be fully represented; strings must be UTF-8. ++ ++* Functions and native objects lose most of their detail. ++ ++* CBOR tags are useful to provide soft decoding information, but the tags ++ are just integers from an IANA controlled space with no space for custom ++ tags. So tags cannot be easily used for private, application specific tags. ++ IANA allows reserving custom tags with little effort however, see ++ https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml. ++ ++Future work ++=========== ++ ++General: ++ ++* Add flags to control encode/decode behavior. ++ ++* Allow decoding with a trailer so that stream parsing is easier. ++ Similar change would be useful for JSON decoding. ++ ++* Reserve CBOR tag for missing value. ++ ++* Reserve other necessary CBOR tags. ++ ++* Explicit support for encoding with and without side effects (e.g. ++ skipping Proxy traps and getters). ++ ++* JSON encoding supports .toJSON(), maybe something like .toCBOR()? ++ ++* Optimize encoding and decoding more. ++ ++Encoding: ++ ++* Tagging of typed arrays: ++ https://datatracker.ietf.org/doc/draft-ietf-cbor-array-tags/. ++ Mixed endian encode must convert to e.g. little endian because ++ no mixed endian tag exists. ++ ++* Encoding typed arrays as integer arrays instead? ++ ++* Float16Array encoding support (once/if supported by main engine). ++ ++* Tagging of array gaps, once IANA reservation is complete: ++ https://github.com/svaarala/duktape/blob/master/doc/cbor-missing-tag.rst. ++ ++* Support 64-bit integer when encoding, e.g. up to 2^53? ++ ++* Definite-length object encoding even when object has more than 23 keys. ++ ++* Map/Set encoding (once supported in the main engine), maybe tagged ++ so they decode back into Map/Set. ++ ++* Bigint encoding (once supported in the main engine), as tagged byte ++ strings like in Python CBOR. ++ ++* String encoding options: combining surrogate pairs, tagging non-UTF-8 ++ byte strings so they decode back to string, using U+FFFD replacement, ++ etc. ++ ++* Detection of Symbols, encode them in a useful tagged form. ++ ++* Better encoding of functions. ++ ++* Hook for serialization, to allow caller to serialize values (especially ++ objects) in a context specific manner (e.g. serialize functions with ++ IPC metadata to allow them to be called remotely). Such a hook should ++ be able to emit tag(s) to mark custom values for decode processing. ++ ++Decoding: ++ ++* Typed array decoding support. Should decoder convert to host ++ endianness? ++ ++* Float16Array decoding support (once/if supported by main engine). ++ ++* Decoding objects with non-string keys, could be represented as a Map. ++ ++* Use bare objects and arrays when decoding? ++ ++* Use a Map rather than a plain object when decoding, which would allow ++ non-string keys. ++ ++* Bigint decoding (once supported in the main engine). ++ ++* Decoding of non-BMP codepoints into surrogate pairs. ++ ++* Decoding of Symbols when call site indicates it is safe. ++ ++* Hooking for revival, to allow caller to revive objects in a context ++ specific manner (e.g. revive serialized function objects into IPC ++ proxy functions). Such a hook should have access to encoding tags, ++ so that revival can depend on tags present. ++ ++* Option to compact decoded objects and arrays. ++ ++* Improve fastint decoding support, e.g. decode non-optimally encoded ++ integers as fastints, decode compatible floating point values as ++ fastints. +diff --git a/dist/source/extras/cbor/cbordecode.py b/dist/source/extras/cbor/cbordecode.py +new file mode 100644 +index 0000000..95db923 +--- /dev/null ++++ b/dist/source/extras/cbor/cbordecode.py +@@ -0,0 +1,24 @@ ++#!/usr/bin/env python2 ++ ++def main(): ++ import sys ++ import cbor ++ import json ++ ++ data = sys.stdin.read() ++ print('LEN: %d' % len(data)) ++ sys.stdout.flush() ++ print('HEX: ' + data.encode('hex')) ++ sys.stdout.flush() ++ doc = cbor.loads(data) ++ print('REPR: ' + repr(doc)) ++ sys.stdout.flush() ++ try: ++ print('JSON: ' + json.dumps(doc)) ++ sys.stdout.flush() ++ except: ++ print('JSON: cannot encode') ++ sys.stdout.flush() ++ ++if __name__ == '__main__': ++ main() +diff --git a/dist/source/extras/cbor/duk_cbor.c b/dist/source/extras/cbor/duk_cbor.c +new file mode 100644 +index 0000000..5304df6 +--- /dev/null ++++ b/dist/source/extras/cbor/duk_cbor.c +@@ -0,0 +1,2075 @@ ++/* ++ * CBOR bindings for Duktape. ++ * ++ * https://tools.ietf.org/html/rfc7049 ++ */ ++ ++#include ++#include ++#include "duktape.h" ++#include "duk_cbor.h" ++ ++/* #define DUK_CBOR_DPRINT */ ++/* #define DUK_CBOR_STRESS */ ++ ++#if 1 ++#define DUK_CBOR_ASSERT(x) do {} while (0) ++#else ++#include ++#include ++#define DUK_CBOR_ASSERT(x) do { \ ++ if (!(x)) { \ ++ fprintf(stderr, "ASSERT FAILED on %s:%d\n", __FILE__, __LINE__); \ ++ fflush(stderr); \ ++ abort(); \ ++ } \ ++ } while (0) ++#endif ++ ++#if 0 ++#define DUK_CBOR_LIKELY(x) __builtin_expect (!!(x), 1) ++#define DUK_CBOR_UNLIKELY(x) __builtin_expect (!!(x), 0) ++#define DUK_CBOR_INLINE inline ++#define DUK_CBOR_NOINLINE __attribute__((noinline)) ++#else ++#define DUK_CBOR_LIKELY(x) (x) ++#define DUK_CBOR_UNLIKELY(x) (x) ++#define DUK_CBOR_INLINE ++#define DUK_CBOR_NOINLINE ++#endif ++ ++/* #define DUK_CBOR_GCC_BUILTINS */ ++ ++/* Default behavior for encoding strings: use CBOR text string if string ++ * is UTF-8 compatible, otherwise use CBOR byte string. These defines ++ * can be used to force either type for all strings. Using text strings ++ * for non-UTF-8 data is technically invalid CBOR. ++ */ ++/* #define DUK_CBOR_TEXT_STRINGS */ ++/* #define DUK_CBOR_BYTE_STRINGS */ ++ ++/* Misc. defines. */ ++/* #define DUK_CBOR_PREFER_SIZE */ ++/* #define DUK_CBOR_DOUBLE_AS_IS */ ++/* #define DUK_CBOR_DECODE_FASTPATH */ ++ ++typedef struct { ++ duk_context *ctx; ++ duk_uint8_t *ptr; ++ duk_uint8_t *buf; ++ duk_uint8_t *buf_end; ++ duk_size_t len; ++ duk_idx_t idx_buf; ++} duk_cbor_encode_context; ++ ++typedef struct { ++ duk_context *ctx; ++ const duk_uint8_t *buf; ++ duk_size_t off; ++ duk_size_t len; ++} duk_cbor_decode_context; ++ ++typedef union { ++ duk_uint8_t x[8]; ++ duk_uint16_t s[4]; ++ duk_uint32_t i[2]; ++#if 0 ++ duk_uint64_t i64[1]; ++#endif ++ double d; ++} duk_cbor_dblunion; ++ ++typedef union { ++ duk_uint8_t x[4]; ++ duk_uint16_t s[2]; ++ duk_uint32_t i[1]; ++ float f; ++} duk_cbor_fltunion; ++ ++static void duk__cbor_encode_value(duk_cbor_encode_context *enc_ctx); ++static void duk__cbor_decode_value(duk_cbor_decode_context *dec_ctx); ++ ++/* ++ * Misc ++ */ ++ ++/* XXX: These are sometimes portability concerns and would be nice to expose ++ * from Duktape itself as portability helpers. ++ */ ++ ++static int duk__cbor_signbit(double d) { ++ return signbit(d); ++} ++ ++static int duk__cbor_fpclassify(double d) { ++ return fpclassify(d); ++} ++ ++static int duk__cbor_isnan(double d) { ++ return isnan(d); ++} ++ ++static int duk__cbor_isinf(double d) { ++ return isinf(d); ++} ++ ++static duk_uint32_t duk__cbor_double_to_uint32(double d) { ++ /* Out of range casts are undefined behavior, so caller must avoid. */ ++ DUK_CBOR_ASSERT(d >= 0.0 && d <= 4294967295.0); ++ return (duk_uint32_t) d; ++} ++ ++/* Endian detection. Technically happens at runtime, but in practice ++ * resolves at compile time to a constant and gets inlined. ++ */ ++#define DUK__CBOR_LITTLE_ENDIAN 1 ++#define DUK__CBOR_MIXED_ENDIAN 2 ++#define DUK__CBOR_BIG_ENDIAN 3 ++ ++static int duk__cbor_check_endian(void) { ++ duk_cbor_dblunion u; ++ ++ /* >>> struct.pack('>d', 1.23456789).encode('hex') ++ * '3ff3c0ca4283de1b' ++ */ ++ ++ u.d = 1.23456789; ++ if (u.x[0] == 0x1bU) { ++ return DUK__CBOR_LITTLE_ENDIAN; ++ } else if (u.x[0] == 0x3fU) { ++ return DUK__CBOR_BIG_ENDIAN; ++ } else if (u.x[0] == 0xcaU) { ++ return DUK__CBOR_MIXED_ENDIAN; ++ } else { ++ DUK_CBOR_ASSERT(0); ++ } ++ return 0; ++} ++ ++static DUK_CBOR_INLINE duk_uint16_t duk__cbor_bswap16(duk_uint16_t x) { ++#if defined(DUK_CBOR_GCC_BUILTINS) ++ return __builtin_bswap16(x); ++#else ++ /* XXX: matches, DUK_BSWAP16(), use that if exposed. */ ++ return (x >> 8) | (x << 8); ++#endif ++} ++ ++static DUK_CBOR_INLINE duk_uint32_t duk__cbor_bswap32(duk_uint32_t x) { ++#if defined(DUK_CBOR_GCC_BUILTINS) ++ return __builtin_bswap32(x); ++#else ++ /* XXX: matches, DUK_BSWAP32(), use that if exposed. */ ++ return (x >> 24) | ((x >> 8) & 0xff00UL) | ((x << 8) & 0xff0000UL) | (x << 24); ++#endif ++} ++ ++#if 0 ++static duk_uint64_t duk__cbor_bswap64(duk_uint64_t x) { ++ /* XXX */ ++} ++#endif ++ ++static DUK_CBOR_INLINE void duk__cbor_write_uint16_big(duk_uint8_t *p, duk_uint16_t x) { ++#if 0 ++ *p++ = (duk_uint8_t) ((x >> 8) & 0xffU); ++ *p++ = (duk_uint8_t) (x & 0xffU); ++#endif ++ duk_uint16_t a; ++ ++ switch (duk__cbor_check_endian()) { ++ case DUK__CBOR_LITTLE_ENDIAN: ++ case DUK__CBOR_MIXED_ENDIAN: ++ a = duk__cbor_bswap16(x); ++ (void) memcpy((void *) p, (const void *) &a, 2); ++ break; ++ case DUK__CBOR_BIG_ENDIAN: ++ a = x; ++ (void) memcpy((void *) p, (const void *) &a, 2); ++ break; ++ default: ++ DUK_CBOR_ASSERT(0); ++ } ++} ++ ++static DUK_CBOR_INLINE duk_uint16_t duk__cbor_read_uint16_big(const duk_uint8_t *p) { ++ duk_uint16_t a, x; ++ ++#if 0 ++ x = (((duk_uint16_t) p[0]) << 8U) + ++ ((duk_uint16_t) p[1]); ++#endif ++ switch (duk__cbor_check_endian()) { ++ case DUK__CBOR_LITTLE_ENDIAN: ++ case DUK__CBOR_MIXED_ENDIAN: ++ (void) memcpy((void *) &a, (const void *) p, 2); ++ x = duk__cbor_bswap16(a); ++ break; ++ case DUK__CBOR_BIG_ENDIAN: ++ (void) memcpy((void *) &a, (const void *) p, 2); ++ x = a; ++ break; ++ default: ++ DUK_CBOR_ASSERT(0); ++ x = 0; ++ } ++ return x; ++} ++ ++static DUK_CBOR_INLINE void duk__cbor_write_uint32_big(duk_uint8_t *p, duk_uint32_t x) { ++#if 0 ++ *p++ = (duk_uint8_t) ((x >> 24) & 0xffU); ++ *p++ = (duk_uint8_t) ((x >> 16) & 0xffU); ++ *p++ = (duk_uint8_t) ((x >> 8) & 0xffU); ++ *p++ = (duk_uint8_t) (x & 0xffU); ++#endif ++ duk_uint32_t a; ++ ++ switch (duk__cbor_check_endian()) { ++ case DUK__CBOR_LITTLE_ENDIAN: ++ case DUK__CBOR_MIXED_ENDIAN: ++ a = duk__cbor_bswap32(x); ++ (void) memcpy((void *) p, (const void *) &a, 4); ++ break; ++ case DUK__CBOR_BIG_ENDIAN: ++ a = x; ++ (void) memcpy((void *) p, (const void *) &a, 4); ++ break; ++ default: ++ DUK_CBOR_ASSERT(0); ++ } ++} ++ ++static DUK_CBOR_INLINE duk_uint32_t duk__cbor_read_uint32_big(const duk_uint8_t *p) { ++ duk_uint32_t a, x; ++ ++#if 0 ++ x = (((duk_uint32_t) p[0]) << 24U) + ++ (((duk_uint32_t) p[1]) << 16U) + ++ (((duk_uint32_t) p[2]) << 8U) + ++ ((duk_uint32_t) p[3]); ++#endif ++ switch (duk__cbor_check_endian()) { ++ case DUK__CBOR_LITTLE_ENDIAN: ++ case DUK__CBOR_MIXED_ENDIAN: ++ (void) memcpy((void *) &a, (const void *) p, 4); ++ x = duk__cbor_bswap32(a); ++ break; ++ case DUK__CBOR_BIG_ENDIAN: ++ (void) memcpy((void *) &a, (const void *) p, 4); ++ x = a; ++ break; ++ default: ++ DUK_CBOR_ASSERT(0); ++ x = 0; ++ } ++ return x; ++} ++ ++static DUK_CBOR_INLINE void duk__cbor_write_double_big(duk_uint8_t *p, double x) { ++ duk_cbor_dblunion u; ++ duk_uint32_t a, b; ++ ++ u.d = x; ++ ++ switch (duk__cbor_check_endian()) { ++ case DUK__CBOR_LITTLE_ENDIAN: ++#if 0 ++ u.i64[0] = duk__cbor_bswap64(u.i64[0]); ++ (void) memcpy((void *) p, (const void *) u.x, 8); ++#endif ++ a = u.i[0]; ++ b = u.i[1]; ++ u.i[0] = duk__cbor_bswap32(b); ++ u.i[1] = duk__cbor_bswap32(a); ++ (void) memcpy((void *) p, (const void *) u.x, 8); ++ break; ++ case DUK__CBOR_MIXED_ENDIAN: ++ a = u.i[0]; ++ b = u.i[1]; ++ u.i[0] = duk__cbor_bswap32(a); ++ u.i[1] = duk__cbor_bswap32(b); ++ (void) memcpy((void *) p, (const void *) u.x, 8); ++ break; ++ case DUK__CBOR_BIG_ENDIAN: ++ (void) memcpy((void *) p, (const void *) u.x, 8); ++ break; ++ default: ++ DUK_CBOR_ASSERT(0); ++ } ++} ++ ++static DUK_CBOR_INLINE void duk__cbor_write_float_big(duk_uint8_t *p, float x) { ++ duk_cbor_fltunion u; ++ duk_uint32_t a; ++ ++ u.f = x; ++ switch (duk__cbor_check_endian()) { ++ case DUK__CBOR_LITTLE_ENDIAN: ++ case DUK__CBOR_MIXED_ENDIAN: ++ a = u.i[0]; ++ u.i[0] = duk__cbor_bswap32(a); ++ (void) memcpy((void *) p, (const void *) u.x, 4); ++ break; ++ case DUK__CBOR_BIG_ENDIAN: ++ (void) memcpy((void *) p, (const void *) u.x, 4); ++ break; ++ default: ++ DUK_CBOR_ASSERT(0); ++ } ++} ++ ++static DUK_CBOR_INLINE void duk__cbor_dblunion_host_to_little(duk_cbor_dblunion *u) { ++ duk_uint32_t a, b; ++ ++ switch (duk__cbor_check_endian()) { ++ case DUK__CBOR_LITTLE_ENDIAN: ++ /* HGFEDCBA -> HGFEDCBA */ ++ break; ++ case DUK__CBOR_MIXED_ENDIAN: ++ /* DCBAHGFE -> HGFEDCBA */ ++ a = u->i[0]; ++ b = u->i[1]; ++ u->i[0] = b; ++ u->i[1] = a; ++ break; ++ case DUK__CBOR_BIG_ENDIAN: ++ /* ABCDEFGH -> HGFEDCBA */ ++#if 0 ++ u->i64[0] = duk__cbor_bswap64(u->i64[0]); ++#endif ++ a = u->i[0]; ++ b = u->i[1]; ++ u->i[0] = duk__cbor_bswap32(b); ++ u->i[1] = duk__cbor_bswap32(a); ++ break; ++ } ++} ++ ++static DUK_CBOR_INLINE void duk__cbor_dblunion_little_to_host(duk_cbor_dblunion *u) { ++ duk__cbor_dblunion_host_to_little(u); ++} ++ ++static DUK_CBOR_INLINE void duk__cbor_dblunion_host_to_big(duk_cbor_dblunion *u) { ++ duk_uint32_t a, b; ++ ++ switch (duk__cbor_check_endian()) { ++ case DUK__CBOR_LITTLE_ENDIAN: ++ /* HGFEDCBA -> ABCDEFGH */ ++#if 0 ++ u->i64[0] = duk__cbor_bswap64(u->i64[0]); ++#else ++ a = u->i[0]; ++ b = u->i[1]; ++ u->i[0] = duk__cbor_bswap32(b); ++ u->i[1] = duk__cbor_bswap32(a); ++#endif ++ break; ++ case DUK__CBOR_MIXED_ENDIAN: ++ /* DCBAHGFE -> ABCDEFGH */ ++ a = u->i[0]; ++ b = u->i[1]; ++ u->i[0] = duk__cbor_bswap32(a); ++ u->i[1] = duk__cbor_bswap32(b); ++ break; ++ case DUK__CBOR_BIG_ENDIAN: ++ /* ABCDEFGH -> ABCDEFGH */ ++ break; ++ } ++} ++ ++static DUK_CBOR_INLINE void duk__cbor_dblunion_big_to_host(duk_cbor_dblunion *u) { ++ duk__cbor_dblunion_host_to_big(u); ++} ++ ++static DUK_CBOR_INLINE void duk__cbor_fltunion_host_to_big(duk_cbor_fltunion *u) { ++ switch (duk__cbor_check_endian()) { ++ case DUK__CBOR_LITTLE_ENDIAN: ++ case DUK__CBOR_MIXED_ENDIAN: ++ /* DCBA -> ABCD */ ++ u->i[0] = duk__cbor_bswap32(u->i[0]); ++ break; ++ case DUK__CBOR_BIG_ENDIAN: ++ /* ABCD -> ABCD */ ++ break; ++ } ++} ++ ++static DUK_CBOR_INLINE void duk__cbor_fltunion_big_to_host(duk_cbor_fltunion *u) { ++ duk__cbor_fltunion_host_to_big(u); ++} ++ ++/* ++ * Encoding ++ */ ++ ++static void duk__cbor_encode_error(duk_cbor_encode_context *enc_ctx) { ++ (void) duk_type_error(enc_ctx->ctx, "cbor encode error"); ++} ++ ++/* Check whether a string is UTF-8 compatible or not. */ ++static int duk__cbor_is_utf8_compatible(const duk_uint8_t *buf, duk_size_t len) { ++ duk_size_t i = 0; ++#if !defined(DUK_CBOR_PREFER_SIZE) ++ duk_size_t len_safe; ++#endif ++ ++ /* Many practical strings are ASCII only, so use a fast path check ++ * to check chunks of bytes at once with minimal branch cost. ++ */ ++#if !defined(DUK_CBOR_PREFER_SIZE) ++ len_safe = len & ~0x03UL; ++ for (; i < len_safe; i += 4) { ++ duk_uint8_t t = buf[i] | buf[i + 1] | buf[i + 2] | buf[i + 3]; ++ if (DUK_CBOR_UNLIKELY((t & 0x80U) != 0U)) { ++ /* At least one byte was outside 0x00-0x7f, break ++ * out to slow path (and remain there). ++ * ++ * XXX: We could also deal with the problem character ++ * and resume fast path later. ++ */ ++ break; ++ } ++ } ++#endif ++ ++ for (; i < len;) { ++ duk_uint8_t t; ++ duk_size_t left; ++ duk_size_t ncont; ++ duk_uint32_t cp; ++ duk_uint32_t mincp; ++ ++ t = buf[i++]; ++ if (DUK_CBOR_LIKELY((t & 0x80U) == 0U)) { ++ /* Fast path, ASCII. */ ++ continue; ++ } ++ ++ /* Non-ASCII start byte, slow path. ++ * ++ * 10xx xxxx -> continuation byte ++ * 110x xxxx + 1*CONT -> [0x80, 0x7ff] ++ * 1110 xxxx + 2*CONT -> [0x800, 0xffff], must reject [0xd800,0xdfff] ++ * 1111 0xxx + 3*CONT -> [0x10000, 0x10ffff] ++ */ ++ left = len - i; ++ if (t <= 0xdfU) { /* 1101 1111 = 0xdf */ ++ if (t <= 0xbfU) { /* 1011 1111 = 0xbf */ ++ return 0; ++ } ++ ncont = 1; ++ mincp = 0x80UL; ++ cp = t & 0x1fU; ++ } else if (t <= 0xefU) { /* 1110 1111 = 0xef */ ++ ncont = 2; ++ mincp = 0x800UL; ++ cp = t & 0x0fU; ++ } else if (t <= 0xf7U) { /* 1111 0111 = 0xf7 */ ++ ncont = 3; ++ mincp = 0x10000UL; ++ cp = t & 0x07U; ++ } else { ++ return 0; ++ } ++ if (left < ncont) { ++ return 0; ++ } ++ while (ncont > 0U) { ++ t = buf[i++]; ++ if ((t & 0xc0U) != 0x80U) { /* 10xx xxxx */ ++ return 0; ++ } ++ cp = (cp << 6) + (t & 0x3fU); ++ ncont--; ++ } ++ if (cp < mincp || cp > 0x10ffffUL || (cp >= 0xd800UL && cp <= 0xdfffUL)) { ++ return 0; ++ } ++ } ++ ++ return 1; ++} ++ ++/* Check that a size_t is in uint32 range to avoid out-of-range casts. */ ++static void duk__cbor_encode_sizet_uint32_check(duk_cbor_encode_context *enc_ctx, duk_size_t len) { ++ if (DUK_CBOR_UNLIKELY(sizeof(duk_size_t) > sizeof(duk_uint32_t) && len > (duk_size_t) DUK_UINT32_MAX)) { ++ duk__cbor_encode_error(enc_ctx); ++ } ++} ++ ++static DUK_CBOR_NOINLINE void duk__cbor_encode_ensure_slowpath(duk_cbor_encode_context *enc_ctx, duk_size_t len) { ++ duk_size_t oldlen; ++ duk_size_t minlen; ++ duk_size_t newlen; ++ duk_uint8_t *p_new; ++ duk_size_t old_data_len; ++ ++ DUK_CBOR_ASSERT(enc_ctx->ptr >= enc_ctx->buf); ++ DUK_CBOR_ASSERT(enc_ctx->buf_end >= enc_ctx->ptr); ++ DUK_CBOR_ASSERT(enc_ctx->buf_end >= enc_ctx->buf); ++ ++ /* Overflow check. ++ * ++ * Limit example: 0xffffffffUL / 2U = 0x7fffffffUL, we reject >= 0x80000000UL. ++ */ ++ oldlen = enc_ctx->len; ++ minlen = oldlen + len; ++ if (DUK_CBOR_UNLIKELY(oldlen > DUK_SIZE_MAX / 2U || minlen < oldlen)) { ++ duk__cbor_encode_error(enc_ctx); ++ } ++ ++#if defined(DUK_CBOR_STRESS) ++ newlen = oldlen + 1U; ++#else ++ newlen = oldlen * 2U; ++#endif ++ DUK_CBOR_ASSERT(newlen >= oldlen); ++ ++ if (minlen > newlen) { ++ newlen = minlen; ++ } ++ DUK_CBOR_ASSERT(newlen >= oldlen); ++ DUK_CBOR_ASSERT(newlen >= minlen); ++ DUK_CBOR_ASSERT(newlen > 0U); ++ ++#if defined(DUK_CBOR_DPRINT) ++ fprintf(stderr, "cbor encode buffer resized to %ld\n", (long) newlen); ++#endif ++ ++ p_new = (duk_uint8_t *) duk_resize_buffer(enc_ctx->ctx, enc_ctx->idx_buf, newlen); ++ DUK_CBOR_ASSERT(p_new != NULL); ++ old_data_len = (duk_size_t) (enc_ctx->ptr - enc_ctx->buf); ++ enc_ctx->buf = p_new; ++ enc_ctx->buf_end = p_new + newlen; ++ enc_ctx->ptr = p_new + old_data_len; ++ enc_ctx->len = newlen; ++} ++ ++static DUK_CBOR_INLINE void duk__cbor_encode_ensure(duk_cbor_encode_context *enc_ctx, duk_size_t len) { ++ if (DUK_CBOR_LIKELY((duk_size_t) (enc_ctx->buf_end - enc_ctx->ptr) >= len)) { ++ return; ++ } ++ duk__cbor_encode_ensure_slowpath(enc_ctx, len); ++} ++ ++static duk_size_t duk__cbor_get_reserve(duk_cbor_encode_context *enc_ctx) { ++ DUK_CBOR_ASSERT(enc_ctx->ptr >= enc_ctx->buf); ++ DUK_CBOR_ASSERT(enc_ctx->ptr <= enc_ctx->buf_end); ++ return (duk_size_t) (enc_ctx->buf_end - enc_ctx->ptr); ++} ++ ++static void duk__cbor_encode_uint32(duk_cbor_encode_context *enc_ctx, duk_uint32_t u, duk_uint8_t base) { ++ duk_uint8_t *p; ++ ++ /* Caller must ensure space. */ ++ DUK_CBOR_ASSERT(duk__cbor_get_reserve(enc_ctx) >= 1 + 4); ++ ++ p = enc_ctx->ptr; ++ if (DUK_CBOR_LIKELY(u <= 23U)) { ++ *p++ = (duk_uint8_t) (base + (duk_uint8_t) u); ++ } else if (u <= 0xffUL) { ++ *p++ = base + 0x18U; ++ *p++ = (duk_uint8_t) u; ++ } else if (u <= 0xffffUL) { ++ *p++ = base + 0x19U; ++ duk__cbor_write_uint16_big(p, (duk_uint16_t) u); ++ p += 2; ++ } else { ++ *p++ = base + 0x1aU; ++ duk__cbor_write_uint32_big(p, u); ++ p += 4; ++ } ++ enc_ctx->ptr = p; ++} ++ ++#if defined(DUK_CBOR_DOUBLE_AS_IS) ++static void duk__cbor_encode_double(duk_cbor_encode_context *enc_ctx, double d) { ++ duk_uint8_t *p; ++ ++ /* Caller must ensure space. */ ++ DUK_CBOR_ASSERT(duk__cbor_get_reserve(enc_ctx) >= 1 + 8); ++ ++ p = enc_ctx->ptr; ++ *p++ = 0xfbU; ++ duk__cbor_write_double_big(p, d); ++ p += 8; ++ enc_ctx->ptr = p; ++} ++#else /* DUK_CBOR_DOUBLE_AS_IS */ ++static void duk__cbor_encode_double_fp(duk_cbor_encode_context *enc_ctx, double d) { ++ duk_cbor_dblunion u; ++ duk_uint16_t u16; ++ duk_int16_t exp; ++ duk_uint8_t *p; ++ ++ DUK_CBOR_ASSERT(duk__cbor_fpclassify(d) != FP_ZERO); ++ ++ /* Caller must ensure space. */ ++ DUK_CBOR_ASSERT(duk__cbor_get_reserve(enc_ctx) >= 1 + 8); ++ ++ /* Organize into little endian (no-op if platform is little endian). */ ++ u.d = d; ++ duk__cbor_dblunion_host_to_little(&u); ++ ++ /* Check if 'd' can represented as a normal half-float. ++ * Denormal half-floats could also be used, but that check ++ * isn't done now (denormal half-floats are decoded of course). ++ * So just check exponent range and that at most 10 significant ++ * bits (excluding implicit leading 1) are used in 'd'. ++ */ ++ u16 = (((duk_uint16_t) u.x[7]) << 8) | ((duk_uint16_t) u.x[6]); ++ exp = (duk_int16_t) ((u16 & 0x7ff0U) >> 4) - 1023; ++ ++ if (exp >= -14 && exp <= 15) { ++ /* Half-float normal exponents (excl. denormals). ++ * ++ * 7 6 5 4 3 2 1 0 (LE index) ++ * double: seeeeeee eeeemmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm ++ * half: seeeee mmmm mmmmmm00 00000000 00000000 00000000 00000000 00000000 ++ */ ++ int use_half_float; ++ ++ use_half_float = ++ (u.x[0] == 0 && u.x[1] == 0 && u.x[2] == 0 && u.x[3] == 0 && ++ u.x[4] == 0 && (u.x[5] & 0x03U) == 0); ++ ++ if (use_half_float) { ++ duk_uint32_t t; ++ ++ exp += 15; ++ t = (duk_uint32_t) (u.x[7] & 0x80U) << 8; ++ t += (duk_uint32_t) exp << 10; ++ t += ((duk_uint32_t) u.x[6] & 0x0fU) << 6; ++ t += ((duk_uint32_t) u.x[5]) >> 2; ++ ++ /* seeeeemm mmmmmmmm */ ++ p = enc_ctx->ptr; ++ *p++ = 0xf9U; ++ duk__cbor_write_uint16_big(p, (duk_uint16_t) t); ++ p += 2; ++ enc_ctx->ptr = p; ++ return; ++ } ++ } ++ ++ /* Same check for plain float. Also no denormal support here. */ ++ if (exp >= -126 && exp <= 127) { ++ /* Float normal exponents (excl. denormals). ++ * ++ * double: seeeeeee eeeemmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm ++ * float: seeee eeeemmmm mmmmmmmm mmmmmmmm mmm00000 00000000 00000000 00000000 ++ */ ++ int use_float; ++ duk_float_t d_float; ++ ++ /* We could do this explicit mantissa check, but doing ++ * a double-float-double cast is fine because we've ++ * already verified that the exponent is in range so ++ * that the narrower cast is not undefined behavior. ++ */ ++#if 0 ++ use_float = ++ (u.x[0] == 0 && u.x[1] == 0 && u.x[2] == 0 && (u.x[3] & 0xe0U) == 0); ++#endif ++ d_float = (duk_float_t) d; ++ use_float = ((duk_double_t) d_float == d); ++ if (use_float) { ++ p = enc_ctx->ptr; ++ *p++ = 0xfaU; ++ duk__cbor_write_float_big(p, d_float); ++ p += 4; ++ enc_ctx->ptr = p; ++ return; ++ } ++ } ++ ++ /* Special handling for NaN and Inf which we want to encode as ++ * half-floats. They share the same (maximum) exponent. ++ */ ++ if (exp == 1024) { ++ DUK_CBOR_ASSERT(duk__cbor_isnan(d) || duk__cbor_isinf(d)); ++ p = enc_ctx->ptr; ++ *p++ = 0xf9U; ++ if (duk__cbor_isnan(d)) { ++ /* Shortest NaN encoding is using a half-float. Lose the ++ * exact NaN bits in the process. IEEE double would be ++ * 7ff8 0000 0000 0000, i.e. a quiet NaN in most architectures ++ * (https://en.wikipedia.org/wiki/NaN#Encoding). The ++ * equivalent half float is 7e00. ++ */ ++ *p++ = 0x7eU; ++ } else { ++ /* Shortest +/- Infinity encoding is using a half-float. */ ++ if (duk__cbor_signbit(d)) { ++ *p++ = 0xfcU; ++ } else { ++ *p++ = 0x7cU; ++ } ++ } ++ *p++ = 0x00U; ++ enc_ctx->ptr = p; ++ return; ++ } ++ ++ /* Cannot use half-float or float, encode as full IEEE double. */ ++ p = enc_ctx->ptr; ++ *p++ = 0xfbU; ++ duk__cbor_write_double_big(p, d); ++ p += 8; ++ enc_ctx->ptr = p; ++} ++ ++static void duk__cbor_encode_double(duk_cbor_encode_context *enc_ctx, double d) { ++ duk_uint8_t *p; ++ double d_floor; ++ ++ /* Integers and floating point values of all types are conceptually ++ * equivalent in CBOR. Try to always choose the shortest encoding ++ * which is not always immediately obvious. For example, NaN and Inf ++ * can be most compactly represented as a half-float (assuming NaN ++ * bits are not preserved), and 0x1'0000'0000 as a single precision ++ * float. Shortest forms in preference order (prefer integer over ++ * float when equal length): ++ * ++ * uint 1 byte [0,23] (not -0) ++ * sint 1 byte [-24,-1] ++ * uint+1 2 bytes [24,255] ++ * sint+1 2 bytes [-256,-25] ++ * uint+2 3 bytes [256,65535] ++ * sint+2 3 bytes [-65536,-257] ++ * half-float 3 bytes -0, NaN, +/- Infinity, range [-65504,65504] ++ * uint+4 5 bytes [65536,4294967295] ++ * sint+4 5 bytes [-4294967296,-258] ++ * float 5 bytes range [-(1 - 2^(-24)) * 2^128, (1 - 2^(-24)) * 2^128] ++ * uint+8 9 bytes [4294967296,18446744073709551615] ++ * sint+8 9 bytes [-18446744073709551616,-4294967297] ++ * double 9 bytes ++ * ++ * For whole numbers (compatible with integers): ++ * - 1-byte or 2-byte uint/sint representation is preferred for ++ * [-256,255]. ++ * - 3-byte uint/sint is preferred for [-65536,65535]. Half floats ++ * are never preferred because they have the same length. ++ * - 5-byte uint/sint is preferred for [-4294967296,4294967295]. ++ * Single precision floats are never preferred, and half-floats ++ * don't reach above the 3-byte uint/sint range so they're never ++ * preferred. ++ * - So, for all integers up to signed/unsigned 32-bit range the ++ * preferred encoding is always an integer uint/sint. ++ * - For integers above 32 bits the situation is more complicated. ++ * Half-floats are never useful for them because of their limited ++ * range, but IEEE single precision floats (5 bytes encoded) can ++ * represent some integers between the 32-bit and 64-bit ranges ++ * which require 9 bytes as a uint/sint. ++ * ++ * For floating point values not compatible with integers, the ++ * preferred encoding is quite clear: ++ * - For +Inf/-Inf use half-float. ++ * - For NaN use a half-float, assuming NaN bits ("payload") is ++ * not worth preserving. Duktape doesn't in general guarantee ++ * preservation of the NaN payload so using a half-float seems ++ * consistent with that. ++ * - For remaining values, prefer the shortest form which doesn't ++ * lose any precision. For normal half-floats and single precision ++ * floats this is simple: just check exponent and mantissa bits ++ * using a fixed mask. For denormal half-floats and single ++ * precision floats the check is a bit more complicated: a normal ++ * IEEE double can sometimes be represented as a denormal ++ * half-float or single precision float. ++ * ++ * https://en.wikipedia.org/wiki/Half-precision_floating-point_format#IEEE_754_half-precision_binary_floating-point_format:_binary16 ++ */ ++ ++ /* Caller must ensure space. */ ++ DUK_CBOR_ASSERT(duk__cbor_get_reserve(enc_ctx) >= 1 + 8); ++ ++ /* Most important path is integers. The floor() test will be true ++ * for Inf too (but not NaN). ++ */ ++ d_floor = floor(d); /* identity if d is +/- 0.0, NaN, or +/- Infinity */ ++ if (DUK_CBOR_LIKELY(d_floor == d)) { ++ DUK_CBOR_ASSERT(!duk__cbor_isnan(d)); /* NaN == NaN compares false. */ ++ if (duk__cbor_signbit(d)) { ++ if (d >= -4294967296.0) { ++ d = -1.0 - d; ++ if (d >= 0.0) { ++ DUK_CBOR_ASSERT(d >= 0.0); ++ duk__cbor_encode_uint32(enc_ctx, duk__cbor_double_to_uint32(d), 0x20U); ++ return; ++ } ++ ++ /* Input was negative zero, d == -1.0 < 0.0. ++ * Shortest -0 is using half-float. ++ */ ++ p = enc_ctx->ptr; ++ *p++ = 0xf9U; ++ *p++ = 0x80U; ++ *p++ = 0x00U; ++ enc_ctx->ptr = p; ++ return; ++ } ++ } else { ++ if (d <= 4294967295.0) { ++ /* Positive zero needs no special handling. */ ++ DUK_CBOR_ASSERT(d >= 0.0); ++ duk__cbor_encode_uint32(enc_ctx, duk__cbor_double_to_uint32(d), 0x00U); ++ return; ++ } ++ } ++ } ++ ++ /* 64-bit integers are not supported at present. So ++ * we also don't need to deal with choosing between a ++ * 64-bit uint/sint representation vs. IEEE double or ++ * float. ++ */ ++ ++ DUK_CBOR_ASSERT(duk__cbor_fpclassify(d) != FP_ZERO); ++ duk__cbor_encode_double_fp(enc_ctx, d); ++} ++#endif /* DUK_CBOR_DOUBLE_AS_IS */ ++ ++static void duk__cbor_encode_string_top(duk_cbor_encode_context *enc_ctx) { ++ const duk_uint8_t *str; ++ duk_size_t len; ++ duk_uint8_t *p; ++ ++ /* CBOR differentiates between UTF-8 text strings and byte strings. ++ * Text strings MUST be valid UTF-8, so not all Duktape strings can ++ * be encoded as valid CBOR text strings. Possible behaviors: ++ * ++ * 1. Use text string when input is valid UTF-8, otherwise use ++ * byte string (maybe tagged to indicate it was an extended ++ * UTF-8 string). ++ * 2. Always use text strings, but sanitize input string so that ++ * invalid UTF-8 is replaced with U+FFFD for example. Combine ++ * surrogates whenever possible. ++ * 3. Always use byte strings. This is simple and produces valid ++ * CBOR, but isn't ideal for interoperability. ++ * 4. Always use text strings, even for invalid UTF-8 such as ++ * codepoints in the surrogate pair range. This is simple but ++ * produces technically invalid CBOR for non-UTF-8 strings which ++ * may affect interoperability. ++ * ++ * Current default is 1; can be changed with defines. ++ */ ++ ++ /* Caller must ensure space. */ ++ DUK_CBOR_ASSERT(duk__cbor_get_reserve(enc_ctx) >= 1 + 8); ++ ++ str = (const duk_uint8_t *) duk_require_lstring(enc_ctx->ctx, -1, &len); ++ if (duk_is_symbol(enc_ctx->ctx, -1)) { ++ /* Symbols, encode as an empty table for now. This matches ++ * the behavior of cbor-js. ++ * ++ * XXX: Maybe encode String() coercion with a tag? ++ * XXX: Option to keep enough information to recover ++ * Symbols when decoding (this is not always desirable). ++ */ ++ p = enc_ctx->ptr; ++ *p++ = 0xa0U; ++ enc_ctx->ptr = p; ++ return; ++ } ++ ++ duk__cbor_encode_sizet_uint32_check(enc_ctx, len); ++#if defined(DUK_CBOR_TEXT_STRINGS) ++ duk__cbor_encode_uint32(enc_ctx, (duk_uint32_t) len, 0x60U); ++#elif defined(DUK_CBOR_BYTE_STRINGS) ++ duk__cbor_encode_uint32(enc_ctx, (duk_uint32_t) len, 0x40U); ++#else ++ duk__cbor_encode_uint32(enc_ctx, (duk_uint32_t) len, ++ (DUK_CBOR_LIKELY(duk__cbor_is_utf8_compatible(str, len)) ? 0x60U : 0x40U)); ++#endif ++ duk__cbor_encode_ensure(enc_ctx, len); ++ p = enc_ctx->ptr; ++ (void) memcpy((void *) p, (const void *) str, len); ++ p += len; ++ enc_ctx->ptr = p; ++} ++ ++static void duk__cbor_encode_object(duk_cbor_encode_context *enc_ctx) { ++ duk_uint8_t *buf; ++ duk_size_t len; ++ duk_uint8_t *p; ++ duk_size_t i; ++ duk_size_t off_ib; ++ duk_uint32_t count; ++ ++ /* Caller must ensure space. */ ++ DUK_CBOR_ASSERT(duk__cbor_get_reserve(enc_ctx) >= 1 + 8); ++ ++ /* XXX: Support for specific built-ins like Date and RegExp. */ ++ if (duk_is_array(enc_ctx->ctx, -1)) { ++ /* Shortest encoding for arrays >= 256 in length is actually ++ * the indefinite length one (3 or more bytes vs. 2 bytes). ++ * We still use the definite length version because it is ++ * more decoding friendly. ++ */ ++ len = duk_get_length(enc_ctx->ctx, -1); ++ duk__cbor_encode_sizet_uint32_check(enc_ctx, len); ++ duk__cbor_encode_uint32(enc_ctx, (duk_uint32_t) len, 0x80U); ++ for (i = 0; i < len; i++) { ++ duk_get_prop_index(enc_ctx->ctx, -1, (duk_uarridx_t) i); ++ duk__cbor_encode_value(enc_ctx); ++ } ++ } else if (duk_is_buffer_data(enc_ctx->ctx, -1)) { ++ /* XXX: Tag buffer data? ++ * XXX: Encode typed arrays as integer arrays rather ++ * than buffer data as is? ++ */ ++ buf = (duk_uint8_t *) duk_require_buffer_data(enc_ctx->ctx, -1, &len); ++ duk__cbor_encode_sizet_uint32_check(enc_ctx, len); ++ duk__cbor_encode_uint32(enc_ctx, (duk_uint32_t) len, 0x40U); ++ duk__cbor_encode_ensure(enc_ctx, len); ++ p = enc_ctx->ptr; ++ (void) memcpy((void *) p, (const void *) buf, len); ++ p += len; ++ enc_ctx->ptr = p; ++ } else { ++ /* We don't know the number of properties in advance ++ * but would still like to encode at least small ++ * objects without indefinite length. Emit an ++ * indefinite length byte initially, and if the final ++ * property count is small enough to also fit in one ++ * byte, backpatch it later. Otherwise keep the ++ * indefinite length. This works well up to 23 ++ * properties which is practical and good enough. ++ */ ++ off_ib = (duk_size_t) (enc_ctx->ptr - enc_ctx->buf); /* XXX: get_offset? */ ++ count = 0U; ++ p = enc_ctx->ptr; ++ *p++ = 0xa0U + 0x1fU; /* indefinite length */ ++ enc_ctx->ptr = p; ++ duk_enum(enc_ctx->ctx, -1, DUK_ENUM_OWN_PROPERTIES_ONLY); ++ while (duk_next(enc_ctx->ctx, -1, 1 /*get_value*/)) { ++ duk_insert(enc_ctx->ctx, -2); /* [ ... key value ] -> [ ... value key ] */ ++ duk__cbor_encode_value(enc_ctx); ++ duk__cbor_encode_value(enc_ctx); ++ count++; ++ if (count == 0U) { ++ duk__cbor_encode_error(enc_ctx); ++ } ++ } ++ duk_pop(enc_ctx->ctx); ++ if (count <= 0x17U) { ++ DUK_CBOR_ASSERT(off_ib < enc_ctx->len); ++ enc_ctx->buf[off_ib] = 0xa0U + (duk_uint8_t) count; ++ } else { ++ duk__cbor_encode_ensure(enc_ctx, 1); ++ p = enc_ctx->ptr; ++ *p++ = 0xffU; /* break */ ++ enc_ctx->ptr = p; ++ } ++ } ++} ++ ++static void duk__cbor_encode_buffer(duk_cbor_encode_context *enc_ctx) { ++ duk_uint8_t *buf; ++ duk_size_t len; ++ duk_uint8_t *p; ++ ++ /* Caller must ensure space. */ ++ DUK_CBOR_ASSERT(duk__cbor_get_reserve(enc_ctx) >= 1 + 8); ++ ++ /* Tag buffer data? */ ++ buf = (duk_uint8_t *) duk_require_buffer(enc_ctx->ctx, -1, &len); ++ duk__cbor_encode_sizet_uint32_check(enc_ctx, len); ++ duk__cbor_encode_uint32(enc_ctx, (duk_uint32_t) len, 0x40U); ++ duk__cbor_encode_ensure(enc_ctx, len); ++ p = enc_ctx->ptr; ++ (void) memcpy((void *) p, (const void *) buf, len); ++ p += len; ++ enc_ctx->ptr = p; ++} ++ ++static void duk__cbor_encode_pointer(duk_cbor_encode_context *enc_ctx) { ++ /* Pointers (void *) are challenging to encode. They can't ++ * be relied to be even 64-bit integer compatible (there are ++ * pointer models larger than that), nor can floats encode ++ * them. They could be encoded as strings (%p format) but ++ * that's not portable. They could be encoded as direct memory ++ * representations. Recovering pointers is non-portable in any ++ * case but it would be nice to be able to detect and recover ++ * compatible pointers. ++ * ++ * For now, encode as "(%p)" string, matching JX. There doesn't ++ * seem to be an appropriate tag, so pointers don't currently ++ * survive a CBOR encode/decode roundtrip intact. ++ */ ++ const char *ptr; ++ ++ ptr = duk_to_string(enc_ctx->ctx, -1); ++ DUK_CBOR_ASSERT(ptr != NULL); ++ duk_push_sprintf(enc_ctx->ctx, "(%s)", ptr); ++ duk_remove(enc_ctx->ctx, -2); ++ duk__cbor_encode_string_top(enc_ctx); ++} ++ ++static void duk__cbor_encode_lightfunc(duk_cbor_encode_context *enc_ctx) { ++ duk_uint8_t *p; ++ ++ /* Caller must ensure space. */ ++ DUK_CBOR_ASSERT(duk__cbor_get_reserve(enc_ctx) >= 1 + 8); ++ ++ /* For now encode as an empty object. */ ++ p = enc_ctx->ptr; ++ *p++ = 0xa0U; ++ enc_ctx->ptr = p; ++} ++ ++static void duk__cbor_encode_value(duk_cbor_encode_context *enc_ctx) { ++ duk_uint8_t *p; ++ ++ /* Encode/decode cycle currently loses some type information. ++ * This can be improved by registering custom tags with IANA. ++ */ ++ ++ /* When working with deeply recursive structures, this is important ++ * to ensure there's no effective depth limit. ++ */ ++ duk_require_stack(enc_ctx->ctx, 4); ++ ++ /* Reserve space for up to 64-bit types (1 initial byte + 8 ++ * followup bytes). This allows encoding of integers, floats, ++ * string/buffer length fields, etc without separate checks ++ * in each code path. ++ */ ++ duk__cbor_encode_ensure(enc_ctx, 1 + 8); ++ ++ switch (duk_get_type(enc_ctx->ctx, -1)) { ++ case DUK_TYPE_UNDEFINED: { ++ p = enc_ctx->ptr; ++ *p++ = 0xf7; ++ enc_ctx->ptr = p; ++ break; ++ } ++ case DUK_TYPE_NULL: { ++ p = enc_ctx->ptr; ++ *p++ = 0xf6; ++ enc_ctx->ptr = p; ++ break; ++ } ++ case DUK_TYPE_BOOLEAN: { ++ duk_uint8_t u8 = duk_get_boolean(enc_ctx->ctx, -1) ? 0xf5U : 0xf4U; ++ p = enc_ctx->ptr; ++ *p++ = u8; ++ enc_ctx->ptr = p; ++ break; ++ } ++ case DUK_TYPE_NUMBER: { ++ duk__cbor_encode_double(enc_ctx, duk_get_number(enc_ctx->ctx, -1)); ++ break; ++ } ++ case DUK_TYPE_STRING: { ++ duk__cbor_encode_string_top(enc_ctx); ++ break; ++ } ++ case DUK_TYPE_OBJECT: { ++ duk__cbor_encode_object(enc_ctx); ++ break; ++ } ++ case DUK_TYPE_BUFFER: { ++ duk__cbor_encode_buffer(enc_ctx); ++ break; ++ } ++ case DUK_TYPE_POINTER: { ++ duk__cbor_encode_pointer(enc_ctx); ++ break; ++ } ++ case DUK_TYPE_LIGHTFUNC: { ++ duk__cbor_encode_lightfunc(enc_ctx); ++ break; ++ } ++ case DUK_TYPE_NONE: ++ default: ++ goto fail; ++ } ++ ++ duk_pop(enc_ctx->ctx); ++ return; ++ ++ fail: ++ duk__cbor_encode_error(enc_ctx); ++} ++ ++/* ++ * Decoding ++ */ ++ ++static void duk__cbor_req_stack(duk_cbor_decode_context *dec_ctx) { ++ duk_require_stack(dec_ctx->ctx, 4); ++} ++ ++static void duk__cbor_decode_error(duk_cbor_decode_context *dec_ctx) { ++ (void) duk_type_error(dec_ctx->ctx, "cbor decode error"); ++} ++ ++static duk_uint8_t duk__cbor_decode_readbyte(duk_cbor_decode_context *dec_ctx) { ++ DUK_CBOR_ASSERT(dec_ctx->off <= dec_ctx->len); ++ if (DUK_CBOR_UNLIKELY(dec_ctx->len - dec_ctx->off < 1U)) { ++ duk__cbor_decode_error(dec_ctx); ++ } ++ return dec_ctx->buf[dec_ctx->off++]; ++} ++ ++static duk_uint16_t duk__cbor_decode_read_u16(duk_cbor_decode_context *dec_ctx) { ++ duk_uint16_t res; ++ ++ if (DUK_CBOR_UNLIKELY(dec_ctx->len - dec_ctx->off < 2U)) { ++ duk__cbor_decode_error(dec_ctx); ++ } ++ res = duk__cbor_read_uint16_big(dec_ctx->buf + dec_ctx->off); ++ dec_ctx->off += 2; ++ return res; ++} ++ ++static duk_uint32_t duk__cbor_decode_read_u32(duk_cbor_decode_context *dec_ctx) { ++ duk_uint32_t res; ++ ++ if (DUK_CBOR_UNLIKELY(dec_ctx->len - dec_ctx->off < 4U)) { ++ duk__cbor_decode_error(dec_ctx); ++ } ++ res = duk__cbor_read_uint32_big(dec_ctx->buf + dec_ctx->off); ++ dec_ctx->off += 4; ++ return res; ++} ++ ++static duk_uint8_t duk__cbor_decode_peekbyte(duk_cbor_decode_context *dec_ctx) { ++ if (DUK_CBOR_UNLIKELY(dec_ctx->off >= dec_ctx->len)) { ++ duk__cbor_decode_error(dec_ctx); ++ } ++ return dec_ctx->buf[dec_ctx->off]; ++} ++ ++static void duk__cbor_decode_rewind(duk_cbor_decode_context *dec_ctx, duk_size_t len) { ++ DUK_CBOR_ASSERT(len <= dec_ctx->off); /* Caller must ensure. */ ++ dec_ctx->off -= len; ++} ++ ++#if 0 ++static void duk__cbor_decode_ensure(duk_cbor_decode_context *dec_ctx, duk_size_t len) { ++ if (dec_ctx->off + len > dec_ctx->len) { ++ duk__cbor_decode_error(dec_ctx); ++ } ++} ++#endif ++ ++static const duk_uint8_t *duk__cbor_decode_consume(duk_cbor_decode_context *dec_ctx, duk_size_t len) { ++ DUK_CBOR_ASSERT(dec_ctx->off <= dec_ctx->len); ++ if (DUK_CBOR_LIKELY(dec_ctx->len - dec_ctx->off >= len)) { ++ const duk_uint8_t *res = dec_ctx->buf + dec_ctx->off; ++ dec_ctx->off += len; ++ return res; ++ } ++ ++ duk__cbor_decode_error(dec_ctx); /* Not enough input. */ ++ return NULL; ++} ++ ++static int duk__cbor_decode_checkbreak(duk_cbor_decode_context *dec_ctx) { ++ if (duk__cbor_decode_peekbyte(dec_ctx) == 0xffU) { ++ DUK_CBOR_ASSERT(dec_ctx->off < dec_ctx->len); ++ dec_ctx->off++; ++#if 0 ++ (void) duk__cbor_decode_readbyte(dec_ctx); ++#endif ++ return 1; ++ } ++ return 0; ++} ++ ++static void duk__cbor_decode_push_aival_int(duk_cbor_decode_context *dec_ctx, duk_uint8_t ib, duk_bool_t negative) { ++ duk_uint8_t ai; ++ duk_uint32_t t, t1, t2; ++#if 0 ++ duk_uint64_t t3; ++#endif ++ duk_double_t d1, d2; ++ duk_double_t d; ++ ++ ai = ib & 0x1fU; ++ if (ai <= 0x17U) { ++ t = ai; ++ goto shared_exit; ++ } ++ ++ switch (ai) { ++ case 0x18U: /* 1 byte */ ++ t = (duk_uint32_t) duk__cbor_decode_readbyte(dec_ctx); ++ goto shared_exit; ++ case 0x19U: /* 2 byte */ ++ t = (duk_uint32_t) duk__cbor_decode_read_u16(dec_ctx); ++ goto shared_exit; ++ case 0x1aU: /* 4 byte */ ++ t = (duk_uint32_t) duk__cbor_decode_read_u32(dec_ctx); ++ goto shared_exit; ++ case 0x1bU: /* 8 byte */ ++ /* For uint64 it's important to handle the -1.0 part before ++ * casting to double: otherwise the adjustment might be lost ++ * in the cast. Uses: -1.0 - d <=> -(d + 1.0). ++ */ ++ t = (duk_uint32_t) duk__cbor_decode_read_u32(dec_ctx); ++ t2 = t; ++ t = (duk_uint32_t) duk__cbor_decode_read_u32(dec_ctx); ++ t1 = t; ++#if 0 ++ t3 = (duk_uint64_t) t2 * 0x100000000ULL + (duk_uint64_t) t1; ++ if (negative) { ++ if (t3 == DUK_UINT64_MAX) { ++ /* -(0xffff'ffff'ffff'ffffULL + 1) = ++ * -0x1'0000'0000'0000'0000 ++ * ++ * >>> -0x10000000000000000 ++ * -18446744073709551616L ++ */ ++ return -18446744073709551616.0; ++ } else { ++ return -((duk_double_t) (t3 + 1ULL)); ++ } ++ } else { ++ return (duk_double_t) t3; /* XXX: cast helper */ ++ } ++#endif ++#if 0 ++ t3 = (duk_uint64_t) t2 * 0x100000000ULL + (duk_uint64_t) t1; ++ if (negative) { ++ /* Simpler version: take advantage of the fact that ++ * 0xffff'ffff'ffff'ffff and 0x1'0000'0000'0000'0000 ++ * both round to 0x1'0000'0000'0000'0000: ++ * > (0xffffffffffffffff).toString(16) ++ * '10000000000000000' ++ * > (0x10000000000000000).toString(16) ++ * '10000000000000000' ++ * ++ * For the DUK_UINT64_MAX case we just skip the +1 ++ * increment to avoid wrapping; the result still ++ * comes out right for an IEEE double cast. ++ */ ++ if (t3 != DUK_UINT64_MAX) { ++ t3++; ++ } ++ return -((duk_double_t) t3); ++ } else { ++ return (duk_double_t) t3; /* XXX: cast helper */ ++ } ++#endif ++#if 1 ++ /* Use two double parts, avoids dependency on 64-bit type. ++ * Avoid precision loss carefully, especially when dealing ++ * with the required +1 for negative values. ++ * ++ * No fastint check for this path at present. ++ */ ++ d1 = (duk_double_t) t1; /* XXX: cast helpers */ ++ d2 = (duk_double_t) t2 * 4294967296.0; ++ if (negative) { ++ d1 += 1.0; ++ } ++ d = d2 + d1; ++ if (negative) { ++ d = -d; ++ } ++#endif ++ /* XXX: a push and check for fastint API would be nice */ ++ duk_push_number(dec_ctx->ctx, d); ++ return; ++ } ++ ++ duk__cbor_decode_error(dec_ctx); ++ return; ++ ++ shared_exit: ++ if (negative) { ++ /* XXX: a push and check for fastint API would be nice */ ++ if ((duk_uint_t) t <= (duk_uint_t) -(DUK_INT_MIN + 1)) { ++ duk_push_int(dec_ctx->ctx, -1 - ((duk_int_t) t)); ++ } else { ++ duk_push_number(dec_ctx->ctx, -1.0 - (duk_double_t) t); ++ } ++ } else { ++ duk_push_uint(dec_ctx->ctx, (duk_uint_t) t); ++ } ++} ++ ++static void duk__cbor_decode_skip_aival_int(duk_cbor_decode_context *dec_ctx, duk_uint8_t ib) { ++ const duk_int8_t skips[32] = { ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 8, -1, -1, -1, -1 ++ }; ++ duk_uint8_t ai; ++ duk_int8_t skip; ++ ++ ai = ib & 0x1fU; ++ skip = skips[ai]; ++ if (DUK_UNLIKELY(skip < 0)) { ++ duk__cbor_decode_error(dec_ctx); ++ } ++ duk__cbor_decode_consume(dec_ctx, (duk_size_t) skip); ++ return; ++} ++ ++static duk_uint32_t duk__cbor_decode_aival_uint32(duk_cbor_decode_context *dec_ctx, duk_uint8_t ib) { ++ duk_uint8_t ai; ++ duk_uint32_t t; ++ ++ ai = ib & 0x1fU; ++ if (ai <= 0x17U) { ++ return (duk_uint32_t) ai; ++ } ++ ++ switch (ai) { ++ case 0x18U: /* 1 byte */ ++ t = (duk_uint32_t) duk__cbor_decode_readbyte(dec_ctx); ++ return t; ++ case 0x19U: /* 2 byte */ ++ t = (duk_uint32_t) duk__cbor_decode_read_u16(dec_ctx); ++ return t; ++ case 0x1aU: /* 4 byte */ ++ t = (duk_uint32_t) duk__cbor_decode_read_u32(dec_ctx); ++ return t; ++ case 0x1bU: /* 8 byte */ ++ t = (duk_uint32_t) duk__cbor_decode_read_u32(dec_ctx); ++ if (t != 0U) { ++ break; ++ } ++ t = (duk_uint32_t) duk__cbor_decode_read_u32(dec_ctx); ++ return t; ++ } ++ ++ duk__cbor_decode_error(dec_ctx); ++ return 0U; ++} ++ ++static void duk__cbor_decode_buffer(duk_cbor_decode_context *dec_ctx, duk_uint8_t expected_base) { ++ duk_uint32_t len; ++ duk_uint8_t *buf; ++ const duk_uint8_t *inp; ++ duk_uint8_t ib; ++ ++ ib = duk__cbor_decode_readbyte(dec_ctx); ++ if ((ib & 0xe0U) != expected_base) { ++ duk__cbor_decode_error(dec_ctx); ++ } ++ /* Indefinite format is rejected by the following on purpose. */ ++ len = duk__cbor_decode_aival_uint32(dec_ctx, ib); ++ inp = duk__cbor_decode_consume(dec_ctx, len); ++ /* XXX: duk_push_fixed_buffer_with_data() would be a nice API addition. */ ++ buf = (duk_uint8_t *) duk_push_fixed_buffer(dec_ctx->ctx, (duk_size_t) len); ++ (void) memcpy((void *) buf, (const void *) inp, (size_t) len); ++} ++ ++static void duk__cbor_decode_join_buffers(duk_cbor_decode_context *dec_ctx, duk_idx_t count) { ++ duk_size_t total_size = 0; ++ duk_idx_t top = duk_get_top(dec_ctx->ctx); ++ duk_idx_t base = top - count; /* count is >= 1 */ ++ duk_idx_t idx; ++ duk_uint8_t *p = NULL; ++ ++ DUK_CBOR_ASSERT(count >= 1); ++ DUK_CBOR_ASSERT(top >= count); ++ ++ for (;;) { ++ /* First round: compute total size. ++ * Second round: copy into place. ++ */ ++ for (idx = base; idx < top; idx++) { ++ duk_uint8_t *buf_data; ++ duk_size_t buf_size; ++ ++ buf_data = (duk_uint8_t *) duk_require_buffer(dec_ctx->ctx, idx, &buf_size); ++ if (p != NULL) { ++ if (buf_size > 0U) { ++ (void) memcpy((void *) p, (const void *) buf_data, buf_size); ++ } ++ p += buf_size; ++ } else { ++ total_size += buf_size; ++ if (DUK_CBOR_UNLIKELY(total_size < buf_size)) { /* Wrap check. */ ++ duk__cbor_decode_error(dec_ctx); ++ } ++ } ++ } ++ ++ if (p != NULL) { ++ break; ++ } else { ++ p = (duk_uint8_t *) duk_push_fixed_buffer(dec_ctx->ctx, total_size); ++ DUK_CBOR_ASSERT(p != NULL); ++ } ++ } ++ ++ duk_replace(dec_ctx->ctx, base); ++ duk_pop_n(dec_ctx->ctx, count - 1); ++} ++ ++static void duk__cbor_decode_and_join_strbuf(duk_cbor_decode_context *dec_ctx, duk_uint8_t expected_base) { ++ duk_idx_t count = 0; ++ for (;;) { ++ if (duk__cbor_decode_checkbreak(dec_ctx)) { ++ break; ++ } ++ duk_require_stack(dec_ctx->ctx, 1); ++ duk__cbor_decode_buffer(dec_ctx, expected_base); ++ count++; ++ if (DUK_UNLIKELY(count <= 0)) { /* Wrap check. */ ++ duk__cbor_decode_error(dec_ctx); ++ } ++ } ++ if (count == 0) { ++ (void) duk_push_fixed_buffer(dec_ctx->ctx, 0); ++ } else if (count > 1) { ++ duk__cbor_decode_join_buffers(dec_ctx, count); ++ } ++} ++ ++static duk_double_t duk__cbor_decode_half_float(duk_cbor_decode_context *dec_ctx) { ++ duk_cbor_dblunion u; ++ const duk_uint8_t *inp; ++ duk_int_t exp; ++ duk_uint_t u16; ++ duk_uint_t tmp; ++ duk_double_t res; ++ ++ inp = duk__cbor_decode_consume(dec_ctx, 2); ++ u16 = ((duk_uint_t) inp[0] << 8) + (duk_uint_t) inp[1]; ++ exp = (duk_int_t) ((u16 >> 10) & 0x1fU) - 15; ++ ++ /* Reconstruct IEEE double into little endian order first, then convert ++ * to host order. ++ */ ++ ++ memset((void *) &u, 0, sizeof(u)); ++ ++ if (exp == -15) { ++ /* Zero or denormal; but note that half float ++ * denormals become double normals. ++ */ ++ if ((u16 & 0x03ffU) == 0) { ++ u.x[7] = inp[0] & 0x80U; ++ } else { ++ /* Create denormal by first creating a double that ++ * contains the denormal bits and a leading implicit ++ * 1-bit. Then subtract away the implicit 1-bit. ++ * ++ * 0.mmmmmmmmmm * 2^-14 ++ * 1.mmmmmmmmmm 0.... * 2^-14 ++ * -1.0000000000 0.... * 2^-14 ++ * ++ * Double exponent: -14 + 1023 = 0x3f1 ++ */ ++ u.x[7] = 0x3fU; ++ u.x[6] = 0x10U + (duk_uint8_t) ((u16 >> 6) & 0x0fU); ++ u.x[5] = (duk_uint8_t) ((u16 << 2) & 0xffU); /* Mask is really 0xfcU */ ++ ++ duk__cbor_dblunion_little_to_host(&u); ++ res = u.d - 0.00006103515625; /* 2^(-14) */ ++ if (u16 & 0x8000U) { ++ res = -res; ++ } ++ return res; ++ } ++ } else if (exp == 16) { ++ /* +/- Inf or NaN. */ ++ if ((u16 & 0x03ffU) == 0) { ++ u.x[7] = (inp[0] & 0x80U) + 0x7fU; ++ u.x[6] = 0xf0U; ++ } else { ++ /* Create a 'quiet NaN' with highest ++ * bit set (there are some platforms ++ * where the NaN payload convention is ++ * the opposite). Keep sign. ++ */ ++ u.x[7] = (inp[0] & 0x80U) + 0x7fU; ++ u.x[6] = 0xf8U; ++ } ++ } else { ++ /* Normal. */ ++ tmp = (inp[0] & 0x80U) ? 0x80000000UL : 0UL; ++ tmp += (duk_uint_t) (exp + 1023) << 20; ++ tmp += (duk_uint_t) (inp[0] & 0x03U) << 18; ++ tmp += (duk_uint_t) (inp[1] & 0xffU) << 10; ++ u.x[7] = (tmp >> 24) & 0xffU; ++ u.x[6] = (tmp >> 16) & 0xffU; ++ u.x[5] = (tmp >> 8) & 0xffU; ++ u.x[4] = (tmp >> 0) & 0xffU; ++ } ++ ++ duk__cbor_dblunion_little_to_host(&u); ++ return u.d; ++} ++ ++static void duk__cbor_decode_string(duk_cbor_decode_context *dec_ctx, duk_uint8_t ib, duk_uint8_t ai) { ++ /* If the CBOR string data is not valid UTF-8 it is technically ++ * invalid CBOR. Possible behaviors at least: ++ * ++ * 1. Reject the input, i.e. throw TypeError. ++ * ++ * 2. Accept the input, but sanitize non-UTF-8 data into UTF-8 ++ * using U+FFFD replacements. Also it might make sense to ++ * decode non-BMP codepoints into surrogates for better ++ * ECMAScript compatibility. ++ * ++ * 3. Accept the input as a Duktape string (which are not always ++ * valid UTF-8), but reject any input that would create a ++ * Symbol representation. ++ * ++ * Current behavior is 3. ++ */ ++ ++ if (ai == 0x1fU) { ++ duk_uint8_t *buf_data; ++ duk_size_t buf_size; ++ ++ duk__cbor_decode_and_join_strbuf(dec_ctx, 0x60U); ++ buf_data = (duk_uint8_t *) duk_require_buffer(dec_ctx->ctx, -1, &buf_size); ++ (void) duk_push_lstring(dec_ctx->ctx, (const char *) buf_data, buf_size); ++ duk_remove(dec_ctx->ctx, -2); ++ } else { ++ duk_uint32_t len; ++ const duk_uint8_t *inp; ++ ++ len = duk__cbor_decode_aival_uint32(dec_ctx, ib); ++ inp = duk__cbor_decode_consume(dec_ctx, len); ++ (void) duk_push_lstring(dec_ctx->ctx, (const char *) inp, (duk_size_t) len); ++ } ++ if (duk_is_symbol(dec_ctx->ctx, -1)) { ++ /* Refuse to create Symbols when decoding. */ ++ duk__cbor_decode_error(dec_ctx); ++ } ++ ++ /* XXX: Here a Duktape API call to convert input -> utf-8 with ++ * replacements would be nice. ++ */ ++} ++ ++static duk_bool_t duk__cbor_decode_array(duk_cbor_decode_context *dec_ctx, duk_uint8_t ib, duk_uint8_t ai) { ++ duk_uint32_t idx, len; ++ ++ duk__cbor_req_stack(dec_ctx); ++ ++ /* Support arrays up to 0xfffffffeU in length. 0xffffffff is ++ * used as an indefinite length marker. ++ */ ++ if (ai == 0x1fU) { ++ len = 0xffffffffUL; ++ } else { ++ len = duk__cbor_decode_aival_uint32(dec_ctx, ib); ++ if (len == 0xffffffffUL) { ++ return 0; ++ } ++ } ++ ++ /* XXX: use bare array? */ ++ duk_push_array(dec_ctx->ctx); ++ for (idx = 0U; ;) { ++ if (len == 0xffffffffUL && duk__cbor_decode_checkbreak(dec_ctx)) { ++ break; ++ } ++ if (idx == len) { ++ if (ai == 0x1fU) { ++ return 0; ++ } ++ break; ++ } ++ duk__cbor_decode_value(dec_ctx); ++ duk_put_prop_index(dec_ctx->ctx, -2, (duk_uarridx_t) idx); ++ idx++; ++ if (idx == 0U) { ++ return 0; /* wrapped */ ++ } ++ } ++ ++ return 1; ++} ++ ++static duk_bool_t duk__cbor_decode_map(duk_cbor_decode_context *dec_ctx, duk_uint8_t ib, duk_uint8_t ai) { ++ duk_uint32_t count; ++ ++ duk__cbor_req_stack(dec_ctx); ++ ++ if (ai == 0x1fU) { ++ count = 0xffffffffUL; ++ } else { ++ count = duk__cbor_decode_aival_uint32(dec_ctx, ib); ++ if (count == 0xffffffffUL) { ++ return 0; ++ } ++ } ++ ++ /* XXX: use bare object? */ ++ duk_push_object(dec_ctx->ctx); ++ for (;;) { ++ if (count == 0xffffffffUL) { ++ if (duk__cbor_decode_checkbreak(dec_ctx)) { ++ break; ++ } ++ } else { ++ if (count == 0UL) { ++ break; ++ } ++ count--; ++ } ++ ++ /* Non-string keys are coerced to strings, ++ * possibly leading to overwriting previous ++ * keys. Last key of a certain coerced name ++ * wins. If key is an object, it will coerce ++ * to '[object Object]' which is consistent ++ * but potentially misleading. One alternative ++ * would be to skip non-string keys. ++ */ ++ duk__cbor_decode_value(dec_ctx); ++ duk__cbor_decode_value(dec_ctx); ++ duk_put_prop(dec_ctx->ctx, -3); ++ } ++ ++ return 1; ++} ++ ++static duk_double_t duk__cbor_decode_float(duk_cbor_decode_context *dec_ctx) { ++ duk_cbor_fltunion u; ++ const duk_uint8_t *inp; ++ inp = duk__cbor_decode_consume(dec_ctx, 4); ++ (void) memcpy((void *) u.x, (const void *) inp, 4); ++ duk__cbor_fltunion_big_to_host(&u); ++ return (duk_double_t) u.f; ++} ++ ++static duk_double_t duk__cbor_decode_double(duk_cbor_decode_context *dec_ctx) { ++ duk_cbor_dblunion u; ++ const duk_uint8_t *inp; ++ inp = duk__cbor_decode_consume(dec_ctx, 8); ++ (void) memcpy((void *) u.x, (const void *) inp, 8); ++ duk__cbor_dblunion_big_to_host(&u); ++ return u.d; ++} ++ ++#if defined(DUK_CBOR_DECODE_FASTPATH) ++#define DUK__CBOR_AI (ib & 0x1fU) ++ ++static void duk__cbor_decode_value(duk_cbor_decode_context *dec_ctx) { ++ duk_uint8_t ib; ++ ++ /* Any paths potentially recursing back to duk__cbor_decode_value() ++ * must perform a Duktape value stack growth check. Avoid the check ++ * here for simple paths like primitive values. ++ */ ++ ++ reread_initial_byte: ++#if defined(DUK_CBOR_DPRINT) ++ fprintf(stderr, "cbor decode off=%ld len=%ld\n", (long) dec_ctx->off, (long) dec_ctx->len); ++#endif ++ ++ ib = duk__cbor_decode_readbyte(dec_ctx); ++ ++ /* Full initial byte switch, footprint cost over baseline is ~+1kB. */ ++ /* XXX: Force full switch with no range check. */ ++ ++ switch (ib) { ++ case 0x00U: case 0x01U: case 0x02U: case 0x03U: case 0x04U: case 0x05U: case 0x06U: case 0x07U: ++ case 0x08U: case 0x09U: case 0x0aU: case 0x0bU: case 0x0cU: case 0x0dU: case 0x0eU: case 0x0fU: ++ case 0x10U: case 0x11U: case 0x12U: case 0x13U: case 0x14U: case 0x15U: case 0x16U: case 0x17U: ++ duk_push_uint(dec_ctx->ctx, ib); ++ break; ++ case 0x18U: case 0x19U: case 0x1aU: case 0x1bU: ++ duk__cbor_decode_push_aival_int(dec_ctx, ib, 0 /*negative*/); ++ break; ++ case 0x1cU: case 0x1dU: case 0x1eU: case 0x1fU: ++ goto format_error; ++ case 0x20U: case 0x21U: case 0x22U: case 0x23U: case 0x24U: case 0x25U: case 0x26U: case 0x27U: ++ case 0x28U: case 0x29U: case 0x2aU: case 0x2bU: case 0x2cU: case 0x2dU: case 0x2eU: case 0x2fU: ++ case 0x30U: case 0x31U: case 0x32U: case 0x33U: case 0x34U: case 0x35U: case 0x36U: case 0x37U: ++ duk_push_int(dec_ctx->ctx, -((duk_int_t) ((ib - 0x20U) + 1U))); ++ break; ++ case 0x38U: case 0x39U: case 0x3aU: case 0x3bU: ++ duk__cbor_decode_push_aival_int(dec_ctx, ib, 1 /*negative*/); ++ break; ++ case 0x3cU: case 0x3dU: case 0x3eU: case 0x3fU: ++ goto format_error; ++ case 0x40U: case 0x41U: case 0x42U: case 0x43U: case 0x44U: case 0x45U: case 0x46U: case 0x47U: ++ case 0x48U: case 0x49U: case 0x4aU: case 0x4bU: case 0x4cU: case 0x4dU: case 0x4eU: case 0x4fU: ++ case 0x50U: case 0x51U: case 0x52U: case 0x53U: case 0x54U: case 0x55U: case 0x56U: case 0x57U: ++ /* XXX: Avoid rewind, we know the length already. */ ++ DUK_CBOR_ASSERT(dec_ctx->off > 0U); ++ dec_ctx->off--; ++ duk__cbor_decode_buffer(dec_ctx, 0x40U); ++ break; ++ case 0x58U: case 0x59U: case 0x5aU: case 0x5bU: ++ /* XXX: Avoid rewind, decode length inline. */ ++ DUK_CBOR_ASSERT(dec_ctx->off > 0U); ++ dec_ctx->off--; ++ duk__cbor_decode_buffer(dec_ctx, 0x40U); ++ break; ++ case 0x5cU: case 0x5dU: case 0x5eU: ++ goto format_error; ++ case 0x5fU: ++ duk__cbor_decode_and_join_strbuf(dec_ctx, 0x40U); ++ break; ++ case 0x60U: case 0x61U: case 0x62U: case 0x63U: case 0x64U: case 0x65U: case 0x66U: case 0x67U: ++ case 0x68U: case 0x69U: case 0x6aU: case 0x6bU: case 0x6cU: case 0x6dU: case 0x6eU: case 0x6fU: ++ case 0x70U: case 0x71U: case 0x72U: case 0x73U: case 0x74U: case 0x75U: case 0x76U: case 0x77U: ++ /* XXX: Avoid double decode of length. */ ++ duk__cbor_decode_string(dec_ctx, ib, DUK__CBOR_AI); ++ break; ++ case 0x78U: case 0x79U: case 0x7aU: case 0x7bU: ++ /* XXX: Avoid double decode of length. */ ++ duk__cbor_decode_string(dec_ctx, ib, DUK__CBOR_AI); ++ break; ++ case 0x7cU: case 0x7dU: case 0x7eU: ++ goto format_error; ++ case 0x7fU: ++ duk__cbor_decode_string(dec_ctx, ib, DUK__CBOR_AI); ++ break; ++ case 0x80U: case 0x81U: case 0x82U: case 0x83U: case 0x84U: case 0x85U: case 0x86U: case 0x87U: ++ case 0x88U: case 0x89U: case 0x8aU: case 0x8bU: case 0x8cU: case 0x8dU: case 0x8eU: case 0x8fU: ++ case 0x90U: case 0x91U: case 0x92U: case 0x93U: case 0x94U: case 0x95U: case 0x96U: case 0x97U: ++ if (DUK_CBOR_UNLIKELY(duk__cbor_decode_array(dec_ctx, ib, DUK__CBOR_AI) == 0)) { ++ goto format_error; ++ } ++ break; ++ case 0x98U: case 0x99U: case 0x9aU: case 0x9bU: ++ if (DUK_CBOR_UNLIKELY(duk__cbor_decode_array(dec_ctx, ib, DUK__CBOR_AI) == 0)) { ++ goto format_error; ++ } ++ break; ++ case 0x9cU: case 0x9dU: case 0x9eU: ++ goto format_error; ++ case 0x9fU: ++ if (DUK_CBOR_UNLIKELY(duk__cbor_decode_array(dec_ctx, ib, DUK__CBOR_AI) == 0)) { ++ goto format_error; ++ } ++ break; ++ case 0xa0U: case 0xa1U: case 0xa2U: case 0xa3U: case 0xa4U: case 0xa5U: case 0xa6U: case 0xa7U: ++ case 0xa8U: case 0xa9U: case 0xaaU: case 0xabU: case 0xacU: case 0xadU: case 0xaeU: case 0xafU: ++ case 0xb0U: case 0xb1U: case 0xb2U: case 0xb3U: case 0xb4U: case 0xb5U: case 0xb6U: case 0xb7U: ++ if (DUK_CBOR_UNLIKELY(duk__cbor_decode_map(dec_ctx, ib, DUK__CBOR_AI) == 0)) { ++ goto format_error; ++ } ++ break; ++ case 0xb8U: case 0xb9U: case 0xbaU: case 0xbbU: ++ if (DUK_CBOR_UNLIKELY(duk__cbor_decode_map(dec_ctx, ib, DUK__CBOR_AI) == 0)) { ++ goto format_error; ++ } ++ break; ++ case 0xbcU: case 0xbdU: case 0xbeU: ++ goto format_error; ++ case 0xbfU: ++ if (DUK_CBOR_UNLIKELY(duk__cbor_decode_map(dec_ctx, ib, DUK__CBOR_AI) == 0)) { ++ goto format_error; ++ } ++ break; ++ case 0xc0U: case 0xc1U: case 0xc2U: case 0xc3U: case 0xc4U: case 0xc5U: case 0xc6U: case 0xc7U: ++ case 0xc8U: case 0xc9U: case 0xcaU: case 0xcbU: case 0xccU: case 0xcdU: case 0xceU: case 0xcfU: ++ case 0xd0U: case 0xd1U: case 0xd2U: case 0xd3U: case 0xd4U: case 0xd5U: case 0xd6U: case 0xd7U: ++ /* Tag 0-23: drop. */ ++ goto reread_initial_byte; ++ case 0xd8U: case 0xd9U: case 0xdaU: case 0xdbU: ++ duk__cbor_decode_skip_aival_int(dec_ctx, ib); ++ goto reread_initial_byte; ++ case 0xdcU: case 0xddU: case 0xdeU: case 0xdfU: ++ goto format_error; ++ case 0xe0U: ++ goto format_error; ++ case 0xe1U: ++ goto format_error; ++ case 0xe2U: ++ goto format_error; ++ case 0xe3U: ++ goto format_error; ++ case 0xe4U: ++ goto format_error; ++ case 0xe5U: ++ goto format_error; ++ case 0xe6U: ++ goto format_error; ++ case 0xe7U: ++ goto format_error; ++ case 0xe8U: ++ goto format_error; ++ case 0xe9U: ++ goto format_error; ++ case 0xeaU: ++ goto format_error; ++ case 0xebU: ++ goto format_error; ++ case 0xecU: ++ goto format_error; ++ case 0xedU: ++ goto format_error; ++ case 0xeeU: ++ goto format_error; ++ case 0xefU: ++ goto format_error; ++ case 0xf0U: ++ goto format_error; ++ case 0xf1U: ++ goto format_error; ++ case 0xf2U: ++ goto format_error; ++ case 0xf3U: ++ goto format_error; ++ case 0xf4U: ++ duk_push_false(dec_ctx->ctx); ++ break; ++ case 0xf5U: ++ duk_push_true(dec_ctx->ctx); ++ break; ++ case 0xf6U: ++ duk_push_null(dec_ctx->ctx); ++ break; ++ case 0xf7U: ++ duk_push_undefined(dec_ctx->ctx); ++ break; ++ case 0xf8U: ++ /* Simple value 32-255, nothing defined yet, so reject. */ ++ goto format_error; ++ case 0xf9U: { ++ duk_double_t d; ++ d = duk__cbor_decode_half_float(dec_ctx); ++ duk_push_number(dec_ctx->ctx, d); ++ break; ++ } ++ case 0xfaU: { ++ duk_double_t d; ++ d = duk__cbor_decode_float(dec_ctx); ++ duk_push_number(dec_ctx->ctx, d); ++ break; ++ } ++ case 0xfbU: { ++ duk_double_t d; ++ d = duk__cbor_decode_double(dec_ctx); ++ duk_push_number(dec_ctx->ctx, d); ++ break; ++ } ++ case 0xfcU: ++ case 0xfdU: ++ case 0xfeU: ++ case 0xffU: ++ goto format_error; ++ } /* end switch */ ++ ++ return; ++ ++ format_error: ++ duk__cbor_decode_error(dec_ctx); ++} ++#else /* DUK_CBOR_DECODE_FASTPATH */ ++static void duk__cbor_decode_value(duk_cbor_decode_context *dec_ctx) { ++ duk_uint8_t ib, mt, ai; ++ ++ /* Any paths potentially recursing back to duk__cbor_decode_value() ++ * must perform a Duktape value stack growth check. Avoid the check ++ * here for simple paths like primitive values. ++ */ ++ ++ reread_initial_byte: ++#if defined(DUK_CBOR_DPRINT) ++ fprintf(stderr, "cbor decode off=%ld len=%ld\n", (long) dec_ctx->off, (long) dec_ctx->len); ++#endif ++ ++ ib = duk__cbor_decode_readbyte(dec_ctx); ++ mt = ib >> 5U; ++ ai = ib & 0x1fU; ++ ++ /* Additional information in [24,27] = [0x18,0x1b] has relatively ++ * uniform handling for all major types: read 1/2/4/8 additional ++ * bytes. For major type 7 the 1-byte value is a 'simple type', and ++ * 2/4/8-byte values are floats. For other major types the 1/2/4/8 ++ * byte values are integers. The lengths are uniform, but the typing ++ * is not. ++ */ ++ ++ switch (mt) { ++ case 0U: { /* unsigned integer */ ++ duk__cbor_decode_push_aival_int(dec_ctx, ib, 0 /*negative*/); ++ break; ++ } ++ case 1U: { /* negative integer */ ++ duk__cbor_decode_push_aival_int(dec_ctx, ib, 1 /*negative*/); ++ break; ++ } ++ case 2U: { /* byte string */ ++ if (ai == 0x1fU) { ++ duk__cbor_decode_and_join_strbuf(dec_ctx, 0x40U); ++ } else { ++ duk__cbor_decode_rewind(dec_ctx, 1U); ++ duk__cbor_decode_buffer(dec_ctx, 0x40U); ++ } ++ break; ++ } ++ case 3U: { /* text string */ ++ duk__cbor_decode_string(dec_ctx, ib, ai); ++ break; ++ } ++ case 4U: { /* array of data items */ ++ if (DUK_CBOR_UNLIKELY(duk__cbor_decode_array(dec_ctx, ib, ai) == 0)) { ++ goto format_error; ++ } ++ break; ++ } ++ case 5U: { /* map of pairs of data items */ ++ if (DUK_CBOR_UNLIKELY(duk__cbor_decode_map(dec_ctx, ib, ai) == 0)) { ++ goto format_error; ++ } ++ break; ++ } ++ case 6U: { /* semantic tagging */ ++ /* Tags are ignored now, re-read initial byte. A tagged ++ * value may itself be tagged (an unlimited number of times) ++ * so keep on peeling away tags. ++ */ ++ duk__cbor_decode_skip_aival_int(dec_ctx, ib); ++ goto reread_initial_byte; ++ } ++ case 7U: { /* floating point numbers, simple data types, break; other */ ++ switch (ai) { ++ case 0x14U: { ++ duk_push_false(dec_ctx->ctx); ++ break; ++ } ++ case 0x15U: { ++ duk_push_true(dec_ctx->ctx); ++ break; ++ } ++ case 0x16U: { ++ duk_push_null(dec_ctx->ctx); ++ break; ++ } ++ case 0x17U: { ++ duk_push_undefined(dec_ctx->ctx); ++ break; ++ } ++ case 0x18U: { /* more simple values (1 byte) */ ++ /* Simple value encoded in additional byte (none ++ * are defined so far). RFC 7049 states that the ++ * follow-up byte must be 32-255 to minimize ++ * confusion. So, a non-shortest encoding like ++ * f815 (= true, shortest encoding f5) must be ++ * rejected. cbor.me tester rejects f815, but ++ * e.g. Python CBOR binding decodes it as true. ++ */ ++ goto format_error; ++ } ++ case 0x19U: { /* half-float (2 bytes) */ ++ duk_double_t d; ++ d = duk__cbor_decode_half_float(dec_ctx); ++ duk_push_number(dec_ctx->ctx, d); ++ break; ++ } ++ case 0x1aU: { /* float (4 bytes) */ ++ duk_double_t d; ++ d = duk__cbor_decode_float(dec_ctx); ++ duk_push_number(dec_ctx->ctx, d); ++ break; ++ } ++ case 0x1bU: { /* double (8 bytes) */ ++ duk_double_t d; ++ d = duk__cbor_decode_double(dec_ctx); ++ duk_push_number(dec_ctx->ctx, d); ++ break; ++ } ++ case 0xffU: /* unexpected break */ ++ default: { ++ goto format_error; ++ } ++ } /* end switch */ ++ break; ++ } ++ default: { ++ goto format_error; /* will never actually occur */ ++ } ++ } /* end switch */ ++ ++ return; ++ ++ format_error: ++ duk__cbor_decode_error(dec_ctx); ++} ++#endif /* DUK_CBOR_DECODE_FASTPATH */ ++ ++/* ++ * Public APIs ++ */ ++ ++static duk_ret_t duk__cbor_encode_binding(duk_context *ctx) { ++ /* Produce an ArrayBuffer by first decoding into a plain buffer which ++ * mimics a Uint8Array and gettings its .buffer property. ++ */ ++ duk_cbor_encode(ctx, -1, 0); ++ duk_get_prop_string(ctx, -1, "buffer"); ++ return 1; ++} ++ ++static duk_ret_t duk__cbor_decode_binding(duk_context *ctx) { ++ /* Lenient: accept any buffer like. */ ++ duk_cbor_decode(ctx, -1, 0); ++ return 1; ++} ++ ++void duk_cbor_init(duk_context *ctx, duk_uint_t flags) { ++ (void) flags; ++ duk_push_global_object(ctx); ++ duk_push_string(ctx, "CBOR"); ++ duk_push_object(ctx); ++ duk_push_string(ctx, "encode"); ++ duk_push_c_function(ctx, duk__cbor_encode_binding, 1); ++ duk_def_prop(ctx, -3, DUK_DEFPROP_ATTR_WC | DUK_DEFPROP_HAVE_VALUE); ++ duk_push_string(ctx, "decode"); ++ duk_push_c_function(ctx, duk__cbor_decode_binding, 1); ++ duk_def_prop(ctx, -3, DUK_DEFPROP_ATTR_WC | DUK_DEFPROP_HAVE_VALUE); ++ duk_def_prop(ctx, -3, DUK_DEFPROP_ATTR_WC | DUK_DEFPROP_HAVE_VALUE); ++ duk_pop(ctx); ++} ++ ++void duk_cbor_encode(duk_context *ctx, duk_idx_t idx, duk_uint_t encode_flags) { ++ duk_cbor_encode_context enc_ctx; ++ duk_uint8_t *buf; ++ ++ (void) encode_flags; ++ ++ idx = duk_require_normalize_index(ctx, idx); ++ ++ enc_ctx.ctx = ctx; ++ enc_ctx.idx_buf = duk_get_top(ctx); ++ ++ enc_ctx.len = 64; ++ buf = (duk_uint8_t *) duk_push_dynamic_buffer(ctx, enc_ctx.len); ++ enc_ctx.ptr = buf; ++ enc_ctx.buf = buf; ++ enc_ctx.buf_end = buf + enc_ctx.len; ++ ++ duk_dup(ctx, idx); ++ duk__cbor_encode_value(&enc_ctx); ++ duk_resize_buffer(enc_ctx.ctx, enc_ctx.idx_buf, (duk_size_t) (enc_ctx.ptr - enc_ctx.buf)); ++ duk_replace(ctx, idx); ++} ++ ++void duk_cbor_decode(duk_context *ctx, duk_idx_t idx, duk_uint_t decode_flags) { ++ duk_cbor_decode_context dec_ctx; ++ ++ (void) decode_flags; ++ ++ /* Suppress compile warnings for functions only needed with e.g. ++ * asserts enabled. ++ */ ++ (void) duk__cbor_get_reserve; ++ (void) duk__cbor_isinf; ++ (void) duk__cbor_fpclassify; ++ ++ idx = duk_require_normalize_index(ctx, idx); ++ ++ dec_ctx.ctx = ctx; ++ dec_ctx.buf = (const duk_uint8_t *) duk_require_buffer_data(ctx, idx, &dec_ctx.len); ++ dec_ctx.off = 0; ++ /* dec_ctx.len: set above */ ++ ++ duk__cbor_req_stack(&dec_ctx); ++ duk__cbor_decode_value(&dec_ctx); ++ if (dec_ctx.off != dec_ctx.len) { ++ (void) duk_type_error(ctx, "trailing garbage"); ++ } ++ ++ duk_replace(ctx, idx); ++} +diff --git a/dist/source/extras/cbor/duk_cbor.h b/dist/source/extras/cbor/duk_cbor.h +new file mode 100644 +index 0000000..659bd90 +--- /dev/null ++++ b/dist/source/extras/cbor/duk_cbor.h +@@ -0,0 +1,20 @@ ++#if !defined(DUK_CBOR_H_INCLUDED) ++#define DUK_CBOR_H_INCLUDED ++ ++#include "duktape.h" ++ ++#if defined(__cplusplus) ++extern "C" { ++#endif ++ ++/* No flags at present. */ ++ ++extern void duk_cbor_init(duk_context *ctx, duk_uint_t flags); ++extern void duk_cbor_encode(duk_context *ctx, duk_idx_t idx, duk_uint_t encode_flags); ++extern void duk_cbor_decode(duk_context *ctx, duk_idx_t idx, duk_uint_t decode_flags); ++ ++#if defined(__cplusplus) ++} ++#endif /* end 'extern "C"' wrapper */ ++ ++#endif /* DUK_CBOR_H_INCLUDED */ +diff --git a/dist/source/extras/cbor/jsoncbor.c b/dist/source/extras/cbor/jsoncbor.c +new file mode 100644 +index 0000000..e31cf50 +--- /dev/null ++++ b/dist/source/extras/cbor/jsoncbor.c +@@ -0,0 +1,207 @@ ++#include ++#include "duktape.h" ++#include "duk_cbor.h" ++ ++#define FMT_CBOR 1 ++#define FMT_JSON 2 ++#define FMT_JX 3 ++#define FMT_JS 4 ++ ++static int read_format = 0; ++static int write_format = 0; ++static int write_indent = 0; ++ ++static void push_stdin(duk_context *ctx, int to_string) { ++ unsigned char *buf; ++ size_t off; ++ size_t len; ++ size_t got; ++ ++ off = 0; ++ len = 256; ++ buf = (unsigned char *) duk_push_dynamic_buffer(ctx, len); ++ ++ for (;;) { ++#if 0 ++ fprintf(stderr, "reading %ld of input\n", (long) (len - off)); ++#endif ++ got = fread(buf + off, 1, len - off, stdin); ++ if (got == 0U) { ++ break; ++ } ++ off += got; ++ if (len - off < 256U) { ++ size_t newlen = len * 2U; ++ buf = (unsigned char *) duk_resize_buffer(ctx, -1, newlen); ++ len = newlen; ++ } ++ } ++ buf = (unsigned char *) duk_resize_buffer(ctx, -1, off); ++ ++ if (to_string) { ++ duk_push_lstring(ctx, (const char *) buf, off); ++ duk_remove(ctx, -2); ++ } ++} ++ ++static void usage_and_exit(void) { ++ fprintf(stderr, "Usage: jsoncbor -r cbor|json|jx|js -w cbor|json|jx [--indent N]\n" ++ " jsoncbor -e # shorthand for -r json -w cbor\n" ++ " jsoncbor -d [--indent N] # shorthand for -r cbor -w json [--indent N]\n" ++ "\n" ++ " Input is read from stdin, output is written to stdout.\n" ++ " 'jx' is a Duktape custom JSON extension.\n" ++ " 'js' means evaluate input as an ECMAScript expression.\n"); ++ exit(1); ++} ++ ++static duk_ret_t transcode_helper(duk_context *ctx, void *udata) { ++ const unsigned char *buf; ++ size_t len; ++ duk_idx_t top; ++ ++ (void) udata; ++ ++ /* For Duktape->JSON conversion map all typed arrays into base-64. ++ * This is generally more useful than the default behavior. However, ++ * the base-64 value doesn't have any kind of 'tag' to allow it to be ++ * parsed back into binary automatically. Right now the CBOR parser ++ * creates plain fixed buffers from incoming binary strings. ++ */ ++ duk_eval_string_noresult(ctx, ++ "(function () {\n" ++ " Object.getPrototypeOf(new Uint8Array(0)).toJSON = function () {\n" ++ " return Duktape.enc('base64', this);\n" ++ " };\n" ++ "}())\n"); ++ ++ top = duk_get_top(ctx); ++ ++ if (read_format == FMT_CBOR) { ++ push_stdin(ctx, 0 /*to_string*/); ++ duk_cbor_decode(ctx, -1, 0); ++ } else if (read_format == FMT_JSON) { ++ push_stdin(ctx, 1 /*to_string*/); ++ duk_json_decode(ctx, -1); ++ } else if (read_format == FMT_JX) { ++ duk_eval_string(ctx, "(function(v){return Duktape.dec('jx',v)})"); ++ push_stdin(ctx, 1 /*to_string*/); ++ duk_call(ctx, 1); ++ } else if (read_format == FMT_JS) { ++ push_stdin(ctx, 1 /*to_string*/); ++ duk_eval(ctx); ++ } else { ++ (void) duk_type_error(ctx, "invalid read format"); ++ } ++ ++ if (duk_get_top(ctx) != top + 1) { ++ fprintf(stderr, "top invalid after decoding: %d vs. %d\n", duk_get_top(ctx), top + 1); ++ exit(1); ++ } ++ ++ if (write_format == FMT_CBOR) { ++ duk_cbor_encode(ctx, -1, 0); ++ } else if (write_format == FMT_JSON) { ++ duk_eval_string(ctx, "(function(v,i){return JSON.stringify(v,null,i)})"); ++ duk_insert(ctx, -2); ++ duk_push_int(ctx, write_indent); ++ duk_call(ctx, 2); ++ } else if (write_format == FMT_JX) { ++ duk_eval_string(ctx, "(function(v,i){return Duktape.enc('jx',v,null,i)})"); ++ duk_insert(ctx, -2); ++ duk_push_int(ctx, write_indent); ++ duk_call(ctx, 2); ++ } else { ++ (void) duk_type_error(ctx, "invalid write format"); ++ } ++ ++ if (duk_is_string(ctx, -1)) { ++ buf = (const unsigned char *) duk_require_lstring(ctx, -1, &len); ++ fwrite((const void *) buf, 1, len, stdout); ++ fprintf(stdout, "\n"); ++ } else { ++ buf = (const unsigned char *) duk_require_buffer_data(ctx, -1, &len); ++ fwrite((const void *) buf, 1, len, stdout); ++ } ++ ++ if (duk_get_top(ctx) != top + 1) { ++ fprintf(stderr, "top invalid after encoding: %d vs. %d\n", duk_get_top(ctx), top + 1); ++ exit(1); ++ } ++ ++ return 0; ++} ++ ++static int parse_format(const char *s) { ++ if (strcmp(s, "cbor") == 0) { ++ return FMT_CBOR; ++ } else if (strcmp(s, "json") == 0) { ++ return FMT_JSON; ++ } else if (strcmp(s, "jx") == 0) { ++ return FMT_JX; ++ } else if (strcmp(s, "js") == 0) { ++ return FMT_JS; ++ } else { ++ return 0; ++ } ++} ++ ++int main(int argc, char *argv[]) { ++ duk_context *ctx; ++ duk_int_t rc; ++ int exitcode = 0; ++ int i; ++ ++ for (i = 1; i < argc; i++) { ++ if (strcmp(argv[i], "-e") == 0) { ++ read_format = FMT_JSON; ++ write_format = FMT_CBOR; ++ } else if (strcmp(argv[i], "-d") == 0) { ++ read_format = FMT_CBOR; ++ write_format = FMT_JSON; ++ } else if (strcmp(argv[i], "-r") == 0) { ++ if (i + 1 >= argc) { ++ goto usage; ++ } ++ read_format = parse_format(argv[i + 1]); ++ i++; ++ } else if (strcmp(argv[i], "-w") == 0) { ++ if (i + 1 >= argc) { ++ goto usage; ++ } ++ write_format = parse_format(argv[i + 1]); ++ i++; ++ } else if (strcmp(argv[i], "--indent") == 0) { ++ if (i + 1 >= argc) { ++ goto usage; ++ } ++ write_indent = atoi(argv[i + 1]); ++ i++; ++ } else { ++ goto usage; ++ } ++ } ++ ++ if (read_format == 0 || write_format == 0) { ++ goto usage; ++ } ++ ++ ctx = duk_create_heap_default(); ++ if (!ctx) { ++ return 1; ++ } ++ ++ rc = duk_safe_call(ctx, transcode_helper, NULL, 0, 1); ++ if (rc != 0) { ++ fprintf(stderr, "%s\n", duk_safe_to_string(ctx, -1)); ++ exitcode = 1; ++ } ++ /* duk_pop(ctx): unnecessary */ ++ ++ duk_destroy_heap(ctx); ++ ++ return exitcode; ++ ++ usage: ++ usage_and_exit(); ++} +diff --git a/dist/source/extras/cbor/run_testvectors.js b/dist/source/extras/cbor/run_testvectors.js +new file mode 100644 +index 0000000..f3f4f59 +--- /dev/null ++++ b/dist/source/extras/cbor/run_testvectors.js +@@ -0,0 +1,30 @@ ++var testvectors = JSON.parse(new TextDecoder().decode(readFile('appendix_a.json'))); ++ ++// Very rudimentary for now, just dump useful information about decode ++// results and (simple, unstructured) comparison to expected. This is ++// only useful for manually inspecting the results right now. ++ ++testvectors.forEach(function (test, idx) { ++ print('===', idx, '->', Duktape.enc('jx', test)); ++ ++ var cbor = Duktape.dec('base64', test.cbor); ++ try { ++ var dec = CBOR.decode(cbor); ++ } catch (e) { ++ print('decode failed: ' + e); ++ return; ++ } ++ ++ print('dec (jx): ' + Duktape.enc('jx', dec)); ++ ++ if (dec !== test.decoded) { ++ print('decoded compare failed'); ++ } ++ if (test.roundtrip) { ++ var enc = CBOR.encode(dec); ++ print('re-enc: ' + Duktape.enc('hex', enc)); ++ if (Duktape.enc('base64', cbor) !== Duktape.enc('base64', enc)) { ++ print('roundtrip failed'); ++ } ++ } ++}); +diff --git a/dist/source/extras/console/Makefile b/dist/source/extras/console/Makefile +new file mode 100644 +index 0000000..cfd9ecf +--- /dev/null ++++ b/dist/source/extras/console/Makefile +@@ -0,0 +1,20 @@ ++# For manual testing; say 'make' in extras/console and run ./test. ++ ++CC = gcc ++ ++.PHONY: test ++test: ++ -rm -rf ./prep ++ python2 ../../tools/configure.py --quiet --output-directory ./prep ++ $(CC) -std=c99 -Wall -Wextra -o $@ -I./prep -I. ./prep/duktape.c duk_console.c test.c -lm ++ ./test 'console.assert(true, "not shown");' ++ ./test 'console.assert(false, "shown", { foo: 123 });' ++ ./test 'console.log(1, 2, 3, { foo: "bar" });' ++ ./test 'a={}; b={}; a.ref=b; console.log(a,b); b.ref=a; console.log(a,b)' # circular ref ++ ./test 'console.trace(1, 2, 3)' ++ ./test 'console.dir({ foo: 123, bar: [ "foo", "bar" ]});' ++ ++.PHONY: clean ++clean: ++ -rm -rf ./prep ++ -rm -f test +diff --git a/dist/source/extras/console/README.rst b/dist/source/extras/console/README.rst +new file mode 100644 +index 0000000..e81b193 +--- /dev/null ++++ b/dist/source/extras/console/README.rst +@@ -0,0 +1,35 @@ ++========================= ++Minimal 'console' binding ++========================= ++ ++Duktape doesn't provide a ``console`` binding (for example ``console.log``) ++by default because it would be a portability issue for some targets. Instead, ++an application should provide its own ``console`` binding. This directory ++contains an example binding: ++ ++* Add ``duk_console.c`` to list of C sources to compile. ++ ++* Ensure ``duk_console.h`` is in the include path. ++ ++* Include the extra header in calling code and initialize the bindings:: ++ ++ #include "duktape.h" ++ #include "duk_console.h" ++ ++ /* After initializing the Duktape heap or when creating a new ++ * thread with a new global environment: ++ */ ++ duk_console_init(ctx, 0 /*flags*/); ++ ++ Use the ``DUK_CONSOLE_PROXY_WRAPPER`` to enable a Proxy wrapper for the ++ console object. The wrapper allows all undefined methods (for example, ++ ``console.foo``) to be handled as no-ops instead of throwing an error. ++ See ``duk_console.h`` for full flags list. ++ ++* After these steps, ``console`` will be registered to the global object ++ and is ready to use. ++ ++* By default the console object will use ``stdout`` for log levels up to ++ info, and ``stderr`` for warning and above. You can change this by ++ providing either ``DUK_CONSOLE_STDOUT_ONLY`` or ``DUK_CONSOLE_STDERR_ONLY`` ++ in ``duk_console_init()`` flags. +diff --git a/dist/source/extras/console/duk_console.c b/dist/source/extras/console/duk_console.c +new file mode 100644 +index 0000000..70ff6f0 +--- /dev/null ++++ b/dist/source/extras/console/duk_console.c +@@ -0,0 +1,185 @@ ++/* ++ * Minimal 'console' binding. ++ * ++ * https://github.com/DeveloperToolsWG/console-object/blob/master/api.md ++ * https://developers.google.com/web/tools/chrome-devtools/debug/console/console-reference ++ * https://developer.mozilla.org/en/docs/Web/API/console ++ */ ++ ++#include ++#include ++#include "duktape.h" ++#include "duk_console.h" ++ ++/* XXX: Add some form of log level filtering. */ ++ ++/* XXX: Should all output be written via e.g. console.write(formattedMsg)? ++ * This would make it easier for user code to redirect all console output ++ * to a custom backend. ++ */ ++ ++/* XXX: Init console object using duk_def_prop() when that call is available. */ ++ ++static duk_ret_t duk__console_log_helper(duk_context *ctx, const char *error_name) { ++ duk_uint_t flags = (duk_uint_t) duk_get_current_magic(ctx); ++ FILE *output = (flags & DUK_CONSOLE_STDOUT_ONLY) ? stdout : stderr; ++ duk_idx_t n = duk_get_top(ctx); ++ duk_idx_t i; ++ ++ duk_get_global_string(ctx, "console"); ++ duk_get_prop_string(ctx, -1, "format"); ++ ++ for (i = 0; i < n; i++) { ++ if (duk_check_type_mask(ctx, i, DUK_TYPE_MASK_OBJECT)) { ++ /* Slow path formatting. */ ++ duk_dup(ctx, -1); /* console.format */ ++ duk_dup(ctx, i); ++ duk_call(ctx, 1); ++ duk_replace(ctx, i); /* arg[i] = console.format(arg[i]); */ ++ } ++ } ++ ++ duk_pop_2(ctx); ++ ++ duk_push_string(ctx, " "); ++ duk_insert(ctx, 0); ++ duk_join(ctx, n); ++ ++ if (error_name) { ++ duk_push_error_object(ctx, DUK_ERR_ERROR, "%s", duk_require_string(ctx, -1)); ++ duk_push_string(ctx, "name"); ++ duk_push_string(ctx, error_name); ++ duk_def_prop(ctx, -3, DUK_DEFPROP_FORCE | DUK_DEFPROP_HAVE_VALUE); /* to get e.g. 'Trace: 1 2 3' */ ++ duk_get_prop_string(ctx, -1, "stack"); ++ } ++ ++ fprintf(output, "%s\n", duk_to_string(ctx, -1)); ++ if (flags & DUK_CONSOLE_FLUSH) { ++ fflush(output); ++ } ++ return 0; ++} ++ ++static duk_ret_t duk__console_assert(duk_context *ctx) { ++ if (duk_to_boolean(ctx, 0)) { ++ return 0; ++ } ++ duk_remove(ctx, 0); ++ ++ return duk__console_log_helper(ctx, "AssertionError"); ++} ++ ++static duk_ret_t duk__console_log(duk_context *ctx) { ++ return duk__console_log_helper(ctx, NULL); ++} ++ ++static duk_ret_t duk__console_trace(duk_context *ctx) { ++ return duk__console_log_helper(ctx, "Trace"); ++} ++ ++static duk_ret_t duk__console_info(duk_context *ctx) { ++ return duk__console_log_helper(ctx, NULL); ++} ++ ++static duk_ret_t duk__console_warn(duk_context *ctx) { ++ return duk__console_log_helper(ctx, NULL); ++} ++ ++static duk_ret_t duk__console_error(duk_context *ctx) { ++ return duk__console_log_helper(ctx, "Error"); ++} ++ ++static duk_ret_t duk__console_dir(duk_context *ctx) { ++ /* For now, just share the formatting of .log() */ ++ return duk__console_log_helper(ctx, 0); ++} ++ ++static void duk__console_reg_vararg_func(duk_context *ctx, duk_c_function func, const char *name, duk_uint_t flags) { ++ duk_push_c_function(ctx, func, DUK_VARARGS); ++ duk_push_string(ctx, "name"); ++ duk_push_string(ctx, name); ++ duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_FORCE); /* Improve stacktraces by displaying function name */ ++ duk_set_magic(ctx, -1, (duk_int_t) flags); ++ duk_put_prop_string(ctx, -2, name); ++} ++ ++void duk_console_init(duk_context *ctx, duk_uint_t flags) { ++ duk_uint_t flags_orig; ++ ++ /* If both DUK_CONSOLE_STDOUT_ONLY and DUK_CONSOLE_STDERR_ONLY where specified, ++ * just turn off DUK_CONSOLE_STDOUT_ONLY and keep DUK_CONSOLE_STDERR_ONLY. ++ */ ++ if ((flags & DUK_CONSOLE_STDOUT_ONLY) && (flags & DUK_CONSOLE_STDERR_ONLY)) { ++ flags &= ~DUK_CONSOLE_STDOUT_ONLY; ++ } ++ /* Remember the (possibly corrected) flags we received. */ ++ flags_orig = flags; ++ ++ duk_push_object(ctx); ++ ++ /* Custom function to format objects; user can replace. ++ * For now, try JX-formatting and if that fails, fall back ++ * to ToString(v). ++ */ ++ duk_eval_string(ctx, ++ "(function (E) {" ++ "return function format(v){" ++ "try{" ++ "return E('jx',v);" ++ "}catch(e){" ++ "return String(v);" /* String() allows symbols, ToString() internal algorithm doesn't. */ ++ "}" ++ "};" ++ "})(Duktape.enc)"); ++ duk_put_prop_string(ctx, -2, "format"); ++ ++ flags = flags_orig; ++ if (!(flags & DUK_CONSOLE_STDOUT_ONLY) && !(flags & DUK_CONSOLE_STDERR_ONLY)) { ++ /* No output indicators were specified; these levels go to stdout. */ ++ flags |= DUK_CONSOLE_STDOUT_ONLY; ++ } ++ duk__console_reg_vararg_func(ctx, duk__console_assert, "assert", flags); ++ duk__console_reg_vararg_func(ctx, duk__console_log, "log", flags); ++ duk__console_reg_vararg_func(ctx, duk__console_log, "debug", flags); /* alias to console.log */ ++ duk__console_reg_vararg_func(ctx, duk__console_trace, "trace", flags); ++ duk__console_reg_vararg_func(ctx, duk__console_info, "info", flags); ++ ++ flags = flags_orig; ++ if (!(flags & DUK_CONSOLE_STDOUT_ONLY) && !(flags & DUK_CONSOLE_STDERR_ONLY)) { ++ /* No output indicators were specified; these levels go to stderr. */ ++ flags |= DUK_CONSOLE_STDERR_ONLY; ++ } ++ duk__console_reg_vararg_func(ctx, duk__console_warn, "warn", flags); ++ duk__console_reg_vararg_func(ctx, duk__console_error, "error", flags); ++ duk__console_reg_vararg_func(ctx, duk__console_error, "exception", flags); /* alias to console.error */ ++ duk__console_reg_vararg_func(ctx, duk__console_dir, "dir", flags); ++ ++ duk_put_global_string(ctx, "console"); ++ ++ /* Proxy wrapping: ensures any undefined console method calls are ++ * ignored silently. This was required specifically by the ++ * DeveloperToolsWG proposal (and was implemented also by Firefox: ++ * https://bugzilla.mozilla.org/show_bug.cgi?id=629607). This is ++ * apparently no longer the preferred way of implementing console. ++ * When Proxy is enabled, whitelist at least .toJSON() to avoid ++ * confusing JX serialization of the console object. ++ */ ++ ++ if (flags & DUK_CONSOLE_PROXY_WRAPPER) { ++ /* Tolerate failure to initialize Proxy wrapper in case ++ * Proxy support is disabled. ++ */ ++ (void) duk_peval_string_noresult(ctx, ++ "(function(){" ++ "var D=function(){};" ++ "var W={toJSON:true};" /* whitelisted */ ++ "console=new Proxy(console,{" ++ "get:function(t,k){" ++ "var v=t[k];" ++ "return typeof v==='function'||W[k]?v:D;" ++ "}" ++ "});" ++ "})();" ++ ); ++ } ++} +diff --git a/dist/source/extras/console/duk_console.h b/dist/source/extras/console/duk_console.h +new file mode 100644 +index 0000000..d341e22 +--- /dev/null ++++ b/dist/source/extras/console/duk_console.h +@@ -0,0 +1,29 @@ ++#if !defined(DUK_CONSOLE_H_INCLUDED) ++#define DUK_CONSOLE_H_INCLUDED ++ ++#include "duktape.h" ++ ++#if defined(__cplusplus) ++extern "C" { ++#endif ++ ++/* Use a proxy wrapper to make undefined methods (console.foo()) no-ops. */ ++#define DUK_CONSOLE_PROXY_WRAPPER (1U << 0) ++ ++/* Flush output after every call. */ ++#define DUK_CONSOLE_FLUSH (1U << 1) ++ ++/* Send output to stdout only (default is mixed stdout/stderr). */ ++#define DUK_CONSOLE_STDOUT_ONLY (1U << 2) ++ ++/* Send output to stderr only (default is mixed stdout/stderr). */ ++#define DUK_CONSOLE_STDERR_ONLY (1U << 3) ++ ++/* Initialize the console system */ ++extern void duk_console_init(duk_context *ctx, duk_uint_t flags); ++ ++#if defined(__cplusplus) ++} ++#endif /* end 'extern "C"' wrapper */ ++ ++#endif /* DUK_CONSOLE_H_INCLUDED */ +diff --git a/dist/source/extras/console/test.c b/dist/source/extras/console/test.c +new file mode 100644 +index 0000000..9dc8183 +--- /dev/null ++++ b/dist/source/extras/console/test.c +@@ -0,0 +1,30 @@ ++#include ++#include "duktape.h" ++#include "duk_console.h" ++ ++int main(int argc, char *argv[]) { ++ duk_context *ctx; ++ int i; ++ int exitcode = 0; ++ ++ ctx = duk_create_heap_default(); ++ if (!ctx) { ++ return 1; ++ } ++ ++ duk_console_init(ctx, DUK_CONSOLE_PROXY_WRAPPER /*flags*/); ++ printf("top after init: %ld\n", (long) duk_get_top(ctx)); ++ ++ for (i = 1; i < argc; i++) { ++ printf("Evaling: %s\n", argv[i]); ++ if (duk_peval_string(ctx, argv[i]) != 0) { ++ exitcode = 1; ++ } ++ printf("--> %s\n", duk_safe_to_string(ctx, -1)); ++ duk_pop(ctx); ++ } ++ ++ printf("Done\n"); ++ duk_destroy_heap(ctx); ++ return exitcode; ++} +diff --git a/dist/source/extras/duk-v1-compat/Makefile b/dist/source/extras/duk-v1-compat/Makefile +new file mode 100644 +index 0000000..96bb313 +--- /dev/null ++++ b/dist/source/extras/duk-v1-compat/Makefile +@@ -0,0 +1,15 @@ ++# For manual testing; say 'make' in extras/duk-v1-compat and run ./test. ++ ++CC = gcc ++ ++.PHONY: test ++test: ++ -rm -rf ./prep ++ python2 ../../tools/configure.py --quiet --output-directory ./prep ++ $(CC) -std=c99 -Wall -Wextra -o $@ -I./prep -I. ./prep/duktape.c duk_v1_compat.c test.c -lm ++ ./test ++ ++.PHONY: clean ++clean: ++ -rm -rf ./prep ++ -rm -f test +diff --git a/dist/source/extras/duk-v1-compat/README.rst b/dist/source/extras/duk-v1-compat/README.rst +new file mode 100644 +index 0000000..5b0d7d5 +--- /dev/null ++++ b/dist/source/extras/duk-v1-compat/README.rst +@@ -0,0 +1,25 @@ ++================================ ++Duktape V1 compatibility helpers ++================================ ++ ++Provides helpers for migrating from Duktape 1.x to 2.x: ++ ++* Add ``duk_v1_compat.c`` to list of C sources to compile. ++ ++* Ensure ``duk_v1_compat.h`` is in the include path. ++ ++* Include the extra header in calling code:: ++ ++ #include "duktape.h" ++ #include "duk_v1_compat.h" ++ ++ /* ... */ ++ ++ duk_dump_context_stdout(ctx); /* Removed in Duktape 2.x. */ ++ ++The helpers don't restore full 1.x compatibility because some API calls such ++as ``duk_safe_call()`` have changed in an incompatible manner. ++ ++The old APIs are documented in: ++ ++* http://duktape.org/1.5.0/api.html +diff --git a/dist/source/extras/duk-v1-compat/duk_v1_compat.c b/dist/source/extras/duk-v1-compat/duk_v1_compat.c +new file mode 100644 +index 0000000..e4a44db +--- /dev/null ++++ b/dist/source/extras/duk-v1-compat/duk_v1_compat.c +@@ -0,0 +1,131 @@ ++#include ++#include "duktape.h" ++#include "duk_v1_compat.h" ++ ++/* ++ * duk_dump_context_{stdout,stderr}() ++ */ ++ ++void duk_dump_context_stdout(duk_context *ctx) { ++ duk_push_context_dump(ctx); ++ fprintf(stdout, "%s\n", duk_safe_to_string(ctx, -1)); ++ duk_pop(ctx); ++} ++ ++void duk_dump_context_stderr(duk_context *ctx) { ++ duk_push_context_dump(ctx); ++ fprintf(stderr, "%s\n", duk_safe_to_string(ctx, -1)); ++ duk_pop(ctx); ++} ++ ++/* ++ * duk_push_string_file() and duk_push_string_file_raw() ++ */ ++ ++const char *duk_push_string_file_raw(duk_context *ctx, const char *path, duk_uint_t flags) { ++ FILE *f = NULL; ++ char *buf; ++ long sz; /* ANSI C typing */ ++ ++ if (!path) { ++ goto fail; ++ } ++ f = fopen(path, "rb"); ++ if (!f) { ++ goto fail; ++ } ++ if (fseek(f, 0, SEEK_END) < 0) { ++ goto fail; ++ } ++ sz = ftell(f); ++ if (sz < 0) { ++ goto fail; ++ } ++ if (fseek(f, 0, SEEK_SET) < 0) { ++ goto fail; ++ } ++ buf = (char *) duk_push_fixed_buffer(ctx, (duk_size_t) sz); ++ if ((size_t) fread(buf, 1, (size_t) sz, f) != (size_t) sz) { ++ duk_pop(ctx); ++ goto fail; ++ } ++ (void) fclose(f); /* ignore fclose() error */ ++ return duk_buffer_to_string(ctx, -1); ++ ++ fail: ++ if (f) { ++ (void) fclose(f); /* ignore fclose() error */ ++ } ++ ++ if (flags & DUK_STRING_PUSH_SAFE) { ++ duk_push_undefined(ctx); ++ } else { ++ (void) duk_type_error(ctx, "read file error"); ++ } ++ return NULL; ++} ++ ++/* ++ * duk_eval_file(), duk_compile_file(), and their variants ++ */ ++ ++void duk_eval_file(duk_context *ctx, const char *path) { ++ duk_push_string_file_raw(ctx, path, 0); ++ duk_push_string(ctx, path); ++ duk_compile(ctx, DUK_COMPILE_EVAL); ++ duk_push_global_object(ctx); /* 'this' binding */ ++ duk_call_method(ctx, 0); ++} ++ ++void duk_eval_file_noresult(duk_context *ctx, const char *path) { ++ duk_eval_file(ctx, path); ++ duk_pop(ctx); ++} ++ ++duk_int_t duk_peval_file(duk_context *ctx, const char *path) { ++ duk_int_t rc; ++ ++ duk_push_string_file_raw(ctx, path, DUK_STRING_PUSH_SAFE); ++ duk_push_string(ctx, path); ++ rc = duk_pcompile(ctx, DUK_COMPILE_EVAL); ++ if (rc != 0) { ++ return rc; ++ } ++ duk_push_global_object(ctx); /* 'this' binding */ ++ rc = duk_pcall_method(ctx, 0); ++ return rc; ++} ++ ++duk_int_t duk_peval_file_noresult(duk_context *ctx, const char *path) { ++ duk_int_t rc; ++ ++ rc = duk_peval_file(ctx, path); ++ duk_pop(ctx); ++ return rc; ++} ++ ++void duk_compile_file(duk_context *ctx, duk_uint_t flags, const char *path) { ++ duk_push_string_file_raw(ctx, path, 0); ++ duk_push_string(ctx, path); ++ duk_compile(ctx, flags); ++} ++ ++duk_int_t duk_pcompile_file(duk_context *ctx, duk_uint_t flags, const char *path) { ++ duk_int_t rc; ++ ++ duk_push_string_file_raw(ctx, path, DUK_STRING_PUSH_SAFE); ++ duk_push_string(ctx, path); ++ rc = duk_pcompile(ctx, flags); ++ return rc; ++} ++ ++/* ++ * duk_to_defaultvalue() ++ */ ++ ++void duk_to_defaultvalue(duk_context *ctx, duk_idx_t idx, duk_int_t hint) { ++ duk_require_type_mask(ctx, idx, DUK_TYPE_MASK_OBJECT | ++ DUK_TYPE_MASK_BUFFER | ++ DUK_TYPE_MASK_LIGHTFUNC); ++ duk_to_primitive(ctx, idx, hint); ++} +diff --git a/dist/source/extras/duk-v1-compat/duk_v1_compat.h b/dist/source/extras/duk-v1-compat/duk_v1_compat.h +new file mode 100644 +index 0000000..09aa5a9 +--- /dev/null ++++ b/dist/source/extras/duk-v1-compat/duk_v1_compat.h +@@ -0,0 +1,36 @@ ++#if !defined(DUK_V1_COMPAT_INCLUDED) ++#define DUK_V1_COMPAT_INCLUDED ++ ++#include "duktape.h" ++ ++#if defined(__cplusplus) ++extern "C" { ++#endif ++ ++/* Straight flag rename */ ++#if !defined(DUK_ENUM_INCLUDE_INTERNAL) ++#define DUK_ENUM_INCLUDE_INTERNAL DUK_ENUM_INCLUDE_HIDDEN ++#endif ++ ++/* Flags for duk_push_string_file_raw() */ ++#define DUK_STRING_PUSH_SAFE (1 << 0) /* no error if file does not exist */ ++ ++extern void duk_dump_context_stdout(duk_context *ctx); ++extern void duk_dump_context_stderr(duk_context *ctx); ++extern const char *duk_push_string_file_raw(duk_context *ctx, const char *path, duk_uint_t flags); ++extern void duk_eval_file(duk_context *ctx, const char *path); ++extern void duk_eval_file_noresult(duk_context *ctx, const char *path); ++extern duk_int_t duk_peval_file(duk_context *ctx, const char *path); ++extern duk_int_t duk_peval_file_noresult(duk_context *ctx, const char *path); ++extern void duk_compile_file(duk_context *ctx, duk_uint_t flags, const char *path); ++extern duk_int_t duk_pcompile_file(duk_context *ctx, duk_uint_t flags, const char *path); ++extern void duk_to_defaultvalue(duk_context *ctx, duk_idx_t idx, duk_int_t hint); ++ ++#define duk_push_string_file(ctx,path) \ ++ duk_push_string_file_raw((ctx), (path), 0) ++ ++#if defined(__cplusplus) ++} ++#endif /* end 'extern "C"' wrapper */ ++ ++#endif /* DUK_V1_COMPAT_INCLUDED */ +diff --git a/dist/source/extras/duk-v1-compat/test.c b/dist/source/extras/duk-v1-compat/test.c +new file mode 100644 +index 0000000..82e97b4 +--- /dev/null ++++ b/dist/source/extras/duk-v1-compat/test.c +@@ -0,0 +1,104 @@ ++#include ++#include "duktape.h" ++#include "duk_v1_compat.h" ++ ++static duk_ret_t my_print(duk_context *ctx) { ++ duk_push_string(ctx, " "); ++ duk_insert(ctx, 0); ++ duk_join(ctx, duk_get_top(ctx) - 1); ++ printf("%s\n", duk_to_string(ctx, -1)); ++ fflush(stdout); ++ return 0; ++} ++ ++int main(int argc, char *argv[]) { ++ duk_context *ctx; ++ int i; ++ duk_int_t rc; ++ ++ ctx = duk_create_heap_default(); ++ if (!ctx) { ++ return 1; ++ } ++ ++ /* Minimal print() provider. */ ++ duk_push_c_function(ctx, my_print, DUK_VARARGS); ++ duk_put_global_string(ctx, "print"); ++ ++ printf("top after init: %ld\n", (long) duk_get_top(ctx)); ++ ++ for (i = 1; i < argc; i++) { ++ printf("Evaling: %s\n", argv[i]); ++ (void) duk_peval_string(ctx, argv[i]); ++ printf("--> %s\n", duk_safe_to_string(ctx, -1)); ++ duk_pop(ctx); ++ } ++ ++ /* Test duk_dump_context_{stdout,stderr}() */ ++ ++ duk_push_string(ctx, "dump to stdout"); ++ duk_dump_context_stdout(ctx); ++ duk_pop(ctx); ++ ++ duk_push_string(ctx, "dump to stderr"); ++ duk_dump_context_stderr(ctx); ++ duk_pop(ctx); ++ ++ /* Test duk_eval_file() and related. */ ++ ++ printf("top before duk_eval_file(): %ld\n", (long) duk_get_top(ctx)); ++ duk_eval_file(ctx, "test_eval1.js"); ++ printf("top after duk_eval_file(): %ld\n", (long) duk_get_top(ctx)); ++ printf(" --> %s\n", duk_safe_to_string(ctx, -1)); ++ duk_pop(ctx); ++ ++ printf("top before duk_eval_file_noresult(): %ld\n", (long) duk_get_top(ctx)); ++ duk_eval_file_noresult(ctx, "test_eval1.js"); ++ printf("top after duk_eval_file_noresult(): %ld\n", (long) duk_get_top(ctx)); ++ ++ printf("top before duk_peval_file(): %ld\n", (long) duk_get_top(ctx)); ++ rc = duk_peval_file(ctx, "test_eval1.js"); ++ printf("top after duk_peval_file(): %ld\n", (long) duk_get_top(ctx)); ++ printf(" --> %ld, %s\n", (long) rc, duk_safe_to_string(ctx, -1)); ++ duk_pop(ctx); ++ ++ printf("top before duk_peval_file(): %ld\n", (long) duk_get_top(ctx)); ++ rc = duk_peval_file(ctx, "test_eval2.js"); ++ printf("top after duk_peval_file(): %ld\n", (long) duk_get_top(ctx)); ++ printf(" --> %ld, %s\n", (long) rc, duk_safe_to_string(ctx, -1)); ++ duk_pop(ctx); ++ ++ printf("top before duk_peval_file_noresult(): %ld\n", (long) duk_get_top(ctx)); ++ rc = duk_peval_file_noresult(ctx, "test_eval1.js"); ++ printf("top after duk_peval_file_noresult(): %ld\n", (long) duk_get_top(ctx)); ++ printf(" --> %ld\n", (long) rc); ++ ++ printf("top before duk_peval_file_noresult(): %ld\n", (long) duk_get_top(ctx)); ++ rc = duk_peval_file_noresult(ctx, "test_eval2.js"); ++ printf("top after duk_peval_file_noresult(): %ld\n", (long) duk_get_top(ctx)); ++ printf(" --> %ld\n", (long) rc); ++ ++ /* Test duk_compile_file() and related. */ ++ ++ printf("top before duk_compile_file(): %ld\n", (long) duk_get_top(ctx)); ++ duk_compile_file(ctx, 0, "test_compile1.js"); ++ printf("top after duk_compile_file(): %ld\n", (long) duk_get_top(ctx)); ++ duk_call(ctx, 0); ++ duk_pop(ctx); ++ ++ printf("top before duk_pcompile_file(): %ld\n", (long) duk_get_top(ctx)); ++ rc = duk_pcompile_file(ctx, 0, "test_compile1.js"); ++ printf("top after duk_pcompile_file(): %ld\n", (long) duk_get_top(ctx)); ++ printf(" --> %ld: %s\n", (long) rc, duk_safe_to_string(ctx, -1)); ++ duk_pop(ctx); ++ ++ printf("top before duk_pcompile_file(): %ld\n", (long) duk_get_top(ctx)); ++ rc = duk_pcompile_file(ctx, 0, "test_compile2.js"); ++ printf("top after duk_pcompile_file(): %ld\n", (long) duk_get_top(ctx)); ++ printf(" --> %ld: %s\n", (long) rc, duk_safe_to_string(ctx, -1)); ++ duk_pop(ctx); ++ ++ printf("Done\n"); ++ duk_destroy_heap(ctx); ++ return 0; ++} +diff --git a/dist/source/extras/duk-v1-compat/test_compile1.js b/dist/source/extras/duk-v1-compat/test_compile1.js +new file mode 100644 +index 0000000..92ee80c +--- /dev/null ++++ b/dist/source/extras/duk-v1-compat/test_compile1.js +@@ -0,0 +1,3 @@ ++// File to compile, no error ++print('Hello from test_compile1.js'); ++print(new Error('test error for traceback (shows filename)').stack); +diff --git a/dist/source/extras/duk-v1-compat/test_compile2.js b/dist/source/extras/duk-v1-compat/test_compile2.js +new file mode 100644 +index 0000000..1cb1ffc +--- /dev/null ++++ b/dist/source/extras/duk-v1-compat/test_compile2.js +@@ -0,0 +1,3 @@ ++// File to compile, syntax error ++print('Hello from test_compile2.js'); ++= y +; +diff --git a/dist/source/extras/duk-v1-compat/test_eval1.js b/dist/source/extras/duk-v1-compat/test_eval1.js +new file mode 100644 +index 0000000..e2ee178 +--- /dev/null ++++ b/dist/source/extras/duk-v1-compat/test_eval1.js +@@ -0,0 +1,4 @@ ++// File to eval, no error ++print('Hello from test_eval1.js'); ++print(new Error('test error for traceback (shows filename)').stack); ++123; +diff --git a/dist/source/extras/duk-v1-compat/test_eval2.js b/dist/source/extras/duk-v1-compat/test_eval2.js +new file mode 100644 +index 0000000..443eb2c +--- /dev/null ++++ b/dist/source/extras/duk-v1-compat/test_eval2.js +@@ -0,0 +1,4 @@ ++// File to eval, throws error ++print('Hello from test_eval2.js'); ++print(new Error('test error for traceback (shows filename)').stack); ++throw new Error('aiee'); +diff --git a/dist/source/extras/logging/Makefile b/dist/source/extras/logging/Makefile +new file mode 100644 +index 0000000..0eda5a5 +--- /dev/null ++++ b/dist/source/extras/logging/Makefile +@@ -0,0 +1,22 @@ ++# For manual testing; say 'make' in extras/logging and run ./test. ++ ++CC = gcc ++ ++.PHONY: test ++test: ++ -rm -rf ./prep ++ python2 ../../tools/configure.py --quiet --output-directory ./prep ++ $(CC) -std=c99 -Wall -Wextra -otest -I./prep ./prep/duktape.c duk_logging.c test.c -lm ++ ./test 'L = new Duktape.Logger(); L.trace("testing"); L.l = 0; L.trace("testing 2");' ++ ./test 'L = new Duktape.Logger(); L.debug("testing"); L.l = 1; L.debug("testing 2");' ++ ./test 'L = new Duktape.Logger(); L.info("testing");' ++ ./test 'L = new Duktape.Logger(); L.warn("testing");' ++ ./test 'L = new Duktape.Logger(); L.error("testing");' ++ ./test 'L = new Duktape.Logger(); L.fatal("testing");' ++ ./test 'L = new Duktape.Logger(); L.info("testing"); L.n = "loggerName"; L.info("new name");' ++ ./test 'L = new Duktape.Logger("argname"); L.info("testing");' ++ ++.PHONY: clean ++clean: ++ -rm -rf ./prep ++ -rm -f test +diff --git a/dist/source/extras/logging/README.rst b/dist/source/extras/logging/README.rst +new file mode 100644 +index 0000000..eb87481 +--- /dev/null ++++ b/dist/source/extras/logging/README.rst +@@ -0,0 +1,33 @@ ++======================================== ++Duktape 1.x compatible logging framework ++======================================== ++ ++The default ``Duktape.Logger`` object and the logging related C API calls ++(``duk_log()``, ``duk_log_va()``) were removed in Duktape 2.x because they ++depended on stdout/stderr and were thus a portability issue for some targets ++(there were also other issues, such as the logging framework not always ++matching user expectations). This directory contains Duktape 1.x compatible ++``Duktape.Logger`` object and logging API calls: ++ ++* Add ``duk_logging.c`` to list of C sources to compile. ++ ++* Ensure ``duk_logging.h`` is in the include path. ++ ++* Include the extra header in calling code and initialize the bindings:: ++ ++ #include "duktape.h" ++ #include "duk_logging.h" ++ ++ /* After initializing the Duktape heap or when creating a new ++ * thread with a new global environment: ++ */ ++ duk_logging_init(ctx, 0 /*flags*/); ++ ++ See ``duk_logging.h`` for available flags. ++ ++* After these steps, ``Duktape.Logger`` will be registered to the ``Duktape`` ++ object and is ready to use. ++ ++See https://github.com/svaarala/duktape/blob/master/doc/logging.rst and ++http://wiki.duktape.org/HowtoLogging.html for more information on the ++logging framework design and functionality. +diff --git a/dist/source/extras/logging/duk_logging.c b/dist/source/extras/logging/duk_logging.c +new file mode 100644 +index 0000000..1b91d95 +--- /dev/null ++++ b/dist/source/extras/logging/duk_logging.c +@@ -0,0 +1,380 @@ ++/* ++ * Logging support ++ */ ++ ++#include ++#include ++#include ++#include "duktape.h" ++#include "duk_logging.h" ++ ++/* XXX: uses stderr always for now, configurable? */ ++ ++#define DUK_LOGGING_FLUSH /* Duktape 1.x: flush stderr */ ++ ++/* 3-letter log level strings. */ ++static const char duk__log_level_strings[] = { ++ 'T', 'R', 'C', 'D', 'B', 'G', 'I', 'N', 'F', ++ 'W', 'R', 'N', 'E', 'R', 'R', 'F', 'T', 'L' ++}; ++ ++/* Log method names. */ ++static const char *duk__log_method_names[] = { ++ "trace", "debug", "info", "warn", "error", "fatal" ++}; ++ ++/* Constructor. */ ++static duk_ret_t duk__logger_constructor(duk_context *ctx) { ++ duk_idx_t nargs; ++ ++ /* Calling as a non-constructor is not meaningful. */ ++ if (!duk_is_constructor_call(ctx)) { ++ return DUK_RET_TYPE_ERROR; ++ } ++ ++ nargs = duk_get_top(ctx); ++ duk_set_top(ctx, 1); ++ ++ duk_push_this(ctx); ++ ++ /* [ name this ] */ ++ ++ if (nargs == 0) { ++ /* Automatic defaulting of logger name from caller. This ++ * would work poorly with tail calls, but constructor calls ++ * are currently never tail calls, so tail calls are not an ++ * issue now. ++ */ ++ ++ duk_inspect_callstack_entry(ctx, -2); ++ if (duk_is_object(ctx, -1)) { ++ if (duk_get_prop_string(ctx, -1, "function")) { ++ if (duk_get_prop_string(ctx, -1, "fileName")) { ++ if (duk_is_string(ctx, -1)) { ++ duk_replace(ctx, 0); ++ } ++ } ++ } ++ } ++ /* Leave values on stack on purpose, ignored below. */ ++ ++ /* Stripping the filename might be a good idea ++ * ("/foo/bar/quux.js" -> logger name "quux"), ++ * but now used verbatim. ++ */ ++ } ++ /* The stack is unbalanced here on purpose; we only rely on the ++ * initial two values: [ name this ]. ++ */ ++ ++ if (duk_is_string(ctx, 0)) { ++ duk_dup(ctx, 0); ++ duk_put_prop_string(ctx, 1, "n"); ++ } else { ++ /* don't set 'n' at all, inherited value is used as name */ ++ } ++ ++ duk_compact(ctx, 1); ++ ++ return 0; /* keep default instance */ ++} ++ ++/* Default function to format objects. Tries to use toLogString() but falls ++ * back to toString(). Any errors are propagated out without catching. ++ */ ++static duk_ret_t duk__logger_prototype_fmt(duk_context *ctx) { ++ if (duk_get_prop_string(ctx, 0, "toLogString")) { ++ /* [ arg toLogString ] */ ++ ++ duk_dup(ctx, 0); ++ duk_call_method(ctx, 0); ++ ++ /* [ arg result ] */ ++ return 1; ++ } ++ ++ /* [ arg undefined ] */ ++ duk_pop(ctx); ++ duk_to_string(ctx, 0); ++ return 1; ++} ++ ++/* Default function to write a formatted log line. Writes to stderr, ++ * appending a newline to the log line. ++ * ++ * The argument is a buffer; avoid coercing the buffer to a string to ++ * avoid string table traffic. ++ */ ++static duk_ret_t duk__logger_prototype_raw(duk_context *ctx) { ++ const char *data; ++ duk_size_t data_len; ++ ++ data = (const char *) duk_require_buffer_data(ctx, 0, &data_len); ++ fwrite((const void *) data, 1, data_len, stderr); ++ fputc((int) '\n', stderr); ++#if defined(DUK_LOGGING_FLUSH) ++ fflush(stderr); ++#endif ++ return 0; ++} ++ ++/* Log frontend shared helper, magic value indicates log level. Provides ++ * frontend functions: trace(), debug(), info(), warn(), error(), fatal(). ++ * This needs to have small footprint, reasonable performance, minimal ++ * memory churn, etc. ++ */ ++static duk_ret_t duk__logger_prototype_log_shared(duk_context *ctx) { ++ duk_double_t now; ++ duk_time_components comp; ++ duk_small_int_t entry_lev; ++ duk_small_int_t logger_lev; ++ duk_int_t nargs; ++ duk_int_t i; ++ duk_size_t tot_len; ++ const duk_uint8_t *arg_str; ++ duk_size_t arg_len; ++ duk_uint8_t *buf, *p; ++ const duk_uint8_t *q; ++ duk_uint8_t date_buf[32]; /* maximum format length is 24+1 (NUL), round up. */ ++ duk_size_t date_len; ++ duk_small_int_t rc; ++ ++ /* XXX: sanitize to printable (and maybe ASCII) */ ++ /* XXX: better multiline */ ++ ++ /* ++ * Logger arguments are: ++ * ++ * magic: log level (0-5) ++ * this: logger ++ * stack: plain log args ++ * ++ * We want to minimize memory churn so a two-pass approach ++ * is used: first pass formats arguments and computes final ++ * string length, second pass copies strings into a buffer ++ * allocated directly with the correct size. If the backend ++ * function plays nice, it won't coerce the buffer to a string ++ * (and thus intern it). ++ */ ++ ++ entry_lev = duk_get_current_magic(ctx); ++ if (entry_lev < DUK_LOG_TRACE || entry_lev > DUK_LOG_FATAL) { ++ /* Should never happen, check just in case. */ ++ return 0; ++ } ++ nargs = duk_get_top(ctx); ++ ++ /* [ arg1 ... argN this ] */ ++ ++ /* ++ * Log level check ++ */ ++ ++ duk_push_this(ctx); ++ ++ duk_get_prop_string(ctx, -1, "l"); ++ logger_lev = (duk_small_int_t) duk_get_int(ctx, -1); ++ if (entry_lev < logger_lev) { ++ return 0; ++ } ++ /* log level could be popped but that's not necessary */ ++ ++ now = duk_get_now(ctx); ++ duk_time_to_components(ctx, now, &comp); ++ sprintf((char *) date_buf, "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", ++ (int) comp.year, (int) comp.month + 1, (int) comp.day, ++ (int) comp.hours, (int) comp.minutes, (int) comp.seconds, ++ (int) comp.milliseconds); ++ ++ date_len = strlen((const char *) date_buf); ++ ++ duk_get_prop_string(ctx, -2, "n"); ++ duk_to_string(ctx, -1); ++ ++ /* [ arg1 ... argN this loggerLevel loggerName ] */ ++ ++ /* ++ * Pass 1 ++ */ ++ ++ /* Line format: