tricu

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

module-stage.js (4170B)


      1 import { readList, readText, text } from './codecs.js';
      2 import { compilerDiagnostic, SOURCE_LIMIT } from './compiler.js';
      3 import { fetchBytes, decodeBundle } from './bundle.js';
      4 import { packageIndex } from './packages.js';
      5 import { localModuleInputs, validateLocalModule } from './local-modules.js';
      6 import { fetchModule } from './module-resolver.js';
      7 export function stageModule(graph, library, source) {
      8     const call = (name, argument) => {
      9         const f = library.get('source_module.' + name);
     10         if (f === undefined)
     11             throw new Error('Missing native module compiler helper');
     12         return graph.apply(f, [argument]);
     13     };
     14     const value = (result, diagnostic = false) => {
     15         const [tag, payload] = graph.pair(result), flag = graph.observe(tag).node;
     16         if (flag.kind === 'leaf')
     17             throw new Error(diagnostic ? compilerDiagnostic(graph, payload) : readText(graph, graph.pair(payload)[0], 512));
     18         if (flag.kind !== 'stem' || graph.observe(flag.child).node.kind !== 'leaf')
     19             throw new Error('Invalid native module result');
     20         return graph.pair(payload)[0];
     21     };
     22     const [edges, rest] = graph.pair(value(call('sourceModuleHeaderStage', text(graph, source, SOURCE_LIMIT)), true));
     23     if (readList(graph, edges, SOURCE_LIMIT).length)
     24         throw new Error('Browser modules require manifest hashes; workspace and build-only sources are unavailable');
     25     const [uses, tail] = graph.pair(rest), [exports, body] = graph.pair(tail);
     26     const shapes = (rows, helper) => readList(graph, rows, SOURCE_LIMIT).map(row => {
     27         const [a, b] = graph.pair(value(call(helper, row)));
     28         return [readText(graph, a, 128), readText(graph, b, 128)];
     29     });
     30     const imports = shapes(uses, 'sourceModuleUseShape'), selected = shapes(exports, 'sourceModuleExportShape');
     31     return { body: readText(graph, body, SOURCE_LIMIT), imports, exports: selected, native: !!(imports.length || selected.length) };
     32 }
     33 export async function moduleDependencies(base, stage, local = []) {
     34     const refs = [...new Set([...stage.imports.map(i => i[0]), ...stage.exports.filter(e => e[0] === 'from').map(e => e[1])])];
     35     const result = new Map();
     36     if (!refs.length)
     37         return result;
     38     const available = new Map(localModuleInputs(local).map(entry => [entry.manifest, entry]));
     39     let index;
     40     for (const ref of refs) {
     41         const entry = available.get(ref);
     42         if (entry) {
     43             const checked = await validateLocalModule(entry.bytes);
     44             if (checked.manifest !== ref)
     45                 throw new Error('Local module manifest mismatch');
     46             result.set(ref, decodeBundle(checked.bytes));
     47         }
     48         else {
     49             index ??= packageIndex(JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(await fetchBytes(new URL('packages.json', base), 2_097_152))));
     50             result.set(ref, await fetchModule(base, ref, index));
     51         }
     52     }
     53     return result;
     54 }
     55 // Projection of already native-parsed export requests onto backend-owned trees.
     56 // Keep executable trees as backend-owned handles during interface selection.
     57 export function selectModuleExports(stage, locals, modules) {
     58     const selected = new Map();
     59     const add = (name, tree) => {
     60         if (selected.has(name))
     61             throw new Error('duplicate export');
     62         if (selected.size >= 4096)
     63             throw new Error('Module export limit exceeded');
     64         selected.set(name, tree);
     65     };
     66     for (const [name, ref] of stage.exports) {
     67         if (name === 'from') {
     68             const values = modules.get(ref);
     69             if (!values)
     70                 throw new Error('Missing re-export dependency');
     71             for (const [key, value] of values)
     72                 add(key, value);
     73         }
     74         else if (name === '*') {
     75             for (const [key, value] of locals)
     76                 add(key, value);
     77         }
     78         else {
     79             if (!locals.has(name))
     80                 throw new Error('undeclared local export');
     81             add(name, locals.get(name));
     82         }
     83     }
     84     if (!selected.size)
     85         throw new Error('module requires an export');
     86     return selected;
     87 }