@onda-lang/wasm-compiler 0.5.4 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,12 +8,12 @@ require LLVM, a Wasm linker, Rust, or `wasm-pack` after installation.
8
8
  import { createCompiler } from "@onda-lang/wasm-compiler";
9
9
 
10
10
  const compiler = await createCompiler();
11
- const artifact = await compiler.compileSource(source, {
11
+ const { artifact, sourceFiles } = await compiler.compileSource(source, {
12
12
  sampleRate: 48_000,
13
13
  blockSize: 128,
14
14
  });
15
15
 
16
- console.log(artifact.wasm, artifact.metadata);
16
+ console.log(artifact.wasm, artifact.metadata, sourceFiles);
17
17
  ```
18
18
 
19
19
  The package composes Onda's embedded Rust frontend with its Binaryen backend. The frontend emits
@@ -31,7 +31,7 @@ Project compilation resolves imports and includes entirely from the supplied sou
31
31
  embedded standard library:
32
32
 
33
33
  ```js
34
- const artifact = await compiler.compileProject({
34
+ const { artifact, sourceFiles } = await compiler.compileProject({
35
35
  entry: "main.onda",
36
36
  sources: {
37
37
  "main.onda": mainSource,
@@ -41,10 +41,17 @@ const artifact = await compiler.compileProject({
41
41
  sampleRate: 48_000,
42
42
  blockSize: 128,
43
43
  });
44
+
45
+ // Entry first, then transitive imports/includes. Embedded stdlib is excluded.
46
+ console.log(sourceFiles);
44
47
  ```
45
48
 
46
49
  Compilation failures throw `OndaCompileError`. Its `diagnostics` property contains structured
47
- parse, semantic, MIR, configuration, or code-generation diagnostics.
50
+ parse, semantic, MIR, configuration, or code-generation diagnostics. Its `sourceFiles` property
51
+ contains every project source resolved before compilation stopped, allowing hosts to retain useful
52
+ watch registrations while the project is temporarily invalid. `unresolvedSourceFiles` contains
53
+ referenced non-standard-library candidates which were not present, allowing hosts to watch for their
54
+ creation without treating them as contributing compilation inputs.
48
55
 
49
56
  ## Browser workers
50
57
 
@@ -52,7 +59,7 @@ Compilation is CPU-intensive. Use the built-in worker client in interactive brow
52
59
 
53
60
  ```js
54
61
  const compiler = await createCompiler({ worker: true });
55
- const artifact = await compiler.compileSource(source, options);
62
+ const { artifact, sourceFiles } = await compiler.compileSource(source, options);
56
63
  await compiler.dispose();
57
64
  ```
58
65
 
package/bin/onda-wasm.js CHANGED
@@ -72,7 +72,7 @@ export async function main(argv = process.argv.slice(2)) {
72
72
  const watOutput = parsed.watOut === undefined ? undefined : resolve(parsed.watOut);
73
73
  const compiler = await createCompiler();
74
74
  try {
75
- const artifact = await compiler.compileProject({ entry, sources }, {
75
+ const { artifact } = await compiler.compileProject({ entry, sources }, {
76
76
  sampleRate: parsed.sampleRate,
77
77
  blockSize: parsed.blockSize,
78
78
  codegen: {
@@ -3,6 +3,8 @@ export {
3
3
  PROCESSOR_ABI_VERSION,
4
4
  PROCESSOR_ARTIFACT_FORMAT,
5
5
  PROCESSOR_ARTIFACT_FORMAT_VERSION,
6
+ PROCESSOR_EXECUTION_OK,
7
+ PROCESSOR_EXECUTION_RUNTIME_SAFETY_FAILURE,
6
8
  PROCESSOR_SNAPSHOT_FORMAT_VERSION,
7
9
  createProcessorArtifactFiles,
8
10
  loadProcessorArtifactFiles,
@@ -1,2 +1,2 @@
1
1
  // Synchronized from format-versions.json; do not edit this copy directly.
2
- export const SUPPORTED_MIR_SCHEMA_VERSION = 1;
2
+ export const SUPPORTED_MIR_SCHEMA_VERSION = 3;
@@ -4,6 +4,8 @@ export {
4
4
  PROCESSOR_ABI_VERSION,
5
5
  PROCESSOR_ARTIFACT_FORMAT,
6
6
  PROCESSOR_ARTIFACT_FORMAT_VERSION,
7
+ PROCESSOR_EXECUTION_OK,
8
+ PROCESSOR_EXECUTION_RUNTIME_SAFETY_FAILURE,
7
9
  PROCESSOR_SNAPSHOT_FORMAT_VERSION,
8
10
  createProcessorArtifactFiles,
9
11
  loadProcessorArtifactFiles,
@@ -8,6 +8,8 @@ import {
8
8
  PROCESSOR_ABI_VERSION,
9
9
  PROCESSOR_ARTIFACT_FORMAT,
10
10
  PROCESSOR_ARTIFACT_FORMAT_VERSION,
11
+ PROCESSOR_EXECUTION_OK,
12
+ PROCESSOR_EXECUTION_RUNTIME_SAFETY_FAILURE,
11
13
  PROCESSOR_SNAPSHOT_FORMAT_VERSION,
12
14
  validateProcessorMetadata,
13
15
  } from "./artifact.js";
@@ -17,6 +19,8 @@ export {
17
19
  PROCESSOR_ABI_VERSION,
18
20
  PROCESSOR_ARTIFACT_FORMAT,
19
21
  PROCESSOR_ARTIFACT_FORMAT_VERSION,
22
+ PROCESSOR_EXECUTION_OK,
23
+ PROCESSOR_EXECUTION_RUNTIME_SAFETY_FAILURE,
20
24
  PROCESSOR_SNAPSHOT_FORMAT_VERSION,
21
25
  createProcessorArtifactFiles,
22
26
  loadProcessorArtifactFiles,
@@ -38,6 +42,7 @@ const MAX_MEMORY_PAGES = 65_536;
38
42
  const WASM32_ADDRESS_SPACE_BYTES = MAX_MEMORY_PAGES * PAGE_BYTES;
39
43
  const DEFAULT_OPTIMIZE_LEVEL = 4;
40
44
  const ONDA_PROCESS_FULL_BLOCK = (1 << 0) | (1 << 1);
45
+ const RUNTIME_FAILURE_GLOBAL = "$onda.runtime_failure";
41
46
  const MATH_KERNEL_INTRINSICS = new Set([
42
47
  "sin",
43
48
  "cos",
@@ -179,6 +184,7 @@ class MirCompiler {
179
184
  ? MATH_KERNEL_RESERVED_END
180
185
  : STATIC_BASE;
181
186
  this.internalHelpers = new Set();
187
+ this.functionMayFail = [];
182
188
  this.nextLabel = 0;
183
189
  }
184
190
 
@@ -297,6 +303,7 @@ class MirCompiler {
297
303
  this.validateCurrentSchemaEnvelope();
298
304
  this.validateProcessEntrySignature();
299
305
  this.validateAcyclicCallGraph();
306
+ this.analyzeRecoverableFailures();
300
307
  }
301
308
 
302
309
  validateCurrentSchemaEnvelope() {
@@ -444,6 +451,86 @@ class MirCompiler {
444
451
  }
445
452
  }
446
453
 
454
+ analyzeRecoverableFailures() {
455
+ const callees = this.mir.functions.map(() => new Set());
456
+ const direct = this.mir.functions.map(() => false);
457
+ const binaryMayFail = (functionId, value) => {
458
+ if (
459
+ value.kind !== "binary"
460
+ || !["divide", "remainder"].includes(value.data?.op)
461
+ ) {
462
+ return false;
463
+ }
464
+ const lhs = value.data.lhs;
465
+ const scalar = lhs.kind === "constant"
466
+ ? lhs.data.type
467
+ : this.requireScalarType(
468
+ this.mir.functions[functionId].locals[lhs.data].ty,
469
+ `binary operand in '${this.mir.functions[functionId].name}'`,
470
+ );
471
+ return scalar === "i32" || scalar === "i64";
472
+ };
473
+ const checkedBoundsKinds = new Set([
474
+ "array_window",
475
+ "buffer_load",
476
+ "buffer_param_load",
477
+ "buffer_param_store",
478
+ "buffer_store",
479
+ "const_data_load",
480
+ "index",
481
+ "input_load",
482
+ "make_slice",
483
+ "output_load",
484
+ "output_store",
485
+ "control_output_store",
486
+ ]);
487
+ const dynamicBoundsKinds = new Set([
488
+ "slice_element",
489
+ "slice_load",
490
+ "slice_store",
491
+ "slice_window",
492
+ ]);
493
+ const scan = (functionId, value) => {
494
+ if (value === null || value === undefined) return;
495
+ if (Array.isArray(value)) {
496
+ for (const entry of value) scan(functionId, entry);
497
+ return;
498
+ }
499
+ if (typeof value !== "object") return;
500
+ if (value.kind === "call" && Number.isInteger(value.data?.function)) {
501
+ callees[functionId].add(value.data.function);
502
+ }
503
+ const bounds = value.data?.bounds;
504
+ if (
505
+ value.kind === "process_frame"
506
+ || value.kind === "slice_copy"
507
+ || binaryMayFail(functionId, value)
508
+ || (checkedBoundsKinds.has(value.kind) && bounds === "checked")
509
+ || (dynamicBoundsKinds.has(value.kind) && bounds !== "unchecked")
510
+ ) {
511
+ direct[functionId] = true;
512
+ }
513
+ for (const child of Object.values(value)) scan(functionId, child);
514
+ };
515
+ for (const [functionId, func] of this.mir.functions.entries()) {
516
+ scan(functionId, func.body);
517
+ }
518
+ this.functionMayFail = [...direct];
519
+ let changed = true;
520
+ while (changed) {
521
+ changed = false;
522
+ for (const [functionId, targets] of callees.entries()) {
523
+ if (
524
+ !this.functionMayFail[functionId]
525
+ && [...targets].some((target) => this.functionMayFail[target])
526
+ ) {
527
+ this.functionMayFail[functionId] = true;
528
+ changed = true;
529
+ }
530
+ }
531
+ }
532
+ }
533
+
447
534
  buildLayouts() {
448
535
  this.stateLayout = this.layoutNamedValues(this.mir.state);
449
536
  this.paramLayout = this.layoutNamedValues(this.mir.interface.params);
@@ -663,6 +750,12 @@ class MirCompiler {
663
750
  for (const name of Object.values(POINTER_GLOBALS)) {
664
751
  this.module.addGlobal(name, binaryen.i32, true, this.module.i32.const(0));
665
752
  }
753
+ this.module.addGlobal(
754
+ RUNTIME_FAILURE_GLOBAL,
755
+ binaryen.i32,
756
+ true,
757
+ this.module.i32.const(0),
758
+ );
666
759
  }
667
760
 
668
761
  addMathKernel() {
@@ -1049,6 +1142,62 @@ class MirCompiler {
1049
1142
  );
1050
1143
  }
1051
1144
 
1145
+ defaultFunctionResult(context) {
1146
+ const scalars = context.function.results.map((typeId, resultId) =>
1147
+ this.requireScalarType(
1148
+ typeId,
1149
+ `result ${resultId} of '${context.function.name}'`,
1150
+ ),
1151
+ );
1152
+ const values = scalars.map((scalar) => this.zero(scalar));
1153
+ if (values.length === 0) return undefined;
1154
+ if (values.length === 1) return values[0];
1155
+ return this.module.tuple.make(values);
1156
+ }
1157
+
1158
+ returnFromCurrentFunction(context) {
1159
+ return this.module.return(this.defaultFunctionResult(context));
1160
+ }
1161
+
1162
+ raiseRuntimeFailure(context) {
1163
+ return this.module.block(null, [
1164
+ this.module.global.set(
1165
+ RUNTIME_FAILURE_GLOBAL,
1166
+ this.module.i32.const(PROCESSOR_EXECUTION_RUNTIME_SAFETY_FAILURE),
1167
+ ),
1168
+ this.returnFromCurrentFunction(context),
1169
+ ]);
1170
+ }
1171
+
1172
+ propagateRuntimeFailure(calleeId, context) {
1173
+ if (!this.functionMayFail[calleeId]) return [];
1174
+ return [
1175
+ this.module.if(
1176
+ this.module.i32.ne(
1177
+ this.module.global.get(RUNTIME_FAILURE_GLOBAL, binaryen.i32),
1178
+ this.module.i32.const(PROCESSOR_EXECUTION_OK),
1179
+ ),
1180
+ this.returnFromCurrentFunction(context),
1181
+ ),
1182
+ ];
1183
+ }
1184
+
1185
+ resetRuntimeFailure(functionId) {
1186
+ if (!this.functionMayFail[functionId]) return [];
1187
+ return [
1188
+ this.module.global.set(
1189
+ RUNTIME_FAILURE_GLOBAL,
1190
+ this.module.i32.const(PROCESSOR_EXECUTION_OK),
1191
+ ),
1192
+ ];
1193
+ }
1194
+
1195
+ executionStatus(functionId) {
1196
+ return this.functionMayFail[functionId]
1197
+ ? this.module.global.get(RUNTIME_FAILURE_GLOBAL, binaryen.i32)
1198
+ : this.module.i32.const(PROCESSOR_EXECUTION_OK);
1199
+ }
1200
+
1052
1201
  addAbiWrappers() {
1053
1202
  const initId = this.mir.entry_points.init;
1054
1203
  const processId = this.mir.entry_points.process;
@@ -1062,6 +1211,7 @@ class MirCompiler {
1062
1211
  (this.options.simd ? binaryen.Features.SIMD128 : 0),
1063
1212
  );
1064
1213
  const initBody = this.module.block(null, [
1214
+ ...this.resetRuntimeFailure(initId),
1065
1215
  this.module.global.set(
1066
1216
  POINTER_GLOBALS.params,
1067
1217
  this.module.local.get(0, binaryen.i32),
@@ -1076,11 +1226,12 @@ class MirCompiler {
1076
1226
  this.module.i32.const(this.stateLayout.byteLength ?? 0),
1077
1227
  ),
1078
1228
  this.module.call(this.functionNames[initId], [], binaryen.none),
1079
- ]);
1229
+ this.executionStatus(initId),
1230
+ ], binaryen.i32);
1080
1231
  this.module.addFunction(
1081
1232
  "$onda.abi.init",
1082
1233
  binaryen.createType([binaryen.i32, binaryen.i32]),
1083
- binaryen.none,
1234
+ binaryen.i32,
1084
1235
  [],
1085
1236
  initBody,
1086
1237
  );
@@ -1121,7 +1272,13 @@ class MirCompiler {
1121
1272
  ),
1122
1273
  );
1123
1274
  const processBody = this.module.block(null, [
1124
- this.module.if(invalidRange, this.module.unreachable()),
1275
+ this.module.if(
1276
+ invalidRange,
1277
+ this.module.return(
1278
+ this.module.i32.const(PROCESSOR_EXECUTION_RUNTIME_SAFETY_FAILURE),
1279
+ ),
1280
+ ),
1281
+ ...this.resetRuntimeFailure(processId),
1125
1282
  this.module.global.set(
1126
1283
  POINTER_GLOBALS.state,
1127
1284
  this.module.local.get(0, binaryen.i32),
@@ -1159,11 +1316,12 @@ class MirCompiler {
1159
1316
  [startFrame(), frames(), flags()],
1160
1317
  binaryen.none,
1161
1318
  ),
1162
- ]);
1319
+ this.executionStatus(processId),
1320
+ ], binaryen.i32);
1163
1321
  this.module.addFunction(
1164
1322
  "$onda.abi.process",
1165
1323
  processParams,
1166
- binaryen.none,
1324
+ binaryen.i32,
1167
1325
  [],
1168
1326
  processBody,
1169
1327
  );
@@ -1182,6 +1340,7 @@ class MirCompiler {
1182
1340
  }
1183
1341
  const wrapperName = `$onda.abi.event.${eventId}`;
1184
1342
  const body = this.module.block(null, [
1343
+ ...this.resetRuntimeFailure(event.handler),
1185
1344
  this.module.global.set(
1186
1345
  POINTER_GLOBALS.eventPayload,
1187
1346
  this.module.local.get(0, binaryen.i32),
@@ -1211,11 +1370,12 @@ class MirCompiler {
1211
1370
  this.module.local.get(6, binaryen.i32),
1212
1371
  ),
1213
1372
  this.module.call(this.functionNames[event.handler], [], binaryen.none),
1214
- ]);
1373
+ this.executionStatus(event.handler),
1374
+ ], binaryen.i32);
1215
1375
  this.module.addFunction(
1216
1376
  wrapperName,
1217
1377
  binaryen.createType(Array.from({ length: 7 }, () => binaryen.i32)),
1218
- binaryen.none,
1378
+ binaryen.i32,
1219
1379
  [],
1220
1380
  body,
1221
1381
  );
@@ -1465,6 +1625,7 @@ class MirCompiler {
1465
1625
  this.module.local.set(resultSpill.index, call),
1466
1626
  ...afterCall,
1467
1627
  ...assignResults,
1628
+ ...this.propagateRuntimeFailure(data.function, context),
1468
1629
  ]);
1469
1630
  }
1470
1631
  let compiledCall;
@@ -1492,8 +1653,18 @@ class MirCompiler {
1492
1653
  ),
1493
1654
  ]);
1494
1655
  }
1495
- if (localReferenceSync.length === 0) return compiledCall;
1496
- return this.module.block(null, [...beforeCall, compiledCall, ...afterCall]);
1656
+ if (localReferenceSync.length === 0) {
1657
+ const propagation = this.propagateRuntimeFailure(data.function, context);
1658
+ return propagation.length === 0
1659
+ ? compiledCall
1660
+ : this.module.block(null, [compiledCall, ...propagation]);
1661
+ }
1662
+ return this.module.block(null, [
1663
+ ...beforeCall,
1664
+ compiledCall,
1665
+ ...afterCall,
1666
+ ...this.propagateRuntimeFailure(data.function, context),
1667
+ ]);
1497
1668
  }
1498
1669
 
1499
1670
  compileOutputStore(data, context) {
@@ -1608,6 +1779,7 @@ class MirCompiler {
1608
1779
  scalar,
1609
1780
  () => this.compileValue(data.lhs, context),
1610
1781
  () => this.compileValue(data.rhs, context),
1782
+ context,
1611
1783
  );
1612
1784
  }
1613
1785
  case "compare": {
@@ -1718,7 +1890,7 @@ class MirCompiler {
1718
1890
  const invalid = this.module.i32.ge_u(offset(), frames());
1719
1891
  return this.module.if(
1720
1892
  invalid,
1721
- this.module.unreachable(),
1893
+ this.raiseRuntimeFailure(context),
1722
1894
  this.module.i32.add(startFrame(), offset()),
1723
1895
  );
1724
1896
  }
@@ -1816,6 +1988,7 @@ class MirCompiler {
1816
1988
  rawIndex,
1817
1989
  () => this.compileBufferParamTotalScalarLen(data.parameter, context),
1818
1990
  data.bounds,
1991
+ context,
1819
1992
  true,
1820
1993
  );
1821
1994
  return this.module.i32.add(
@@ -1849,6 +2022,7 @@ class MirCompiler {
1849
2022
  rawIndex,
1850
2023
  () => this.compileBufferTotalScalarLen(data.buffer),
1851
2024
  data.bounds,
2025
+ context,
1852
2026
  true,
1853
2027
  );
1854
2028
  const pointer = this.loadBufferTableValue(
@@ -2061,6 +2235,7 @@ class MirCompiler {
2061
2235
  () => this.compileValue(data.len, context),
2062
2236
  () => source()[1],
2063
2237
  data.bounds,
2238
+ context,
2064
2239
  );
2065
2240
  return [
2066
2241
  this.module.i32.add(
@@ -2075,7 +2250,7 @@ class MirCompiler {
2075
2250
  ];
2076
2251
  }
2077
2252
 
2078
- compileSliceRange(start, len, sourceLen, bounds) {
2253
+ compileSliceRange(start, len, sourceLen, bounds, context) {
2079
2254
  const zero = () => this.module.i32.const(0);
2080
2255
  if (bounds === "unchecked") {
2081
2256
  return { start, len };
@@ -2111,7 +2286,7 @@ class MirCompiler {
2111
2286
  };
2112
2287
  return { start: normalizedStart, len: normalizedLen };
2113
2288
  }
2114
- if (bounds === "trap") {
2289
+ if (bounds === "checked") {
2115
2290
  const invalid = () => {
2116
2291
  const remaining = () => this.module.i32.sub(sourceLen(), start());
2117
2292
  return this.module.i32.or(
@@ -2127,7 +2302,7 @@ class MirCompiler {
2127
2302
  };
2128
2303
  return {
2129
2304
  start: () =>
2130
- this.module.if(invalid(), this.module.unreachable(), start()),
2305
+ this.module.if(invalid(), this.raiseRuntimeFailure(context), start()),
2131
2306
  len,
2132
2307
  };
2133
2308
  }
@@ -2188,6 +2363,7 @@ class MirCompiler {
2188
2363
  () => this.compileValue(source.data.channel, context),
2189
2364
  channels,
2190
2365
  "clamp",
2366
+ context,
2191
2367
  true,
2192
2368
  );
2193
2369
  return [
@@ -2221,6 +2397,7 @@ class MirCompiler {
2221
2397
  () => this.compileValue(source.data.channel, context),
2222
2398
  channels,
2223
2399
  "clamp",
2400
+ context,
2224
2401
  true,
2225
2402
  );
2226
2403
  return [
@@ -2260,14 +2437,16 @@ class MirCompiler {
2260
2437
  () => this.compileSliceValue(slice, context),
2261
2438
  () => this.compileValue(index, context),
2262
2439
  bounds,
2440
+ context,
2263
2441
  );
2264
2442
  }
2265
2443
 
2266
- compileSliceAddressWithFactories(slice, index, bounds) {
2444
+ compileSliceAddressWithFactories(slice, index, bounds, context) {
2267
2445
  const bounded = this.compileDynamicBoundedIndex(
2268
2446
  index,
2269
2447
  () => slice()[1],
2270
2448
  bounds,
2449
+ context,
2271
2450
  );
2272
2451
  return this.module.i32.add(
2273
2452
  slice()[0],
@@ -2295,6 +2474,7 @@ class MirCompiler {
2295
2474
  sourceType.data.len - parameterType.data.len,
2296
2475
  ),
2297
2476
  data.bounds,
2477
+ context,
2298
2478
  );
2299
2479
  return this.module.i32.add(
2300
2480
  this.placeAddress(data.array, context),
@@ -2318,6 +2498,7 @@ class MirCompiler {
2318
2498
  this.module.i32.const(requiredLen),
2319
2499
  ),
2320
2500
  data.bounds,
2501
+ context,
2321
2502
  );
2322
2503
  const address = () =>
2323
2504
  this.module.i32.add(
@@ -2340,12 +2521,12 @@ class MirCompiler {
2340
2521
  );
2341
2522
  return this.module.if(
2342
2523
  invalidShape(),
2343
- this.module.unreachable(),
2524
+ this.raiseRuntimeFailure(context),
2344
2525
  address(),
2345
2526
  );
2346
2527
  }
2347
2528
 
2348
- compileWindowStart(start, maximum, bounds) {
2529
+ compileWindowStart(start, maximum, bounds, context) {
2349
2530
  const zero = () => this.module.i32.const(0);
2350
2531
  if (bounds === "unchecked") {
2351
2532
  return start;
@@ -2365,14 +2546,14 @@ class MirCompiler {
2365
2546
  );
2366
2547
  };
2367
2548
  }
2368
- if (bounds === "trap") {
2549
+ if (bounds === "checked") {
2369
2550
  return () =>
2370
2551
  this.module.if(
2371
2552
  this.module.i32.or(
2372
2553
  this.module.i32.lt_s(start(), zero()),
2373
2554
  this.module.i32.gt_s(start(), maximum()),
2374
2555
  ),
2375
- this.module.unreachable(),
2556
+ this.raiseRuntimeFailure(context),
2376
2557
  start(),
2377
2558
  );
2378
2559
  }
@@ -2629,7 +2810,7 @@ class MirCompiler {
2629
2810
  source()[1],
2630
2811
  ),
2631
2812
  ),
2632
- this.module.if(invalidOverlap(), this.module.unreachable()),
2813
+ this.module.if(invalidOverlap(), this.raiseRuntimeFailure(context)),
2633
2814
  this.module.local.set(counter, this.module.i32.const(0)),
2634
2815
  copy,
2635
2816
  ]);
@@ -2892,7 +3073,7 @@ class MirCompiler {
2892
3073
  ),
2893
3074
  );
2894
3075
  }
2895
- if (bounds === "trap") {
3076
+ if (bounds === "checked") {
2896
3077
  const outOfBounds = this.module.i32.or(
2897
3078
  this.module.i32.lt_s(
2898
3079
  this.compileValue(value, context),
@@ -2905,14 +3086,20 @@ class MirCompiler {
2905
3086
  );
2906
3087
  return this.module.if(
2907
3088
  outOfBounds,
2908
- this.module.unreachable(),
3089
+ this.raiseRuntimeFailure(context),
2909
3090
  this.compileValue(value, context),
2910
3091
  );
2911
3092
  }
2912
3093
  this.fail(`unknown bounds mode '${String(bounds)}'`);
2913
3094
  }
2914
3095
 
2915
- compileDynamicBoundedIndex(index, length, bounds, clampLengthKnownPositive = false) {
3096
+ compileDynamicBoundedIndex(
3097
+ index,
3098
+ length,
3099
+ bounds,
3100
+ context,
3101
+ clampLengthKnownPositive = false,
3102
+ ) {
2916
3103
  if (bounds === "unchecked") {
2917
3104
  return index();
2918
3105
  }
@@ -2932,17 +3119,17 @@ class MirCompiler {
2932
3119
  if (clampLengthKnownPositive) return clamped();
2933
3120
  return this.module.if(
2934
3121
  this.module.i32.le_s(length(), this.module.i32.const(0)),
2935
- this.module.unreachable(),
3122
+ this.raiseRuntimeFailure(context),
2936
3123
  clamped(),
2937
3124
  );
2938
3125
  }
2939
- if (bounds === "trap") {
3126
+ if (bounds === "checked") {
2940
3127
  return this.module.if(
2941
3128
  this.module.i32.or(
2942
3129
  this.module.i32.lt_s(index(), this.module.i32.const(0)),
2943
3130
  this.module.i32.ge_s(index(), length()),
2944
3131
  ),
2945
- this.module.unreachable(),
3132
+ this.raiseRuntimeFailure(context),
2946
3133
  index(),
2947
3134
  );
2948
3135
  }
@@ -2966,7 +3153,7 @@ class MirCompiler {
2966
3153
  }
2967
3154
  }
2968
3155
 
2969
- compileBinary(op, scalar, lhs, rhs) {
3156
+ compileBinary(op, scalar, lhs, rhs, context) {
2970
3157
  if (!supportsMirOperation("binary", op, scalar)) {
2971
3158
  this.fail(`binary operation '${String(op)}' does not support scalar '${scalar}'`);
2972
3159
  }
@@ -2990,13 +3177,26 @@ class MirCompiler {
2990
3177
  wasm.eq(lhs(), minimum()),
2991
3178
  wasm.eq(rhs(), negativeOne()),
2992
3179
  );
2993
- return this.module.if(overflow, minimum(), wasm.div_s(lhs(), rhs()));
3180
+ const division = this.module.if(
3181
+ overflow,
3182
+ minimum(),
3183
+ wasm.div_s(lhs(), rhs()),
3184
+ );
3185
+ return this.module.if(
3186
+ wasm.eq(rhs(), this.zero(scalar)),
3187
+ this.raiseRuntimeFailure(context),
3188
+ division,
3189
+ );
2994
3190
  }
2995
3191
  case "remainder":
2996
3192
  if (!integer) {
2997
3193
  return this.compileMathKernelCall("remainder", scalar, [lhs(), rhs()]);
2998
3194
  }
2999
- return wasm.rem_s(lhs(), rhs());
3195
+ return this.module.if(
3196
+ wasm.eq(rhs(), this.zero(scalar)),
3197
+ this.raiseRuntimeFailure(context),
3198
+ wasm.rem_s(lhs(), rhs()),
3199
+ );
3000
3200
  case "bit_and": return wasm.and(lhs(), rhs());
3001
3201
  case "bit_or": return wasm.or(lhs(), rhs());
3002
3202
  case "bit_xor": return wasm.xor(lhs(), rhs());
@@ -3083,6 +3283,16 @@ class MirCompiler {
3083
3283
  return this.module.select(wasm.lt_s(arg(0), arg(1)), arg(0), arg(1));
3084
3284
  case "max":
3085
3285
  return this.module.select(wasm.gt_s(arg(0), arg(1)), arg(0), arg(1));
3286
+ case "range_clamp":
3287
+ return this.module.select(
3288
+ wasm.lt_s(arg(0), arg(1)),
3289
+ arg(1),
3290
+ this.module.select(
3291
+ wasm.gt_s(arg(0), arg(2)),
3292
+ arg(2),
3293
+ arg(0),
3294
+ ),
3295
+ );
3086
3296
  default:
3087
3297
  this.fail(`intrinsic '${data.intrinsic}' requires f32 or f64 operands`);
3088
3298
  }
@@ -3096,6 +3306,22 @@ class MirCompiler {
3096
3306
  case "trunc": return wasm.trunc(args[0]);
3097
3307
  case "min": return wasm.min(args[0], args[1]);
3098
3308
  case "max": return wasm.max(args[0], args[1]);
3309
+ case "range_clamp": {
3310
+ const arg = (index) => this.compileValue(data.args[index], context);
3311
+ return this.module.select(
3312
+ wasm.ne(arg(0), arg(0)),
3313
+ arg(1),
3314
+ this.module.select(
3315
+ wasm.lt(arg(0), arg(1)),
3316
+ arg(1),
3317
+ this.module.select(
3318
+ wasm.gt(arg(0), arg(2)),
3319
+ arg(2),
3320
+ arg(0),
3321
+ ),
3322
+ ),
3323
+ );
3324
+ }
3099
3325
  case "fma": return this.compileMathKernelCall(data.intrinsic, scalar, args);
3100
3326
  case "sin":
3101
3327
  case "cos":
@@ -3418,6 +3644,7 @@ class MirCompiler {
3418
3644
  default_reprs: null,
3419
3645
  range_min_repr: null,
3420
3646
  range_max_repr: null,
3647
+ param_control: null,
3421
3648
  })),
3422
3649
  params: this.mir.interface.params.map((param, id) => ({
3423
3650
  name: param.name,
@@ -3432,6 +3659,15 @@ class MirCompiler {
3432
3659
  default_reprs: this.constantReprs(param.default),
3433
3660
  range_min_repr: this.scalarRepr(param.range?.min),
3434
3661
  range_max_repr: this.scalarRepr(param.range?.max),
3662
+ param_control: this.storageShape(param.ty).length === 1 && param.range
3663
+ ? {
3664
+ scale: param.control.scale,
3665
+ curve: param.control.curve,
3666
+ unit: param.control.unit,
3667
+ step_repr: this.scalarRepr(param.control.step),
3668
+ step_count: param.control.step_count,
3669
+ }
3670
+ : null,
3435
3671
  })),
3436
3672
  buffers: this.mir.interface.buffers.map((buffer) => {
3437
3673
  const channels = this.bufferChannelMetadata(buffer.channels);
@@ -3509,6 +3745,7 @@ class MirCompiler {
3509
3745
  default_reprs: null,
3510
3746
  range_min_repr: null,
3511
3747
  range_max_repr: null,
3748
+ param_control: null,
3512
3749
  }));
3513
3750
  }
3514
3751
 
package/dist/build.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
- "ondaVersion": "0.5.4",
2
+ "ondaVersion": "0.6.0",
3
3
  "binaryenVersion": "130.0.0",
4
4
  "frontendPackage": "onda_compiler_web",
5
5
  "frontendOptimization": {
6
6
  "tool": "wasm-opt",
7
7
  "binaryenVersion": "130.0.0",
8
8
  "optimizeLevel": 4,
9
- "inputBytes": 6441305,
10
- "outputBytes": 4878025
9
+ "inputBytes": 6579319,
10
+ "outputBytes": 4975533
11
11
  }
12
12
  }
@@ -15,12 +15,16 @@ The package build runs `wasm-pack --release` and then the workspace-pinned Binar
15
15
  optimizer so every local, CI, website, and npm build uses the same Binaryen release.
16
16
 
17
17
  The production exports `compile_to_mir_messagepack(source, sampleRate, blockSize)` and
18
- `compile_project_to_mir_messagepack(entryPath, sourcesJson, sampleRate, blockSize)` return compact
19
- schema-versioned bytes. JSON variants with the same names ending in `_json` remain available for
20
- inspection and tooling. Project compilation accepts a JSON object of project-relative paths to
21
- source strings, resolving imports and includes entirely in memory. Paths cannot escape the virtual
22
- project root. `mir_schema_version()` exposes the producer version for integration checks.
23
- Compilation failures reject with a JSON-encoded array of structured diagnostics.
18
+ `compile_project_to_mir_messagepack(entryPath, sourcesJson, sampleRate, blockSize)` return a
19
+ `FrontendMessagePackCompilation` containing compact schema-versioned bytes plus the ordered
20
+ contributing project paths. JSON variants return the equivalent `FrontendJsonCompilation` for
21
+ inspection and tooling. Both result types expose `take_mir()` and `source_files_json()`. Project
22
+ compilation accepts a JSON object of project-relative paths to source strings, resolving imports and
23
+ includes entirely in memory. Paths cannot escape the virtual project root. Embedded
24
+ standard-library modules are omitted from the source manifest. `mir_schema_version()` exposes the
25
+ producer version for integration checks. Compilation failures reject with a JSON-encoded object
26
+ containing structured diagnostics, the partially resolved source list, and unresolved
27
+ non-standard-library candidates which a host may watch for creation.
24
28
 
25
29
  The current producer and `packages/onda_binaryen_web` both use the current MIR schema. The browser playground
26
30
  under `examples/web/onda_wasm_playground` passes the generated MessagePack directly to the explicitly
@@ -1,6 +1,22 @@
1
1
  /* tslint:disable */
2
2
  /* eslint-disable */
3
3
 
4
+ export class FrontendJsonCompilation {
5
+ private constructor();
6
+ free(): void;
7
+ [Symbol.dispose](): void;
8
+ source_files_json(): string;
9
+ take_mir(): string;
10
+ }
11
+
12
+ export class FrontendMessagePackCompilation {
13
+ private constructor();
14
+ free(): void;
15
+ [Symbol.dispose](): void;
16
+ source_files_json(): string;
17
+ take_mir(): Uint8Array;
18
+ }
19
+
4
20
  /**
5
21
  * Stateful `onda lsp` server for browser Worker transports.
6
22
  */
@@ -16,13 +32,13 @@ export class OndaLsp {
16
32
  set_analysis_options(sample_rate: number, block_size: number): void;
17
33
  }
18
34
 
19
- export function compile_project_to_mir_json(entry_path: string, sources_json: string, sample_rate: number, block_size: number): string;
35
+ export function compile_project_to_mir_json(entry_path: string, sources_json: string, sample_rate: number, block_size: number): FrontendJsonCompilation;
20
36
 
21
- export function compile_project_to_mir_messagepack(entry_path: string, sources_json: string, sample_rate: number, block_size: number): Uint8Array;
37
+ export function compile_project_to_mir_messagepack(entry_path: string, sources_json: string, sample_rate: number, block_size: number): FrontendMessagePackCompilation;
22
38
 
23
- export function compile_to_mir_json(source: string, sample_rate: number, block_size: number): string;
39
+ export function compile_to_mir_json(source: string, sample_rate: number, block_size: number): FrontendJsonCompilation;
24
40
 
25
- export function compile_to_mir_messagepack(source: string, sample_rate: number, block_size: number): Uint8Array;
41
+ export function compile_to_mir_messagepack(source: string, sample_rate: number, block_size: number): FrontendMessagePackCompilation;
26
42
 
27
43
  export function mir_schema_version(): number;
28
44
 
@@ -30,15 +46,21 @@ export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembl
30
46
 
31
47
  export interface InitOutput {
32
48
  readonly memory: WebAssembly.Memory;
49
+ readonly __wbg_frontendjsoncompilation_free: (a: number, b: number) => void;
50
+ readonly __wbg_frontendmessagepackcompilation_free: (a: number, b: number) => void;
33
51
  readonly __wbg_ondalsp_free: (a: number, b: number) => void;
34
- readonly compile_project_to_mir_json: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number, number, number];
35
- readonly compile_project_to_mir_messagepack: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number, number, number];
36
- readonly compile_to_mir_json: (a: number, b: number, c: number, d: number) => [number, number, number, number];
37
- readonly compile_to_mir_messagepack: (a: number, b: number, c: number, d: number) => [number, number, number, number];
52
+ readonly compile_project_to_mir_json: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number, number];
53
+ readonly compile_project_to_mir_messagepack: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number, number];
54
+ readonly compile_to_mir_json: (a: number, b: number, c: number, d: number) => [number, number, number];
55
+ readonly compile_to_mir_messagepack: (a: number, b: number, c: number, d: number) => [number, number, number];
56
+ readonly frontendjsoncompilation_source_files_json: (a: number) => [number, number];
57
+ readonly frontendjsoncompilation_take_mir: (a: number) => [number, number];
58
+ readonly frontendmessagepackcompilation_source_files_json: (a: number) => [number, number];
38
59
  readonly mir_schema_version: () => number;
39
60
  readonly ondalsp_handle_message: (a: number, b: number, c: number) => [number, number, number, number];
40
61
  readonly ondalsp_new: () => number;
41
62
  readonly ondalsp_set_analysis_options: (a: number, b: number, c: number) => [number, number];
63
+ readonly frontendmessagepackcompilation_take_mir: (a: number) => [number, number];
42
64
  readonly __wbindgen_externrefs: WebAssembly.Table;
43
65
  readonly __wbindgen_malloc: (a: number, b: number) => number;
44
66
  readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
@@ -1,5 +1,101 @@
1
1
  /* @ts-self-types="./onda_compiler_web.d.ts" */
2
2
 
3
+ export class FrontendJsonCompilation {
4
+ static __wrap(ptr) {
5
+ ptr = ptr >>> 0;
6
+ const obj = Object.create(FrontendJsonCompilation.prototype);
7
+ obj.__wbg_ptr = ptr;
8
+ FrontendJsonCompilationFinalization.register(obj, obj.__wbg_ptr, obj);
9
+ return obj;
10
+ }
11
+ __destroy_into_raw() {
12
+ const ptr = this.__wbg_ptr;
13
+ this.__wbg_ptr = 0;
14
+ FrontendJsonCompilationFinalization.unregister(this);
15
+ return ptr;
16
+ }
17
+ free() {
18
+ const ptr = this.__destroy_into_raw();
19
+ wasm.__wbg_frontendjsoncompilation_free(ptr, 0);
20
+ }
21
+ /**
22
+ * @returns {string}
23
+ */
24
+ source_files_json() {
25
+ let deferred1_0;
26
+ let deferred1_1;
27
+ try {
28
+ const ret = wasm.frontendjsoncompilation_source_files_json(this.__wbg_ptr);
29
+ deferred1_0 = ret[0];
30
+ deferred1_1 = ret[1];
31
+ return getStringFromWasm0(ret[0], ret[1]);
32
+ } finally {
33
+ wasm.__wbindgen_free(deferred1_0, deferred1_1, 1);
34
+ }
35
+ }
36
+ /**
37
+ * @returns {string}
38
+ */
39
+ take_mir() {
40
+ let deferred1_0;
41
+ let deferred1_1;
42
+ try {
43
+ const ret = wasm.frontendjsoncompilation_take_mir(this.__wbg_ptr);
44
+ deferred1_0 = ret[0];
45
+ deferred1_1 = ret[1];
46
+ return getStringFromWasm0(ret[0], ret[1]);
47
+ } finally {
48
+ wasm.__wbindgen_free(deferred1_0, deferred1_1, 1);
49
+ }
50
+ }
51
+ }
52
+ if (Symbol.dispose) FrontendJsonCompilation.prototype[Symbol.dispose] = FrontendJsonCompilation.prototype.free;
53
+
54
+ export class FrontendMessagePackCompilation {
55
+ static __wrap(ptr) {
56
+ ptr = ptr >>> 0;
57
+ const obj = Object.create(FrontendMessagePackCompilation.prototype);
58
+ obj.__wbg_ptr = ptr;
59
+ FrontendMessagePackCompilationFinalization.register(obj, obj.__wbg_ptr, obj);
60
+ return obj;
61
+ }
62
+ __destroy_into_raw() {
63
+ const ptr = this.__wbg_ptr;
64
+ this.__wbg_ptr = 0;
65
+ FrontendMessagePackCompilationFinalization.unregister(this);
66
+ return ptr;
67
+ }
68
+ free() {
69
+ const ptr = this.__destroy_into_raw();
70
+ wasm.__wbg_frontendmessagepackcompilation_free(ptr, 0);
71
+ }
72
+ /**
73
+ * @returns {string}
74
+ */
75
+ source_files_json() {
76
+ let deferred1_0;
77
+ let deferred1_1;
78
+ try {
79
+ const ret = wasm.frontendmessagepackcompilation_source_files_json(this.__wbg_ptr);
80
+ deferred1_0 = ret[0];
81
+ deferred1_1 = ret[1];
82
+ return getStringFromWasm0(ret[0], ret[1]);
83
+ } finally {
84
+ wasm.__wbindgen_free(deferred1_0, deferred1_1, 1);
85
+ }
86
+ }
87
+ /**
88
+ * @returns {Uint8Array}
89
+ */
90
+ take_mir() {
91
+ const ret = wasm.frontendmessagepackcompilation_take_mir(this.__wbg_ptr);
92
+ var v1 = getArrayU8FromWasm0(ret[0], ret[1]).slice();
93
+ wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);
94
+ return v1;
95
+ }
96
+ }
97
+ if (Symbol.dispose) FrontendMessagePackCompilation.prototype[Symbol.dispose] = FrontendMessagePackCompilation.prototype.free;
98
+
3
99
  /**
4
100
  * Stateful `onda lsp` server for browser Worker transports.
5
101
  */
@@ -64,29 +160,18 @@ if (Symbol.dispose) OndaLsp.prototype[Symbol.dispose] = OndaLsp.prototype.free;
64
160
  * @param {string} sources_json
65
161
  * @param {number} sample_rate
66
162
  * @param {number} block_size
67
- * @returns {string}
163
+ * @returns {FrontendJsonCompilation}
68
164
  */
69
165
  export function compile_project_to_mir_json(entry_path, sources_json, sample_rate, block_size) {
70
- let deferred4_0;
71
- let deferred4_1;
72
- try {
73
- const ptr0 = passStringToWasm0(entry_path, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
74
- const len0 = WASM_VECTOR_LEN;
75
- const ptr1 = passStringToWasm0(sources_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
76
- const len1 = WASM_VECTOR_LEN;
77
- const ret = wasm.compile_project_to_mir_json(ptr0, len0, ptr1, len1, sample_rate, block_size);
78
- var ptr3 = ret[0];
79
- var len3 = ret[1];
80
- if (ret[3]) {
81
- ptr3 = 0; len3 = 0;
82
- throw takeFromExternrefTable0(ret[2]);
83
- }
84
- deferred4_0 = ptr3;
85
- deferred4_1 = len3;
86
- return getStringFromWasm0(ptr3, len3);
87
- } finally {
88
- wasm.__wbindgen_free(deferred4_0, deferred4_1, 1);
166
+ const ptr0 = passStringToWasm0(entry_path, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
167
+ const len0 = WASM_VECTOR_LEN;
168
+ const ptr1 = passStringToWasm0(sources_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
169
+ const len1 = WASM_VECTOR_LEN;
170
+ const ret = wasm.compile_project_to_mir_json(ptr0, len0, ptr1, len1, sample_rate, block_size);
171
+ if (ret[2]) {
172
+ throw takeFromExternrefTable0(ret[1]);
89
173
  }
174
+ return FrontendJsonCompilation.__wrap(ret[0]);
90
175
  }
91
176
 
92
177
  /**
@@ -94,7 +179,7 @@ export function compile_project_to_mir_json(entry_path, sources_json, sample_rat
94
179
  * @param {string} sources_json
95
180
  * @param {number} sample_rate
96
181
  * @param {number} block_size
97
- * @returns {Uint8Array}
182
+ * @returns {FrontendMessagePackCompilation}
98
183
  */
99
184
  export function compile_project_to_mir_messagepack(entry_path, sources_json, sample_rate, block_size) {
100
185
  const ptr0 = passStringToWasm0(entry_path, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
@@ -102,57 +187,42 @@ export function compile_project_to_mir_messagepack(entry_path, sources_json, sam
102
187
  const ptr1 = passStringToWasm0(sources_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
103
188
  const len1 = WASM_VECTOR_LEN;
104
189
  const ret = wasm.compile_project_to_mir_messagepack(ptr0, len0, ptr1, len1, sample_rate, block_size);
105
- if (ret[3]) {
106
- throw takeFromExternrefTable0(ret[2]);
190
+ if (ret[2]) {
191
+ throw takeFromExternrefTable0(ret[1]);
107
192
  }
108
- var v3 = getArrayU8FromWasm0(ret[0], ret[1]).slice();
109
- wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);
110
- return v3;
193
+ return FrontendMessagePackCompilation.__wrap(ret[0]);
111
194
  }
112
195
 
113
196
  /**
114
197
  * @param {string} source
115
198
  * @param {number} sample_rate
116
199
  * @param {number} block_size
117
- * @returns {string}
200
+ * @returns {FrontendJsonCompilation}
118
201
  */
119
202
  export function compile_to_mir_json(source, sample_rate, block_size) {
120
- let deferred3_0;
121
- let deferred3_1;
122
- try {
123
- const ptr0 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
124
- const len0 = WASM_VECTOR_LEN;
125
- const ret = wasm.compile_to_mir_json(ptr0, len0, sample_rate, block_size);
126
- var ptr2 = ret[0];
127
- var len2 = ret[1];
128
- if (ret[3]) {
129
- ptr2 = 0; len2 = 0;
130
- throw takeFromExternrefTable0(ret[2]);
131
- }
132
- deferred3_0 = ptr2;
133
- deferred3_1 = len2;
134
- return getStringFromWasm0(ptr2, len2);
135
- } finally {
136
- wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
203
+ const ptr0 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
204
+ const len0 = WASM_VECTOR_LEN;
205
+ const ret = wasm.compile_to_mir_json(ptr0, len0, sample_rate, block_size);
206
+ if (ret[2]) {
207
+ throw takeFromExternrefTable0(ret[1]);
137
208
  }
209
+ return FrontendJsonCompilation.__wrap(ret[0]);
138
210
  }
139
211
 
140
212
  /**
141
213
  * @param {string} source
142
214
  * @param {number} sample_rate
143
215
  * @param {number} block_size
144
- * @returns {Uint8Array}
216
+ * @returns {FrontendMessagePackCompilation}
145
217
  */
146
218
  export function compile_to_mir_messagepack(source, sample_rate, block_size) {
147
219
  const ptr0 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
148
220
  const len0 = WASM_VECTOR_LEN;
149
221
  const ret = wasm.compile_to_mir_messagepack(ptr0, len0, sample_rate, block_size);
150
- if (ret[3]) {
151
- throw takeFromExternrefTable0(ret[2]);
222
+ if (ret[2]) {
223
+ throw takeFromExternrefTable0(ret[1]);
152
224
  }
153
- var v2 = getArrayU8FromWasm0(ret[0], ret[1]).slice();
154
- wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);
155
- return v2;
225
+ return FrontendMessagePackCompilation.__wrap(ret[0]);
156
226
  }
157
227
 
158
228
  /**
@@ -190,6 +260,12 @@ function __wbg_get_imports() {
190
260
  };
191
261
  }
192
262
 
263
+ const FrontendJsonCompilationFinalization = (typeof FinalizationRegistry === 'undefined')
264
+ ? { register: () => {}, unregister: () => {} }
265
+ : new FinalizationRegistry(ptr => wasm.__wbg_frontendjsoncompilation_free(ptr >>> 0, 1));
266
+ const FrontendMessagePackCompilationFinalization = (typeof FinalizationRegistry === 'undefined')
267
+ ? { register: () => {}, unregister: () => {} }
268
+ : new FinalizationRegistry(ptr => wasm.__wbg_frontendmessagepackcompilation_free(ptr >>> 0, 1));
193
269
  const OndaLspFinalization = (typeof FinalizationRegistry === 'undefined')
194
270
  ? { register: () => {}, unregister: () => {} }
195
271
  : new FinalizationRegistry(ptr => wasm.__wbg_ondalsp_free(ptr >>> 0, 1));
@@ -1,15 +1,21 @@
1
1
  /* tslint:disable */
2
2
  /* eslint-disable */
3
3
  export const memory: WebAssembly.Memory;
4
+ export const __wbg_frontendjsoncompilation_free: (a: number, b: number) => void;
5
+ export const __wbg_frontendmessagepackcompilation_free: (a: number, b: number) => void;
4
6
  export const __wbg_ondalsp_free: (a: number, b: number) => void;
5
- export const compile_project_to_mir_json: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number, number, number];
6
- export const compile_project_to_mir_messagepack: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number, number, number];
7
- export const compile_to_mir_json: (a: number, b: number, c: number, d: number) => [number, number, number, number];
8
- export const compile_to_mir_messagepack: (a: number, b: number, c: number, d: number) => [number, number, number, number];
7
+ export const compile_project_to_mir_json: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number, number];
8
+ export const compile_project_to_mir_messagepack: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number, number];
9
+ export const compile_to_mir_json: (a: number, b: number, c: number, d: number) => [number, number, number];
10
+ export const compile_to_mir_messagepack: (a: number, b: number, c: number, d: number) => [number, number, number];
11
+ export const frontendjsoncompilation_source_files_json: (a: number) => [number, number];
12
+ export const frontendjsoncompilation_take_mir: (a: number) => [number, number];
13
+ export const frontendmessagepackcompilation_source_files_json: (a: number) => [number, number];
9
14
  export const mir_schema_version: () => number;
10
15
  export const ondalsp_handle_message: (a: number, b: number, c: number) => [number, number, number, number];
11
16
  export const ondalsp_new: () => number;
12
17
  export const ondalsp_set_analysis_options: (a: number, b: number, c: number) => [number, number];
18
+ export const frontendmessagepackcompilation_take_mir: (a: number) => [number, number];
13
19
  export const __wbindgen_externrefs: WebAssembly.Table;
14
20
  export const __wbindgen_malloc: (a: number, b: number) => number;
15
21
  export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
@@ -5,7 +5,7 @@
5
5
  "onda contributors"
6
6
  ],
7
7
  "description": "Browser-safe Onda source-to-MIR compiler",
8
- "version": "0.5.4",
8
+ "version": "0.6.0",
9
9
  "license": "MIT",
10
10
  "repository": {
11
11
  "type": "git",
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const ONDA_VERSION = "0.5.4";
1
+ export const ONDA_VERSION = "0.6.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onda-lang/wasm-compiler",
3
- "version": "0.5.4",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "description": "Onda source-to-WebAssembly compiler for browsers and Node.js",
6
6
  "license": "MIT",
@@ -59,7 +59,7 @@
59
59
  "prepack": "npm run build"
60
60
  },
61
61
  "dependencies": {
62
- "@onda-lang/processor-abi": "0.5.4",
62
+ "@onda-lang/processor-abi": "0.6.0",
63
63
  "binaryen": "130.0.0"
64
64
  },
65
65
  "publishConfig": {
package/src/index.d.ts CHANGED
@@ -5,6 +5,8 @@ export {
5
5
  PROCESSOR_ABI_VERSION,
6
6
  PROCESSOR_ARTIFACT_FORMAT,
7
7
  PROCESSOR_ARTIFACT_FORMAT_VERSION,
8
+ PROCESSOR_EXECUTION_OK,
9
+ PROCESSOR_EXECUTION_RUNTIME_SAFETY_FAILURE,
8
10
  PROCESSOR_SNAPSHOT_FORMAT_VERSION,
9
11
  createProcessorArtifactFiles,
10
12
  loadProcessorArtifactFiles,
@@ -53,15 +55,21 @@ export interface OndaProject {
53
55
  sources: Record<string, string>;
54
56
  }
55
57
 
58
+ export interface OndaCompilationResult {
59
+ artifact: OndaProcessorArtifact;
60
+ /** Entry first, then transitive non-stdlib imports/includes in discovery order. */
61
+ sourceFiles: string[];
62
+ }
63
+
56
64
  export interface OndaCompilerInstance {
57
65
  compileSource(
58
66
  source: string,
59
67
  options?: OndaCompileOptions,
60
- ): Promise<OndaProcessorArtifact>;
68
+ ): Promise<OndaCompilationResult>;
61
69
  compileProject(
62
70
  project: OndaProject,
63
71
  options?: OndaCompileOptions,
64
- ): Promise<OndaProcessorArtifact>;
72
+ ): Promise<OndaCompilationResult>;
65
73
  sendLspMessage(message: OndaLspMessage): Promise<OndaLspMessage[]>;
66
74
  setLspAnalysisOptions(options?: OndaCompileOptions): Promise<void>;
67
75
  dispose(): Promise<void>;
@@ -108,6 +116,9 @@ export class OndaCompilerError extends Error {
108
116
 
109
117
  export class OndaCompileError extends OndaCompilerError {
110
118
  readonly diagnostics: OndaCompilerDiagnostic[];
119
+ readonly sourceFiles: string[];
120
+ /** Referenced non-stdlib source candidates which were not present. */
121
+ readonly unresolvedSourceFiles: string[];
111
122
  }
112
123
 
113
124
  export class OndaBinaryenError extends Error {}
package/src/index.js CHANGED
@@ -3,6 +3,8 @@ import {
3
3
  PROCESSOR_ABI_VERSION,
4
4
  PROCESSOR_ARTIFACT_FORMAT,
5
5
  PROCESSOR_ARTIFACT_FORMAT_VERSION,
6
+ PROCESSOR_EXECUTION_OK,
7
+ PROCESSOR_EXECUTION_RUNTIME_SAFETY_FAILURE,
6
8
  PROCESSOR_SNAPSHOT_FORMAT_VERSION,
7
9
  createProcessorArtifactFiles,
8
10
  loadProcessorArtifactFiles,
@@ -27,6 +29,8 @@ export {
27
29
  PROCESSOR_ABI_VERSION,
28
30
  PROCESSOR_ARTIFACT_FORMAT,
29
31
  PROCESSOR_ARTIFACT_FORMAT_VERSION,
32
+ PROCESSOR_EXECUTION_OK,
33
+ PROCESSOR_EXECUTION_RUNTIME_SAFETY_FAILURE,
30
34
  PROCESSOR_SNAPSHOT_FORMAT_VERSION,
31
35
  createProcessorArtifactFiles,
32
36
  loadProcessorArtifactFiles,
@@ -46,7 +50,10 @@ export class OndaCompilerError extends Error {
46
50
  }
47
51
 
48
52
  export class OndaCompileError extends OndaCompilerError {
49
- constructor(diagnostics, { cause } = {}) {
53
+ constructor(
54
+ diagnostics,
55
+ { cause, sourceFiles = [], unresolvedSourceFiles = [] } = {},
56
+ ) {
50
57
  const normalized = normalizeDiagnostics(diagnostics);
51
58
  const first = normalized[0];
52
59
  const message = first
@@ -55,6 +62,8 @@ export class OndaCompileError extends OndaCompilerError {
55
62
  super(message, { cause });
56
63
  this.name = "OndaCompileError";
57
64
  this.diagnostics = normalized;
65
+ this.sourceFiles = normalizeSourceFiles(sourceFiles);
66
+ this.unresolvedSourceFiles = normalizeSourceFiles(unresolvedSourceFiles);
58
67
  }
59
68
  }
60
69
 
@@ -72,9 +81,9 @@ class OndaCompiler {
72
81
  throw configurationError("source must be a string");
73
82
  }
74
83
  const compile = normalizeCompileOptions(options);
75
- let mir;
84
+ let frontendCompilation;
76
85
  try {
77
- mir = this.frontend.compile_to_mir_messagepack(
86
+ frontendCompilation = this.frontend.compile_to_mir_messagepack(
78
87
  source,
79
88
  compile.sampleRate,
80
89
  compile.blockSize,
@@ -82,7 +91,14 @@ class OndaCompiler {
82
91
  } catch (error) {
83
92
  throw diagnosticsFromFrontend(error);
84
93
  }
85
- return compileMirTransport(mir, compile.codegen, this.compileTrustedMir);
94
+ const { mir, sourceFiles } = consumeFrontendCompilation(frontendCompilation);
95
+ const artifact = compileMirTransport(
96
+ mir,
97
+ compile.codegen,
98
+ this.compileTrustedMir,
99
+ sourceFiles,
100
+ );
101
+ return { artifact, sourceFiles };
86
102
  }
87
103
 
88
104
  async compileProject(project, options = {}) {
@@ -102,9 +118,9 @@ class OndaCompiler {
102
118
  }
103
119
 
104
120
  const compile = normalizeCompileOptions(options);
105
- let mir;
121
+ let frontendCompilation;
106
122
  try {
107
- mir = this.frontend.compile_project_to_mir_messagepack(
123
+ frontendCompilation = this.frontend.compile_project_to_mir_messagepack(
108
124
  project.entry,
109
125
  JSON.stringify(project.sources),
110
126
  compile.sampleRate,
@@ -113,7 +129,14 @@ class OndaCompiler {
113
129
  } catch (error) {
114
130
  throw diagnosticsFromFrontend(error);
115
131
  }
116
- return compileMirTransport(mir, compile.codegen, this.compileTrustedMir);
132
+ const { mir, sourceFiles } = consumeFrontendCompilation(frontendCompilation);
133
+ const artifact = compileMirTransport(
134
+ mir,
135
+ compile.codegen,
136
+ this.compileTrustedMir,
137
+ sourceFiles,
138
+ );
139
+ return { artifact, sourceFiles };
117
140
  }
118
141
 
119
142
  async sendLspMessage(message) {
@@ -212,7 +235,10 @@ class WorkerOndaCompiler {
212
235
  return;
213
236
  }
214
237
  const error = message.error?.diagnostics
215
- ? new OndaCompileError(message.error.diagnostics)
238
+ ? new OndaCompileError(message.error.diagnostics, {
239
+ sourceFiles: message.error.sourceFiles,
240
+ unresolvedSourceFiles: message.error.unresolvedSourceFiles,
241
+ })
216
242
  : new OndaCompilerError(message.error?.message ?? "compiler worker failed");
217
243
  if (message.error?.name) error.name = message.error.name;
218
244
  if (message.error?.stack) error.stack = message.error.stack;
@@ -310,7 +336,17 @@ function normalizeCompileOptions(options) {
310
336
  return { sampleRate, blockSize, codegen: options.codegen ?? {} };
311
337
  }
312
338
 
313
- function compileMirTransport(mir, codegen, compileTrustedMir) {
339
+ function consumeFrontendCompilation(compilation) {
340
+ try {
341
+ const mir = compilation.take_mir();
342
+ const sourceFiles = normalizeSourceFiles(JSON.parse(compilation.source_files_json()));
343
+ return { mir, sourceFiles };
344
+ } finally {
345
+ compilation.free();
346
+ }
347
+ }
348
+
349
+ function compileMirTransport(mir, codegen, compileTrustedMir, sourceFiles) {
314
350
  try {
315
351
  return compileTrustedMir(mir, codegen);
316
352
  } catch (cause) {
@@ -325,7 +361,7 @@ function compileMirTransport(mir, codegen, compileTrustedMir) {
325
361
  end_line: 0,
326
362
  end_column: 0,
327
363
  trace: [],
328
- }], { cause });
364
+ }], { cause, sourceFiles });
329
365
  }
330
366
  }
331
367
 
@@ -333,9 +369,16 @@ function diagnosticsFromFrontend(error) {
333
369
  const encoded = typeof error === "string" ? error : error?.message;
334
370
  if (typeof encoded === "string") {
335
371
  try {
336
- const diagnostics = JSON.parse(encoded);
337
- if (Array.isArray(diagnostics)) {
338
- return new OndaCompileError(diagnostics, { cause: error });
372
+ const failure = JSON.parse(encoded);
373
+ if (Array.isArray(failure)) {
374
+ return new OndaCompileError(failure, { cause: error });
375
+ }
376
+ if (failure && Array.isArray(failure.diagnostics)) {
377
+ return new OndaCompileError(failure.diagnostics, {
378
+ cause: error,
379
+ sourceFiles: failure.source_files,
380
+ unresolvedSourceFiles: failure.unresolved_source_files,
381
+ });
339
382
  }
340
383
  } catch {}
341
384
  }
@@ -382,3 +425,8 @@ function normalizeDiagnostics(diagnostics) {
382
425
  : [],
383
426
  }));
384
427
  }
428
+
429
+ function normalizeSourceFiles(sourceFiles) {
430
+ if (!Array.isArray(sourceFiles)) return [];
431
+ return sourceFiles.map((path) => String(path));
432
+ }
package/src/worker.js CHANGED
@@ -12,19 +12,19 @@ globalThis.addEventListener("message", async (event) => {
12
12
  return;
13
13
  }
14
14
  if (message.type === "compileSource") {
15
- const artifact = await (await compiler()).compileSource(
15
+ const result = await (await compiler()).compileSource(
16
16
  message.source,
17
17
  message.options,
18
18
  );
19
- respond(requestId, artifact, [artifact.wasm.buffer]);
19
+ respond(requestId, result, [result.artifact.wasm.buffer]);
20
20
  return;
21
21
  }
22
22
  if (message.type === "compileProject") {
23
- const artifact = await (await compiler()).compileProject(
23
+ const result = await (await compiler()).compileProject(
24
24
  message.project,
25
25
  message.options,
26
26
  );
27
- respond(requestId, artifact, [artifact.wasm.buffer]);
27
+ respond(requestId, result, [result.artifact.wasm.buffer]);
28
28
  return;
29
29
  }
30
30
  if (message.type === "lspMessage") {
@@ -53,6 +53,8 @@ globalThis.addEventListener("message", async (event) => {
53
53
  message: error?.message ?? String(error),
54
54
  stack: error?.stack,
55
55
  diagnostics: error?.diagnostics,
56
+ sourceFiles: error?.sourceFiles,
57
+ unresolvedSourceFiles: error?.unresolvedSourceFiles,
56
58
  },
57
59
  });
58
60
  }