From 6dfbbfb43b117458cabefc4a1e10eeeed32087e9 Mon Sep 17 00:00:00 2001 From: CN_SZTL Date: Fri, 10 Jul 2020 23:22:39 +0800 Subject: [PATCH] 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: