tricu

An interpreted language for exploring Tree Calculus
Log | Files | Refs | README | LICENSE

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 };