browser-page.mjs (6019B)
1 import {CompilerWorker} from './client.mjs'; 2 import {runParity} from './parity.mjs'; 3 const assert=(v,message)=>{if(!v)throw Error(message);}; 4 async function rejects(action,pattern){try{await action();}catch(e){assert(pattern.test(e.message),'Unexpected error: '+e.message);return;}throw Error('Expected rejection');} 5 window.runProbe=async()=>{ 6 const goldens=await (await fetch('./goldens.json')).json(); 7 const report={userAgent:navigator.userAgent,cold:[],jobs:[],memory:[],checks:[]}; 8 // Fresh workers; browser/network/module caches may be warm after the first. 9 for(let i=0;i<3;i++){ 10 const w=new CompilerWorker();try{report.cold.push(await w.request({type:'init'}));}finally{w.dispose();} 11 } 12 const corrupt=new CompilerWorker(); 13 try{await rejects(()=>corrupt.request({type:'init',corrupt:'compiler.dag'}),/SHA-256 mismatch/);} 14 finally{corrupt.dispose();} 15 const corruptEnv=new CompilerWorker(); 16 try{await rejects(()=>corruptEnv.request({type:'init',corrupt:'environment.tsv'}),/SHA-256 mismatch/);} 17 finally{corruptEnv.dispose();} 18 const corruptExports=new CompilerWorker(); 19 try{await rejects(()=>corruptExports.request({type:'init',corrupt:'compiler-exports.tsv'}),/SHA-256 mismatch/);} 20 finally{corruptExports.dispose();} 21 report.checks.push('corrupt prepared DAG/environment/compiler-export roots rejected'); 22 const w=new CompilerWorker(); 23 try{ 24 report.initialization=await w.request({type:'init'}); 25 for(let round=0;round<5;round++)for(const [id,c] of goldens.cases.entries()){ 26 if(c.mode==='io')continue; 27 if(c.diagnostic){ 28 let diagnostic; 29 try{await w.request({type:'compile',source:c.source});}catch(e){diagnostic=e.message;} 30 assert(diagnostic===c.diagnostic,'Compiler diagnostic mismatch: '+diagnostic); 31 continue; 32 } 33 const compile=await w.request({type:'compile',source:c.source}); 34 assert(JSON.stringify(compile.text.trim().split('\n'))===JSON.stringify(Object.keys(c.definitions)),'Export names mismatch'); 35 const dump=await w.request({type:'dump'}); 36 assert(dump.text===Object.entries(c.definitions).map(([n,t])=>n+'\t'+t+'\n').join(''),'Complete definitions mismatch'); 37 const run=await w.request({type:'observe',name:c.export,arguments:c.arguments}); 38 assert(run.text===c.result,'Compiled execution mismatch'); 39 report.jobs.push({round,id,compileMs:compile.milliseconds,dumpMs:dump.milliseconds,runMs:run.milliseconds, 40 roundtripMs:compile.roundtripMs+dump.roundtripMs+run.roundtripMs,linearBytes:run.linearBytes}); 41 } 42 const unicode=goldens.cases.find(c=>c.mode==='io'); 43 await w.request({type:'compile',source:'value = '+JSON.stringify(unicode.input)}); 44 assert((await w.request({type:'observe',name:'value',arguments:[]})).text===unicode.result,'Unicode scalar source/result mismatch'); 45 report.checks.push('Unicode source crosses UTF-8 boundary and matches independent scalar-tree golden'); 46 const identity=goldens.cases[0]; 47 const compiling=w.request({type:'compile',source:identity.source}); 48 await rejects(()=>w.request({type:'dump'}),/busy/); 49 await compiling; 50 await rejects(()=>w.request({type:'compile',source:'\ud800'}),/Unicode/); 51 await rejects(()=>w.request({type:'compile',source:'x'.repeat(16385)}),/limit/); 52 await rejects(()=>w.request({type:'compile',source:goldens.cases.find(c=>c.diagnostic).source}),/Compiler diagnostic/); 53 await rejects(()=>w.request({type:'observe',name:'id',arguments:identity.arguments}),/missing export/); 54 await w.request({type:'compile',source:identity.source}); 55 await rejects(()=>w.request({type:'observe',name:'id',arguments:['2']}),/invalid ternary/); 56 assert((await w.request({type:'observe',name:'id',arguments:identity.arguments})).text===identity.result,'Malformed argument poisoned runtime'); 57 report.checks.push('compiler/Unicode/size errors invalidate replaced definitions; argument errors preserve them'); 58 for(let round=0;round<30;round++){ 59 const c=goldens.cases[round%2===0?3:5]; 60 await w.request({type:'compile',source:c.source}); 61 const result=await w.request({type:'observe',name:c.export,arguments:c.arguments}); 62 assert(result.text===c.result,'Repeated execution mismatch'); 63 const reset=await w.request({type:'reset'});report.memory.push(reset.linearBytes); 64 } 65 await rejects(()=>w.request({type:'observe',name:'main',arguments:[]}),/missing export/); 66 assert(Math.max(...report.memory.slice(15))<=Math.max(...report.memory.slice(0,15))+2*65536,'Linear memory did not plateau'); 67 report.checks.push('30 compile/run/reset cycles; old exports inaccessible; linear memory plateau'); 68 await w.request({type:'compile',source:identity.source}); 69 let ticks=0;const heartbeat=setInterval(()=>ticks++,5); 70 const start=performance.now(); 71 try{ 72 // Known self-application diverger from the site's lazy conformance tests. 73 await rejects(()=>w.request({type:'observe',name:'id',arguments:['212110021100','212110021100']},75),/deadline/); 74 }finally{clearInterval(heartbeat);} 75 report.termination={milliseconds:performance.now()-start,mainThreadTicks:ticks}; 76 assert(ticks>0,'Main thread blocked'); 77 await rejects(()=>w.request({type:'dump'}),/Disposed/); 78 report.checks.push('non-returning reduction terminated; main thread responsive'); 79 }finally{w.dispose();} 80 const replacement=new CompilerWorker(); 81 try{ 82 await replacement.request({type:'init'}); 83 await replacement.request({type:'compile',source:goldens.cases[0].source}); 84 assert((await replacement.request({type:'observe',name:'id',arguments:goldens.cases[0].arguments})).text===goldens.cases[0].result,'Replacement Worker failed'); 85 report.checks.push('fresh Worker succeeds after cancellation'); 86 }finally{replacement.dispose();} 87 // Do not serialize transferred byte buffers into the report. 88 for(const x of [...report.cold,report.initialization])delete x.bytes; 89 report.parity=await runParity(); 90 return report; 91 };