compiler.test.mjs (4925B)
1 import test from 'node:test'; 2 import assert from 'node:assert/strict'; 3 import {readFile} from 'node:fs/promises'; 4 import {createHash} from 'node:crypto'; 5 import {Graph} from '../dist/graph.js'; 6 import {natural,text,list,readText} from '../dist/codecs.js'; 7 import {compiledDefinitions,sourceLiteral,sourceText,SOURCE_LIMIT} from '../dist/compiler.js'; 8 test('compiler shipped pin matches authenticated reference and Haskell golden provenance',async()=>{ 9 const descriptor=JSON.parse(await readFile(new URL('../compiler.json',import.meta.url))); 10 const goldens=JSON.parse(await readFile(new URL('./compiler-conformance.json',import.meta.url))); 11 const shipped=await readFile(new URL('../bundles/'+descriptor.hash+'.arboricx',import.meta.url)); 12 assert.equal(createHash('sha256').update(shipped).digest('hex'),descriptor.hash); 13 assert.equal(goldens.compilerBundleSha256,descriptor.hash); 14 const historical=goldens.cases.filter(c=>c.mode==='io'); 15 assert.equal(historical.length,1); 16 assert.equal(historical[0].compileOnly,true); 17 assert.equal(historical[0].executionRejected,'Unsupported tricuIO version'); 18 assert.equal(historical[0].result,undefined); 19 assert.match(descriptor.referenceCommit,/^[0-9a-f]{40}$/); 20 // The site's accepted compiler is intentionally pinned independently of HEAD. 21 // Authenticate it against its retained publication metadata below, not the 22 // current compiler-library fixture. Never ask assert to diff megabyte buffers. 23 const metadata=(await readFile(new URL('../compiler-reference.v1',import.meta.url),'utf8')).trim().split('\n'); 24 assert.equal(metadata[0],'tricu.compiler-library-fixture.v1'); 25 const fixtureDigest=createHash('sha256').update('tricu.compiler-library-fixture.v1\0').update(shipped).digest('hex'); 26 assert.deepEqual(metadata.filter(row=>row.startsWith('bundle\t')),['bundle\t'+fixtureDigest]); 27 const sources=metadata.filter(row=>row.startsWith('module\t')); 28 assert.equal(sources.length,34); 29 for(const alias of ['source_binding','source_binding_analysis','source_binding_support']) 30 assert.ok(sources.some(row=>row.split('\t')[2]===alias),alias); 31 assert.ok(sources.every(row=>row.split('\t')[2]!=='patterns')); 32 for(const row of sources){ 33 const pin=row.split('\t')[4];assert.match(pin,/^[0-9a-f]{64}$/); 34 const source=await readFile(new URL('../../../test/fixtures/reference-source/'+pin+'.tri',import.meta.url)); 35 assert.equal(createHash('sha256').update('arboricx.source.v1\0').update(source).digest('hex'),pin); 36 } 37 assert.equal(descriptor.export,'source_unit.compileStableModuleDefinitions'); 38 }); 39 test('bounded compiler result decoder preserves lazy program handles',()=>{ 40 const g=new Graph({fuel:100000}),q=g.fromTernary('212110021100'),loop=g.app(q,q); 41 const binding=g.fork(text(g,'loop'),loop); 42 const success=defs=>g.fork(g.stem(g.leaf),g.fork(defs,g.leaf)); 43 const exports=compiledDefinitions(g,success(list(g,[binding]))); 44 assert.equal(compiledDefinitions(g,success(list(g,Array.from({length:64},(_,i)=>g.fork(text(g,'local'+i),loop))))).size,64); 45 assert.equal(exports.get('loop'),loop); // No forcing/expansion of code to name exports. 46 assert.throws(()=>compiledDefinitions(g,success(list(g,[binding,binding]))),/duplicate/); 47 assert.throws(()=>compiledDefinitions(g,success(list(g,Array(4097).fill(binding)))),/limit/); 48 assert.throws(()=>compiledDefinitions(g,success(g.leaf)),/no definitions/); 49 assert.throws(()=>compiledDefinitions(g,g.fork(g.stem(g.stem(g.leaf)),g.leaf)),/tag/); 50 assert.throws(()=>sourceLiteral(g,'x'.repeat(SOURCE_LIMIT+1)),/limit/); 51 assert.throws(()=>sourceLiteral(g,''),/Enter/); 52 assert.throws(()=>sourceLiteral(g,'\ud800'),/surrogate/); 53 }); 54 test('structured compiler diagnostics ignore arbitrary found/context/remainder trees',()=>{ 55 const g=new Graph({fuel:100000}),q=g.fromTernary('212110021100'),loop=g.app(q,q); 56 const location=g.fork(natural(g,2n),g.fork(natural(g,3n),loop)); 57 const diagnostic=g.fork(natural(g,2n),g.fork(natural(g,7n),g.fork(loop,g.fork(location,loop)))); 58 const failure=g.fork(g.leaf,g.fork(diagnostic,loop)); 59 assert.throws(()=>compiledDefinitions(g,failure),/^Error: Compiler diagnostic 2 at 2:3: expected token kind 7$/); 60 assert.ok(g.stats.work<1000); 61 }); 62 63 test('16,384 scalar source validation and Worker encoding have no hidden 256/main-heap cap',()=>{ 64 assert.equal(SOURCE_LIMIT,16384); 65 const ascii='x'.repeat(SOURCE_LIMIT), unicode='🙂'.repeat(SOURCE_LIMIT); 66 assert.equal(sourceText(ascii),ascii);assert.equal(sourceText(unicode),unicode); 67 assert.throws(()=>sourceText(unicode+'x'),/limit/); 68 assert.throws(()=>sourceText(ascii+'x'),/limit/); 69 assert.throws(()=>sourceText(' \n '),/Enter/); 70 assert.throws(()=>sourceText('x\udc00'),/surrogate/); 71 const g=new Graph({nodes:1_000_000,fuel:2_000_000}); 72 const encoded=sourceLiteral(g,unicode); 73 assert.ok(g.stats.nodes>250000);assert.equal(readText(g,encoded,SOURCE_LIMIT),unicode); 74 });