code wiki / _hdl_build / nx_js_consumer_swap_gate.nx
nx_js_consumer_swap_gate.nx source
↩ module page · 162 lines · 9313 B
1// nx_js_consumer_swap_gate.nx -- CONSUMER SWAP: route the renderer's page scripts through the VM/JIT
2// (compile_run_doc via the doc hook) instead of the tree-walker, with a tree-walker FALLBACK for features
3// the VM can't compile (template ${}). Proves (A) byte-IDENTICAL serialized DOM: rendering each page with
4// the hook OFF (tree-walker) vs ON (VM/JIT) must produce the exact same HTML -- a strong DOM-correctness
5// net covering innerHTML/createElement/appendChild/setAttribute/querySelector/textContent/setTimeout
6// hydration; (B) FALLBACK: a template-${} script (VM declines) still renders correctly via the tree-walker;
7// (C) SPEEDUP on a script-heavy page. This is the payoff wiring that makes real page scripts run on the fast
8// engine. expect_exit: 0 license_tier: ORIGINAL
9import "nx_js_vm.nx"
10
11func cs_w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
12func cs_n(v: i64) -> i64 {
13 if v == 0 { sys_write(1, "0" as *u8, 1); return 0 }
14 var m: i64 = v
15 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m }
16 let t: *u8 = sys_mmap(32)
17 var k: i64 = 0
18 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
19 let o: *u8 = sys_mmap(32)
20 var q: i64 = k - 1
21 var x: i64 = 0
22 while q >= 0 { o[x] = t[q]; x = x + 1; q = q - 1 }
23 sys_write(1, o, x)
24 return 0
25}
26func cs_hlen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
27// render `html` with the doc hook OFF (tree-walker) vs ON (VM/JIT); require byte-identical serialized output.
28func swap_case(html: *u8, label: *u8, pp: *i64, tp: *i64) -> i64 {
29 tp[0] = tp[0] + 1
30 let hlen: i64 = cs_hlen(html)
31 let cap: i64 = 65536
32 let bt: *u8 = sys_mmap(cap)
33 let bv: *u8 = sys_mmap(cap)
34 js_vm_doc_bind(0) // OFF -> tree-walker
35 let lt: i64 = js_render_page(html, hlen, bt, cap)
36 js_vm_doc_enable() // ON -> VM/JIT (with fallback)
37 let lv: i64 = js_render_page(html, hlen, bv, cap)
38 js_vm_doc_bind(0)
39 cs_w(" ")
40 cs_w(label)
41 cs_w(": ")
42 var ok: i64 = 1
43 if lt != lv { ok = 0 }
44 if ok == 1 { var i: i64 = 0; while i < lt { if (bt[i] & 0xff) != (bv[i] & 0xff) { ok = 0; i = lt } else { i = i + 1 } } }
45 if ok == 1 { pp[0] = pp[0] + 1; cs_w("IDENTICAL ("); cs_n(lt); cs_w(" bytes, tree==VM)\n" as *u8); return 0 }
46 cs_w("DIVERGED tree-len=")
47 cs_n(lt)
48 cs_w(" vm-len=")
49 cs_n(lv)
50 cs_w("\n tree=[")
51 sys_write(1, bt, lt)
52 cs_w("]\n vm =[")
53 sys_write(1, bv, lv)
54 cs_w("]\n" as *u8)
55 tp[0] = tp[0] + 100000 // force RED
56 return 0
57}
58
59func cs_contains(buf: *u8, blen: i64, needle: *u8) -> i64 {
60 let nl: i64 = cs_hlen(needle)
61 if nl == 0 { return 1 }
62 var i: i64 = 0
63 while (i + nl) <= blen {
64 var j: i64 = 0
65 var ok: i64 = 1
66 while j < nl { if (buf[i + j] & 0xff) != (needle[j] & 0xff) { ok = 0; j = nl } else { j = j + 1 } }
67 if ok == 1 { return 1 }
68 i = i + 1
69 }
70 return 0
71}
72// ABSOLUTE proof: render with the VM/JIT hook ON, require the output to CONTAIN the known-correct mutation
73// (not merely equal the tree). Guards against a VM==tree-but-both-wrong illusion on untrusted pages.
74func vm_expect(html: *u8, needle: *u8, label: *u8, pp: *i64, tp: *i64) -> i64 {
75 tp[0] = tp[0] + 1
76 let cap: i64 = 65536
77 let b: *u8 = sys_mmap(cap)
78 js_vm_doc_enable()
79 let ln: i64 = js_render_page(html, cs_hlen(html), b, cap)
80 js_vm_doc_bind(0)
81 cs_w(" ")
82 cs_w(label)
83 if cs_contains(b, ln, needle) == 1 { pp[0] = pp[0] + 1; cs_w(": ok (VM output contains expected)\n" as *u8); return 0 }
84 cs_w(": FAIL (VM output missing expected)\n" as *u8)
85 tp[0] = tp[0] + 100000
86 return 0
87}
88
89func main(argc: i64, argv: *i64) -> i64 {
90 cs_w("=== nx_js_consumer_swap_gate: page scripts via VM/JIT == tree-walker (byte-identical DOM) ===\n" as *u8)
91 let pp: *i64 = sys_mmap(8) as *i64
92 let tp: *i64 = sys_mmap(8) as *i64
93 cs_w(" [A: hook OFF (tree) vs ON (VM/JIT) -- serialized DOM must be byte-identical]\n" as *u8)
94 swap_case("<div id=\"a\">old</div><script>document.getElementById('a').innerHTML='NEW';</script>" as *u8, "C1 innerHTML write" as *u8, pp, tp)
95 swap_case("<div id=\"a\"></div><script>var e=document.createElement('span');e.setAttribute('class','x');document.getElementById('a').appendChild(e);</script>" as *u8, "C2 createElement+appendChild+setAttribute" as *u8, pp, tp)
96 swap_case("<p class=\"t\">old</p><script>document.querySelector('.t').textContent='hi there';</script>" as *u8, "C3 querySelector+textContent" as *u8, pp, tp)
97 swap_case("<div id=\"a\">load</div><script>setTimeout(function(){document.getElementById('a').textContent='done';},0);</script>" as *u8, "C4 setTimeout hydration (async)" as *u8, pp, tp)
98 swap_case("<ul id=\"a\"></ul><script>function row(i){return '<li>item'+i+'</li>';}var s='';var i=0;while(i<40){s=s+row(i);i=i+1;}document.getElementById('a').innerHTML=s;</script>" as *u8, "C5 loop+fn build (JIT-native)" as *u8, pp, tp)
99 swap_case("<div id=\"a\">x</div><script>var o={v:7};document.getElementById('a').textContent=''+(o.v*6);</script>" as *u8, "C6 object/arith into DOM" as *u8, pp, tp)
100 swap_case("<div id=\"a\"></div><script>var w=document.createElement('div');var s=document.createElement('span');s.textContent='deep';w.appendChild(s);document.getElementById('a').appendChild(w);</script>" as *u8, "C8 nested createElement tree" as *u8, pp, tp)
101 swap_case("<a id=\"a\">x</a><script>var e=document.getElementById('a');e.setAttribute('href','/p');e.setAttribute('data-n','3');</script>" as *u8, "C9 multiple setAttribute" as *u8, pp, tp)
102 swap_case("<div id=\"a\">x</div><script>document.getElementById('a').className='big red';</script>" as *u8, "C10 className write" as *u8, pp, tp)
103 swap_case("<ul id=\"a\"></ul><script>var i=0;while(i<3){var li=document.createElement('li');li.textContent='n'+i;document.getElementById('a').appendChild(li);i=i+1;}</script>" as *u8, "C11 loop createElement+append" as *u8, pp, tp)
104 swap_case("<div id=\"a\">x</div><div id=\"b\">y</div><script>document.getElementById('a').textContent='A';</script><script>document.getElementById('b').textContent='B';</script>" as *u8, "C12 multiple <script> blocks" as *u8, pp, tp)
105 swap_case("<div id=\"a\">x</div><script>document.getElementById('a').innerHTML='<b>bold</b> and <i>it</i>';</script>" as *u8, "C13 innerHTML with nested tags" as *u8, pp, tp)
106 cs_w(" [B: FALLBACK -- template ${} (VM declines) must still render via the tree-walker]\n" as *u8)
107 swap_case("<div id=\"a\">x</div><script>var n='world';document.getElementById('a').textContent=`hi ${n}!`;</script>" as *u8, "C7 template-interp fallback" as *u8, pp, tp)
108
109 cs_w(" [D: ABSOLUTE -- VM/JIT output must CONTAIN the known-correct mutation]\n" as *u8)
110 vm_expect("<div id=\"a\">old</div><script>document.getElementById('a').innerHTML='<p>JS built</p>';</script>" as *u8, "<p>JS built</p>" as *u8, "V1 innerHTML content present" as *u8, pp, tp)
111 vm_expect("<span id=\"s\">x</span><script>document.getElementById('s').textContent='HELLO';</script>" as *u8, ">HELLO<" as *u8, "V2 textContent present" as *u8, pp, tp)
112 vm_expect("<div id=\"a\"></div><script>var e=document.createElement('b');e.textContent='hi';document.getElementById('a').appendChild(e);</script>" as *u8, "<b>hi</b>" as *u8, "V3 created element present" as *u8, pp, tp)
113 vm_expect("<a id=\"a\">x</a><script>document.getElementById('a').setAttribute('href','/go');</script>" as *u8, "href=\"/go\"" as *u8, "V4 attribute present" as *u8, pp, tp)
114
115 cs_w(" --- correctness: ")
116 cs_n(pp[0])
117 cs_w("/")
118 cs_n(tp[0])
119 cs_w(" (VM/JIT-rendered DOM == tree-walker byte-identical + fallback + absolute-expected) ---\n" as *u8)
120
121 // [C] speed: a script-heavy page (build 400 list items in a loop). hook off (tree) vs on (VM/JIT).
122 let heavy: *u8 = "<ul id=\"a\"></ul><script>function cell(i){return '<li class=\"r\">row '+i+' = '+(i*i)+'</li>';}var s='';var i=0;while(i<400){s=s+cell(i);i=i+1;}document.getElementById('a').innerHTML=s;</script>"
123 let hl: i64 = cs_hlen(heavy)
124 let cap2: i64 = 262144
125 let bb: *u8 = sys_mmap(cap2)
126 var tus: i64 = 0
127 var vus: i64 = 0
128 var r: i64 = 0
129 while r < 3 {
130 js_vm_doc_bind(0)
131 let t0: i64 = sys_now_us()
132 js_render_page(heavy, hl, bb, cap2)
133 let t1: i64 = sys_now_us()
134 js_vm_doc_enable()
135 let t2: i64 = sys_now_us()
136 js_render_page(heavy, hl, bb, cap2)
137 let t3: i64 = sys_now_us()
138 js_vm_doc_bind(0)
139 var a: i64 = t1 - t0
140 if a < 1 { a = 1 }
141 var b: i64 = t3 - t2
142 if b < 1 { b = 1 }
143 if r == 0 { tus = a; vus = b }
144 if a < tus { tus = a }
145 if b < vus { vus = b }
146 r = r + 1
147 }
148 cs_w(" [C: script-heavy page render -- tree=")
149 cs_n(tus)
150 cs_w("us VM/JIT=")
151 cs_n(vus)
152 cs_w("us speedup=")
153 cs_n(tus * 100 / vus)
154 cs_w("/100x]\n" as *u8)
155
156 if pp[0] == tp[0] { if vus < tus {
157 cs_w("=== GREEN: page scripts run on the VM/JIT with byte-identical DOM + tree-walker fallback + faster ===\n" as *u8)
158 return 0
159 } }
160 cs_w("=== RED ===\n" as *u8)
161 return 1
162}