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