MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

add stack scanning for rooting

+109
+4
include/gc.h
··· 34 34 #define GC_UPDATE_ARGS ant_t *js, GC_FWD_OFF, GC_WEAK_OFF, GC_FWD_ARGS 35 35 #define GC_OP_VAL_ARGS GC_OP_VAL, GC_CTX 36 36 37 + #define GC_HEAP_TYPE_MASK ( \ 38 + (1u << T_OBJ) | (1u << T_PROP) | (1u << T_STR) | (1u << T_FUNC) | \ 39 + (1u << T_ARR) | (1u << T_PROMISE) | (1u << T_BIGINT) | (1u << T_GENERATOR)) 40 + 37 41 void js_gc_maybe(ant_t *js); 38 42 void js_gc_throttle(bool enabled); 39 43
+76
src/gc.c
··· 7 7 #include <stdlib.h> 8 8 #include <stdio.h> 9 9 #include <time.h> 10 + #include <setjmp.h> 10 11 11 12 #ifdef _WIN32 12 13 #include <windows.h> ··· 569 570 return fwd_lookup(&ctx->fwd, old_off); 570 571 } 571 572 573 + static inline bool gc_get_stack_bounds(void *base, uintptr_t *lo, uintptr_t *hi) { 574 + if (!base) return false; 575 + volatile uint8_t sp_marker; 576 + uintptr_t bp = (uintptr_t)base; 577 + uintptr_t sp = (uintptr_t)&sp_marker; 578 + if (sp < bp) { *lo = sp; *hi = bp; } 579 + else { *lo = bp; *hi = sp; } 580 + *lo &= ~(uintptr_t)7; 581 + *hi &= ~(uintptr_t)7; 582 + return true; 583 + } 584 + 585 + __attribute__((noinline)) 586 + static void gc_scan_stack_reserve(gc_ctx_t *ctx) { 587 + jmp_buf jb; 588 + if (setjmp(jb) != 0) return; 589 + 590 + uintptr_t lo, hi; 591 + if (!gc_get_stack_bounds(ctx->js->cstk, &lo, &hi)) return; 592 + 593 + jsoff_t old_brk = ctx->js->brk; 594 + 595 + for (uintptr_t addr = lo; addr < hi; addr += sizeof(uint64_t)) { 596 + uint64_t w; 597 + memcpy(&w, (void *)addr, sizeof(w)); 598 + 599 + if ((w >> 53) != NANBOX_PREFIX_CHK) continue; 600 + 601 + uint8_t type = (w >> NANBOX_TYPE_SHIFT) & NANBOX_TYPE_MASK; 602 + if (type > T_FFI) continue; 603 + if (!((1u << type) & GC_HEAP_TYPE_MASK)) continue; 604 + 605 + jsoff_t old_off = (jsoff_t)(w & NANBOX_DATA_MASK); 606 + if (old_off == 0 || old_off >= old_brk) continue; 607 + 608 + if (fwd_lookup(&ctx->fwd, old_off) != (jsoff_t)~0) continue; 609 + gc_update_val(ctx, w); 610 + } 611 + } 612 + 613 + __attribute__((noinline)) 614 + static void gc_scan_stack_update(gc_ctx_t *ctx) { 615 + jmp_buf jb; 616 + if (setjmp(jb) != 0) return; 617 + 618 + uintptr_t lo, hi; 619 + if (!gc_get_stack_bounds(ctx->js->cstk, &lo, &hi)) return; 620 + 621 + jsoff_t old_brk = ctx->js->brk; 622 + 623 + for (uintptr_t addr = lo; addr < hi; addr += sizeof(uint64_t)) { 624 + uint64_t w; 625 + memcpy(&w, (void *)addr, sizeof(w)); 626 + 627 + if ((w >> 53) != NANBOX_PREFIX_CHK) continue; 628 + 629 + uint8_t type = (w >> NANBOX_TYPE_SHIFT) & NANBOX_TYPE_MASK; 630 + if (type > T_FFI) continue; 631 + if (!((1u << type) & GC_HEAP_TYPE_MASK)) continue; 632 + 633 + jsoff_t old_off = (jsoff_t)(w & NANBOX_DATA_MASK); 634 + if (old_off == 0 || old_off >= old_brk) continue; 635 + 636 + jsoff_t new_off = fwd_lookup(&ctx->fwd, old_off); 637 + if (new_off == (jsoff_t)~0 || new_off == old_off) continue; 638 + 639 + uint64_t updated = gc_mkval(type, new_off); 640 + memcpy((void *)addr, &updated, sizeof(updated)); 641 + } 642 + } 643 + 572 644 size_t js_gc_compact(ant_t *js) { 573 645 if (!js || js->brk == 0) return 0; 574 646 if (js->brk < 2 * 1024 * 1024) return 0; ··· 653 725 &ctx 654 726 ); gc_drain_work_queue(&ctx); 655 727 728 + gc_scan_stack_reserve(&ctx); 729 + gc_drain_work_queue(&ctx); 730 + 656 731 if (ctx.failed) { 657 732 free(mark_bits); 658 733 work_free(&ctx.work); ··· 666 741 gc_apply_val_callback, 667 742 &ctx); 668 743 744 + gc_scan_stack_update(&ctx); 669 745 memcpy(js->mem, new_mem, ctx.new_brk); 670 746 js->brk = ctx.new_brk; 671 747
+29
tests/test_gc_stress10.js
··· 1 + import { Screen, colors, codes, pad, truncate } from '../examples/tui/tuey.js'; 2 + 3 + const screen = new Screen({ fullscreen: false, hideCursor: false }); 4 + 5 + const logs = [ 6 + { time: '10:23:45', level: 'INFO', message: 'Application started' }, 7 + { time: '10:23:48', level: 'WARN', message: 'Cache directory not found, creating...' }, 8 + { time: '10:24:05', level: 'ERROR', message: 'Failed to load plugin: missing-plugin' }, 9 + { time: '10:24:06', level: 'WARN', message: 'Running with reduced functionality' }, 10 + { time: '10:24:10', level: 'INFO', message: 'Ready to accept connections' }, 11 + ]; 12 + 13 + function render() { 14 + screen.write(2, 14, colors.bold + colors.magenta + 'โ”Œโ”€ Recent Activity โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”' + codes.reset); 15 + for (let i = 0; i < 5; i++) { 16 + const log = logs[i]; 17 + const levelColor = log.level === 'ERROR' ? colors.red : log.level === 'WARN' ? colors.yellow : colors.green; 18 + screen.write(2, 15 + i, colors.magenta + 'โ”‚' + codes.reset); 19 + screen.write(4, 15 + i, `${colors.dim}${log.time}${codes.reset} ${levelColor}${log.level}${codes.reset} ${truncate(log.message, 30)}`); 20 + screen.write(45, 15 + i, colors.magenta + 'โ”‚' + codes.reset); 21 + } 22 + screen.write(2, 20, colors.magenta + 'โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜' + codes.reset); 23 + } 24 + 25 + for (let frame = 0; frame < 50000; frame++) { 26 + render(); 27 + } 28 + 29 + console.log('OK');