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.

slot in vm range upval capture

+137 -14
+13 -4
include/silver/engine.h
··· 383 383 return frame && frame->func && frame->func->is_strict; 384 384 } 385 385 386 + static inline bool sv_slot_in_range( 387 + const ant_value_t *base, size_t count, 388 + const ant_value_t *slot 389 + ) { 390 + if (!base || !slot || count == 0) return false; 391 + 392 + uintptr_t lo = (uintptr_t)base; 393 + uintptr_t hi = lo + count * sizeof(*base); 394 + uintptr_t addr = (uintptr_t)slot; 395 + return addr >= lo && addr < hi; 396 + } 397 + 386 398 static inline bool sv_slot_in_vm_stack(const sv_vm_t *vm, const ant_value_t *slot) { 387 - return 388 - vm && vm->stack && slot && 389 - slot >= vm->stack && 390 - slot < vm->stack + vm->stack_size; 399 + return vm && sv_slot_in_range(vm->stack, (size_t)vm->stack_size, slot); 391 400 } 392 401 393 402 static inline bool sv_is_nullish_this(ant_value_t v) {
+1 -2
src/silver/engine.c
··· 86 86 } 87 87 for (sv_upvalue_t *uv = vm->open_upvalues; uv; uv = uv->next) if ( 88 88 uv->location != &uv->closed && 89 - uv->location >= old && 90 - uv->location < old + old_size 89 + sv_slot_in_range(old, (size_t)old_size, uv->location) 91 90 ) uv->location += delta; 92 91 } 93 92
+1 -1
src/silver/ops/exceptions.h
··· 27 27 while (*pp) { 28 28 sv_upvalue_t *uv = *pp; 29 29 ant_value_t *loc = uv->location; 30 - if (loc >= slot && sv_slot_in_vm_stack(vm, loc)) { 30 + if (sv_slot_in_vm_stack(vm, loc) && loc >= slot) { 31 31 uv->closed = *loc; 32 32 uv->location = &uv->closed; 33 33 *pp = uv->next;
+8 -7
src/silver/ops/upvalues.h
··· 62 62 63 63 sv_upvalue_t **pp = &vm->open_upvalues; 64 64 while (*pp) { 65 - sv_upvalue_t *uv = *pp; 66 - if (uv->location >= slot) { 67 - uv->closed = *uv->location; 68 - uv->location = &uv->closed; 69 - *pp = uv->next; 70 - } else pp = &uv->next; 65 + sv_upvalue_t *uv = *pp; 66 + ant_value_t *loc = uv->location; 67 + if (sv_slot_in_vm_stack(vm, loc) && loc >= slot) { 68 + uv->closed = *loc; 69 + uv->location = &uv->closed; 70 + *pp = uv->next; 71 71 } 72 - } 72 + else pp = &uv->next; 73 + }} 73 74 74 75 static inline sv_upvalue_t *sv_capture_upvalue(sv_vm_t *vm, ant_value_t *slot) { 75 76 sv_upvalue_t **pp = &vm->open_upvalues;
+79
tests/repro_interp_close_on_jit_upvalues.cjs
··· 1 + // Repro for mixed-mode upvalue corruption: 2 + // a hot child closure bails out to the interpreter, then executes OP_CLOSE_UPVAL 3 + // while its hot parent frame still has open captured locals in JIT storage. 4 + 5 + function makeHandle(prefix) { 6 + return function handle(seed, triggerBailout) { 7 + let count = seed + 1; 8 + const box = { prefix, seed }; 9 + 10 + function sibling() { 11 + return prefix.length + count + box.seed; 12 + } 13 + 14 + function next(value, bail) { 15 + let total = prefix.length + count; 16 + 17 + for (let i = 0; i < 1; i++) { 18 + let captured = 7; 19 + const readCaptured = () => captured; 20 + total += readCaptured(); 21 + 22 + // Warm numerically, then force a bailout before the loop scope closes. 23 + if (bail) total = total + value; 24 + } 25 + 26 + return total; 27 + } 28 + 29 + const first = next(triggerBailout ? '!' : 1, triggerBailout); 30 + 31 + // If the child interpreter close path corrupts this upvalue, sibling() 32 + // will stop observing the live parent slot after we mutate count. 33 + count += 10; 34 + 35 + return { 36 + first, 37 + sibling: sibling(), 38 + count, 39 + }; 40 + }; 41 + } 42 + 43 + const handle = makeHandle('root'); 44 + 45 + for (let i = 0; i < 250; i++) { 46 + const got = handle(i, false); 47 + const want = { 48 + first: 4 + (i + 1) + 7, 49 + sibling: 4 + (i + 11) + i, 50 + count: i + 11, 51 + }; 52 + 53 + if ( 54 + got.first !== want.first || 55 + got.sibling !== want.sibling || 56 + got.count !== want.count 57 + ) { 58 + throw new Error( 59 + `warmup mismatch at ${i}: expected ${JSON.stringify(want)}, got ${JSON.stringify(got)}` 60 + ); 61 + } 62 + } 63 + 64 + const got = handle(10, true); 65 + const want = { 66 + first: '22!', 67 + sibling: 35, 68 + count: 21, 69 + }; 70 + 71 + if ( 72 + got.first !== want.first || 73 + got.sibling !== want.sibling || 74 + got.count !== want.count 75 + ) { 76 + throw new Error(`expected ${JSON.stringify(want)}, got ${JSON.stringify(got)}`); 77 + } 78 + 79 + console.log('OK: bailout + interpreter close preserved parent JIT upvalues', JSON.stringify(got));
+35
tests/repro_jit_mixed_upvalues_return.cjs
··· 1 + function factory(prefix) { 2 + return function handle(n) { 3 + let count = n + 1; 4 + let state = { value: prefix + ':' + n }; 5 + let items = [prefix, n]; 6 + 7 + function trimPrefix(x) { 8 + return x + ':' + items[0]; 9 + } 10 + 11 + function next() { 12 + return state.value + '|' + count + '|' + trimPrefix('ok'); 13 + } 14 + 15 + return next; 16 + }; 17 + } 18 + 19 + const handle = factory('root'); 20 + 21 + for (let i = 0; i < 150; i++) { 22 + const fn = handle(i); 23 + if (fn() !== `root:${i}|${i + 1}|ok:root`) { 24 + throw new Error(`warmup mismatch at ${i}: ${fn()}`); 25 + } 26 + } 27 + 28 + const next = handle(7); 29 + const got = next(); 30 + const want = 'root:7|8|ok:root'; 31 + if (got !== want) { 32 + throw new Error(`expected ${want}, got ${got}`); 33 + } 34 + 35 + console.log('OK: mixed upvalue returned closure survived', got);