From 1e1b9fc960d7d78e433da30137992ad7f6679c8d Mon Sep 17 00:00:00 2001 From: theMackabu Date: Sat, 10 Jan 2026 22:53:48 -0800 Subject: [PATCH] refactor: replace static buffers with dynamic allocations --- README.txt | 89 +++++++++++- examples/demo/mandelbrot.js | 65 +++++++++ src/ant.c | 280 ++++++++++++++++++++++-------------- tests/mandelbrot.js | 39 ----- 4 files changed, 324 insertions(+), 149 deletions(-) create mode 100644 examples/demo/mandelbrot.js delete mode 100644 tests/mandelbrot.js diff --git a/README.txt b/README.txt index a2effed..3daa4ea 100644 --- a/README.txt +++ b/README.txt @@ -5,12 +5,17 @@ A minimal embedded JavaScript engine with async/await, promises, modules, and built-in APIs for HTTP servers, timers, crypto, and JSON. Check about my blog post about Ant! https://s.tail.so/js-in-one-month +INSTALL: +``` +curl -fsSL https://ant.themackabu.com/install | bash + +# or with MbedTLS (darwin only) +curl -fsSL https://ant.themackabu.com/install | MBEDTLS=1 bash +``` + BUILD: meson setup build && meson compile -C build -RUN: - ./build/ant script.js - EXAMPLE: See examples/server/server.js for a good server example using Ant.serve() with `rou3` routing, parameter handling, and various response types. @@ -20,6 +25,7 @@ MODULES: HTTP & Networking: - Ant.serve() - HTTP server with uv_tcp (TLS support via tlsuv) - fetch() - HTTP client with TLS support (GET, POST, etc.) + - URL module imports - Import directly from URLs Timers & Scheduling (built-in): - setTimeout() - Execute callback after delay @@ -55,6 +61,48 @@ MODULES: - result.stdout - Raw stdout - result.stderr - Raw stderr + Child Process (import from 'child_process'): + - spawn() - Spawn child process with async streaming + - exec() - Execute command and buffer output + - execSync() - Execute command synchronously + - spawnSync() - Spawn synchronous child process + - fork() - Fork module as child process + - ChildProcess events - on('exit'), on('close'), on('error'), on('data') + - kill() - Send signal to child process + + Readline (import from 'readline'): + - createInterface() - Create readline interface + - question() - Prompt for user input with promise support + - on('line') - Handle line input events + - on('close') - Handle interface close + - pause()/resume() - Control input stream + - close() - Close interface + - History support - Command history with navigation + + OS (import from 'os'): + - arch() - CPU architecture (x64, arm64, etc.) + - platform() - Operating system platform + - type() - Operating system name + - release() - OS release version + - version() - OS version string + - hostname() - System hostname + - homedir() - User home directory + - tmpdir() - Temporary directory path + - cpus() - CPU core information + - totalmem() - Total system memory + - freemem() - Free system memory + - uptime() - System uptime in seconds + - networkInterfaces() - Network interface information + - userInfo() - Current user information + - constants - OS signals and errno constants + - EOL - Platform-specific end-of-line marker + + Navigator + - navigator.userAgent - User agent string + - navigator.platform - Platform string + - navigator.hardwareConcurrency - CPU thread count + - navigator.locks - Web Locks API for coordination + Cryptography (crypto): - random() - Cryptographically secure random number - randomBytes() - Generate random bytes @@ -81,6 +129,25 @@ MODULES: - exit() - Exit process with code - cpuUsage() - Get CPU usage statistics + Performance: + - performance.now() - High-resolution timestamp + - performance.timeOrigin - Time origin for measurements + + Ant Global + - Ant.version - Runtime version + - Ant.target - Build target + - Ant.revision - Git revision + - Ant.buildDate - Build date + - Ant.serve() - Start HTTP server + - Ant.signal() - Register signal handlers + - Ant.sleep() - Sleep in seconds + - Ant.msleep() - Sleep in milliseconds + - Ant.usleep() - Sleep in microseconds + - Ant.gc() - Trigger garbage collection + - Ant.alloc() - Get memory allocation info + - Ant.stats() - Get runtime statistics + - Ant.typeof() - Get internal type name + JSON: - JSON.parse() - Parse JSON string - JSON.stringify() - Stringify to JSON @@ -177,6 +244,16 @@ MODULES: - import 'ant:path' - Import built-in path module - import 'ant:shell' - Import built-in shell module ($`...`) - import 'ant:ffi' - Import built-in FFI module + - import 'node:*' - Node.js-style module aliases + - import from URL - Import modules from HTTP/HTTPS URLs + - import '.json' - Import JSON files as modules + - import '.txt' - Import text files as strings + +TYPESCRIPT: + Built-in TypeScript type stripping via oxc (no type checking, strip only): + - Run .ts files directly: ./build/ant script.ts + - Type annotations are stripped at parse time + - Full type definitions available in src/types/ JAVASCRIPT FEATURES: Full ES1-ES5 compliance with ES6+ extensions: @@ -218,6 +295,7 @@ JAVASCRIPT FEATURES: - Default parameters in functions - Array and object destructuring with defaults - Computed property names in object literals + - Shebang support (#!/usr/bin/env ant) CONCURRENCY: - Minicoro-based coroutines for async/await @@ -228,7 +306,7 @@ CONCURRENCY: - Virtual memory allocation for coroutine stacks SYSTEM: - - Signal handlers (SIGINT, SIGTERM, etc.) + - Signal handlers (SIGINT, SIGTERM, etc.) via Ant.signal() - Mark-copy compacting garbage collector + Boehm-Demers-Weiser - Coroutine execution tracking for proper GC - Forward reference tracking for memory compaction @@ -238,8 +316,9 @@ SYSTEM: - Non-configurable properties support (Object.defineProperty) - Native library integration via FFI - libuv-based async I/O for files and networking + - TLS support via mbedtls or tlsuv - LTO (Link Time Optimization) build support - Gzip compression support for HTTP responses LICENSE: - MIT License - See LICENSE.txt for details \ No newline at end of file + MIT License - See LICENSE.txt for details diff --git a/examples/demo/mandelbrot.js b/examples/demo/mandelbrot.js new file mode 100644 index 0000000..f2379f7 --- /dev/null +++ b/examples/demo/mandelbrot.js @@ -0,0 +1,65 @@ +function mandelbrot(center_x, center_y, scale, w, h, max_it) { + const colors = [14, 15, 7, 8, 0, 4, 12, 5, 13, 1, 9, 3, 11, 10, 2, 6]; + const numColors = colors.length; + const fx = (scale * 0.5) / Math.min(w, h); + const fy = fx * 2; + const halfW = w * 0.5; + const halfH = h * 0.5; + + const cxArr = new Float64Array(w); + for (let x1 = 0; x1 < w; x1++) { + cxArr[x1] = (x1 - halfW) * fx + center_x; + } + + function iterate(cx, cy) { + const q = (cx - 0.25) * (cx - 0.25) + cy * cy; + if (q * (q + (cx - 0.25)) <= 0.25 * cy * cy) return max_it; + + const cx1 = cx + 1; + if (cx1 * cx1 + cy * cy <= 0.0625) return max_it; + + let x = 0, + y = 0, + x2 = 0, + y2 = 0; + let i = 0; + + while (i < max_it && x2 + y2 < 4) { + y = 2 * x * y + cy; + x = x2 - y2 + cx; + x2 = x * x; + y2 = y * y; + i++; + } + return i; + } + + const output = []; + + for (let y1 = 0; y1 < h; y1++) { + const cy0 = (y1 - halfH) * fy + center_y; + const cy1 = (y1 + 0.5 - halfH) * fy + center_y; + const chars = new Array(w + 1); + + for (let x1 = 0; x1 < w; x1++) { + const cx = cxArr[x1]; + + const i0 = iterate(cx, cy0); + const i1 = iterate(cx, cy1); + + const c0 = i0 >= max_it ? 0 : colors[i0 % numColors]; + const c1 = i1 >= max_it ? 0 : colors[i1 % numColors]; + + const fg = c0 >= 8 ? 82 + c0 : 30 + c0; + const bg = c1 >= 8 ? 92 + c1 : 40 + c1; + + chars[x1] = '\x1b[' + fg + ';' + bg + 'm\u2580'; + } + chars[w] = '\x1b[0m'; + output.push(chars.join('')); + } + + console.log(output.join('\n')); +} + +mandelbrot(-0.75, 0.0, 2.0, 80, 25, 50); diff --git a/src/ant.c b/src/ant.c index 7d49387..5868330 100644 --- a/src/ant.c +++ b/src/ant.c @@ -2886,20 +2886,26 @@ static jsval_t builtin_bigint_toString(struct js *js, jsval_t *args, int nargs) const char *digits = bigint_digits(js, val, &dlen); if (radix == 10) { - char buf[1024]; + size_t buflen = dlen + 2; + char *buf = (char *)ANT_GC_MALLOC(buflen); + if (!buf) return js_mkerr(js, "oom"); size_t n = 0; if (neg) buf[n++] = '-'; memcpy(buf + n, digits, dlen); n += dlen; - return js_mkstr(js, buf, n); + jsval_t ret = js_mkstr(js, buf, n); + ANT_GC_FREE(buf); + return ret; } - char result[2048]; - size_t rpos = sizeof(result) - 1; + size_t result_cap = dlen * 4 + 16; + char *result = (char *)ANT_GC_MALLOC(result_cap); + if (!result) return js_mkerr(js, "oom"); + size_t rpos = result_cap - 1; result[rpos] = '\0'; - char *num = (char *)malloc(dlen + 1); - if (!num) return js_mkerr(js, "oom"); + char *num = (char *)ANT_GC_MALLOC(dlen + 1); + if (!num) { ANT_GC_FREE(result); return js_mkerr(js, "oom"); } memcpy(num, digits, dlen); num[dlen] = '\0'; size_t numlen = dlen; @@ -2916,19 +2922,32 @@ static jsval_t builtin_bigint_toString(struct js *js, jsval_t *args, int nargs) memmove(num, num + start, numlen - start + 1); numlen -= start; if (numlen == 1 && num[0] == '0') numlen = 0; + if (rpos == 0) { + size_t new_cap = result_cap * 2; + char *new_result = (char *)ANT_GC_MALLOC(new_cap); + if (!new_result) { ANT_GC_FREE(num); ANT_GC_FREE(result); return js_mkerr(js, "oom"); } + size_t used = result_cap - rpos; + memcpy(new_result + new_cap - used, result + rpos, used); + ANT_GC_FREE(result); + result = new_result; + rpos = new_cap - used; + result_cap = new_cap; + } rpos--; result[rpos] = remainder < 10 ? '0' + remainder : 'a' + (remainder - 10); } - free(num); + ANT_GC_FREE(num); - if (rpos == sizeof(result) - 1) { + if (rpos == result_cap - 1) { result[--rpos] = '0'; } if (neg) result[--rpos] = '-'; - return js_mkstr(js, result + rpos, sizeof(result) - 1 - rpos); + jsval_t ret = js_mkstr(js, result + rpos, result_cap - 1 - rpos); + ANT_GC_FREE(result); + return ret; } static jsval_t mkobj(struct js *js, jsoff_t parent) { @@ -14769,15 +14788,24 @@ static jsval_t builtin_array_join(struct js *js, jsval_t *args, int nargs) { } if (len == 0) return js_mkstr(js, "", 0); - char result[1024] = ""; + + size_t capacity = 1024; size_t result_len = 0; + char *result = (char *)ANT_GC_MALLOC(capacity); + if (!result) return js_mkerr(js, "oom"); for (jsoff_t i = 0; i < len; i++) { char idxstr[16]; size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)i); jsoff_t elem_off = lkp(js, arr, idxstr, idxlen); - if (i > 0 && result_len + sep_len < sizeof(result)) { + if (i > 0) { + if (result_len + sep_len >= capacity) { + capacity = (result_len + sep_len + 1) * 2; + char *new_result = (char *)ANT_GC_REALLOC(result, capacity); + if (!new_result) return js_mkerr(js, "oom"); + result = new_result; + } memcpy(result + result_len, sep, sep_len); result_len += sep_len; } @@ -14786,29 +14814,43 @@ static jsval_t builtin_array_join(struct js *js, jsval_t *args, int nargs) { jsval_t elem = resolveprop(js, mkval(T_PROP, elem_off)); if (vtype(elem) == T_STR) { jsoff_t elem_len, elem_off_str = vstr(js, elem, &elem_len); - if (result_len + elem_len < sizeof(result)) { - memcpy(result + result_len, &js->mem[elem_off_str], elem_len); - result_len += elem_len; + if (result_len + elem_len >= capacity) { + capacity = (result_len + elem_len + 1) * 2; + char *new_result = (char *)ANT_GC_REALLOC(result, capacity); + if (!new_result) return js_mkerr(js, "oom"); + result = new_result; } + memcpy(result + result_len, &js->mem[elem_off_str], elem_len); + result_len += elem_len; } else if (vtype(elem) == T_NUM) { char numstr[32]; snprintf(numstr, sizeof(numstr), "%g", tod(elem)); size_t num_len = strlen(numstr); - if (result_len + num_len < sizeof(result)) { - memcpy(result + result_len, numstr, num_len); - result_len += num_len; + if (result_len + num_len >= capacity) { + capacity = (result_len + num_len + 1) * 2; + char *new_result = (char *)ANT_GC_REALLOC(result, capacity); + if (!new_result) return js_mkerr(js, "oom"); + result = new_result; } + memcpy(result + result_len, numstr, num_len); + result_len += num_len; } else if (vtype(elem) == T_BOOL) { const char *boolstr = vdata(elem) ? "true" : "false"; size_t bool_len = strlen(boolstr); - if (result_len + bool_len < sizeof(result)) { - memcpy(result + result_len, boolstr, bool_len); - result_len += bool_len; + if (result_len + bool_len >= capacity) { + capacity = (result_len + bool_len + 1) * 2; + char *new_result = (char *)ANT_GC_REALLOC(result, capacity); + if (!new_result) return js_mkerr(js, "oom"); + result = new_result; } + memcpy(result + result_len, boolstr, bool_len); + result_len += bool_len; } } } - return js_mkstr(js, result, result_len); + jsval_t ret = js_mkstr(js, result, result_len); + ANT_GC_FREE(result); + return ret; } static jsval_t builtin_array_includes(struct js *js, jsval_t *args, int nargs) { @@ -16750,7 +16792,7 @@ static jsval_t builtin_string_replace(struct js *js, jsval_t *args, int nargs) { bool is_regex = false; bool global_flag = false; bool is_func_replacement = (vtype(replacement) == T_FUNC); - char pattern_buf[256]; + char *pattern_buf = NULL; jsoff_t pattern_len = 0; if (vtype(search) == T_OBJ) { @@ -16762,9 +16804,11 @@ static jsval_t builtin_string_replace(struct js *js, jsval_t *args, int nargs) { is_regex = true; jsoff_t plen, poff = vstr(js, pattern_val, &plen); - pattern_len = plen < sizeof(pattern_buf) - 1 ? plen : sizeof(pattern_buf) - 1; - memcpy(pattern_buf, &js->mem[poff], pattern_len); - pattern_buf[pattern_len] = '\0'; + pattern_len = plen; + pattern_buf = (char *)ANT_GC_MALLOC(plen + 1); + if (!pattern_buf) return js_mkerr(js, "oom"); + memcpy(pattern_buf, &js->mem[poff], plen); + pattern_buf[plen] = '\0'; jsoff_t flags_off = lkp(js, search, "flags", 5); if (flags_off == 0) goto not_regex; @@ -16789,17 +16833,31 @@ static jsval_t builtin_string_replace(struct js *js, jsval_t *args, int nargs) { repl_ptr = (char *) &js->mem[repl_off]; } - char result[4096]; - jsoff_t result_len = 0; + size_t result_cap = str_len + repl_len + 256; + size_t result_len = 0; + char *result = (char *)ANT_GC_MALLOC(result_cap); + if (!result) return js_mkerr(js, "oom"); + +#define ENSURE_RESULT_CAP(need) do { \ + if (result_len + (need) >= result_cap) { \ + result_cap = (result_len + (need) + 1) * 2; \ + char *nr = (char *)ANT_GC_REALLOC(result, result_cap); \ + if (!nr) return js_mkerr(js, "oom"); \ + result = nr; \ + } \ +} while(0) if (is_regex) { - char pcre2_pattern[512]; - size_t pcre2_len = js_to_pcre2_pattern(pattern_buf, pattern_len, pcre2_pattern, sizeof(pcre2_pattern)); + size_t pcre2_cap = pattern_len * 2 + 16; + char *pcre2_pattern = (char *)ANT_GC_MALLOC(pcre2_cap); + if (!pcre2_pattern) return js_mkerr(js, "oom"); + size_t pcre2_len = js_to_pcre2_pattern(pattern_buf, pattern_len, pcre2_pattern, pcre2_cap); uint32_t options = PCRE2_UTF | PCRE2_UCP | PCRE2_MATCH_UNSET_BACKREF; int errcode; PCRE2_SIZE erroffset; pcre2_code *re = pcre2_compile((PCRE2_SPTR)pcre2_pattern, pcre2_len, options, &errcode, &erroffset, NULL); + ANT_GC_FREE(pcre2_pattern); if (re == NULL) return js_mkerr(js, "invalid regex pattern"); pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(re, NULL); @@ -16818,10 +16876,9 @@ static jsval_t builtin_string_replace(struct js *js, jsval_t *args, int nargs) { PCRE2_SIZE match_end = ovector[1]; PCRE2_SIZE before_len = match_start - pos; - if (result_len + before_len < sizeof(result)) { - memcpy(result + result_len, str_ptr + pos, before_len); - result_len += (jsoff_t)before_len; - } + ENSURE_RESULT_CAP(before_len); + memcpy(result + result_len, str_ptr + pos, before_len); + result_len += before_len; if (is_func_replacement) { int nargs_cb = 1 + capture_count + 2; @@ -16855,44 +16912,40 @@ static jsval_t builtin_string_replace(struct js *js, jsval_t *args, int nargs) { if (vtype(cb_result) == T_STR) { jsoff_t cb_len, cb_off = vstr(js, cb_result, &cb_len); - if (result_len + cb_len < sizeof(result)) { - memcpy(result + result_len, &js->mem[cb_off], cb_len); - result_len += cb_len; - } + ENSURE_RESULT_CAP(cb_len); + memcpy(result + result_len, &js->mem[cb_off], cb_len); + result_len += cb_len; } else { char numbuf[32]; size_t n = tostr(js, cb_result, numbuf, sizeof(numbuf)); - if (result_len + n < sizeof(result)) { - memcpy(result + result_len, numbuf, n); - result_len += (jsoff_t)n; - } + ENSURE_RESULT_CAP(n); + memcpy(result + result_len, numbuf, n); + result_len += n; } } else { - for (jsoff_t ri = 0; ri < repl_len && result_len < sizeof(result); ) { + for (jsoff_t ri = 0; ri < repl_len; ) { if (repl_ptr[ri] == '$' && ri + 1 < repl_len) { char next = repl_ptr[ri + 1]; if (next == '$') { + ENSURE_RESULT_CAP(1); result[result_len++] = '$'; ri += 2; } else if (next == '&') { PCRE2_SIZE mlen = match_end - match_start; - if (result_len + mlen < sizeof(result)) { - memcpy(result + result_len, str_ptr + match_start, mlen); - result_len += (jsoff_t)mlen; - } + ENSURE_RESULT_CAP(mlen); + memcpy(result + result_len, str_ptr + match_start, mlen); + result_len += mlen; ri += 2; } else if (next == '`') { - if (result_len + match_start < sizeof(result)) { - memcpy(result + result_len, str_ptr, match_start); - result_len += (jsoff_t)match_start; - } + ENSURE_RESULT_CAP(match_start); + memcpy(result + result_len, str_ptr, match_start); + result_len += match_start; ri += 2; } else if (next == '\'') { PCRE2_SIZE after_len = str_len - match_end; - if (result_len + after_len < sizeof(result)) { - memcpy(result + result_len, str_ptr + match_end, after_len); - result_len += (jsoff_t)after_len; - } + ENSURE_RESULT_CAP(after_len); + memcpy(result + result_len, str_ptr + match_end, after_len); + result_len += after_len; ri += 2; } else if (next >= '0' && next <= '9') { int group_num = next - '0'; @@ -16910,99 +16963,105 @@ static jsval_t builtin_string_replace(struct js *js, jsval_t *args, int nargs) { PCRE2_SIZE cap_end = ovector[2*group_num+1]; if (cap_start != PCRE2_UNSET) { PCRE2_SIZE cap_len = cap_end - cap_start; - if (result_len + cap_len < sizeof(result)) { - memcpy(result + result_len, str_ptr + cap_start, cap_len); - result_len += (jsoff_t)cap_len; - } + ENSURE_RESULT_CAP(cap_len); + memcpy(result + result_len, str_ptr + cap_start, cap_len); + result_len += cap_len; } } else { + ENSURE_RESULT_CAP(2); result[result_len++] = '$'; - if (result_len < sizeof(result)) result[result_len++] = next; + result[result_len++] = next; } } else { + ENSURE_RESULT_CAP(1); result[result_len++] = repl_ptr[ri++]; } } else { + ENSURE_RESULT_CAP(1); result[result_len++] = repl_ptr[ri++]; } } } if (match_start == match_end) { - if (pos < str_len && result_len < sizeof(result)) { + if (pos < str_len) { + ENSURE_RESULT_CAP(1); result[result_len++] = str_ptr[pos]; } pos = match_end + 1; - } else { - pos = match_end; - } + } else pos = match_end; replaced = true; if (!global_flag) break; } - if (pos < str_len && result_len + (str_len - pos) < sizeof(result)) { - memcpy(result + result_len, str_ptr + pos, str_len - pos); - result_len += (jsoff_t)(str_len - pos); + if (pos < str_len) { + size_t remaining = str_len - pos; + ENSURE_RESULT_CAP(remaining); + memcpy(result + result_len, str_ptr + pos, remaining); + result_len += remaining; } pcre2_match_data_free(match_data); pcre2_code_free(re); - return replaced ? js_mkstr(js, result, result_len) : str; + jsval_t ret = replaced ? js_mkstr(js, result, result_len) : str; + ANT_GC_FREE(result); + return ret; } else { - if (vtype(search) != T_STR) return str; + if (vtype(search) != T_STR) { ANT_GC_FREE(result); return str; } jsoff_t search_len, search_off = vstr(js, search, &search_len); const char *search_ptr = (char *) &js->mem[search_off]; - if (search_len > str_len) return str; + if (search_len > str_len) { ANT_GC_FREE(result); return str; } for (jsoff_t i = 0; i <= str_len - search_len; i++) { if (memcmp(str_ptr + i, search_ptr, search_len) == 0) { - if (result_len + i < sizeof(result)) { - memcpy(result + result_len, str_ptr, i); - result_len += i; - } + ENSURE_RESULT_CAP(i); + memcpy(result + result_len, str_ptr, i); + result_len += i; if (is_func_replacement) { jsval_t match_str = js_mkstr(js, search_ptr, search_len); jsval_t cb_args[1] = { match_str }; jsval_t cb_result = js_call(js, replacement, cb_args, 1); - if (vtype(cb_result) == T_ERR) return cb_result; + if (vtype(cb_result) == T_ERR) { ANT_GC_FREE(result); return cb_result; } if (vtype(cb_result) == T_STR) { jsoff_t cb_len, cb_off = vstr(js, cb_result, &cb_len); - if (result_len + cb_len < sizeof(result)) { - memcpy(result + result_len, &js->mem[cb_off], cb_len); - result_len += cb_len; - } + ENSURE_RESULT_CAP(cb_len); + memcpy(result + result_len, &js->mem[cb_off], cb_len); + result_len += cb_len; } else { char numbuf[32]; size_t n = tostr(js, cb_result, numbuf, sizeof(numbuf)); - if (result_len + n < sizeof(result)) { - memcpy(result + result_len, numbuf, n); - result_len += (jsoff_t)n; - } + ENSURE_RESULT_CAP(n); + memcpy(result + result_len, numbuf, n); + result_len += n; } } else { - if (result_len + repl_len < sizeof(result)) { - memcpy(result + result_len, repl_ptr, repl_len); - result_len += repl_len; - } + ENSURE_RESULT_CAP(repl_len); + memcpy(result + result_len, repl_ptr, repl_len); + result_len += repl_len; } jsoff_t after_start = i + search_len; jsoff_t after_len = str_len - after_start; - if (after_len > 0 && result_len + after_len < sizeof(result)) { + if (after_len > 0) { + ENSURE_RESULT_CAP(after_len); memcpy(result + result_len, str_ptr + after_start, after_len); result_len += after_len; } - return js_mkstr(js, result, result_len); + jsval_t ret = js_mkstr(js, result, result_len); + ANT_GC_FREE(result); + return ret; } } + ANT_GC_FREE(result); return str; } +#undef ENSURE_RESULT_CAP } static jsval_t builtin_string_replaceAll(struct js *js, jsval_t *args, int nargs) { @@ -17273,11 +17332,22 @@ static jsval_t builtin_string_template(struct js *js, jsval_t *args, int nargs) jsoff_t str_len, str_off = vstr(js, str, &str_len); const char *str_ptr = (char *) &js->mem[str_off]; - char result[2048]; - jsoff_t result_len = 0; + size_t result_cap = str_len + 256; + size_t result_len = 0; + char *result = (char *)ANT_GC_MALLOC(result_cap); + if (!result) return js_mkerr(js, "oom"); jsoff_t i = 0; + +#define ENSURE_CAP(need) do { \ + if (result_len + (need) >= result_cap) { \ + result_cap = (result_len + (need) + 1) * 2; \ + char *nr = (char *)ANT_GC_REALLOC(result, result_cap); \ + if (!nr) return js_mkerr(js, "oom"); \ + result = nr; \ + } \ +} while(0) - while (i < str_len && result_len < sizeof(result) - 1) { + while (i < str_len) { if (i < str_len - 3 && str_ptr[i] == '{' && str_ptr[i + 1] == '{') { jsoff_t start = i + 2; jsoff_t end = start; @@ -17292,35 +17362,35 @@ static jsval_t builtin_string_template(struct js *js, jsval_t *args, int nargs) jsval_t value = resolveprop(js, mkval(T_PROP, prop_off)); if (vtype(value) == T_STR) { jsoff_t val_len, val_off = vstr(js, value, &val_len); - if (result_len + val_len < sizeof(result)) { - memcpy(result + result_len, &js->mem[val_off], val_len); - result_len += val_len; - } + ENSURE_CAP(val_len); + memcpy(result + result_len, &js->mem[val_off], val_len); + result_len += val_len; } else if (vtype(value) == T_NUM) { char numstr[32]; snprintf(numstr, sizeof(numstr), "%g", tod(value)); size_t num_len = strlen(numstr); - if (result_len + num_len < sizeof(result)) { - memcpy(result + result_len, numstr, num_len); - result_len += num_len; - } + ENSURE_CAP(num_len); + memcpy(result + result_len, numstr, num_len); + result_len += num_len; } else if (vtype(value) == T_BOOL) { const char *boolstr = vdata(value) ? "true" : "false"; size_t bool_len = strlen(boolstr); - if (result_len + bool_len < sizeof(result)) { - memcpy(result + result_len, boolstr, bool_len); - result_len += bool_len; - } + ENSURE_CAP(bool_len); + memcpy(result + result_len, boolstr, bool_len); + result_len += bool_len; } - } else { } i = end + 2; continue; } } + ENSURE_CAP(1); result[result_len++] = str_ptr[i++]; } - return js_mkstr(js, result, result_len); + jsval_t ret = js_mkstr(js, result, result_len); + ANT_GC_FREE(result); + return ret; +#undef ENSURE_CAP } static jsval_t builtin_string_charCodeAt(struct js *js, jsval_t *args, int nargs) { diff --git a/tests/mandelbrot.js b/tests/mandelbrot.js deleted file mode 100644 index 40b6dfa..0000000 --- a/tests/mandelbrot.js +++ /dev/null @@ -1,39 +0,0 @@ -/* Mandelbrot display on a color terminal - (c) 2025 Fabrice Bellard - MIT license -*/ -function mandelbrot(center_x, center_y, scale, w, h, max_it) -{ - var x1, y1, y2, i, x, y, cx, cy, fx, fy, i, t, c, s, c0; - var colors = [ 14, 15, 7, 8, 0, 4, 12, 5, 13, 1, 9, 3, 11, 10, 2, 6]; - fx = scale * 0.5 / Math.min(w, h); - fy = fx * 2; - for(y1 = 0; y1 < h; y1++) { - s = ""; - for(x1 = 0; x1 < w; x1++) { - for(y2 = 0; y2 < 2; y2++) { - cx = (x1 - w * 0.5) * fx + center_x; - cy = (y1 + y2 * 0.5 - h * 0.5) * fy + center_y; - x = 0; - y = 0; - for(i = 0; i < max_it && x * x + y * y < 4; i++) { - t = x * x - y * y + cx; - y = 2 * x * y + cy; - x = t; - } - if (i >= max_it) { - c = 0; - } else { - c = colors[i % colors.length]; - } - if (y2 == 0) - c0 = c; - } - s += "\x1b[" + (c0 >= 8 ? 82 + c0 : 30 + c0) + ";" + (c >= 8 ? 92 + c : 40 + c) + "m\u2580"; - } - s += "\x1b[0m"; /* reset the colors */ - console.log(s); - } -} - -mandelbrot(-0.75, 0.0, 2.0, 80, 25, 50); -- 2.51.2