├── extras ├── duk-v1-compat │ ├── test_compile2.js │ ├── test_compile1.js │ ├── test_eval1.js │ ├── test_eval2.js │ ├── Makefile │ ├── README.rst │ ├── duk_v1_compat.h │ ├── duk_v1_compat.c │ └── test.c ├── alloc-pool │ ├── ptrcomp_fixup.h │ ├── ptrcomp.yaml │ ├── Makefile │ ├── README.rst │ ├── test.c │ ├── duk_alloc_pool.h │ └── duk_alloc_pool.c ├── minimal-printf │ ├── Makefile │ ├── duk_minimal_printf.h │ ├── README.rst │ ├── test.c │ └── duk_minimal_printf.c ├── print-alert │ ├── duk_print_alert.h │ ├── Makefile │ ├── test.c │ ├── README.rst │ └── duk_print_alert.c ├── cbor │ ├── README.rst │ ├── duk_cbor.h │ ├── cbordecode.py │ ├── run_testvectors.js │ ├── jsoncbor.c │ └── Makefile ├── module-node │ ├── duk_module_node.h │ ├── Makefile │ ├── test.c │ ├── README.rst │ └── duk_module_node.c ├── README.rst ├── module-duktape │ ├── duk_module_duktape.h │ ├── Makefile │ ├── README.rst │ ├── test.c │ └── duk_module_duktape.c ├── console │ ├── test.c │ ├── Makefile │ ├── duk_console.h │ ├── README.rst │ └── duk_console.c └── logging │ ├── duk_logging.h │ ├── Makefile │ ├── README.rst │ ├── test.c │ └── duk_logging.c ├── library.properties ├── update-duktape.sh ├── LICENSE ├── examples └── hello_world │ └── hello_world.ino └── src └── duk_source_meta.json /extras/duk-v1-compat/test_compile2.js: -------------------------------------------------------------------------------- 1 | // File to compile, syntax error 2 | print('Hello from test_compile2.js'); 3 | = y +; 4 | -------------------------------------------------------------------------------- /extras/alloc-pool/ptrcomp_fixup.h: -------------------------------------------------------------------------------- 1 | /* To provide declarations for inline pointer compression functions. */ 2 | #include "duk_alloc_pool.h" 3 | -------------------------------------------------------------------------------- /extras/duk-v1-compat/test_compile1.js: -------------------------------------------------------------------------------- 1 | // File to compile, no error 2 | print('Hello from test_compile1.js'); 3 | print(new Error('test error for traceback (shows filename)').stack); 4 | -------------------------------------------------------------------------------- /extras/duk-v1-compat/test_eval1.js: -------------------------------------------------------------------------------- 1 | // File to eval, no error 2 | print('Hello from test_eval1.js'); 3 | print(new Error('test error for traceback (shows filename)').stack); 4 | 123; 5 | -------------------------------------------------------------------------------- /extras/duk-v1-compat/test_eval2.js: -------------------------------------------------------------------------------- 1 | // File to eval, throws error 2 | print('Hello from test_eval2.js'); 3 | print(new Error('test error for traceback (shows filename)').stack); 4 | throw new Error('aiee'); 5 | -------------------------------------------------------------------------------- /extras/minimal-printf/Makefile: -------------------------------------------------------------------------------- 1 | # Just for manual testing 2 | CC = gcc 3 | 4 | .PHONY: test 5 | test: duk_minimal_printf.c 6 | $(CC) -std=c99 -Wall -Wextra -fno-stack-protector -m32 -Os -fomit-frame-pointer -otest duk_minimal_printf.c test.c 7 | ./test 8 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=JavaScript 2 | version=0.0.1 3 | author=Joseph Read 4 | maintainer=Joseph Read 5 | sentence=JavaScript interpreter for Arduino 6 | paragraph=JerryScript-based javascript interpreter for Arduino 7 | category=Device Control 8 | url=https://github.com/joeqread/javascript-arduino 9 | architectures=esp8266 esp32 10 | includes=duktape.h 11 | -------------------------------------------------------------------------------- /extras/duk-v1-compat/Makefile: -------------------------------------------------------------------------------- 1 | # For manual testing; say 'make' in extras/duk-v1-compat and run ./test. 2 | 3 | CC = gcc 4 | 5 | .PHONY: test 6 | test: 7 | -rm -rf ./prep 8 | python2 ../../tools/configure.py --quiet --output-directory ./prep 9 | $(CC) -std=c99 -Wall -Wextra -o $@ -I./prep -I. ./prep/duktape.c duk_v1_compat.c test.c -lm 10 | ./test 11 | 12 | .PHONY: clean 13 | clean: 14 | -rm -rf ./prep 15 | -rm -f test 16 | -------------------------------------------------------------------------------- /extras/print-alert/duk_print_alert.h: -------------------------------------------------------------------------------- 1 | #if !defined(DUK_PRINT_ALERT_H_INCLUDED) 2 | #define DUK_PRINT_ALERT_H_INCLUDED 3 | 4 | #include "duktape.h" 5 | 6 | #if defined(__cplusplus) 7 | extern "C" { 8 | #endif 9 | 10 | /* No flags at the moment. */ 11 | 12 | extern void duk_print_alert_init(duk_context *ctx, duk_uint_t flags); 13 | 14 | #if defined(__cplusplus) 15 | } 16 | #endif /* end 'extern "C"' wrapper */ 17 | 18 | #endif /* DUK_PRINT_ALERT_H_INCLUDED */ 19 | -------------------------------------------------------------------------------- /extras/cbor/README.rst: -------------------------------------------------------------------------------- 1 | =================== 2 | JSON/CBOR converter 3 | =================== 4 | 5 | Overview 6 | ======== 7 | 8 | A simple command line utility to convert between JSON and CBOR. Basic usage:: 9 | 10 | $ make jsoncbor 11 | [...] 12 | $ cat test.json | ./jsoncbor -e # writes CBOR to stdout 13 | $ cat test.cbor | ./jsoncbor -d # writes JSON to stdout 14 | 15 | CBOR objects are decoded into ECMAScript objects, with non-string keys 16 | coerced into strings. 17 | -------------------------------------------------------------------------------- /extras/module-node/duk_module_node.h: -------------------------------------------------------------------------------- 1 | #if !defined(DUK_MODULE_NODE_H_INCLUDED) 2 | #define DUK_MODULE_NODE_H_INCLUDED 3 | 4 | #include "duktape.h" 5 | 6 | #if defined(__cplusplus) 7 | extern "C" { 8 | #endif 9 | 10 | extern duk_ret_t duk_module_node_peval_main(duk_context *ctx, const char *path); 11 | extern void duk_module_node_init(duk_context *ctx); 12 | 13 | #if defined(__cplusplus) 14 | } 15 | #endif /* end 'extern "C"' wrapper */ 16 | 17 | #endif /* DUK_MODULE_NODE_H_INCLUDED */ 18 | -------------------------------------------------------------------------------- /extras/print-alert/Makefile: -------------------------------------------------------------------------------- 1 | # For manual testing; say 'make' in extras/print-alert and run ./test. 2 | 3 | CC = gcc 4 | 5 | .PHONY: test 6 | test: 7 | -rm -rf ./prep 8 | python2 ../../tools/configure.py --quiet --output-directory ./prep 9 | $(CC) -std=c99 -Wall -Wextra -otest -I./prep ./prep/duktape.c duk_print_alert.c test.c -lm 10 | ./test 'print("foo", "bar", 1, 2, 3)' 11 | ./test 'alert("foo", "bar", 1, 2, 3)' 12 | 13 | .PHONY: clean 14 | clean: 15 | -rm -rf ./prep 16 | -rm -f test 17 | -------------------------------------------------------------------------------- /extras/alloc-pool/ptrcomp.yaml: -------------------------------------------------------------------------------- 1 | DUK_USE_REFCOUNT16: true 2 | DUK_USE_STRHASH16: true 3 | DUK_USE_STRLEN16: true 4 | DUK_USE_BUFLEN16: true 5 | DUK_USE_OBJSIZES16: true 6 | DUK_USE_HSTRING_CLEN: false 7 | DUK_USE_HOBJECT_HASH_PART: false 8 | DUK_USE_HEAPPTR16: true 9 | DUK_USE_HEAPPTR_ENC16: 10 | verbatim: "#define DUK_USE_HEAPPTR_ENC16(ud,p) duk_alloc_pool_enc16((p))" 11 | DUK_USE_HEAPPTR_DEC16: 12 | verbatim: "#define DUK_USE_HEAPPTR_DEC16(ud,p) duk_alloc_pool_dec16((p))" 13 | 14 | #DUK_USE_ROM_OBJECTS: true 15 | #DUK_USE_ROM_STRINGS: true 16 | #DUK_USE_ROM_GLOBAL_INHERIT: true 17 | -------------------------------------------------------------------------------- /extras/cbor/duk_cbor.h: -------------------------------------------------------------------------------- 1 | #if !defined(DUK_CBOR_H_INCLUDED) 2 | #define DUK_CBOR_H_INCLUDED 3 | 4 | #include "duktape.h" 5 | 6 | #if defined(__cplusplus) 7 | extern "C" { 8 | #endif 9 | 10 | /* No flags at present. */ 11 | 12 | extern void duk_cbor_init(duk_context *ctx, duk_uint_t flags); 13 | extern void duk_cbor_encode(duk_context *ctx, duk_idx_t idx, duk_uint_t encode_flags); 14 | extern void duk_cbor_decode(duk_context *ctx, duk_idx_t idx, duk_uint_t decode_flags); 15 | 16 | #if defined(__cplusplus) 17 | } 18 | #endif /* end 'extern "C"' wrapper */ 19 | 20 | #endif /* DUK_CBOR_H_INCLUDED */ 21 | -------------------------------------------------------------------------------- /extras/cbor/cbordecode.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | def main(): 4 | import sys 5 | import cbor 6 | import json 7 | 8 | data = sys.stdin.read() 9 | print('LEN: %d' % len(data)) 10 | sys.stdout.flush() 11 | print('HEX: ' + data.encode('hex')) 12 | sys.stdout.flush() 13 | doc = cbor.loads(data) 14 | print('REPR: ' + repr(doc)) 15 | sys.stdout.flush() 16 | try: 17 | print('JSON: ' + json.dumps(doc)) 18 | sys.stdout.flush() 19 | except: 20 | print('JSON: cannot encode') 21 | sys.stdout.flush() 22 | 23 | if __name__ == '__main__': 24 | main() 25 | -------------------------------------------------------------------------------- /extras/README.rst: -------------------------------------------------------------------------------- 1 | ============== 2 | Duktape extras 3 | ============== 4 | 5 | Extra modules and utilities. Extras provide functionality that doesn't 6 | comfortably fit into the main Duktape library, perhaps for footprint or 7 | portability reasons, but are still useful for most users. 8 | 9 | Extras are maintained and will be bug fixed. However, they don't have the 10 | same semantic versioning guarantees like the main Duktape library. Extras 11 | may be dropped without warning as Duktape is versioned. For instance, if 12 | an extra breaks due to Duktape changes and there is no time to fix it, the 13 | missing extra won't block a release and will be dropped. 14 | -------------------------------------------------------------------------------- /extras/module-duktape/duk_module_duktape.h: -------------------------------------------------------------------------------- 1 | #if !defined(DUK_MODULE_DUKTAPE_H_INCLUDED) 2 | #define DUK_MODULE_DUKTAPE_H_INCLUDED 3 | 4 | #include "duktape.h" 5 | 6 | #if defined(__cplusplus) 7 | extern "C" { 8 | #endif 9 | 10 | /* Maximum length of CommonJS module identifier to resolve. Length includes 11 | * both current module ID, requested (possibly relative) module ID, and a 12 | * slash in between. 13 | */ 14 | #define DUK_COMMONJS_MODULE_ID_LIMIT 256 15 | 16 | extern void duk_module_duktape_init(duk_context *ctx); 17 | 18 | #if defined(__cplusplus) 19 | } 20 | #endif /* end 'extern "C"' wrapper */ 21 | 22 | #endif /* DUK_MODULE_DUKTAPE_H_INCLUDED */ 23 | -------------------------------------------------------------------------------- /extras/print-alert/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "duktape.h" 3 | #include "duk_print_alert.h" 4 | 5 | int main(int argc, char *argv[]) { 6 | duk_context *ctx; 7 | int i; 8 | int exitcode = 0; 9 | 10 | ctx = duk_create_heap_default(); 11 | if (!ctx) { 12 | return 1; 13 | } 14 | 15 | duk_print_alert_init(ctx, 0 /*flags*/); 16 | printf("top after init: %ld\n", (long) duk_get_top(ctx)); 17 | 18 | for (i = 1; i < argc; i++) { 19 | printf("Evaling: %s\n", argv[i]); 20 | if (duk_peval_string(ctx, argv[i]) != 0) { 21 | exitcode = 1; 22 | } 23 | printf("--> %s\n", duk_safe_to_string(ctx, -1)); 24 | duk_pop(ctx); 25 | } 26 | 27 | printf("Done\n"); 28 | duk_destroy_heap(ctx); 29 | return exitcode; 30 | } 31 | -------------------------------------------------------------------------------- /extras/console/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "duktape.h" 3 | #include "duk_console.h" 4 | 5 | int main(int argc, char *argv[]) { 6 | duk_context *ctx; 7 | int i; 8 | int exitcode = 0; 9 | 10 | ctx = duk_create_heap_default(); 11 | if (!ctx) { 12 | return 1; 13 | } 14 | 15 | duk_console_init(ctx, DUK_CONSOLE_PROXY_WRAPPER /*flags*/); 16 | printf("top after init: %ld\n", (long) duk_get_top(ctx)); 17 | 18 | for (i = 1; i < argc; i++) { 19 | printf("Evaling: %s\n", argv[i]); 20 | if (duk_peval_string(ctx, argv[i]) != 0) { 21 | exitcode = 1; 22 | } 23 | printf("--> %s\n", duk_safe_to_string(ctx, -1)); 24 | duk_pop(ctx); 25 | } 26 | 27 | printf("Done\n"); 28 | duk_destroy_heap(ctx); 29 | return exitcode; 30 | } 31 | -------------------------------------------------------------------------------- /extras/minimal-printf/duk_minimal_printf.h: -------------------------------------------------------------------------------- 1 | #if !defined(DUK_MINIMAL_PRINTF_H_INCLUDED) 2 | #define DUK_MINIMAL_PRINTF_H_INCLUDED 3 | 4 | #include /* va_list etc */ 5 | #include /* size_t */ 6 | 7 | #if defined(__cplusplus) 8 | extern "C" { 9 | #endif 10 | 11 | extern int duk_minimal_sprintf(char *str, const char *format, ...); 12 | extern int duk_minimal_snprintf(char *str, size_t size, const char *format, ...); 13 | extern int duk_minimal_vsnprintf(char *str, size_t size, const char *format, va_list ap); 14 | extern int duk_minimal_sscanf(const char *str, const char *format, ...); 15 | 16 | #if defined(__cplusplus) 17 | } 18 | #endif /* end 'extern "C"' wrapper */ 19 | 20 | #endif /* DUK_MINIMAL_PRINTF_H_INCLUDED */ 21 | -------------------------------------------------------------------------------- /extras/console/Makefile: -------------------------------------------------------------------------------- 1 | # For manual testing; say 'make' in extras/console and run ./test. 2 | 3 | CC = gcc 4 | 5 | .PHONY: test 6 | test: 7 | -rm -rf ./prep 8 | python2 ../../tools/configure.py --quiet --output-directory ./prep 9 | $(CC) -std=c99 -Wall -Wextra -o $@ -I./prep -I. ./prep/duktape.c duk_console.c test.c -lm 10 | ./test 'console.assert(true, "not shown");' 11 | ./test 'console.assert(false, "shown", { foo: 123 });' 12 | ./test 'console.log(1, 2, 3, { foo: "bar" });' 13 | ./test 'a={}; b={}; a.ref=b; console.log(a,b); b.ref=a; console.log(a,b)' # circular ref 14 | ./test 'console.trace(1, 2, 3)' 15 | ./test 'console.dir({ foo: 123, bar: [ "foo", "bar" ]});' 16 | 17 | .PHONY: clean 18 | clean: 19 | -rm -rf ./prep 20 | -rm -f test 21 | -------------------------------------------------------------------------------- /update-duktape.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | pwd=`pwd` 3 | 4 | [ ! -d "${pwd}/dep" ] && mkdir ${pwd}/dep 5 | cd dep 6 | 7 | if [ -d "duktape" ]; then 8 | cd duktape 9 | git checkout master 10 | git reset --hard 11 | git fetch --prune 12 | git pull 13 | else 14 | git clone --recurse-submodules https://github.com/svaarala/duktape.git duktape || exit 10; 15 | cd duktape 16 | fi 17 | 18 | cd src-tools 19 | 20 | out=`make` 21 | if [[ $? != 0 ]]; then 22 | echo "Error running make: ${out}" 23 | exit 1; 24 | fi 25 | 26 | out=`node duktool.js configure --source-directory ../src-input --output-directory ../../../src` 27 | if [[ $? != 0 ]]; then 28 | echo "Error running configure: ${out}" 29 | exit 1; 30 | fi 31 | 32 | cd $pwd 33 | cd -R dep/duktape/extras ./extras 34 | echo "Done!" 35 | 36 | -------------------------------------------------------------------------------- /extras/duk-v1-compat/README.rst: -------------------------------------------------------------------------------- 1 | ================================ 2 | Duktape V1 compatibility helpers 3 | ================================ 4 | 5 | Provides helpers for migrating from Duktape 1.x to 2.x: 6 | 7 | * Add ``duk_v1_compat.c`` to list of C sources to compile. 8 | 9 | * Ensure ``duk_v1_compat.h`` is in the include path. 10 | 11 | * Include the extra header in calling code:: 12 | 13 | #include "duktape.h" 14 | #include "duk_v1_compat.h" 15 | 16 | /* ... */ 17 | 18 | duk_dump_context_stdout(ctx); /* Removed in Duktape 2.x. */ 19 | 20 | The helpers don't restore full 1.x compatibility because some API calls such 21 | as ``duk_safe_call()`` have changed in an incompatible manner. 22 | 23 | The old APIs are documented in: 24 | 25 | * http://duktape.org/1.5.0/api.html 26 | -------------------------------------------------------------------------------- /extras/console/duk_console.h: -------------------------------------------------------------------------------- 1 | #if !defined(DUK_CONSOLE_H_INCLUDED) 2 | #define DUK_CONSOLE_H_INCLUDED 3 | 4 | #include "duktape.h" 5 | 6 | #if defined(__cplusplus) 7 | extern "C" { 8 | #endif 9 | 10 | /* Use a proxy wrapper to make undefined methods (console.foo()) no-ops. */ 11 | #define DUK_CONSOLE_PROXY_WRAPPER (1U << 0) 12 | 13 | /* Flush output after every call. */ 14 | #define DUK_CONSOLE_FLUSH (1U << 1) 15 | 16 | /* Send output to stdout only (default is mixed stdout/stderr). */ 17 | #define DUK_CONSOLE_STDOUT_ONLY (1U << 2) 18 | 19 | /* Send output to stderr only (default is mixed stdout/stderr). */ 20 | #define DUK_CONSOLE_STDERR_ONLY (1U << 3) 21 | 22 | /* Initialize the console system */ 23 | extern void duk_console_init(duk_context *ctx, duk_uint_t flags); 24 | 25 | #if defined(__cplusplus) 26 | } 27 | #endif /* end 'extern "C"' wrapper */ 28 | 29 | #endif /* DUK_CONSOLE_H_INCLUDED */ 30 | -------------------------------------------------------------------------------- /extras/logging/duk_logging.h: -------------------------------------------------------------------------------- 1 | #if !defined(DUK_LOGGING_H_INCLUDED) 2 | #define DUK_LOGGING_H_INCLUDED 3 | 4 | #include "duktape.h" 5 | 6 | #if defined(__cplusplus) 7 | extern "C" { 8 | #endif 9 | 10 | /* Log levels. */ 11 | #define DUK_LOG_TRACE 0 12 | #define DUK_LOG_DEBUG 1 13 | #define DUK_LOG_INFO 2 14 | #define DUK_LOG_WARN 3 15 | #define DUK_LOG_ERROR 4 16 | #define DUK_LOG_FATAL 5 17 | 18 | /* No flags at the moment. */ 19 | 20 | extern void duk_logging_init(duk_context *ctx, duk_uint_t flags); 21 | extern void duk_log_va(duk_context *ctx, duk_int_t level, const char *fmt, va_list ap); 22 | extern void duk_log(duk_context *ctx, duk_int_t level, const char *fmt, ...); 23 | 24 | #if defined(__cplusplus) 25 | } 26 | #endif /* end 'extern "C"' wrapper */ 27 | 28 | #endif /* DUK_LOGGING_H_INCLUDED */ 29 | -------------------------------------------------------------------------------- /extras/logging/Makefile: -------------------------------------------------------------------------------- 1 | # For manual testing; say 'make' in extras/logging and run ./test. 2 | 3 | CC = gcc 4 | 5 | .PHONY: test 6 | test: 7 | -rm -rf ./prep 8 | python2 ../../tools/configure.py --quiet --output-directory ./prep 9 | $(CC) -std=c99 -Wall -Wextra -otest -I./prep ./prep/duktape.c duk_logging.c test.c -lm 10 | ./test 'L = new Duktape.Logger(); L.trace("testing"); L.l = 0; L.trace("testing 2");' 11 | ./test 'L = new Duktape.Logger(); L.debug("testing"); L.l = 1; L.debug("testing 2");' 12 | ./test 'L = new Duktape.Logger(); L.info("testing");' 13 | ./test 'L = new Duktape.Logger(); L.warn("testing");' 14 | ./test 'L = new Duktape.Logger(); L.error("testing");' 15 | ./test 'L = new Duktape.Logger(); L.fatal("testing");' 16 | ./test 'L = new Duktape.Logger(); L.info("testing"); L.n = "loggerName"; L.info("new name");' 17 | ./test 'L = new Duktape.Logger("argname"); L.info("testing");' 18 | 19 | .PHONY: clean 20 | clean: 21 | -rm -rf ./prep 22 | -rm -f test 23 | -------------------------------------------------------------------------------- /extras/cbor/run_testvectors.js: -------------------------------------------------------------------------------- 1 | var testvectors = JSON.parse(new TextDecoder().decode(readFile('appendix_a.json'))); 2 | 3 | // Very rudimentary for now, just dump useful information about decode 4 | // results and (simple, unstructured) comparison to expected. This is 5 | // only useful for manually inspecting the results right now. 6 | 7 | testvectors.forEach(function (test, idx) { 8 | print('===', idx, '->', Duktape.enc('jx', test)); 9 | 10 | var cbor = Duktape.dec('base64', test.cbor); 11 | try { 12 | var dec = CBOR.decode(cbor); 13 | } catch (e) { 14 | print('decode failed: ' + e); 15 | return; 16 | } 17 | 18 | print('dec (jx): ' + Duktape.enc('jx', dec)); 19 | 20 | if (dec !== test.decoded) { 21 | print('decoded compare failed'); 22 | } 23 | if (test.roundtrip) { 24 | var enc = CBOR.encode(dec); 25 | print('re-enc: ' + Duktape.enc('hex', enc)); 26 | if (Duktape.enc('base64', cbor) !== Duktape.enc('base64', enc)) { 27 | print('roundtrip failed'); 28 | } 29 | } 30 | }); 31 | -------------------------------------------------------------------------------- /extras/print-alert/README.rst: -------------------------------------------------------------------------------- 1 | =================================================== 2 | Duktape 1.x compatible print() and alert() bindings 3 | =================================================== 4 | 5 | The default ``print()`` and ``alert()`` built-ins were removed in Duktape 2.x 6 | because they depended on stdout/stderr and were thus a portability issue for 7 | some targets. This directory contains Duktape 1.x compatible optional 8 | print() and alert() bindings: 9 | 10 | * Add ``duk_print_alert.c`` to list of C sources to compile. 11 | 12 | * Ensure ``duk_print_alert.h`` is in the include path. 13 | 14 | * Include the extra header in calling code and initialize the bindings:: 15 | 16 | #include "duktape.h" 17 | #include "duk_print_alert.h" 18 | 19 | /* After initializing the Duktape heap or when creating a new 20 | * thread with a new global environment: 21 | */ 22 | duk_print_alert_init(ctx, 0 /*flags*/); 23 | 24 | See ``duk_print_alert.h`` for available flags. 25 | 26 | * After these steps, ``print()`` and ``alert()`` will be registered to the 27 | global object and are ready to use. 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Joe Read 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /extras/module-duktape/Makefile: -------------------------------------------------------------------------------- 1 | # For manual testing; say 'make' in extras/module-duktape and run ./test. 2 | # There's test coverage in tests/ecmascript, so tests here are very simple. 3 | 4 | CC = gcc 5 | 6 | .PHONY: test 7 | test: 8 | -rm -rf ./prep 9 | python2 ../../tools/configure.py --quiet --output-directory ./prep 10 | $(CC) -std=c99 -Wall -Wextra -o $@ -I./prep -I. ./prep/duktape.c duk_module_duktape.c test.c -lm 11 | @printf '\n' 12 | ./test 'assert(typeof require === "function");' 13 | ./test 'assert(require.name === "require");' 14 | ./test 'assert(typeof Duktape.modLoaded === "object");' 15 | ./test 'assert(typeof Duktape.modSearch === "undefined");' 16 | ./test 'Duktape.modSearch = function myModSearch(id) { return "exports.foo = 123;" }; assert(require("dummy").foo === 123);' 17 | ./test 'Duktape.modSearch = function myModSearch(id) { return "exports.foo = 234;" }; delete Duktape; assert(typeof Duktape === "undefined"); assert(require("dummy").foo === 234);' 18 | ./test 'Duktape.modSearch = function myModSearch(id) { return "exports.foo = 234; // comment" }; delete Duktape; assert(typeof Duktape === "undefined"); assert(require("dummy").foo === 234);' 19 | 20 | .PHONY: clean 21 | clean: 22 | -rm -rf ./prep 23 | -rm -f test 24 | -------------------------------------------------------------------------------- /extras/module-duktape/README.rst: -------------------------------------------------------------------------------- 1 | =============================================== 2 | Duktape 1.x compatible module loading framework 3 | =============================================== 4 | 5 | The default built-in module loading framework was removed in Duktape 2.x 6 | because more flexibility was needed for module loading. This directory 7 | contains a Duktape 1.x compatible module loading framework which you can 8 | add to your build: 9 | 10 | * Add ``duk_module_duktape.c`` to list of C sources to compile. 11 | 12 | * Ensure ``duk_module_duktape.h`` is in the include path. 13 | 14 | * Include the extra header in calling code and initialize the bindings:: 15 | 16 | #include "duktape.h" 17 | #include "duk_module_duktape.h" 18 | 19 | /* After initializing the Duktape heap or when creating a new 20 | * thread with a new global environment: 21 | */ 22 | duk_module_duktape_init(ctx); 23 | 24 | Don't call ``duk_module_duktape_init()`` more than once for the same global 25 | environment. 26 | 27 | * As usual in Duktape 1.x, you should define ``Duktape.modSearch()`` to provide 28 | environment specific module lookups. 29 | 30 | * After these steps, ``require()`` will be registered to the global object and 31 | the module system is ready to use. 32 | -------------------------------------------------------------------------------- /examples/hello_world/hello_world.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | duk_context *ctx; 5 | 6 | static duk_ret_t js_native_print(duk_context *ctx) { 7 | duk_push_string(ctx, " "); 8 | duk_insert(ctx, 0); 9 | duk_join(ctx, duk_get_top(ctx) - 1); 10 | Serial.printf("%s", duk_to_string(ctx, -1)); 11 | 12 | return 0; 13 | } 14 | 15 | void js_setup () 16 | { 17 | ctx = duk_create_heap_default(); 18 | 19 | duk_push_c_function(ctx, js_native_print, DUK_VARARGS); 20 | duk_put_global_string(ctx, "print"); 21 | } 22 | 23 | void js_eval (char *code) 24 | { 25 | Serial.printf("\n>>> Eval Code: %s\n", code); 26 | duk_push_string(ctx, code); 27 | duk_int_t rc = duk_peval(ctx); 28 | 29 | if (rc != 0) { 30 | duk_safe_to_stacktrace(ctx, -1); 31 | } else { 32 | duk_safe_to_string(ctx, -1); 33 | } 34 | 35 | String res = duk_get_string(ctx, -1); 36 | 37 | Serial.printf("\n>>> Reslt: %s\n", res ? res : "null"); 38 | 39 | duk_pop(ctx); 40 | // duk_destroy_heap(ctx); 41 | } 42 | 43 | void setup() { 44 | Serial.begin(115200); 45 | while (!Serial) delay(100); 46 | 47 | Serial.println("Loading JS Example..."); 48 | 49 | // put your setup code here, to run once: 50 | js_setup(); 51 | js_eval("print('Hello, world!');"); 52 | } 53 | 54 | void loop() { 55 | // put your main code here, to run repeatedly: 56 | 57 | } 58 | -------------------------------------------------------------------------------- /extras/module-duktape/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "duktape.h" 4 | #include "duk_module_duktape.h" 5 | 6 | static duk_ret_t handle_print(duk_context *ctx) { 7 | printf("%s\n", duk_safe_to_string(ctx, 0)); 8 | return 0; 9 | } 10 | 11 | static duk_ret_t handle_assert(duk_context *ctx) { 12 | if (duk_to_boolean(ctx, 0)) { 13 | return 0; 14 | } 15 | (void) duk_generic_error(ctx, "assertion failed: %s", duk_safe_to_string(ctx, 1)); 16 | return 0; 17 | } 18 | 19 | 20 | int main(int argc, char *argv[]) { 21 | duk_context *ctx; 22 | int i; 23 | int exitcode = 0; 24 | 25 | ctx = duk_create_heap_default(); 26 | if (!ctx) { 27 | return 1; 28 | } 29 | 30 | duk_push_c_function(ctx, handle_print, 1); 31 | duk_put_global_string(ctx, "print"); 32 | duk_push_c_function(ctx, handle_assert, 2); 33 | duk_put_global_string(ctx, "assert"); 34 | 35 | duk_module_duktape_init(ctx); 36 | printf("top after init: %ld\n", (long) duk_get_top(ctx)); 37 | 38 | for (i = 1; i < argc; i++) { 39 | printf("Evaling: %s\n", argv[i]); 40 | if (duk_peval_string(ctx, argv[i]) != 0) { 41 | if (duk_get_prop_string(ctx, -1, "stack")) { 42 | duk_replace(ctx, -2); 43 | } else { 44 | duk_pop(ctx); 45 | } 46 | exitcode = 1; 47 | } 48 | printf("--> %s\n", duk_safe_to_string(ctx, -1)); 49 | duk_pop(ctx); 50 | } 51 | 52 | printf("Done\n"); 53 | duk_destroy_heap(ctx); 54 | return exitcode; 55 | } 56 | -------------------------------------------------------------------------------- /extras/logging/README.rst: -------------------------------------------------------------------------------- 1 | ======================================== 2 | Duktape 1.x compatible logging framework 3 | ======================================== 4 | 5 | The default ``Duktape.Logger`` object and the logging related C API calls 6 | (``duk_log()``, ``duk_log_va()``) were removed in Duktape 2.x because they 7 | depended on stdout/stderr and were thus a portability issue for some targets 8 | (there were also other issues, such as the logging framework not always 9 | matching user expectations). This directory contains Duktape 1.x compatible 10 | ``Duktape.Logger`` object and logging API calls: 11 | 12 | * Add ``duk_logging.c`` to list of C sources to compile. 13 | 14 | * Ensure ``duk_logging.h`` is in the include path. 15 | 16 | * Include the extra header in calling code and initialize the bindings:: 17 | 18 | #include "duktape.h" 19 | #include "duk_logging.h" 20 | 21 | /* After initializing the Duktape heap or when creating a new 22 | * thread with a new global environment: 23 | */ 24 | duk_logging_init(ctx, 0 /*flags*/); 25 | 26 | See ``duk_logging.h`` for available flags. 27 | 28 | * After these steps, ``Duktape.Logger`` will be registered to the ``Duktape`` 29 | object and is ready to use. 30 | 31 | See https://github.com/svaarala/duktape/blob/master/doc/logging.rst and 32 | http://wiki.duktape.org/HowtoLogging.html for more information on the 33 | logging framework design and functionality. 34 | -------------------------------------------------------------------------------- /extras/duk-v1-compat/duk_v1_compat.h: -------------------------------------------------------------------------------- 1 | #if !defined(DUK_V1_COMPAT_INCLUDED) 2 | #define DUK_V1_COMPAT_INCLUDED 3 | 4 | #include "duktape.h" 5 | 6 | #if defined(__cplusplus) 7 | extern "C" { 8 | #endif 9 | 10 | /* Straight flag rename */ 11 | #if !defined(DUK_ENUM_INCLUDE_INTERNAL) 12 | #define DUK_ENUM_INCLUDE_INTERNAL DUK_ENUM_INCLUDE_HIDDEN 13 | #endif 14 | 15 | /* Flags for duk_push_string_file_raw() */ 16 | #define DUK_STRING_PUSH_SAFE (1 << 0) /* no error if file does not exist */ 17 | 18 | extern void duk_dump_context_stdout(duk_context *ctx); 19 | extern void duk_dump_context_stderr(duk_context *ctx); 20 | extern const char *duk_push_string_file_raw(duk_context *ctx, const char *path, duk_uint_t flags); 21 | extern void duk_eval_file(duk_context *ctx, const char *path); 22 | extern void duk_eval_file_noresult(duk_context *ctx, const char *path); 23 | extern duk_int_t duk_peval_file(duk_context *ctx, const char *path); 24 | extern duk_int_t duk_peval_file_noresult(duk_context *ctx, const char *path); 25 | extern void duk_compile_file(duk_context *ctx, duk_uint_t flags, const char *path); 26 | extern duk_int_t duk_pcompile_file(duk_context *ctx, duk_uint_t flags, const char *path); 27 | extern void duk_to_defaultvalue(duk_context *ctx, duk_idx_t idx, duk_int_t hint); 28 | 29 | #define duk_push_string_file(ctx,path) \ 30 | duk_push_string_file_raw((ctx), (path), 0) 31 | 32 | #if defined(__cplusplus) 33 | } 34 | #endif /* end 'extern "C"' wrapper */ 35 | 36 | #endif /* DUK_V1_COMPAT_INCLUDED */ 37 | -------------------------------------------------------------------------------- /extras/alloc-pool/Makefile: -------------------------------------------------------------------------------- 1 | # For manual testing; say 'make' in extras/alloc-pool and run ./test. 2 | 3 | CC = gcc 4 | DEFS = 5 | #DEFS += '-DDUK_ALLOC_POOL_DEBUG' 6 | 7 | .PHONY: test 8 | test: 9 | rm -rf ./prep 10 | echo 'DUK_USE_FATAL_HANDLER:' > opts.yaml 11 | echo ' verbatim: "#define DUK_USE_FATAL_HANDLER(udata,msg) my_fatal((msg))"' >> opts.yaml 12 | python2 ../../tools/configure.py \ 13 | --output-directory ./prep \ 14 | --option-file ./opts.yaml \ 15 | --fixup-line 'extern void my_fatal(const char *msg);' 16 | $(CC) -std=c99 -Wall -Wextra -m32 -Os -otest \ 17 | -I./prep ./prep/duktape.c \ 18 | $(DEFS) \ 19 | duk_alloc_pool.c test.c \ 20 | -lm 21 | ./test 'print("foo", "bar", 1, 2, 3)' 22 | ./test 'alert("foo", "bar", 1, 2, 3)' 23 | 24 | .PHONY: ptrcomptest 25 | ptrcomptest: 26 | rm -rf ./prep 27 | echo 'DUK_USE_FATAL_HANDLER:' > opts.yaml 28 | echo ' verbatim: "#define DUK_USE_FATAL_HANDLER(udata,msg) my_fatal((msg))"' >> opts.yaml 29 | python2 ../../tools/configure.py \ 30 | --output-directory ./prep \ 31 | --option-file ./opts.yaml \ 32 | --fixup-line 'extern void my_fatal(const char *msg);' \ 33 | --option-file ../../config/examples/low_memory.yaml \ 34 | --option-file ptrcomp.yaml \ 35 | --fixup-file ptrcomp_fixup.h 36 | $(CC) -std=c99 -Wall -Wextra -m32 -Os -optrcomptest \ 37 | -I. -I./prep ./prep/duktape.c \ 38 | $(DEFS) \ 39 | duk_alloc_pool.c test.c \ 40 | -lm 41 | ./ptrcomptest 'print("foo", "bar", 1, 2, 3)' 42 | ./ptrcomptest 'alert("foo", "bar", 1, 2, 3)' 43 | -------------------------------------------------------------------------------- /extras/console/README.rst: -------------------------------------------------------------------------------- 1 | ========================= 2 | Minimal 'console' binding 3 | ========================= 4 | 5 | Duktape doesn't provide a ``console`` binding (for example ``console.log``) 6 | by default because it would be a portability issue for some targets. Instead, 7 | an application should provide its own ``console`` binding. This directory 8 | contains an example binding: 9 | 10 | * Add ``duk_console.c`` to list of C sources to compile. 11 | 12 | * Ensure ``duk_console.h`` is in the include path. 13 | 14 | * Include the extra header in calling code and initialize the bindings:: 15 | 16 | #include "duktape.h" 17 | #include "duk_console.h" 18 | 19 | /* After initializing the Duktape heap or when creating a new 20 | * thread with a new global environment: 21 | */ 22 | duk_console_init(ctx, 0 /*flags*/); 23 | 24 | Use the ``DUK_CONSOLE_PROXY_WRAPPER`` to enable a Proxy wrapper for the 25 | console object. The wrapper allows all undefined methods (for example, 26 | ``console.foo``) to be handled as no-ops instead of throwing an error. 27 | See ``duk_console.h`` for full flags list. 28 | 29 | * After these steps, ``console`` will be registered to the global object 30 | and is ready to use. 31 | 32 | * By default the console object will use ``stdout`` for log levels up to 33 | info, and ``stderr`` for warning and above. You can change this by 34 | providing either ``DUK_CONSOLE_STDOUT_ONLY`` or ``DUK_CONSOLE_STDERR_ONLY`` 35 | in ``duk_console_init()`` flags. 36 | -------------------------------------------------------------------------------- /extras/logging/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "duktape.h" 3 | #include "duk_logging.h" 4 | 5 | static duk_ret_t init_logging(duk_context *ctx, void *udata) { 6 | (void) udata; 7 | duk_logging_init(ctx, 0 /*flags*/); 8 | printf("top after init: %ld\n", (long) duk_get_top(ctx)); 9 | 10 | /* C API test */ 11 | duk_eval_string_noresult(ctx, "Duktape.Logger.clog.l = 0;"); 12 | duk_log(ctx, DUK_LOG_TRACE, "c logger test: %d", 123); 13 | duk_log(ctx, DUK_LOG_DEBUG, "c logger test: %d", 123); 14 | duk_log(ctx, DUK_LOG_INFO, "c logger test: %d", 123); 15 | duk_log(ctx, DUK_LOG_WARN, "c logger test: %d", 123); 16 | duk_log(ctx, DUK_LOG_ERROR, "c logger test: %d", 123); 17 | duk_log(ctx, DUK_LOG_FATAL, "c logger test: %d", 123); 18 | duk_log(ctx, -1, "negative level: %s %d 0x%08lx", "arg string", -123, 0xdeadbeefUL); 19 | duk_log(ctx, 6, "level too large: %s %d 0x%08lx", "arg string", 123, 0x1234abcdUL); 20 | 21 | return 0; 22 | } 23 | 24 | int main(int argc, char *argv[]) { 25 | duk_context *ctx; 26 | int i; 27 | int exitcode = 0; 28 | 29 | ctx = duk_create_heap_default(); 30 | if (!ctx) { 31 | return 1; 32 | } 33 | 34 | (void) duk_safe_call(ctx, init_logging, NULL, 0, 1); 35 | printf("logging init: %s\n", duk_safe_to_string(ctx, -1)); 36 | duk_pop(ctx); 37 | 38 | for (i = 1; i < argc; i++) { 39 | printf("Evaling: %s\n", argv[i]); 40 | duk_push_string(ctx, argv[i]); 41 | duk_push_string(ctx, "evalCodeFileName"); /* for automatic logger name testing */ 42 | if (duk_pcompile(ctx, DUK_COMPILE_EVAL) != 0) { 43 | exitcode = 1; 44 | } else { 45 | if (duk_pcall(ctx, 0) != 0) { 46 | exitcode = 1; 47 | } 48 | } 49 | printf("--> %s\n", duk_safe_to_string(ctx, -1)); 50 | duk_pop(ctx); 51 | } 52 | 53 | printf("Done\n"); 54 | duk_destroy_heap(ctx); 55 | return exitcode; 56 | } 57 | -------------------------------------------------------------------------------- /extras/alloc-pool/README.rst: -------------------------------------------------------------------------------- 1 | ===================================== 2 | Pool allocator for low memory targets 3 | ===================================== 4 | 5 | A simple pool allocator which satisfies allocations from preallocated pools 6 | containing blocks of a certain size. The caller provides a continuous memory 7 | region and a pool configuration when initializing the allocator. 8 | 9 | The pool configuration specifies the block sizes used, and parameters to 10 | control how many entries are allocated for each block size. The parameters 11 | are specified with respect to an arbitrary floating point scaling parameter 12 | ``t`` as follows:: 13 | 14 | bytes = A*t + B 15 | count = floor(bytes / block_size) 16 | = floor((A*t + B) / block_size) 17 | 18 | A: constant which indicates how quickly more bytes are assigned for this 19 | block size as the total allocation grows 20 | 21 | B: constant which indicates the base allocation for this block size, i.e. 22 | the allocated needed by Duktape initialization 23 | 24 | Pool initialization finds the largest floating point ``t`` which still fits in 25 | the memory region provided. Any leftover bytes are sprinkled to the pools to 26 | minimize wasted space. 27 | 28 | A pool configuration can be written manually (by trial and error) or using 29 | some automatic tooling such as ``pool_simulator.py``. 30 | 31 | When using pointer compression only a single global pool is supported. This 32 | reduces code footprint and is usually sufficient in low memory targets. 33 | 34 | Pointer compression functions are defined as inline functions in 35 | ``duk_alloc_pool.h`` to allow the compiler to inline pointer compression when 36 | appropriate. As a side effect ``duk_config.h`` must include 37 | ``duk_alloc_pool.h`` so that the declarations are visible when compiling 38 | Duktape. 39 | -------------------------------------------------------------------------------- /extras/module-node/Makefile: -------------------------------------------------------------------------------- 1 | # For manual testing; say 'make' in extras/module-node and run ./test. 2 | 3 | CC = gcc 4 | 5 | .PHONY: test 6 | test: 7 | -rm -rf ./prep 8 | python2 ../../tools/configure.py --quiet --output-directory ./prep 9 | $(CC) -std=c99 -Wall -Wextra -o $@ -I./prep -I. ./prep/duktape.c duk_module_node.c test.c -lm 10 | @printf '\n' 11 | ./test 'assert(typeof require("pig") === "string", "basic require()");' 12 | ./test 'assert(require("cow").indexOf("pig") !== -1, "nested require()");' 13 | ./test 'var ape1 = require("ape"); var ape2 = require("ape"); assert(ape1 === ape2, "caching");' 14 | ./test 'var ape1 = require("ape"); var inCache = "ape.js" in require.cache; delete require.cache["ape.js"]; var ape2 = require("ape"); assert(inCache && ape2 !== ape1, "require.cache");' 15 | ./test 'var ape = require("ape"); assert(typeof ape.module.require === "function", "module.require()");' 16 | ./test 'var ape = require("ape"); assert(ape.module.exports === ape, "module.exports");' 17 | ./test 'var ape = require("ape"); assert(ape.module.id === "ape.js" && ape.module.id === ape.module.filename, "module.id");' 18 | ./test 'var ape = require("ape"); assert(ape.module.filename === "ape.js", "module.filename");' 19 | ./test 'var ape = require("ape"); assert(ape.module.loaded === true && ape.wasLoaded === false, "module.loaded");' 20 | ./test 'var ape = require("ape"); assert(ape.__filename === "ape.js", "__filename");' 21 | ./test 'var badger = require("badger"); assert(badger.foo === 123 && badger.bar === 234, "exports.foo assignment");' 22 | ./test 'var comment = require("comment"); assert(comment.foo === 123 && comment.bar === 234, "last line with // comment");' 23 | ./test 'var shebang = require("shebang"); assert(shebang.foo === 123 && shebang.bar === 234, "shebang");' 24 | 25 | .PHONY: clean 26 | clean: 27 | -rm -rf ./prep 28 | -rm -f test 29 | -------------------------------------------------------------------------------- /extras/module-node/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "duktape.h" 4 | #include "duk_module_node.h" 5 | 6 | static duk_ret_t cb_resolve_module(duk_context *ctx) { 7 | const char *module_id; 8 | const char *parent_id; 9 | 10 | module_id = duk_require_string(ctx, 0); 11 | parent_id = duk_require_string(ctx, 1); 12 | 13 | duk_push_sprintf(ctx, "%s.js", module_id); 14 | printf("resolve_cb: id:'%s', parent-id:'%s', resolve-to:'%s'\n", 15 | module_id, parent_id, duk_get_string(ctx, -1)); 16 | 17 | return 1; 18 | } 19 | 20 | static duk_ret_t cb_load_module(duk_context *ctx) { 21 | const char *filename; 22 | const char *module_id; 23 | 24 | module_id = duk_require_string(ctx, 0); 25 | duk_get_prop_string(ctx, 2, "filename"); 26 | filename = duk_require_string(ctx, -1); 27 | 28 | printf("load_cb: id:'%s', filename:'%s'\n", module_id, filename); 29 | 30 | if (strcmp(module_id, "pig.js") == 0) { 31 | duk_push_sprintf(ctx, "module.exports = 'you\\'re about to get eaten by %s';", 32 | module_id); 33 | } else if (strcmp(module_id, "cow.js") == 0) { 34 | duk_push_string(ctx, "module.exports = require('pig');"); 35 | } else if (strcmp(module_id, "ape.js") == 0) { 36 | duk_push_string(ctx, "module.exports = { module: module, __filename: __filename, wasLoaded: module.loaded };"); 37 | } else if (strcmp(module_id, "badger.js") == 0) { 38 | duk_push_string(ctx, "exports.foo = 123; exports.bar = 234;"); 39 | } else if (strcmp(module_id, "comment.js") == 0) { 40 | duk_push_string(ctx, "exports.foo = 123; exports.bar = 234; // comment"); 41 | } else if (strcmp(module_id, "shebang.js") == 0) { 42 | duk_push_string(ctx, "#!ignored\nexports.foo = 123; exports.bar = 234;"); 43 | } else { 44 | (void) duk_type_error(ctx, "cannot find module: %s", module_id); 45 | } 46 | 47 | return 1; 48 | } 49 | 50 | static duk_ret_t handle_print(duk_context *ctx) { 51 | printf("%s\n", duk_safe_to_string(ctx, 0)); 52 | return 0; 53 | } 54 | 55 | static duk_ret_t handle_assert(duk_context *ctx) { 56 | if (duk_to_boolean(ctx, 0)) { 57 | return 0; 58 | } 59 | (void) duk_generic_error(ctx, "assertion failed: %s", duk_safe_to_string(ctx, 1)); 60 | return 0; 61 | } 62 | 63 | int main(int argc, char *argv[]) { 64 | duk_context *ctx; 65 | int i; 66 | int exitcode = 0; 67 | 68 | ctx = duk_create_heap_default(); 69 | if (!ctx) { 70 | return 1; 71 | } 72 | 73 | duk_push_c_function(ctx, handle_print, 1); 74 | duk_put_global_string(ctx, "print"); 75 | duk_push_c_function(ctx, handle_assert, 2); 76 | duk_put_global_string(ctx, "assert"); 77 | 78 | duk_push_object(ctx); 79 | duk_push_c_function(ctx, cb_resolve_module, DUK_VARARGS); 80 | duk_put_prop_string(ctx, -2, "resolve"); 81 | duk_push_c_function(ctx, cb_load_module, DUK_VARARGS); 82 | duk_put_prop_string(ctx, -2, "load"); 83 | duk_module_node_init(ctx); 84 | printf("top after init: %ld\n", (long) duk_get_top(ctx)); 85 | 86 | for (i = 1; i < argc; i++) { 87 | printf("Evaling: %s\n", argv[i]); 88 | if (duk_peval_string(ctx, argv[i]) != 0) { 89 | if (duk_get_prop_string(ctx, -1, "stack")) { 90 | duk_replace(ctx, -2); 91 | } else { 92 | duk_pop(ctx); 93 | } 94 | exitcode = 1; 95 | } 96 | printf("--> %s\n", duk_safe_to_string(ctx, -1)); 97 | duk_pop(ctx); 98 | } 99 | 100 | printf("Done\n"); 101 | duk_destroy_heap(ctx); 102 | return exitcode; 103 | } 104 | -------------------------------------------------------------------------------- /extras/alloc-pool/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "duktape.h" 4 | #include "duk_alloc_pool.h" 5 | 6 | void my_fatal(const char *msg) { 7 | fprintf(stderr, "*** FATAL: %s\n", msg ? msg : "no message"); 8 | fflush(stderr); 9 | abort(); 10 | } 11 | 12 | static duk_ret_t my_print(duk_context *ctx) { 13 | duk_push_string(ctx, " "); 14 | duk_insert(ctx, 0); 15 | duk_join(ctx, duk_get_top(ctx) - 1); 16 | printf("%s\n", duk_safe_to_string(ctx, -1)); 17 | return 1; 18 | } 19 | 20 | static void dump_pool_state(duk_pool_global *g) { 21 | int i; 22 | long total_size = 0; 23 | long total_used = 0; 24 | 25 | for (i = 0; i < g->num_pools; i++) { 26 | duk_pool_state *st = g->states + i; 27 | int free, used; 28 | duk_pool_free *f; 29 | 30 | for (free = 0, f = st->first; f; f = f->next) { 31 | free++; 32 | } 33 | used = st->count - free; 34 | printf("Pool %2d: block size %5d, count %4d/%4d, bytes %6d/%6d\n", 35 | i, (int) st->size, used, (int) st->count, 36 | (int) st->size * used, (int) st->size * (int) st->count); 37 | 38 | total_size += (long) st->size * (long) st->count; 39 | total_used += (long) st->size * (long) used; 40 | } 41 | printf("=== Total: %ld/%ld, free %ld\n", 42 | (long) total_used, (long) total_size, (long) (total_size - total_used)); 43 | } 44 | 45 | int main(int argc, char *argv[]) { 46 | duk_context *ctx; 47 | int i; 48 | int exitcode = 0; 49 | 50 | /* NOTE! This pool configuration is NOT a good pool configuration 51 | * for practical use (and is not intended to be one). A production 52 | * pool configuration should be created using measurements. 53 | */ 54 | const duk_pool_config pool_configs[15] = { 55 | { 16, 20, 200 }, 56 | { 20, 40, 100 }, 57 | { 24, 40, 100 }, 58 | { 32, 60, 50 }, 59 | { 40, 60, 50 }, 60 | { 48, 60, 50 }, 61 | { 56, 60, 50 }, 62 | { 64, 60, 50 }, 63 | { 80, 60, 50 }, 64 | { 256, 100, 10 }, 65 | { 1024, 20, 2 }, 66 | { 2048, 20, 2 }, 67 | { 4096, 100, 2 }, 68 | { 6144, 60, 2 }, 69 | { 8192, 100, 2 }, 70 | }; 71 | duk_pool_state pool_states[15]; /* Count must match pool_configs[]. */ 72 | duk_pool_global pool_global; 73 | 74 | char buffer[200000]; 75 | void *pool_udata; 76 | 77 | pool_udata = duk_alloc_pool_init(buffer, sizeof(buffer), pool_configs, pool_states, sizeof(pool_configs) / sizeof(duk_pool_config), &pool_global); 78 | if (!pool_udata) { 79 | return 1; 80 | } 81 | 82 | printf("Pool after pool init:\n"); 83 | dump_pool_state(&pool_global); 84 | 85 | ctx = duk_create_heap(duk_alloc_pool, duk_realloc_pool, duk_free_pool, pool_udata, NULL); 86 | if (!ctx) { 87 | return 1; 88 | } 89 | 90 | printf("Pool after Duktape heap creation:\n"); 91 | dump_pool_state(&pool_global); 92 | 93 | duk_push_c_function(ctx, my_print, DUK_VARARGS); 94 | duk_put_global_string(ctx, "print"); 95 | duk_push_c_function(ctx, my_print, DUK_VARARGS); 96 | duk_put_global_string(ctx, "alert"); 97 | printf("top after init: %ld\n", (long) duk_get_top(ctx)); 98 | 99 | for (i = 1; i < argc; i++) { 100 | printf("Evaling: %s\n", argv[i]); 101 | if (duk_peval_string(ctx, argv[i]) != 0) { 102 | exitcode = 1; 103 | } 104 | printf("--> %s\n", duk_safe_to_string(ctx, -1)); 105 | duk_pop(ctx); 106 | } 107 | 108 | printf("Pool after evaling code:\n"); 109 | dump_pool_state(&pool_global); 110 | 111 | printf("Done\n"); 112 | duk_destroy_heap(ctx); 113 | return exitcode; 114 | } 115 | -------------------------------------------------------------------------------- /extras/duk-v1-compat/duk_v1_compat.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "duktape.h" 3 | #include "duk_v1_compat.h" 4 | 5 | /* 6 | * duk_dump_context_{stdout,stderr}() 7 | */ 8 | 9 | void duk_dump_context_stdout(duk_context *ctx) { 10 | duk_push_context_dump(ctx); 11 | fprintf(stdout, "%s\n", duk_safe_to_string(ctx, -1)); 12 | duk_pop(ctx); 13 | } 14 | 15 | void duk_dump_context_stderr(duk_context *ctx) { 16 | duk_push_context_dump(ctx); 17 | fprintf(stderr, "%s\n", duk_safe_to_string(ctx, -1)); 18 | duk_pop(ctx); 19 | } 20 | 21 | /* 22 | * duk_push_string_file() and duk_push_string_file_raw() 23 | */ 24 | 25 | const char *duk_push_string_file_raw(duk_context *ctx, const char *path, duk_uint_t flags) { 26 | FILE *f = NULL; 27 | char *buf; 28 | long sz; /* ANSI C typing */ 29 | 30 | if (!path) { 31 | goto fail; 32 | } 33 | f = fopen(path, "rb"); 34 | if (!f) { 35 | goto fail; 36 | } 37 | if (fseek(f, 0, SEEK_END) < 0) { 38 | goto fail; 39 | } 40 | sz = ftell(f); 41 | if (sz < 0) { 42 | goto fail; 43 | } 44 | if (fseek(f, 0, SEEK_SET) < 0) { 45 | goto fail; 46 | } 47 | buf = (char *) duk_push_fixed_buffer(ctx, (duk_size_t) sz); 48 | if ((size_t) fread(buf, 1, (size_t) sz, f) != (size_t) sz) { 49 | duk_pop(ctx); 50 | goto fail; 51 | } 52 | (void) fclose(f); /* ignore fclose() error */ 53 | return duk_buffer_to_string(ctx, -1); 54 | 55 | fail: 56 | if (f) { 57 | (void) fclose(f); /* ignore fclose() error */ 58 | } 59 | 60 | if (flags & DUK_STRING_PUSH_SAFE) { 61 | duk_push_undefined(ctx); 62 | } else { 63 | (void) duk_type_error(ctx, "read file error"); 64 | } 65 | return NULL; 66 | } 67 | 68 | /* 69 | * duk_eval_file(), duk_compile_file(), and their variants 70 | */ 71 | 72 | void duk_eval_file(duk_context *ctx, const char *path) { 73 | duk_push_string_file_raw(ctx, path, 0); 74 | duk_push_string(ctx, path); 75 | duk_compile(ctx, DUK_COMPILE_EVAL); 76 | duk_push_global_object(ctx); /* 'this' binding */ 77 | duk_call_method(ctx, 0); 78 | } 79 | 80 | void duk_eval_file_noresult(duk_context *ctx, const char *path) { 81 | duk_eval_file(ctx, path); 82 | duk_pop(ctx); 83 | } 84 | 85 | duk_int_t duk_peval_file(duk_context *ctx, const char *path) { 86 | duk_int_t rc; 87 | 88 | duk_push_string_file_raw(ctx, path, DUK_STRING_PUSH_SAFE); 89 | duk_push_string(ctx, path); 90 | rc = duk_pcompile(ctx, DUK_COMPILE_EVAL); 91 | if (rc != 0) { 92 | return rc; 93 | } 94 | duk_push_global_object(ctx); /* 'this' binding */ 95 | rc = duk_pcall_method(ctx, 0); 96 | return rc; 97 | } 98 | 99 | duk_int_t duk_peval_file_noresult(duk_context *ctx, const char *path) { 100 | duk_int_t rc; 101 | 102 | rc = duk_peval_file(ctx, path); 103 | duk_pop(ctx); 104 | return rc; 105 | } 106 | 107 | void duk_compile_file(duk_context *ctx, duk_uint_t flags, const char *path) { 108 | duk_push_string_file_raw(ctx, path, 0); 109 | duk_push_string(ctx, path); 110 | duk_compile(ctx, flags); 111 | } 112 | 113 | duk_int_t duk_pcompile_file(duk_context *ctx, duk_uint_t flags, const char *path) { 114 | duk_int_t rc; 115 | 116 | duk_push_string_file_raw(ctx, path, DUK_STRING_PUSH_SAFE); 117 | duk_push_string(ctx, path); 118 | rc = duk_pcompile(ctx, flags); 119 | return rc; 120 | } 121 | 122 | /* 123 | * duk_to_defaultvalue() 124 | */ 125 | 126 | void duk_to_defaultvalue(duk_context *ctx, duk_idx_t idx, duk_int_t hint) { 127 | duk_require_type_mask(ctx, idx, DUK_TYPE_MASK_OBJECT | 128 | DUK_TYPE_MASK_BUFFER | 129 | DUK_TYPE_MASK_LIGHTFUNC); 130 | duk_to_primitive(ctx, idx, hint); 131 | } 132 | -------------------------------------------------------------------------------- /extras/module-node/README.rst: -------------------------------------------------------------------------------- 1 | ===================================== 2 | Node.js-like module loading framework 3 | ===================================== 4 | 5 | This directory contains an example module resolution and loading framework and 6 | ``require()`` implementation based on the Node.js module system: 7 | 8 | * https://nodejs.org/api/modules.html 9 | 10 | The application needs only to provide the module resolution and loading logic: 11 | 12 | * Add ``duk_module_node.c`` to list of C sources to compile. 13 | 14 | * Ensure ``duk_module_node.h`` is in the include path. 15 | 16 | * Include the extra header in calling code and initialize the bindings:: 17 | 18 | #include "duktape.h" 19 | #include "duk_module_node.h" 20 | 21 | /* After initializing the Duktape heap or when creating a new 22 | * thread with a new global environment: 23 | */ 24 | duk_push_object(ctx); 25 | duk_push_c_function(ctx, cb_resolve_module, DUK_VARARGS); 26 | duk_put_prop_string(ctx, -2, "resolve"); 27 | duk_push_c_function(ctx, cb_load_module, DUK_VARARGS); 28 | duk_put_prop_string(ctx, -2, "load"); 29 | duk_module_node_init(ctx); 30 | 31 | Do not call ``duk_module_node_init()`` more than once for the same global 32 | environment. Doing so is undefined behavior and may put the module system 33 | in an inconsistent state. 34 | 35 | It is possible to replace the callbacks after initialization by setting the 36 | following internal properties on the global stash: 37 | 38 | - ``\xffmodResolve`` 39 | 40 | - ``\xffmodLoad`` 41 | 42 | * The resolve callback is a Duktape/C function which takes the string passed 43 | to ``require()`` and resolves it to a canonical module ID (for Node.js this 44 | is usually the fully resolved filename of the module):: 45 | 46 | duk_ret_t cb_resolve_module(duk_context *ctx) { 47 | /* 48 | * Entry stack: [ requested_id parent_id ] 49 | */ 50 | 51 | const char *requested_id = duk_get_string(ctx, 0); 52 | const char *parent_id = duk_get_string(ctx, 1); /* calling module */ 53 | const char *resolved_id; 54 | 55 | /* Arrive at the canonical module ID somehow. */ 56 | 57 | duk_push_string(ctx, resolved_id); 58 | return 1; /*nrets*/ 59 | } 60 | 61 | If the module ID cannot be resolved, the resolve callback should throw an 62 | error, which will propagate out of the ``require()`` call. Note also that 63 | when the global ``require()`` is called, the parent ID is an empty string. 64 | 65 | * The load callback is a Duktape/C function which takes the resolved module ID 66 | and: (1) returns the ECMAScript source code for the module or ``undefined`` 67 | if there's no source code, e.g. for pure C modules, (2) can populate 68 | ``module.exports`` itself, and (3) can replace ``module.exports``:: 69 | 70 | duk_ret_t cb_load_module(duk_context *ctx) { 71 | /* 72 | * Entry stack: [ resolved_id exports module ] 73 | */ 74 | 75 | /* Arrive at the JS source code for the module somehow. */ 76 | 77 | duk_push_string(ctx, module_source); 78 | return 1; /*nrets*/ 79 | } 80 | 81 | As with the resolve callback, the load callback should throw an error if the 82 | module cannot be loaded for any reason. 83 | 84 | * After these steps, ``require()`` will be registered to the global object and 85 | the module system is ready to use. 86 | 87 | * The main module (file being evaluated) should be loaded using 88 | ``duk_module_node_peval_main()``. This function registers the module in 89 | ``require.main`` and thus should only be called once. 90 | -------------------------------------------------------------------------------- /extras/minimal-printf/README.rst: -------------------------------------------------------------------------------- 1 | ============================================== 2 | Minimal sprintf/sscanf replacement for Duktape 3 | ============================================== 4 | 5 | The ``duk_minimal_printf.c`` provides a portable provider for sprintf()/scanf() 6 | with a feature set matching minimally what Duktape internals need. The provider 7 | compiles to less than 1kB. The functions provided are:: 8 | 9 | sprintf() 10 | snprintf() 11 | vsnprintf() 12 | sscanf() 13 | 14 | In addition to Duktape internals, the set of supported formats affects the 15 | ``duk_push_(v)sprintf()`` call which may have an impact on user code. 16 | 17 | Assumptions: 18 | 19 | * ``sizeof(void *) <= sizeof(long)`` 20 | 21 | * ``sizeof(long) <= 8`` 22 | 23 | Note that these assumptions don't hold e.g. on 64-bit Windows. This printf 24 | provider is mostly useful for low memory targets where these assumptions are 25 | typically not an issue. The limitations are easy to fix if one relies more 26 | on platform typing. 27 | 28 | Supported formatting 29 | ==================== 30 | 31 | sprintf() 32 | --------- 33 | 34 | Duktape relies on a ``sprintf()`` provider which supports at least the 35 | following (this list is from Duktape 1.5.0):: 36 | 37 | %c 38 | %s 39 | %p 40 | 41 | %02d 42 | %03d 43 | %ld 44 | %lld (JSON fast path only) 45 | 46 | %lu 47 | 48 | %lx 49 | %02lx 50 | %08lx 51 | 52 | This minimal provider supports the following slightly different, wider set. 53 | 54 | * Character format ``%c``. 55 | 56 | * String format ``%s``. 57 | 58 | * Pointer format ``%p``. 59 | 60 | * Integer formats ``%d``, ``%ld``, ``%lu`` with optional padding and 61 | length modifiers. 62 | 63 | * Hex formats ``%x``, ``%lx`` with optional padding and length modifiers. 64 | 65 | The wider set is useful to make ``duk_push_(v)sprintf()`` behave reasonably 66 | for any user code call sites. 67 | 68 | The ``%lld`` format is not supported to avoid depending on the ``long long`` 69 | type; this makes the replacement incompatible with the JSON fast path which 70 | must thus be disabled. 71 | 72 | sscanf() 73 | -------- 74 | 75 | There's only one call site for ``sscanf()``, for JX parsing of pointers:: 76 | 77 | duk_bi_json.c: (void) DUK_SSCANF((const char *) js_ctx->p, DUK_STR_FMT_PTR, &voidptr); 78 | 79 | The exact format string here is ``%p`` and nothing else needs to be supported. 80 | Further, when the minimal printf/scanf providers are used together we only 81 | need to parse what we produce. In particular: 82 | 83 | * Pointer prefix is ``0x``, no need to match ``0X`` for example. 84 | 85 | * All digits are ``[0-9a-f]`` with no need to match uppercase. 86 | 87 | Building "duk" with minimal printf/scanf 88 | ======================================== 89 | 90 | The necessary defines in ``duk_config.h`` can be given to genconfig, but you 91 | can also just make the following manual additions to the bottom of the config 92 | file:: 93 | 94 | #include "duk_minimal_printf.h" 95 | 96 | #undef DUK_SPRINTF 97 | #define DUK_SPRINTF duk_minimal_sprintf 98 | #undef DUK_SNPRINTF 99 | #define DUK_SNPRINTF duk_minimal_snprintf 100 | #undef DUK_VSNPRINTF 101 | #define DUK_VSNPRINTF duk_minimal_vsnprintf 102 | #undef DUK_SSCANF 103 | #define DUK_SSCANF duk_minimal_sscanf 104 | 105 | Then just add ``duk_minimal_printf.c`` to build and compile the application. 106 | 107 | Future work 108 | =========== 109 | 110 | * Add support for ``%lld`` (maybe conditional) to allow JSON fast path to 111 | be supported. 112 | 113 | * Add support for platforms such as 64-bit Windows where 114 | ``sizeof(long) < sizeof(void *)``. This can be achieved by using a few 115 | typedefs internally; typedef an integer type large enough to hold all 116 | formatted types. 117 | -------------------------------------------------------------------------------- /extras/duk-v1-compat/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "duktape.h" 3 | #include "duk_v1_compat.h" 4 | 5 | static duk_ret_t my_print(duk_context *ctx) { 6 | duk_push_string(ctx, " "); 7 | duk_insert(ctx, 0); 8 | duk_join(ctx, duk_get_top(ctx) - 1); 9 | printf("%s\n", duk_to_string(ctx, -1)); 10 | fflush(stdout); 11 | return 0; 12 | } 13 | 14 | int main(int argc, char *argv[]) { 15 | duk_context *ctx; 16 | int i; 17 | duk_int_t rc; 18 | 19 | ctx = duk_create_heap_default(); 20 | if (!ctx) { 21 | return 1; 22 | } 23 | 24 | /* Minimal print() provider. */ 25 | duk_push_c_function(ctx, my_print, DUK_VARARGS); 26 | duk_put_global_string(ctx, "print"); 27 | 28 | printf("top after init: %ld\n", (long) duk_get_top(ctx)); 29 | 30 | for (i = 1; i < argc; i++) { 31 | printf("Evaling: %s\n", argv[i]); 32 | (void) duk_peval_string(ctx, argv[i]); 33 | printf("--> %s\n", duk_safe_to_string(ctx, -1)); 34 | duk_pop(ctx); 35 | } 36 | 37 | /* Test duk_dump_context_{stdout,stderr}() */ 38 | 39 | duk_push_string(ctx, "dump to stdout"); 40 | duk_dump_context_stdout(ctx); 41 | duk_pop(ctx); 42 | 43 | duk_push_string(ctx, "dump to stderr"); 44 | duk_dump_context_stderr(ctx); 45 | duk_pop(ctx); 46 | 47 | /* Test duk_eval_file() and related. */ 48 | 49 | printf("top before duk_eval_file(): %ld\n", (long) duk_get_top(ctx)); 50 | duk_eval_file(ctx, "test_eval1.js"); 51 | printf("top after duk_eval_file(): %ld\n", (long) duk_get_top(ctx)); 52 | printf(" --> %s\n", duk_safe_to_string(ctx, -1)); 53 | duk_pop(ctx); 54 | 55 | printf("top before duk_eval_file_noresult(): %ld\n", (long) duk_get_top(ctx)); 56 | duk_eval_file_noresult(ctx, "test_eval1.js"); 57 | printf("top after duk_eval_file_noresult(): %ld\n", (long) duk_get_top(ctx)); 58 | 59 | printf("top before duk_peval_file(): %ld\n", (long) duk_get_top(ctx)); 60 | rc = duk_peval_file(ctx, "test_eval1.js"); 61 | printf("top after duk_peval_file(): %ld\n", (long) duk_get_top(ctx)); 62 | printf(" --> %ld, %s\n", (long) rc, duk_safe_to_string(ctx, -1)); 63 | duk_pop(ctx); 64 | 65 | printf("top before duk_peval_file(): %ld\n", (long) duk_get_top(ctx)); 66 | rc = duk_peval_file(ctx, "test_eval2.js"); 67 | printf("top after duk_peval_file(): %ld\n", (long) duk_get_top(ctx)); 68 | printf(" --> %ld, %s\n", (long) rc, duk_safe_to_string(ctx, -1)); 69 | duk_pop(ctx); 70 | 71 | printf("top before duk_peval_file_noresult(): %ld\n", (long) duk_get_top(ctx)); 72 | rc = duk_peval_file_noresult(ctx, "test_eval1.js"); 73 | printf("top after duk_peval_file_noresult(): %ld\n", (long) duk_get_top(ctx)); 74 | printf(" --> %ld\n", (long) rc); 75 | 76 | printf("top before duk_peval_file_noresult(): %ld\n", (long) duk_get_top(ctx)); 77 | rc = duk_peval_file_noresult(ctx, "test_eval2.js"); 78 | printf("top after duk_peval_file_noresult(): %ld\n", (long) duk_get_top(ctx)); 79 | printf(" --> %ld\n", (long) rc); 80 | 81 | /* Test duk_compile_file() and related. */ 82 | 83 | printf("top before duk_compile_file(): %ld\n", (long) duk_get_top(ctx)); 84 | duk_compile_file(ctx, 0, "test_compile1.js"); 85 | printf("top after duk_compile_file(): %ld\n", (long) duk_get_top(ctx)); 86 | duk_call(ctx, 0); 87 | duk_pop(ctx); 88 | 89 | printf("top before duk_pcompile_file(): %ld\n", (long) duk_get_top(ctx)); 90 | rc = duk_pcompile_file(ctx, 0, "test_compile1.js"); 91 | printf("top after duk_pcompile_file(): %ld\n", (long) duk_get_top(ctx)); 92 | printf(" --> %ld: %s\n", (long) rc, duk_safe_to_string(ctx, -1)); 93 | duk_pop(ctx); 94 | 95 | printf("top before duk_pcompile_file(): %ld\n", (long) duk_get_top(ctx)); 96 | rc = duk_pcompile_file(ctx, 0, "test_compile2.js"); 97 | printf("top after duk_pcompile_file(): %ld\n", (long) duk_get_top(ctx)); 98 | printf(" --> %ld: %s\n", (long) rc, duk_safe_to_string(ctx, -1)); 99 | duk_pop(ctx); 100 | 101 | printf("Done\n"); 102 | duk_destroy_heap(ctx); 103 | return 0; 104 | } 105 | -------------------------------------------------------------------------------- /extras/print-alert/duk_print_alert.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Duktape 1.x compatible print() and alert() bindings. 3 | */ 4 | 5 | #include 6 | #include 7 | #include "duktape.h" 8 | #include "duk_print_alert.h" 9 | 10 | #define DUK_PRINT_ALERT_FLUSH /* Flush after stdout/stderr write (Duktape 1.x: yes) */ 11 | #undef DUK_PRINT_ALERT_SMALL /* Prefer smaller footprint (but slower and more memory churn) */ 12 | 13 | #if defined(DUK_PRINT_ALERT_SMALL) 14 | static duk_ret_t duk__print_alert_helper(duk_context *ctx, FILE *fh) { 15 | duk_idx_t nargs; 16 | 17 | nargs = duk_get_top(ctx); 18 | 19 | /* If argument count is 1 and first argument is a buffer, write the buffer 20 | * as raw data into the file without a newline; this allows exact control 21 | * over stdout/stderr without an additional entrypoint (useful for now). 22 | * Otherwise current print/alert semantics are to ToString() coerce 23 | * arguments, join them with a single space, and append a newline. 24 | */ 25 | 26 | if (nargs == 1 && duk_is_buffer_data(ctx, 0)) { 27 | buf = (const duk_uint8_t *) duk_get_buffer_data(ctx, 0, &sz_buf); 28 | fwrite((const void *) buf, 1, (size_t) sz_buf, fh); 29 | } else { 30 | duk_push_string(ctx, " "); 31 | duk_insert(ctx, 0); 32 | duk_concat(ctx, nargs); 33 | fprintf(fh, "%s\n", duk_require_string(ctx, -1)); 34 | } 35 | 36 | #if defined(DUK_PRINT_ALERT_FLUSH) 37 | fflush(fh); 38 | #endif 39 | 40 | return 0; 41 | } 42 | #else 43 | /* Faster, less churn, higher footprint option. */ 44 | static duk_ret_t duk__print_alert_helper(duk_context *ctx, FILE *fh) { 45 | duk_idx_t nargs; 46 | const duk_uint8_t *buf; 47 | duk_size_t sz_buf; 48 | const char nl = '\n'; 49 | duk_uint8_t buf_stack[256]; 50 | 51 | nargs = duk_get_top(ctx); 52 | 53 | /* If argument count is 1 and first argument is a buffer, write the buffer 54 | * as raw data into the file without a newline; this allows exact control 55 | * over stdout/stderr without an additional entrypoint (useful for now). 56 | * Otherwise current print/alert semantics are to ToString() coerce 57 | * arguments, join them with a single space, and append a newline. 58 | */ 59 | 60 | if (nargs == 1 && duk_is_buffer_data(ctx, 0)) { 61 | buf = (const duk_uint8_t *) duk_get_buffer_data(ctx, 0, &sz_buf); 62 | } else if (nargs > 0) { 63 | duk_idx_t i; 64 | duk_size_t sz_str; 65 | const duk_uint8_t *p_str; 66 | duk_uint8_t *p; 67 | 68 | sz_buf = (duk_size_t) nargs; /* spaces (nargs - 1) + newline */ 69 | for (i = 0; i < nargs; i++) { 70 | (void) duk_to_lstring(ctx, i, &sz_str); 71 | sz_buf += sz_str; 72 | } 73 | 74 | if (sz_buf <= sizeof(buf_stack)) { 75 | p = (duk_uint8_t *) buf_stack; 76 | } else { 77 | p = (duk_uint8_t *) duk_push_fixed_buffer(ctx, sz_buf); 78 | } 79 | 80 | buf = (const duk_uint8_t *) p; 81 | for (i = 0; i < nargs; i++) { 82 | p_str = (const duk_uint8_t *) duk_get_lstring(ctx, i, &sz_str); 83 | memcpy((void *) p, (const void *) p_str, sz_str); 84 | p += sz_str; 85 | *p++ = (duk_uint8_t) (i == nargs - 1 ? '\n' : ' '); 86 | } 87 | } else { 88 | buf = (const duk_uint8_t *) &nl; 89 | sz_buf = 1; 90 | } 91 | 92 | /* 'buf' contains the string to write, 'sz_buf' contains the length 93 | * (which may be zero). 94 | */ 95 | 96 | if (sz_buf > 0) { 97 | fwrite((const void *) buf, 1, (size_t) sz_buf, fh); 98 | #if defined(DUK_PRINT_ALERT_FLUSH) 99 | fflush(fh); 100 | #endif 101 | } 102 | 103 | return 0; 104 | } 105 | #endif 106 | 107 | static duk_ret_t duk__print(duk_context *ctx) { 108 | return duk__print_alert_helper(ctx, stdout); 109 | } 110 | 111 | static duk_ret_t duk__alert(duk_context *ctx) { 112 | return duk__print_alert_helper(ctx, stderr); 113 | } 114 | 115 | void duk_print_alert_init(duk_context *ctx, duk_uint_t flags) { 116 | (void) flags; /* unused at the moment */ 117 | 118 | /* XXX: use duk_def_prop_list(). */ 119 | duk_push_global_object(ctx); 120 | duk_push_string(ctx, "print"); 121 | duk_push_c_function(ctx, duk__print, DUK_VARARGS); 122 | duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_WRITABLE | DUK_DEFPROP_SET_CONFIGURABLE); 123 | duk_push_string(ctx, "alert"); 124 | duk_push_c_function(ctx, duk__alert, DUK_VARARGS); 125 | duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_WRITABLE | DUK_DEFPROP_SET_CONFIGURABLE); 126 | duk_pop(ctx); 127 | } 128 | -------------------------------------------------------------------------------- /extras/minimal-printf/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "duk_minimal_printf.h" 4 | 5 | char buffer[32]; 6 | 7 | static void init_buffer(void) { 8 | int i; 9 | 10 | for (i = 0; i < (int) sizeof(buffer); i++) { 11 | buffer[i] = 0xff; 12 | } 13 | } 14 | 15 | static void dump_buffer(void) { 16 | int i; 17 | unsigned char c; 18 | 19 | printf("Buffer: '"); 20 | for (i = 0; i < (int) sizeof(buffer); i++) { 21 | c = (unsigned char) buffer[i]; 22 | if (c < 0x20 || c >= 0x7e) { 23 | printf("<%02x>", (unsigned int) c); 24 | } else { 25 | printf("%c", (int) c); 26 | } 27 | } 28 | printf("'"); 29 | #if 0 30 | printf(" -> "); 31 | printf("Buffer:"); 32 | for (i = 0; i < sizeof(buffer); i++) { 33 | c = (unsigned char) buffer[i]; 34 | if (c <= 0x20 || c >= 0x7e) { 35 | printf(" <%02x>", (unsigned int) c); 36 | } else { 37 | printf(" %c", (char) c); 38 | } 39 | } 40 | #endif 41 | printf("\n"); 42 | } 43 | 44 | int main(int argc, char *argv[]) { 45 | int ret; 46 | void *voidptr; 47 | int i; 48 | 49 | (void) argc; (void) argv; 50 | 51 | /* Char format. */ 52 | init_buffer(); 53 | duk_minimal_snprintf(buffer, sizeof(buffer), "foo %c bar", 'Z'); 54 | dump_buffer(); 55 | 56 | /* Signed long format. */ 57 | init_buffer(); 58 | duk_minimal_snprintf(buffer, sizeof(buffer), "%ld %9ld", (long) 123, (long) 4321); 59 | dump_buffer(); 60 | 61 | /* Signed long with zero padding. */ 62 | init_buffer(); 63 | duk_minimal_snprintf(buffer, sizeof(buffer), "%09ld", (long) 4321); 64 | dump_buffer(); 65 | init_buffer(); 66 | duk_minimal_snprintf(buffer, sizeof(buffer), "%03ld %03ld %03ld", (long) -4321, (long) -432, (long) -43); 67 | dump_buffer(); 68 | 69 | /* Unsigned long with zero padding. */ 70 | init_buffer(); 71 | duk_minimal_snprintf(buffer, sizeof(buffer), "%03lu %03lu %03lu", (long) -4321, (long) -432, (long) -43); 72 | dump_buffer(); 73 | 74 | /* Signed integer. */ 75 | init_buffer(); 76 | duk_minimal_snprintf(buffer, sizeof(buffer), "%d %9d", (int) 0, (int) 4321); 77 | dump_buffer(); 78 | 79 | /* Signed negative integer, fixed field width. */ 80 | init_buffer(); 81 | duk_minimal_snprintf(buffer, sizeof(buffer), "%9d", (int) -321); 82 | dump_buffer(); 83 | init_buffer(); 84 | duk_minimal_snprintf(buffer, sizeof(buffer), "%09d", (int) -321); 85 | dump_buffer(); 86 | printf(" -- printf comparison: %9d %09d\n", -321, -321); 87 | 88 | /* Hex formatting. */ 89 | init_buffer(); 90 | duk_minimal_snprintf(buffer, sizeof(buffer), "%03x %03lx 0x%08lx", (int) 510, (long) 5105, (long) 0xdeadbeef); 91 | dump_buffer(); 92 | 93 | /* Pointer formatting, NULL and non-NULL. */ 94 | init_buffer(); 95 | duk_minimal_snprintf(buffer, sizeof(buffer), "%p %p", (void *) NULL, (void *) buffer); 96 | dump_buffer(); 97 | 98 | /* File/line like format test. */ 99 | init_buffer(); 100 | duk_minimal_snprintf(buffer, sizeof(buffer), "%s:%d", "foo bar quux", 123); 101 | dump_buffer(); 102 | 103 | /* Zero size output buffer. */ 104 | init_buffer(); 105 | duk_minimal_snprintf(buffer, 0, "%s:%d", "foo bar quux", 123); 106 | dump_buffer(); 107 | init_buffer(); 108 | duk_minimal_snprintf(buffer, 0, ""); 109 | dump_buffer(); 110 | 111 | /* NUL terminator boundary test. */ 112 | init_buffer(); 113 | duk_minimal_snprintf(buffer, 7, "foo: %s", "bar"); 114 | dump_buffer(); 115 | init_buffer(); 116 | duk_minimal_snprintf(buffer, 8, "foo: %s", "bar"); 117 | dump_buffer(); 118 | init_buffer(); 119 | duk_minimal_snprintf(buffer, 9, "foo: %s", "bar"); 120 | dump_buffer(); 121 | 122 | /* sprintf() binding, uses SIZE_MAX internally. */ 123 | init_buffer(); 124 | duk_minimal_sprintf(buffer, "unbounded print %s", "foo"); 125 | dump_buffer(); 126 | 127 | /* Pointer formatting; non-NULL and NULL. */ 128 | init_buffer(); 129 | duk_minimal_snprintf(buffer, sizeof(buffer), "%p %p", (void *) NULL, (void *) 0xdeadbeef); 130 | dump_buffer(); 131 | 132 | /* Pointer parsing, non-NULL (32-bit) pointer. */ 133 | voidptr = (void *) 123; 134 | ret = duk_minimal_sscanf("0xdeadbeef", "%p", &voidptr); 135 | printf("ret=%d, void pointer: %p\n", ret, voidptr); 136 | 137 | /* Pointer parsing, NULL (32-bit) pointer. */ 138 | voidptr = (void *) 123; 139 | ret = duk_minimal_sscanf("0x00000000", "%p", &voidptr); 140 | printf("ret=%d, void pointer: %p\n", ret, voidptr); 141 | 142 | /* Pointer parsing, non-NULL (32-bit) pointer but garbage follows. */ 143 | voidptr = (void *) 123; 144 | ret = duk_minimal_sscanf("0xdeadbeefx", "%p", &voidptr); 145 | printf("ret=%d, void pointer: %p\n", ret, voidptr); 146 | 147 | /* Fixed width test over a range of widths. */ 148 | for (i = 0; i <= 9; i++) { 149 | char fmtbuf[16]; 150 | 151 | printf("--- pos/neg fixed width test, i=%d\n", i); 152 | 153 | /* %0d. %00d makes no sense, but tested anyway. */ 154 | memset((void *) fmtbuf, 0, sizeof(fmtbuf)); 155 | fmtbuf[0] = (char) '%'; 156 | fmtbuf[1] = (char) '0'; 157 | fmtbuf[2] = (char) ('0' + i); 158 | fmtbuf[3] = 'd'; 159 | init_buffer(); 160 | duk_minimal_sprintf(buffer, (const char *) fmtbuf, 321); 161 | dump_buffer(); 162 | init_buffer(); 163 | duk_minimal_sprintf(buffer, (const char *) fmtbuf, -321); 164 | dump_buffer(); 165 | printf(" ==> printf: |"); 166 | printf((const char *) fmtbuf, 321); 167 | printf("| |"); 168 | printf((const char *) fmtbuf, -321); 169 | printf("|\n"); 170 | 171 | /* %d. */ 172 | memset((void *) fmtbuf, 0, sizeof(fmtbuf)); 173 | fmtbuf[0] = (char) '%'; 174 | fmtbuf[1] = (char) ('0' + i); 175 | fmtbuf[2] = 'd'; 176 | init_buffer(); 177 | duk_minimal_sprintf(buffer, (const char *) fmtbuf, 321); 178 | dump_buffer(); 179 | init_buffer(); 180 | duk_minimal_sprintf(buffer, (const char *) fmtbuf, -321); 181 | dump_buffer(); 182 | printf(" ==> printf: |"); 183 | printf((const char *) fmtbuf, 321); 184 | printf("| |"); 185 | printf((const char *) fmtbuf, -321); 186 | printf("|\n"); 187 | } 188 | 189 | return 0; 190 | } 191 | -------------------------------------------------------------------------------- /extras/cbor/jsoncbor.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "duktape.h" 3 | #include "duk_cbor.h" 4 | 5 | #define FMT_CBOR 1 6 | #define FMT_JSON 2 7 | #define FMT_JX 3 8 | #define FMT_JS 4 9 | 10 | static int read_format = 0; 11 | static int write_format = 0; 12 | static int write_indent = 0; 13 | 14 | static void push_stdin(duk_context *ctx, int to_string) { 15 | unsigned char *buf; 16 | size_t off; 17 | size_t len; 18 | size_t got; 19 | 20 | off = 0; 21 | len = 256; 22 | buf = (unsigned char *) duk_push_dynamic_buffer(ctx, len); 23 | 24 | for (;;) { 25 | #if 0 26 | fprintf(stderr, "reading %ld of input\n", (long) (len - off)); 27 | #endif 28 | got = fread(buf + off, 1, len - off, stdin); 29 | if (got == 0U) { 30 | break; 31 | } 32 | off += got; 33 | if (len - off < 256U) { 34 | size_t newlen = len * 2U; 35 | buf = (unsigned char *) duk_resize_buffer(ctx, -1, newlen); 36 | len = newlen; 37 | } 38 | } 39 | buf = (unsigned char *) duk_resize_buffer(ctx, -1, off); 40 | 41 | if (to_string) { 42 | duk_push_lstring(ctx, (const char *) buf, off); 43 | duk_remove(ctx, -2); 44 | } 45 | } 46 | 47 | static void usage_and_exit(void) { 48 | fprintf(stderr, "Usage: jsoncbor -r cbor|json|jx|js -w cbor|json|jx [--indent N]\n" 49 | " jsoncbor -e # shorthand for -r json -w cbor\n" 50 | " jsoncbor -d [--indent N] # shorthand for -r cbor -w json [--indent N]\n" 51 | "\n" 52 | " Input is read from stdin, output is written to stdout.\n" 53 | " 'jx' is a Duktape custom JSON extension.\n" 54 | " 'js' means evaluate input as an ECMAScript expression.\n"); 55 | exit(1); 56 | } 57 | 58 | static duk_ret_t transcode_helper(duk_context *ctx, void *udata) { 59 | const unsigned char *buf; 60 | size_t len; 61 | duk_idx_t top; 62 | 63 | (void) udata; 64 | 65 | /* For Duktape->JSON conversion map all typed arrays into base-64. 66 | * This is generally more useful than the default behavior. However, 67 | * the base-64 value doesn't have any kind of 'tag' to allow it to be 68 | * parsed back into binary automatically. Right now the CBOR parser 69 | * creates plain fixed buffers from incoming binary strings. 70 | */ 71 | duk_eval_string_noresult(ctx, 72 | "(function () {\n" 73 | " Object.getPrototypeOf(new Uint8Array(0)).toJSON = function () {\n" 74 | " return Duktape.enc('base64', this);\n" 75 | " };\n" 76 | "}())\n"); 77 | 78 | top = duk_get_top(ctx); 79 | 80 | if (read_format == FMT_CBOR) { 81 | push_stdin(ctx, 0 /*to_string*/); 82 | duk_cbor_decode(ctx, -1, 0); 83 | } else if (read_format == FMT_JSON) { 84 | push_stdin(ctx, 1 /*to_string*/); 85 | duk_json_decode(ctx, -1); 86 | } else if (read_format == FMT_JX) { 87 | duk_eval_string(ctx, "(function(v){return Duktape.dec('jx',v)})"); 88 | push_stdin(ctx, 1 /*to_string*/); 89 | duk_call(ctx, 1); 90 | } else if (read_format == FMT_JS) { 91 | push_stdin(ctx, 1 /*to_string*/); 92 | duk_eval(ctx); 93 | } else { 94 | (void) duk_type_error(ctx, "invalid read format"); 95 | } 96 | 97 | if (duk_get_top(ctx) != top + 1) { 98 | fprintf(stderr, "top invalid after decoding: %d vs. %d\n", duk_get_top(ctx), top + 1); 99 | exit(1); 100 | } 101 | 102 | if (write_format == FMT_CBOR) { 103 | duk_cbor_encode(ctx, -1, 0); 104 | } else if (write_format == FMT_JSON) { 105 | duk_eval_string(ctx, "(function(v,i){return JSON.stringify(v,null,i)})"); 106 | duk_insert(ctx, -2); 107 | duk_push_int(ctx, write_indent); 108 | duk_call(ctx, 2); 109 | } else if (write_format == FMT_JX) { 110 | duk_eval_string(ctx, "(function(v,i){return Duktape.enc('jx',v,null,i)})"); 111 | duk_insert(ctx, -2); 112 | duk_push_int(ctx, write_indent); 113 | duk_call(ctx, 2); 114 | } else { 115 | (void) duk_type_error(ctx, "invalid write format"); 116 | } 117 | 118 | if (duk_is_string(ctx, -1)) { 119 | buf = (const unsigned char *) duk_require_lstring(ctx, -1, &len); 120 | fwrite((const void *) buf, 1, len, stdout); 121 | fprintf(stdout, "\n"); 122 | } else { 123 | buf = (const unsigned char *) duk_require_buffer_data(ctx, -1, &len); 124 | fwrite((const void *) buf, 1, len, stdout); 125 | } 126 | 127 | if (duk_get_top(ctx) != top + 1) { 128 | fprintf(stderr, "top invalid after encoding: %d vs. %d\n", duk_get_top(ctx), top + 1); 129 | exit(1); 130 | } 131 | 132 | return 0; 133 | } 134 | 135 | static int parse_format(const char *s) { 136 | if (strcmp(s, "cbor") == 0) { 137 | return FMT_CBOR; 138 | } else if (strcmp(s, "json") == 0) { 139 | return FMT_JSON; 140 | } else if (strcmp(s, "jx") == 0) { 141 | return FMT_JX; 142 | } else if (strcmp(s, "js") == 0) { 143 | return FMT_JS; 144 | } else { 145 | return 0; 146 | } 147 | } 148 | 149 | int main(int argc, char *argv[]) { 150 | duk_context *ctx; 151 | duk_int_t rc; 152 | int exitcode = 0; 153 | int i; 154 | 155 | for (i = 1; i < argc; i++) { 156 | if (strcmp(argv[i], "-e") == 0) { 157 | read_format = FMT_JSON; 158 | write_format = FMT_CBOR; 159 | } else if (strcmp(argv[i], "-d") == 0) { 160 | read_format = FMT_CBOR; 161 | write_format = FMT_JSON; 162 | } else if (strcmp(argv[i], "-r") == 0) { 163 | if (i + 1 >= argc) { 164 | goto usage; 165 | } 166 | read_format = parse_format(argv[i + 1]); 167 | i++; 168 | } else if (strcmp(argv[i], "-w") == 0) { 169 | if (i + 1 >= argc) { 170 | goto usage; 171 | } 172 | write_format = parse_format(argv[i + 1]); 173 | i++; 174 | } else if (strcmp(argv[i], "--indent") == 0) { 175 | if (i + 1 >= argc) { 176 | goto usage; 177 | } 178 | write_indent = atoi(argv[i + 1]); 179 | i++; 180 | } else { 181 | goto usage; 182 | } 183 | } 184 | 185 | if (read_format == 0 || write_format == 0) { 186 | goto usage; 187 | } 188 | 189 | ctx = duk_create_heap_default(); 190 | if (!ctx) { 191 | return 1; 192 | } 193 | 194 | rc = duk_safe_call(ctx, transcode_helper, NULL, 0, 1); 195 | if (rc != 0) { 196 | fprintf(stderr, "%s\n", duk_safe_to_string(ctx, -1)); 197 | exitcode = 1; 198 | } 199 | /* duk_pop(ctx): unnecessary */ 200 | 201 | duk_destroy_heap(ctx); 202 | 203 | return exitcode; 204 | 205 | usage: 206 | usage_and_exit(); 207 | } 208 | -------------------------------------------------------------------------------- /extras/console/duk_console.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Minimal 'console' binding. 3 | * 4 | * https://github.com/DeveloperToolsWG/console-object/blob/master/api.md 5 | * https://developers.google.com/web/tools/chrome-devtools/debug/console/console-reference 6 | * https://developer.mozilla.org/en/docs/Web/API/console 7 | */ 8 | 9 | #include 10 | #include 11 | #include "duktape.h" 12 | #include "duk_console.h" 13 | 14 | /* XXX: Add some form of log level filtering. */ 15 | 16 | /* XXX: Should all output be written via e.g. console.write(formattedMsg)? 17 | * This would make it easier for user code to redirect all console output 18 | * to a custom backend. 19 | */ 20 | 21 | /* XXX: Init console object using duk_def_prop() when that call is available. */ 22 | 23 | static duk_ret_t duk__console_log_helper(duk_context *ctx, const char *error_name) { 24 | duk_uint_t flags = (duk_uint_t) duk_get_current_magic(ctx); 25 | FILE *output = (flags & DUK_CONSOLE_STDOUT_ONLY) ? stdout : stderr; 26 | duk_idx_t n = duk_get_top(ctx); 27 | duk_idx_t i; 28 | 29 | duk_get_global_string(ctx, "console"); 30 | duk_get_prop_string(ctx, -1, "format"); 31 | 32 | for (i = 0; i < n; i++) { 33 | if (duk_check_type_mask(ctx, i, DUK_TYPE_MASK_OBJECT)) { 34 | /* Slow path formatting. */ 35 | duk_dup(ctx, -1); /* console.format */ 36 | duk_dup(ctx, i); 37 | duk_call(ctx, 1); 38 | duk_replace(ctx, i); /* arg[i] = console.format(arg[i]); */ 39 | } 40 | } 41 | 42 | duk_pop_2(ctx); 43 | 44 | duk_push_string(ctx, " "); 45 | duk_insert(ctx, 0); 46 | duk_join(ctx, n); 47 | 48 | if (error_name) { 49 | duk_push_error_object(ctx, DUK_ERR_ERROR, "%s", duk_require_string(ctx, -1)); 50 | duk_push_string(ctx, "name"); 51 | duk_push_string(ctx, error_name); 52 | duk_def_prop(ctx, -3, DUK_DEFPROP_FORCE | DUK_DEFPROP_HAVE_VALUE); /* to get e.g. 'Trace: 1 2 3' */ 53 | duk_get_prop_string(ctx, -1, "stack"); 54 | } 55 | 56 | fprintf(output, "%s\n", duk_to_string(ctx, -1)); 57 | if (flags & DUK_CONSOLE_FLUSH) { 58 | fflush(output); 59 | } 60 | return 0; 61 | } 62 | 63 | static duk_ret_t duk__console_assert(duk_context *ctx) { 64 | if (duk_to_boolean(ctx, 0)) { 65 | return 0; 66 | } 67 | duk_remove(ctx, 0); 68 | 69 | return duk__console_log_helper(ctx, "AssertionError"); 70 | } 71 | 72 | static duk_ret_t duk__console_log(duk_context *ctx) { 73 | return duk__console_log_helper(ctx, NULL); 74 | } 75 | 76 | static duk_ret_t duk__console_trace(duk_context *ctx) { 77 | return duk__console_log_helper(ctx, "Trace"); 78 | } 79 | 80 | static duk_ret_t duk__console_info(duk_context *ctx) { 81 | return duk__console_log_helper(ctx, NULL); 82 | } 83 | 84 | static duk_ret_t duk__console_warn(duk_context *ctx) { 85 | return duk__console_log_helper(ctx, NULL); 86 | } 87 | 88 | static duk_ret_t duk__console_error(duk_context *ctx) { 89 | return duk__console_log_helper(ctx, "Error"); 90 | } 91 | 92 | static duk_ret_t duk__console_dir(duk_context *ctx) { 93 | /* For now, just share the formatting of .log() */ 94 | return duk__console_log_helper(ctx, 0); 95 | } 96 | 97 | static void duk__console_reg_vararg_func(duk_context *ctx, duk_c_function func, const char *name, duk_uint_t flags) { 98 | duk_push_c_function(ctx, func, DUK_VARARGS); 99 | duk_push_string(ctx, "name"); 100 | duk_push_string(ctx, name); 101 | duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_FORCE); /* Improve stacktraces by displaying function name */ 102 | duk_set_magic(ctx, -1, (duk_int_t) flags); 103 | duk_put_prop_string(ctx, -2, name); 104 | } 105 | 106 | void duk_console_init(duk_context *ctx, duk_uint_t flags) { 107 | duk_uint_t flags_orig; 108 | 109 | /* If both DUK_CONSOLE_STDOUT_ONLY and DUK_CONSOLE_STDERR_ONLY where specified, 110 | * just turn off DUK_CONSOLE_STDOUT_ONLY and keep DUK_CONSOLE_STDERR_ONLY. 111 | */ 112 | if ((flags & DUK_CONSOLE_STDOUT_ONLY) && (flags & DUK_CONSOLE_STDERR_ONLY)) { 113 | flags &= ~DUK_CONSOLE_STDOUT_ONLY; 114 | } 115 | /* Remember the (possibly corrected) flags we received. */ 116 | flags_orig = flags; 117 | 118 | duk_push_object(ctx); 119 | 120 | /* Custom function to format objects; user can replace. 121 | * For now, try JX-formatting and if that fails, fall back 122 | * to ToString(v). 123 | */ 124 | duk_eval_string(ctx, 125 | "(function (E) {" 126 | "return function format(v){" 127 | "try{" 128 | "return E('jx',v);" 129 | "}catch(e){" 130 | "return String(v);" /* String() allows symbols, ToString() internal algorithm doesn't. */ 131 | "}" 132 | "};" 133 | "})(Duktape.enc)"); 134 | duk_put_prop_string(ctx, -2, "format"); 135 | 136 | flags = flags_orig; 137 | if (!(flags & DUK_CONSOLE_STDOUT_ONLY) && !(flags & DUK_CONSOLE_STDERR_ONLY)) { 138 | /* No output indicators were specified; these levels go to stdout. */ 139 | flags |= DUK_CONSOLE_STDOUT_ONLY; 140 | } 141 | duk__console_reg_vararg_func(ctx, duk__console_assert, "assert", flags); 142 | duk__console_reg_vararg_func(ctx, duk__console_log, "log", flags); 143 | duk__console_reg_vararg_func(ctx, duk__console_log, "debug", flags); /* alias to console.log */ 144 | duk__console_reg_vararg_func(ctx, duk__console_trace, "trace", flags); 145 | duk__console_reg_vararg_func(ctx, duk__console_info, "info", flags); 146 | 147 | flags = flags_orig; 148 | if (!(flags & DUK_CONSOLE_STDOUT_ONLY) && !(flags & DUK_CONSOLE_STDERR_ONLY)) { 149 | /* No output indicators were specified; these levels go to stderr. */ 150 | flags |= DUK_CONSOLE_STDERR_ONLY; 151 | } 152 | duk__console_reg_vararg_func(ctx, duk__console_warn, "warn", flags); 153 | duk__console_reg_vararg_func(ctx, duk__console_error, "error", flags); 154 | duk__console_reg_vararg_func(ctx, duk__console_error, "exception", flags); /* alias to console.error */ 155 | duk__console_reg_vararg_func(ctx, duk__console_dir, "dir", flags); 156 | 157 | duk_put_global_string(ctx, "console"); 158 | 159 | /* Proxy wrapping: ensures any undefined console method calls are 160 | * ignored silently. This was required specifically by the 161 | * DeveloperToolsWG proposal (and was implemented also by Firefox: 162 | * https://bugzilla.mozilla.org/show_bug.cgi?id=629607). This is 163 | * apparently no longer the preferred way of implementing console. 164 | * When Proxy is enabled, whitelist at least .toJSON() to avoid 165 | * confusing JX serialization of the console object. 166 | */ 167 | 168 | if (flags & DUK_CONSOLE_PROXY_WRAPPER) { 169 | /* Tolerate failure to initialize Proxy wrapper in case 170 | * Proxy support is disabled. 171 | */ 172 | (void) duk_peval_string_noresult(ctx, 173 | "(function(){" 174 | "var D=function(){};" 175 | "var W={toJSON:true};" /* whitelisted */ 176 | "console=new Proxy(console,{" 177 | "get:function(t,k){" 178 | "var v=t[k];" 179 | "return typeof v==='function'||W[k]?v:D;" 180 | "}" 181 | "});" 182 | "})();" 183 | ); 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /extras/minimal-printf/duk_minimal_printf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Minimal vsnprintf(), snprintf(), sprintf(), and sscanf() for Duktape. 3 | * The supported conversion formats narrowly match what Duktape needs. 4 | */ 5 | 6 | #include /* va_list etc */ 7 | #include /* size_t */ 8 | #include /* SIZE_MAX */ 9 | 10 | /* Write character with bound checking. Offset 'off' is updated regardless 11 | * of whether an actual write is made. This is necessary to satisfy snprintf() 12 | * return value semantics. 13 | */ 14 | #define DUK__WRITE_CHAR(c) do { \ 15 | if (off < size) { \ 16 | str[off] = (char) c; \ 17 | } \ 18 | off++; \ 19 | } while (0) 20 | 21 | /* Digits up to radix 16. */ 22 | static const char duk__format_digits[16] = { 23 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' 24 | }; 25 | 26 | /* Format an unsigned long with various options. An unsigned long is large 27 | * enough for formatting all supported types. 28 | */ 29 | static size_t duk__format_long(char *str, 30 | size_t size, 31 | size_t off, 32 | int fixed_length, 33 | char pad, 34 | int radix, 35 | int neg_sign, 36 | unsigned long v) { 37 | char buf[24]; /* 2^64 = 18446744073709552000, length 20 */ 38 | char *required; 39 | char *p; 40 | int i; 41 | 42 | /* Format in reverse order first. Ensure at least one digit is output 43 | * to handle '0' correctly. Note that space padding and zero padding 44 | * handle negative sign differently: 45 | * 46 | * %9d and -321 => ' -321' 47 | * %09d and -321 => '-00000321' 48 | */ 49 | 50 | for (i = 0; i < (int) sizeof(buf); i++) { 51 | buf[i] = pad; /* compiles into memset() equivalent, avoid memset() dependency */ 52 | } 53 | 54 | p = buf; 55 | do { 56 | *p++ = duk__format_digits[v % radix]; 57 | v /= radix; 58 | } while (v != 0); 59 | 60 | required = buf + fixed_length; 61 | if (p < required && pad == (char) '0') { 62 | /* Zero padding and we didn't reach maximum length: place 63 | * negative sign at the last position. We can't get here 64 | * with fixed_length == 0 so that required[-1] is safe. 65 | * 66 | * Technically we should only do this for 'neg_sign == 1', 67 | * but it's OK to advance the pointer even when that's not 68 | * the case. 69 | */ 70 | p = required - 1; 71 | } 72 | if (neg_sign) { 73 | *p++ = (char) '-'; 74 | } 75 | if (p < required) { 76 | p = required; 77 | } 78 | 79 | /* Now [buf,p[ contains the result in reverse; copy into place. */ 80 | 81 | while (p > buf) { 82 | p--; 83 | DUK__WRITE_CHAR(*p); 84 | } 85 | 86 | return off; 87 | } 88 | 89 | /* Parse a pointer. Must parse whatever is produced by '%p' in sprintf(). */ 90 | static int duk__parse_pointer(const char *str, void **out) { 91 | const unsigned char *p; 92 | unsigned char ch; 93 | int count; 94 | int limit; 95 | long val; /* assume void * fits into long */ 96 | 97 | /* We only need to parse what our minimal printf() produces, so that 98 | * we can check for a '0x' prefix, and assume all hex digits are 99 | * lowercase. 100 | */ 101 | 102 | p = (const unsigned char *) str; 103 | if (p[0] != (unsigned char) '0' || p[1] != (unsigned char) 'x') { 104 | return 0; 105 | } 106 | p += 2; 107 | 108 | for (val = 0, count = 0, limit = sizeof(void *) * 2; count < limit; count++) { 109 | ch = *p++; 110 | 111 | val <<= 4; 112 | if (ch >= (unsigned char) '0' && ch <= (unsigned char) '9') { 113 | val += ch - (unsigned char) '0'; 114 | } else if (ch >= (unsigned char) 'a' && ch <= (unsigned char) 'f') { 115 | val += ch - (unsigned char) 'a' + 0x0a; 116 | } else { 117 | return 0; 118 | } 119 | } 120 | 121 | /* The input may end at a NUL or garbage may follow. As long as we 122 | * parse the '%p' correctly, garbage is allowed to follow, and the 123 | * JX pointer parsing also relies on that. 124 | */ 125 | 126 | *out = (void *) val; 127 | return 1; 128 | } 129 | 130 | /* Minimal vsnprintf() entry point. */ 131 | int duk_minimal_vsnprintf(char *str, size_t size, const char *format, va_list ap) { 132 | size_t off = 0; 133 | const char *p; 134 | #if 0 135 | const char *p_tmp; 136 | const char *p_fmt_start; 137 | #endif 138 | char c; 139 | char pad; 140 | int fixed_length; 141 | int is_long; 142 | 143 | /* Assume str != NULL unless size == 0. 144 | * Assume format != NULL. 145 | */ 146 | 147 | p = format; 148 | for (;;) { 149 | c = *p++; 150 | if (c == (char) 0) { 151 | break; 152 | } 153 | if (c != (char) '%') { 154 | DUK__WRITE_CHAR(c); 155 | continue; 156 | } 157 | 158 | /* Start format sequence. Scan flags and format specifier. */ 159 | 160 | #if 0 161 | p_fmt_start = p - 1; 162 | #endif 163 | is_long = 0; 164 | pad = ' '; 165 | fixed_length = 0; 166 | for (;;) { 167 | c = *p++; 168 | if (c == (char) 'l') { 169 | is_long = 1; 170 | } else if (c == (char) '0') { 171 | /* Only support pad character '0'. */ 172 | pad = '0'; 173 | } else if (c >= (char) '1' && c <= (char) '9') { 174 | /* Only support fixed lengths 1-9. */ 175 | fixed_length = (int) (c - (char) '0'); 176 | } else if (c == (char) 'd') { 177 | long v; 178 | int neg_sign = 0; 179 | if (is_long) { 180 | v = va_arg(ap, long); 181 | } else { 182 | v = (long) va_arg(ap, int); 183 | } 184 | if (v < 0) { 185 | neg_sign = 1; 186 | v = -v; 187 | } 188 | off = duk__format_long(str, size, off, fixed_length, pad, 10, neg_sign, (unsigned long) v); 189 | break; 190 | } else if (c == (char) 'u') { 191 | unsigned long v; 192 | if (is_long) { 193 | v = va_arg(ap, unsigned long); 194 | } else { 195 | v = (unsigned long) va_arg(ap, unsigned int); 196 | } 197 | off = duk__format_long(str, size, off, fixed_length, pad, 10, 0, v); 198 | break; 199 | } else if (c == (char) 'x') { 200 | unsigned long v; 201 | if (is_long) { 202 | v = va_arg(ap, unsigned long); 203 | } else { 204 | v = (unsigned long) va_arg(ap, unsigned int); 205 | } 206 | off = duk__format_long(str, size, off, fixed_length, pad, 16, 0, v); 207 | break; 208 | } else if (c == (char) 'c') { 209 | char v; 210 | v = (char) va_arg(ap, int); /* intentionally not 'char' */ 211 | DUK__WRITE_CHAR(v); 212 | break; 213 | } else if (c == (char) 's') { 214 | const char *v; 215 | char c_tmp; 216 | v = va_arg(ap, const char *); 217 | if (v) { 218 | for (;;) { 219 | c_tmp = *v++; 220 | if (c_tmp) { 221 | DUK__WRITE_CHAR(c_tmp); 222 | } else { 223 | break; 224 | } 225 | } 226 | } 227 | break; 228 | } else if (c == (char) 'p') { 229 | /* Assume a void * can be represented by 'long'. This is not 230 | * always the case. NULL pointer is printed out as 0x0000... 231 | */ 232 | void *v; 233 | v = va_arg(ap, void *); 234 | DUK__WRITE_CHAR('0'); 235 | DUK__WRITE_CHAR('x'); 236 | off = duk__format_long(str, size, off, sizeof(void *) * 2, '0', 16, 0, (unsigned long) v); 237 | break; 238 | } else { 239 | /* Unrecognized, bail out early. We could also emit the format 240 | * specifier verbatim, but it'd be a waste of footprint because 241 | * this case should never happen in practice. 242 | */ 243 | #if 0 244 | DUK__WRITE_CHAR('!'); 245 | #endif 246 | #if 0 247 | for (p_tmp = p_fmt_start; p_tmp != p; p_tmp++) { 248 | DUK__WRITE_CHAR(*p_tmp); 249 | } 250 | break; 251 | #endif 252 | goto finish; 253 | } 254 | } 255 | } 256 | 257 | finish: 258 | if (off < size) { 259 | str[off] = (char) 0; /* No increment for 'off', not counted in return value. */ 260 | } else if (size > 0) { 261 | /* Forced termination. */ 262 | str[size - 1] = 0; 263 | } 264 | 265 | return (int) off; 266 | } 267 | 268 | /* Minimal snprintf() entry point. */ 269 | int duk_minimal_snprintf(char *str, size_t size, const char *format, ...) { 270 | va_list ap; 271 | int ret; 272 | 273 | va_start(ap, format); 274 | ret = duk_minimal_vsnprintf(str, size, format, ap); 275 | va_end(ap); 276 | 277 | return ret; 278 | } 279 | 280 | /* Minimal sprintf() entry point. */ 281 | int duk_minimal_sprintf(char *str, const char *format, ...) { 282 | va_list ap; 283 | int ret; 284 | 285 | va_start(ap, format); 286 | ret = duk_minimal_vsnprintf(str, SIZE_MAX, format, ap); 287 | va_end(ap); 288 | 289 | return ret; 290 | } 291 | 292 | /* Minimal sscanf() entry point. */ 293 | int duk_minimal_sscanf(const char *str, const char *format, ...) { 294 | va_list ap; 295 | int ret; 296 | void **out; 297 | 298 | /* Only the exact "%p" format is supported. */ 299 | if (format[0] != (char) '%' || 300 | format[1] != (char) 'p' || 301 | format[2] != (char) 0) { 302 | } 303 | 304 | va_start(ap, format); 305 | out = va_arg(ap, void **); 306 | ret = duk__parse_pointer(str, out); 307 | va_end(ap); 308 | 309 | return ret; 310 | } 311 | 312 | #undef DUK__WRITE_CHAR 313 | -------------------------------------------------------------------------------- /extras/alloc-pool/duk_alloc_pool.h: -------------------------------------------------------------------------------- 1 | #if !defined(DUK_ALLOC_POOL_H_INCLUDED) 2 | #define DUK_ALLOC_POOL_H_INCLUDED 3 | 4 | #include "duktape.h" 5 | 6 | #if defined(__cplusplus) 7 | extern "C" { 8 | #endif 9 | 10 | /* 32-bit (big endian) marker used at the end of pool entries so that wasted 11 | * space can be detected. Waste tracking must be enabled explicitly. 12 | */ 13 | #if defined(DUK_ALLOC_POOL_TRACK_WASTE) 14 | #define DUK_ALLOC_POOL_WASTE_MARKER 0xedcb2345UL 15 | #endif 16 | 17 | /* Pointer compression with ROM strings/objects: 18 | * 19 | * For now, use DUK_USE_ROM_OBJECTS to signal the need for compressed ROM 20 | * pointers. DUK_USE_ROM_PTRCOMP_FIRST is provided for the ROM pointer 21 | * compression range minimum to avoid duplication in user code. 22 | */ 23 | #if defined(DUK_USE_ROM_OBJECTS) && defined(DUK_USE_HEAPPTR16) 24 | #define DUK_ALLOC_POOL_ROMPTR_COMPRESSION 25 | #define DUK_ALLOC_POOL_ROMPTR_FIRST DUK_USE_ROM_PTRCOMP_FIRST 26 | 27 | /* This extern declaration is provided by duktape.h, array provided by duktape.c. 28 | * Because duk_config.h may include this file (to get the inline functions) we 29 | * need to forward declare this also here. 30 | */ 31 | extern const void * const duk_rom_compressed_pointers[]; 32 | #endif 33 | 34 | /* Pool configuration for a certain block size. */ 35 | typedef struct { 36 | unsigned int size; /* must be divisible by 4 and >= sizeof(void *) */ 37 | unsigned int a; /* bytes (not count) to allocate: a*t + b, t is an arbitrary scale parameter */ 38 | unsigned int b; 39 | } duk_pool_config; 40 | 41 | /* Freelist entry, must fit into the smallest block size. */ 42 | struct duk_pool_free; 43 | typedef struct duk_pool_free duk_pool_free; 44 | struct duk_pool_free { 45 | duk_pool_free *next; 46 | }; 47 | 48 | /* Pool state for a certain block size. */ 49 | typedef struct { 50 | duk_pool_free *first; 51 | char *alloc_end; 52 | unsigned int size; 53 | unsigned int count; 54 | #if defined(DUK_ALLOC_POOL_TRACK_HIGHWATER) 55 | unsigned int hwm_used_count; 56 | #endif 57 | } duk_pool_state; 58 | 59 | /* Statistics for a certain pool. */ 60 | typedef struct { 61 | size_t used_count; 62 | size_t used_bytes; 63 | size_t free_count; 64 | size_t free_bytes; 65 | size_t waste_bytes; 66 | size_t hwm_used_count; 67 | } duk_pool_stats; 68 | 69 | /* Top level state for all pools. Pointer to this struct is used as the allocator 70 | * userdata pointer. 71 | */ 72 | typedef struct { 73 | int num_pools; 74 | duk_pool_state *states; 75 | #if defined(DUK_ALLOC_POOL_TRACK_HIGHWATER) 76 | size_t hwm_used_bytes; 77 | size_t hwm_waste_bytes; 78 | #endif 79 | } duk_pool_global; 80 | 81 | /* Statistics for the entire set of pools. */ 82 | typedef struct { 83 | size_t used_bytes; 84 | size_t free_bytes; 85 | size_t waste_bytes; 86 | size_t hwm_used_bytes; 87 | size_t hwm_waste_bytes; 88 | } duk_pool_global_stats; 89 | 90 | /* Initialize a pool allocator, arguments: 91 | * - buffer and size: continuous region to use for pool, must align to 4 92 | * - config: configuration for pools in ascending block size 93 | * - state: state for pools, matches config order 94 | * - num_pools: number of entries in 'config' and 'state' 95 | * - global: global state structure 96 | * 97 | * The 'config', 'state', and 'global' pointers must be valid beyond the init 98 | * call, as long as the pool is used. 99 | * 100 | * Returns a void pointer to be used as userdata for the allocator functions. 101 | * Concretely the return value will be "(void *) global", i.e. the global 102 | * state struct. If pool init fails, the return value will be NULL. 103 | */ 104 | void *duk_alloc_pool_init(char *buffer, 105 | size_t size, 106 | const duk_pool_config *configs, 107 | duk_pool_state *states, 108 | int num_pools, 109 | duk_pool_global *global); 110 | 111 | /* Duktape allocation providers. Typing matches Duktape requirements. */ 112 | void *duk_alloc_pool(void *udata, duk_size_t size); 113 | void *duk_realloc_pool(void *udata, void *ptr, duk_size_t size); 114 | void duk_free_pool(void *udata, void *ptr); 115 | 116 | /* Stats. */ 117 | void duk_alloc_pool_get_pool_stats(duk_pool_state *s, duk_pool_stats *res); 118 | void duk_alloc_pool_get_global_stats(duk_pool_global *g, duk_pool_global_stats *res); 119 | 120 | /* Duktape pointer compression global state (assumes single pool). */ 121 | #if defined(DUK_USE_ROM_OBJECTS) && defined(DUK_USE_HEAPPTR16) 122 | extern const void *duk_alloc_pool_romptr_low; 123 | extern const void *duk_alloc_pool_romptr_high; 124 | duk_uint16_t duk_alloc_pool_enc16_rom(void *ptr); 125 | #endif 126 | #if defined(DUK_USE_HEAPPTR16) 127 | extern void *duk_alloc_pool_ptrcomp_base; 128 | #endif 129 | 130 | #if 0 131 | duk_uint16_t duk_alloc_pool_enc16(void *ptr); 132 | void *duk_alloc_pool_dec16(duk_uint16_t val); 133 | #endif 134 | 135 | /* Inlined pointer compression functions. Gcc and clang -Os won't in 136 | * practice inline these without an "always inline" attribute because it's 137 | * more size efficient (by a few kB) to use explicit calls instead. Having 138 | * these defined inline here allows performance optimized builds to inline 139 | * pointer compression operations. 140 | * 141 | * Pointer compression assumes there's a single globally registered memory 142 | * pool which makes pointer compression more efficient. This would be easy 143 | * to fix by adding a userdata pointer to the compression functions and 144 | * plumbing the heap userdata from the compression/decompression macros. 145 | */ 146 | 147 | /* DUK_ALWAYS_INLINE is not a public API symbol so it may go away in even a 148 | * minor update. But it's pragmatic for this extra because it handles many 149 | * compilers via duk_config.h detection. Check that the macro exists so that 150 | * if it's gone, we can still compile. 151 | */ 152 | #if defined(DUK_ALWAYS_INLINE) 153 | #define DUK__ALLOC_POOL_ALWAYS_INLINE DUK_ALWAYS_INLINE 154 | #else 155 | #define DUK__ALLOC_POOL_ALWAYS_INLINE /* nop */ 156 | #endif 157 | 158 | #if defined(DUK_USE_HEAPPTR16) 159 | static DUK__ALLOC_POOL_ALWAYS_INLINE duk_uint16_t duk_alloc_pool_enc16(void *ptr) { 160 | if (ptr == NULL) { 161 | /* With 'return 0' gcc and clang -Os generate inefficient code. 162 | * For example, gcc -Os generates: 163 | * 164 | * 0804911d : 165 | * 804911d: 55 push %ebp 166 | * 804911e: 85 c0 test %eax,%eax 167 | * 8049120: 89 e5 mov %esp,%ebp 168 | * 8049122: 74 0b je 804912f 169 | * 8049124: 2b 05 e4 90 07 08 sub 0x80790e4,%eax 170 | * 804912a: c1 e8 02 shr $0x2,%eax 171 | * 804912d: eb 02 jmp 8049131 172 | * 804912f: 31 c0 xor %eax,%eax 173 | * 8049131: 5d pop %ebp 174 | * 8049132: c3 ret 175 | * 176 | * The NULL path checks %eax for zero; if it is zero, a zero 177 | * is unnecessarily loaded into %eax again. The non-zero path 178 | * has an unnecessary jump as a side effect of this. 179 | * 180 | * Using 'return (duk_uint16_t) (intptr_t) ptr;' generates similarly 181 | * inefficient code; not sure how to make the result better. 182 | */ 183 | return 0; 184 | } 185 | #if defined(DUK_ALLOC_POOL_ROMPTR_COMPRESSION) 186 | if (ptr >= duk_alloc_pool_romptr_low && ptr <= duk_alloc_pool_romptr_high) { 187 | /* This is complex enough now to need a separate function. */ 188 | return duk_alloc_pool_enc16_rom(ptr); 189 | } 190 | #endif 191 | return (duk_uint16_t) (((size_t) ((char *) ptr - (char *) duk_alloc_pool_ptrcomp_base)) >> 2); 192 | } 193 | 194 | static DUK__ALLOC_POOL_ALWAYS_INLINE void *duk_alloc_pool_dec16(duk_uint16_t val) { 195 | if (val == 0) { 196 | /* As with enc16 the gcc and clang -Os output is inefficient, 197 | * e.g. gcc -Os: 198 | * 199 | * 08049133 : 200 | * 8049133: 55 push %ebp 201 | * 8049134: 66 85 c0 test %ax,%ax 202 | * 8049137: 89 e5 mov %esp,%ebp 203 | * 8049139: 74 0e je 8049149 204 | * 804913b: 8b 15 e4 90 07 08 mov 0x80790e4,%edx 205 | * 8049141: 0f b7 c0 movzwl %ax,%eax 206 | * 8049144: 8d 04 82 lea (%edx,%eax,4),%eax 207 | * 8049147: eb 02 jmp 804914b 208 | * 8049149: 31 c0 xor %eax,%eax 209 | * 804914b: 5d pop %ebp 210 | * 804914c: c3 ret 211 | */ 212 | return NULL; 213 | } 214 | #if defined(DUK_ALLOC_POOL_ROMPTR_COMPRESSION) 215 | if (val >= DUK_ALLOC_POOL_ROMPTR_FIRST) { 216 | /* This is a blind lookup, could check index validity. 217 | * Duktape should never decompress a pointer which would 218 | * be out-of-bounds here. 219 | */ 220 | return (void *) (intptr_t) (duk_rom_compressed_pointers[val - DUK_ALLOC_POOL_ROMPTR_FIRST]); 221 | } 222 | #endif 223 | return (void *) ((char *) duk_alloc_pool_ptrcomp_base + (((size_t) val) << 2)); 224 | } 225 | #endif 226 | 227 | #if defined(__cplusplus) 228 | } 229 | #endif /* end 'extern "C"' wrapper */ 230 | 231 | #endif /* DUK_ALLOC_POOL_H_INCLUDED */ 232 | -------------------------------------------------------------------------------- /extras/module-node/duk_module_node.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Node.js-like module loading framework for Duktape 3 | * 4 | * https://nodejs.org/api/modules.html 5 | */ 6 | 7 | #include "duktape.h" 8 | #include "duk_module_node.h" 9 | 10 | #if DUK_VERSION >= 19999 11 | static duk_int_t duk__eval_module_source(duk_context *ctx, void *udata); 12 | #else 13 | static duk_int_t duk__eval_module_source(duk_context *ctx); 14 | #endif 15 | static void duk__push_module_object(duk_context *ctx, const char *id, duk_bool_t main); 16 | 17 | static duk_bool_t duk__get_cached_module(duk_context *ctx, const char *id) { 18 | duk_push_global_stash(ctx); 19 | (void) duk_get_prop_string(ctx, -1, "\xff" "requireCache"); 20 | if (duk_get_prop_string(ctx, -1, id)) { 21 | duk_remove(ctx, -2); 22 | duk_remove(ctx, -2); 23 | return 1; 24 | } else { 25 | duk_pop_3(ctx); 26 | return 0; 27 | } 28 | } 29 | 30 | /* Place a `module` object on the top of the value stack into the require cache 31 | * based on its `.id` property. As a convenience to the caller, leave the 32 | * object on top of the value stack afterwards. 33 | */ 34 | static void duk__put_cached_module(duk_context *ctx) { 35 | /* [ ... module ] */ 36 | 37 | duk_push_global_stash(ctx); 38 | (void) duk_get_prop_string(ctx, -1, "\xff" "requireCache"); 39 | duk_dup(ctx, -3); 40 | 41 | /* [ ... module stash req_cache module ] */ 42 | 43 | (void) duk_get_prop_string(ctx, -1, "id"); 44 | duk_dup(ctx, -2); 45 | duk_put_prop(ctx, -4); 46 | 47 | duk_pop_3(ctx); /* [ ... module ] */ 48 | } 49 | 50 | static void duk__del_cached_module(duk_context *ctx, const char *id) { 51 | duk_push_global_stash(ctx); 52 | (void) duk_get_prop_string(ctx, -1, "\xff" "requireCache"); 53 | duk_del_prop_string(ctx, -1, id); 54 | duk_pop_2(ctx); 55 | } 56 | 57 | static duk_ret_t duk__handle_require(duk_context *ctx) { 58 | /* 59 | * Value stack handling here is a bit sloppy but should be correct. 60 | * Call handling will clean up any extra garbage for us. 61 | */ 62 | 63 | const char *id; 64 | const char *parent_id; 65 | duk_idx_t module_idx; 66 | duk_idx_t stash_idx; 67 | duk_int_t ret; 68 | 69 | duk_push_global_stash(ctx); 70 | stash_idx = duk_normalize_index(ctx, -1); 71 | 72 | duk_push_current_function(ctx); 73 | (void) duk_get_prop_string(ctx, -1, "\xff" "moduleId"); 74 | parent_id = duk_require_string(ctx, -1); 75 | (void) parent_id; /* not used directly; suppress warning */ 76 | 77 | /* [ id stash require parent_id ] */ 78 | 79 | id = duk_require_string(ctx, 0); 80 | 81 | (void) duk_get_prop_string(ctx, stash_idx, "\xff" "modResolve"); 82 | duk_dup(ctx, 0); /* module ID */ 83 | duk_dup(ctx, -3); /* parent ID */ 84 | duk_call(ctx, 2); 85 | 86 | /* [ ... stash ... resolved_id ] */ 87 | 88 | id = duk_require_string(ctx, -1); 89 | 90 | if (duk__get_cached_module(ctx, id)) { 91 | goto have_module; /* use the cached module */ 92 | } 93 | 94 | duk__push_module_object(ctx, id, 0 /*main*/); 95 | duk__put_cached_module(ctx); /* module remains on stack */ 96 | 97 | /* 98 | * From here on out, we have to be careful not to throw. If it can't be 99 | * avoided, the error must be caught and the module removed from the 100 | * require cache before rethrowing. This allows the application to 101 | * reattempt loading the module. 102 | */ 103 | 104 | module_idx = duk_normalize_index(ctx, -1); 105 | 106 | /* [ ... stash ... resolved_id module ] */ 107 | 108 | (void) duk_get_prop_string(ctx, stash_idx, "\xff" "modLoad"); 109 | duk_dup(ctx, -3); /* resolved ID */ 110 | (void) duk_get_prop_string(ctx, module_idx, "exports"); 111 | duk_dup(ctx, module_idx); 112 | ret = duk_pcall(ctx, 3); 113 | if (ret != DUK_EXEC_SUCCESS) { 114 | duk__del_cached_module(ctx, id); 115 | (void) duk_throw(ctx); /* rethrow */ 116 | } 117 | 118 | if (duk_is_string(ctx, -1)) { 119 | duk_int_t ret; 120 | 121 | /* [ ... module source ] */ 122 | 123 | #if DUK_VERSION >= 19999 124 | ret = duk_safe_call(ctx, duk__eval_module_source, NULL, 2, 1); 125 | #else 126 | ret = duk_safe_call(ctx, duk__eval_module_source, 2, 1); 127 | #endif 128 | if (ret != DUK_EXEC_SUCCESS) { 129 | duk__del_cached_module(ctx, id); 130 | (void) duk_throw(ctx); /* rethrow */ 131 | } 132 | } else if (duk_is_undefined(ctx, -1)) { 133 | duk_pop(ctx); 134 | } else { 135 | duk__del_cached_module(ctx, id); 136 | (void) duk_type_error(ctx, "invalid module load callback return value"); 137 | } 138 | 139 | /* fall through */ 140 | 141 | have_module: 142 | /* [ ... module ] */ 143 | 144 | (void) duk_get_prop_string(ctx, -1, "exports"); 145 | return 1; 146 | } 147 | 148 | static void duk__push_require_function(duk_context *ctx, const char *id) { 149 | duk_push_c_function(ctx, duk__handle_require, 1); 150 | duk_push_string(ctx, "name"); 151 | duk_push_string(ctx, "require"); 152 | duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE); 153 | duk_push_string(ctx, id); 154 | duk_put_prop_string(ctx, -2, "\xff" "moduleId"); 155 | 156 | /* require.cache */ 157 | duk_push_global_stash(ctx); 158 | (void) duk_get_prop_string(ctx, -1, "\xff" "requireCache"); 159 | duk_put_prop_string(ctx, -3, "cache"); 160 | duk_pop(ctx); 161 | 162 | /* require.main */ 163 | duk_push_global_stash(ctx); 164 | (void) duk_get_prop_string(ctx, -1, "\xff" "mainModule"); 165 | duk_put_prop_string(ctx, -3, "main"); 166 | duk_pop(ctx); 167 | } 168 | 169 | static void duk__push_module_object(duk_context *ctx, const char *id, duk_bool_t main) { 170 | duk_push_object(ctx); 171 | 172 | /* Set this as the main module, if requested */ 173 | if (main) { 174 | duk_push_global_stash(ctx); 175 | duk_dup(ctx, -2); 176 | duk_put_prop_string(ctx, -2, "\xff" "mainModule"); 177 | duk_pop(ctx); 178 | } 179 | 180 | /* Node.js uses the canonicalized filename of a module for both module.id 181 | * and module.filename. We have no concept of a file system here, so just 182 | * use the module ID for both values. 183 | */ 184 | duk_push_string(ctx, id); 185 | duk_dup(ctx, -1); 186 | duk_put_prop_string(ctx, -3, "filename"); 187 | duk_put_prop_string(ctx, -2, "id"); 188 | 189 | /* module.exports = {} */ 190 | duk_push_object(ctx); 191 | duk_put_prop_string(ctx, -2, "exports"); 192 | 193 | /* module.loaded = false */ 194 | duk_push_false(ctx); 195 | duk_put_prop_string(ctx, -2, "loaded"); 196 | 197 | /* module.require */ 198 | duk__push_require_function(ctx, id); 199 | duk_put_prop_string(ctx, -2, "require"); 200 | } 201 | 202 | #if DUK_VERSION >= 19999 203 | static duk_int_t duk__eval_module_source(duk_context *ctx, void *udata) { 204 | #else 205 | static duk_int_t duk__eval_module_source(duk_context *ctx) { 206 | #endif 207 | const char *src; 208 | 209 | /* 210 | * Stack: [ ... module source ] 211 | */ 212 | 213 | #if DUK_VERSION >= 19999 214 | (void) udata; 215 | #endif 216 | 217 | /* Wrap the module code in a function expression. This is the simplest 218 | * way to implement CommonJS closure semantics and matches the behavior of 219 | * e.g. Node.js. 220 | */ 221 | duk_push_string(ctx, "(function(exports,require,module,__filename,__dirname){"); 222 | src = duk_require_string(ctx, -2); 223 | duk_push_string(ctx, (src[0] == '#' && src[1] == '!') ? "//" : ""); /* Shebang support. */ 224 | duk_dup(ctx, -3); /* source */ 225 | duk_push_string(ctx, "\n})"); /* Newline allows module last line to contain a // comment. */ 226 | duk_concat(ctx, 4); 227 | 228 | /* [ ... module source func_src ] */ 229 | 230 | (void) duk_get_prop_string(ctx, -3, "filename"); 231 | duk_compile(ctx, DUK_COMPILE_EVAL); 232 | duk_call(ctx, 0); 233 | 234 | /* [ ... module source func ] */ 235 | 236 | /* Set name for the wrapper function. */ 237 | duk_push_string(ctx, "name"); 238 | duk_push_string(ctx, "main"); 239 | duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_FORCE); 240 | 241 | /* call the function wrapper */ 242 | (void) duk_get_prop_string(ctx, -3, "exports"); /* exports */ 243 | (void) duk_get_prop_string(ctx, -4, "require"); /* require */ 244 | duk_dup(ctx, -5); /* module */ 245 | (void) duk_get_prop_string(ctx, -6, "filename"); /* __filename */ 246 | duk_push_undefined(ctx); /* __dirname */ 247 | duk_call(ctx, 5); 248 | 249 | /* [ ... module source result(ignore) ] */ 250 | 251 | /* module.loaded = true */ 252 | duk_push_true(ctx); 253 | duk_put_prop_string(ctx, -4, "loaded"); 254 | 255 | /* [ ... module source retval ] */ 256 | 257 | duk_pop_2(ctx); 258 | 259 | /* [ ... module ] */ 260 | 261 | return 1; 262 | } 263 | 264 | /* Load a module as the 'main' module. */ 265 | duk_ret_t duk_module_node_peval_main(duk_context *ctx, const char *path) { 266 | /* 267 | * Stack: [ ... source ] 268 | */ 269 | 270 | duk__push_module_object(ctx, path, 1 /*main*/); 271 | /* [ ... source module ] */ 272 | 273 | duk_dup(ctx, 0); 274 | /* [ ... source module source ] */ 275 | 276 | #if DUK_VERSION >= 19999 277 | return duk_safe_call(ctx, duk__eval_module_source, NULL, 2, 1); 278 | #else 279 | return duk_safe_call(ctx, duk__eval_module_source, 2, 1); 280 | #endif 281 | } 282 | 283 | void duk_module_node_init(duk_context *ctx) { 284 | /* 285 | * Stack: [ ... options ] => [ ... ] 286 | */ 287 | 288 | duk_idx_t options_idx; 289 | 290 | duk_require_object_coercible(ctx, -1); /* error before setting up requireCache */ 291 | options_idx = duk_require_normalize_index(ctx, -1); 292 | 293 | /* Initialize the require cache to a fresh object. */ 294 | duk_push_global_stash(ctx); 295 | #if DUK_VERSION >= 19999 296 | duk_push_bare_object(ctx); 297 | #else 298 | duk_push_object(ctx); 299 | duk_push_undefined(ctx); 300 | duk_set_prototype(ctx, -2); 301 | #endif 302 | duk_put_prop_string(ctx, -2, "\xff" "requireCache"); 303 | duk_pop(ctx); 304 | 305 | /* Stash callbacks for later use. User code can overwrite them later 306 | * on directly by accessing the global stash. 307 | */ 308 | duk_push_global_stash(ctx); 309 | duk_get_prop_string(ctx, options_idx, "resolve"); 310 | duk_require_function(ctx, -1); 311 | duk_put_prop_string(ctx, -2, "\xff" "modResolve"); 312 | duk_get_prop_string(ctx, options_idx, "load"); 313 | duk_require_function(ctx, -1); 314 | duk_put_prop_string(ctx, -2, "\xff" "modLoad"); 315 | duk_pop(ctx); 316 | 317 | /* Stash main module. */ 318 | duk_push_global_stash(ctx); 319 | duk_push_undefined(ctx); 320 | duk_put_prop_string(ctx, -2, "\xff" "mainModule"); 321 | duk_pop(ctx); 322 | 323 | /* register `require` as a global function. */ 324 | duk_push_global_object(ctx); 325 | duk_push_string(ctx, "require"); 326 | duk__push_require_function(ctx, ""); 327 | duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | 328 | DUK_DEFPROP_SET_WRITABLE | 329 | DUK_DEFPROP_SET_CONFIGURABLE); 330 | duk_pop(ctx); 331 | 332 | duk_pop(ctx); /* pop argument */ 333 | } 334 | -------------------------------------------------------------------------------- /extras/logging/duk_logging.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Logging support 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include "duktape.h" 9 | #include "duk_logging.h" 10 | 11 | #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || \ 12 | defined(WIN64) || defined(_WIN64) || defined(__WIN64__) 13 | /* Suppress warnings about plain fopen() etc. */ 14 | #define _CRT_SECURE_NO_WARNINGS 15 | #if defined(_MSC_VER) && (_MSC_VER < 1900) 16 | /* Workaround for snprintf() missing in older MSVC versions. 17 | * Note that _snprintf() may not NUL terminate the string, but 18 | * this difference does not matter here as a NUL terminator is 19 | * always explicitly added. 20 | */ 21 | #define snprintf _snprintf 22 | #endif 23 | #endif 24 | 25 | #if defined(__GNUC__) && !defined(__clang__) 26 | #if __GNUC__ >= 7 27 | #define DUK__LOGGING_GCC_PRAGMAS 28 | #endif 29 | #endif 30 | 31 | #if defined(DUK__LOGGING_GCC_PRAGMAS) 32 | #pragma GCC diagnostic push 33 | #pragma GCC diagnostic ignored "-Wformat-truncation" 34 | #endif 35 | 36 | /* XXX: uses stderr always for now, configurable? */ 37 | 38 | #define DUK_LOGGING_FLUSH /* Duktape 1.x: flush stderr */ 39 | 40 | /* 3-letter log level strings. */ 41 | static const char duk__log_level_strings[] = { 42 | 'T', 'R', 'C', 'D', 'B', 'G', 'I', 'N', 'F', 43 | 'W', 'R', 'N', 'E', 'R', 'R', 'F', 'T', 'L' 44 | }; 45 | 46 | /* Log method names. */ 47 | static const char *duk__log_method_names[] = { 48 | "trace", "debug", "info", "warn", "error", "fatal" 49 | }; 50 | 51 | /* Constructor. */ 52 | static duk_ret_t duk__logger_constructor(duk_context *ctx) { 53 | duk_idx_t nargs; 54 | 55 | /* Calling as a non-constructor is not meaningful. */ 56 | if (!duk_is_constructor_call(ctx)) { 57 | return DUK_RET_TYPE_ERROR; 58 | } 59 | 60 | nargs = duk_get_top(ctx); 61 | duk_set_top(ctx, 1); 62 | 63 | duk_push_this(ctx); 64 | 65 | /* [ name this ] */ 66 | 67 | if (nargs == 0) { 68 | /* Automatic defaulting of logger name from caller. This 69 | * would work poorly with tail calls, but constructor calls 70 | * are currently never tail calls, so tail calls are not an 71 | * issue now. 72 | */ 73 | 74 | duk_inspect_callstack_entry(ctx, -2); 75 | if (duk_is_object(ctx, -1)) { 76 | if (duk_get_prop_string(ctx, -1, "function")) { 77 | if (duk_get_prop_string(ctx, -1, "fileName")) { 78 | if (duk_is_string(ctx, -1)) { 79 | duk_replace(ctx, 0); 80 | } 81 | } 82 | } 83 | } 84 | /* Leave values on stack on purpose, ignored below. */ 85 | 86 | /* Stripping the filename might be a good idea 87 | * ("/foo/bar/quux.js" -> logger name "quux"), 88 | * but now used verbatim. 89 | */ 90 | } 91 | /* The stack is unbalanced here on purpose; we only rely on the 92 | * initial two values: [ name this ]. 93 | */ 94 | 95 | if (duk_is_string(ctx, 0)) { 96 | duk_dup(ctx, 0); 97 | duk_put_prop_string(ctx, 1, "n"); 98 | } else { 99 | /* Don't set 'n' at all, inherited value is used as name. 100 | * 101 | * A natural extension would be to allow an object argument 102 | * for extensible parameters, i.e. 103 | * new Duktape.Logger({ name: 'foo', ... }) 104 | */ 105 | } 106 | 107 | duk_compact(ctx, 1); 108 | 109 | return 0; /* keep default instance */ 110 | } 111 | 112 | /* Default function to format objects. Tries to use toLogString() but falls 113 | * back to toString(). Any errors are propagated out without catching. 114 | */ 115 | static duk_ret_t duk__logger_prototype_fmt(duk_context *ctx) { 116 | if (duk_get_prop_string(ctx, 0, "toLogString")) { 117 | /* [ arg toLogString ] */ 118 | 119 | duk_dup(ctx, 0); 120 | duk_call_method(ctx, 0); 121 | 122 | /* [ arg result ] */ 123 | return 1; 124 | } 125 | 126 | /* [ arg undefined ] */ 127 | duk_pop(ctx); 128 | duk_to_string(ctx, 0); 129 | return 1; 130 | } 131 | 132 | /* Default function to write a formatted log line. Writes to stderr, 133 | * appending a newline to the log line. 134 | * 135 | * The argument is a buffer; avoid coercing the buffer to a string to 136 | * avoid string table traffic. 137 | */ 138 | static duk_ret_t duk__logger_prototype_raw(duk_context *ctx) { 139 | const char *data; 140 | duk_size_t data_len; 141 | 142 | data = (const char *) duk_require_buffer_data(ctx, 0, &data_len); 143 | fwrite((const void *) data, 1, data_len, stderr); 144 | fputc((int) '\n', stderr); 145 | #if defined(DUK_LOGGING_FLUSH) 146 | fflush(stderr); 147 | #endif 148 | return 0; 149 | } 150 | 151 | /* Log frontend shared helper, magic value indicates log level. Provides 152 | * frontend functions: trace(), debug(), info(), warn(), error(), fatal(). 153 | * This needs to have small footprint, reasonable performance, minimal 154 | * memory churn, etc. 155 | */ 156 | static duk_ret_t duk__logger_prototype_log_shared(duk_context *ctx) { 157 | duk_double_t now; 158 | duk_time_components comp; 159 | duk_small_int_t entry_lev; 160 | duk_small_int_t logger_lev; 161 | duk_int_t nargs; 162 | duk_int_t i; 163 | duk_size_t tot_len; 164 | const duk_uint8_t *arg_str; 165 | duk_size_t arg_len; 166 | duk_uint8_t *buf, *p; 167 | const duk_uint8_t *q; 168 | duk_uint8_t date_buf[32]; /* maximum format length is 24+1 (NUL), round up. */ 169 | duk_size_t date_len; 170 | duk_small_int_t rc; 171 | 172 | /* XXX: sanitize to printable (and maybe ASCII) */ 173 | /* XXX: better multiline */ 174 | 175 | /* 176 | * Logger arguments are: 177 | * 178 | * magic: log level (0-5) 179 | * this: logger 180 | * stack: plain log args 181 | * 182 | * We want to minimize memory churn so a two-pass approach 183 | * is used: first pass formats arguments and computes final 184 | * string length, second pass copies strings into a buffer 185 | * allocated directly with the correct size. If the backend 186 | * function plays nice, it won't coerce the buffer to a string 187 | * (and thus intern it). 188 | */ 189 | 190 | entry_lev = duk_get_current_magic(ctx); 191 | if (entry_lev < DUK_LOG_TRACE || entry_lev > DUK_LOG_FATAL) { 192 | /* Should never happen, check just in case. */ 193 | return 0; 194 | } 195 | nargs = duk_get_top(ctx); 196 | 197 | /* [ arg1 ... argN this ] */ 198 | 199 | /* 200 | * Log level check 201 | */ 202 | 203 | duk_push_this(ctx); 204 | 205 | duk_get_prop_string(ctx, -1, "l"); 206 | logger_lev = (duk_small_int_t) duk_get_int(ctx, -1); 207 | if (entry_lev < logger_lev) { 208 | return 0; 209 | } 210 | /* log level could be popped but that's not necessary */ 211 | 212 | now = duk_get_now(ctx); 213 | duk_time_to_components(ctx, now, &comp); 214 | (void) snprintf((char *) date_buf, sizeof(date_buf), "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", 215 | (int) comp.year, (int) comp.month + 1, (int) comp.day, 216 | (int) comp.hours, (int) comp.minutes, (int) comp.seconds, 217 | (int) comp.milliseconds); 218 | date_buf[sizeof(date_buf) - 1] = 0; 219 | 220 | date_len = strlen((const char *) date_buf); 221 | 222 | duk_get_prop_string(ctx, -2, "n"); 223 | duk_to_string(ctx, -1); 224 | 225 | /* [ arg1 ... argN this loggerLevel loggerName ] */ 226 | 227 | /* 228 | * Pass 1 229 | */ 230 | 231 | /* Line format: