code wiki / _hdl_build / nx_js_event_loop_gate.nx

nx_js_event_loop_gate.nx source

↩ module page · 71 lines · 5491 B

1import "nx_gate_gn.nx" 2import "nx_gate_base.nx" 3// nx_js_event_loop_gate.nx -- proves the R-JS-EVENTLOOP rung: the WHATWG event-loop model actually 4// EXECUTES queued callbacks in the correct ORDER (not just "setTimeout no longer ReferenceErrors"). 5// Runs each script, DRAINS the loop (js_run_source_read), then reads the final value of a global and 6// asserts it -- so the assertion observes effects that land AFTER the last top-level statement. 7// The discriminating tests (T1 sync<micro<macro, T3 macro-enqueues-micro=AmB) FAIL on any non-WHATWG 8// order, so a pass is real proof, not a constant. license_tier: ORIGINAL 9import "nx_js_eval.nx" 10 11func grow(name: *u8, ok: i64) -> i64 { if ok==1 { gw(" PASS " as *u8) } else { gw(" FAIL " as *u8) } gw(name); gw(" 12" as *u8); return ok } 13func gsl(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 14 15// run+drain, read global `rn`, assert it EQUALS the string `want`. 16func gr_str(label: *u8, src: *u8, rn: *u8, want: *u8) -> i64 { 17 let out: *i64 = sys_mmap(16) as *i64 18 let rc: i64 = js_run_source_read(src, gsl(src), rn, out) 19 gw(" "); gw(label); gw(": " as *u8) 20 if rc != 0 { gw("FAIL (eval rc=1)\n" as *u8); return 0 } 21 if out[0] != VAL_STR { gw("FAIL (not a string)\n" as *u8); return 0 } 22 let rec: *i64 = (out[1]) as *i64 23 let wl: i64 = gsl(want) 24 if ev_str_len(rec) != wl { gw("FAIL (len; got '" as *u8); sys_write(1, ev_str_bytes(rec), ev_str_len(rec)); gw("')\n" as *u8); return 0 } 25 let rb: *u8 = ev_str_bytes(rec) 26 var i: i64 = 0 27 while i < wl { if (rb[i] & 0xff) != (want[i] & 0xff) { gw("FAIL (got '" as *u8); sys_write(1, rb, wl); gw("' want '" as *u8); gw(want); gw("')\n" as *u8); return 0 } i = i + 1 } 28 gw("ok ('" as *u8); gw(want); gw("')\n" as *u8); return 1 29} 30// run+drain, read global `rn`, assert it EQUALS the number `want`. 31func gr_num(label: *u8, src: *u8, rn: *u8, want: i64) -> i64 { 32 let out: *i64 = sys_mmap(16) as *i64 33 let rc: i64 = js_run_source_read(src, gsl(src), rn, out) 34 gw(" "); gw(label); gw(": " as *u8) 35 if rc != 0 { gw("FAIL (eval rc=1)\n" as *u8); return 0 } 36 if out[0] != VAL_NUM { gw("FAIL (not a number)\n" as *u8); return 0 } 37 if out[1] != want { gw("FAIL (got "); gn(out[1]); gw(" want "); gn(want); gw(")\n" as *u8); return 0 } 38 gw("ok ("); gn(want); gw(")\n" as *u8); return 1 39} 40 41func main(argc: i64, argv: *i64) -> i64 { 42 gw("=== nx_js_event_loop_gate: WHATWG event-loop EXECUTION + ORDER ===\n" as *u8) 43 var pass: i64 = 0 44 var tot: i64 = 0 45 46 // T0 sync-only: no regression + proves js_run_source_read reads a global correctly. 47 tot = tot + 1; pass = pass + gr_num("T0 sync-only (no regression)" as *u8, "var y=5;y=y+1;" as *u8, "y" as *u8, 6) 48 // T1 THE ORDER LAW: synchronous first, then microtask, then macrotask -> 'SMT'. Any other model fails. 49 tot = tot + 1; pass = pass + gr_str("T1 sync<micro<macro = SMT" as *u8, "var log='';setTimeout(function(){log=log+'T';},0);queueMicrotask(function(){log=log+'M';});log=log+'S';" as *u8, "log" as *u8, "SMT" as *u8) 50 // T2 the setTimeout BODY actually executes (mutates a global), not merely enqueues. 51 tot = tot + 1; pass = pass + gr_num("T2 setTimeout body runs" as *u8, "var x=0;setTimeout(function(){x=42;},0);" as *u8, "x" as *u8, 42) 52 // T3 DISCRIMINATOR: macro A enqueues micro m; m must drain BEFORE macro B -> 'AmB' (not 'ABm'). 53 tot = tot + 1; pass = pass + gr_str("T3 macro-enqueues-micro = AmB" as *u8, "var log='';setTimeout(function(){log=log+'A';queueMicrotask(function(){log=log+'m';});},0);setTimeout(function(){log=log+'B';},0);" as *u8, "log" as *u8, "AmB" as *u8) 54 // T4 a timer callback is a real CLOSURE (captures its factory's arg). 55 tot = tot + 1; pass = pass + gr_num("T4 timer closure captures" as *u8, "function mk(v){return function(){r=v;};}var r=0;setTimeout(mk(7),0);" as *u8, "r" as *u8, 7) 56 // T5 requestAnimationFrame body runs (macrotask family). 57 tot = tot + 1; pass = pass + gr_num("T5 rAF body runs" as *u8, "var z=0;requestAnimationFrame(function(){z=9;});" as *u8, "z" as *u8, 9) 58 // T6 nested microtasks drain to exhaustion -> '012'. 59 tot = tot + 1; pass = pass + gr_str("T6 nested microtasks drain" as *u8, "var log='';queueMicrotask(function(){log=log+'1';queueMicrotask(function(){log=log+'2';});});log=log+'0';" as *u8, "log" as *u8, "012" as *u8) 60 // T7 many timers all run (FIFO; sum is order-independent = proves all three bodies fire). 61 tot = tot + 1; pass = pass + gr_num("T7 three timers all fire" as *u8, "var s=0;setTimeout(function(){s=s+1;},0);setTimeout(function(){s=s+10;},0);setTimeout(function(){s=s+100;},0);" as *u8, "s" as *u8, 111) 62 // T8 LIAR-KILL: a WRONG expectation must FAIL (the harness can distinguish orders). We assert the 63 // engine does NOT produce 'STM' for the T1 script -> gr_str returns 0 (mismatch) -> counts as pass here. 64 tot = tot + 1 65 let neg: i64 = gr_str("T8 liar-kill (expect NOT 'STM')" as *u8, "var log='';setTimeout(function(){log=log+'T';},0);queueMicrotask(function(){log=log+'M';});log=log+'S';" as *u8, "log" as *u8, "STM" as *u8) 66 if neg == 0 { pass = pass + 1; gw(" (correctly did NOT match wrong order -> liar-kill holds)\n" as *u8) } else { gw(" !! liar-kill BROKE: harness matched a wrong order\n" as *u8) } 67 68 gw("=== "); gn(pass); gw("/"); gn(tot); gw(" "); if pass == tot { gw("GREEN ===\n" as *u8) } else { gw("RED ===\n" as *u8) } 69 if pass == tot { return 0 } 70 return 1 71}