gofront 1.5.1 → 1.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.
Files changed (60) hide show
  1. package/README.md +174 -466
  2. package/package.json +16 -20
  3. package/src/backend/js/expressions.js +50 -19
  4. package/src/backend/js/index.js +100 -55
  5. package/src/backend/js/runtime.js +102 -0
  6. package/src/backend/js/source-map.js +32 -0
  7. package/src/backend/js/statements.js +96 -16
  8. package/src/backend/js/stdlib/errors.js +2 -2
  9. package/src/backend/js/stdlib/fmt.js +26 -8
  10. package/src/backend/js/stdlib/index.js +3 -0
  11. package/src/backend/js/stdlib/maps.js +1 -1
  12. package/src/backend/js/stdlib/path.js +2 -2
  13. package/src/backend/js/stdlib/shared.js +17 -0
  14. package/src/backend/js/stdlib/slices.js +1 -1
  15. package/src/backend/js/stdlib/sort.js +1 -1
  16. package/src/backend/js/stdlib/strconv.js +34 -9
  17. package/src/backend/js/stdlib/strings.js +4 -1
  18. package/src/backend/js/stdlib/testing.js +2 -2
  19. package/src/backend/js/stdlib/time.js +3 -3
  20. package/src/backend/js/templ.js +1 -1
  21. package/src/backend/wasm/boundary.js +486 -33
  22. package/src/backend/wasm/emit-builtins.js +1103 -0
  23. package/src/backend/wasm/emit-exprs.js +1908 -0
  24. package/src/backend/wasm/emit-maps.js +1863 -0
  25. package/src/backend/wasm/emit-shared.js +519 -0
  26. package/src/backend/wasm/emit-stdlib.js +3006 -0
  27. package/src/backend/wasm/emit-stmts.js +2472 -0
  28. package/src/backend/wasm/emit.js +606 -5009
  29. package/src/backend/wasm/encode.js +212 -64
  30. package/src/backend/wasm/glue.js +19 -5
  31. package/src/backend/wasm/index.js +1426 -49
  32. package/src/backend/wasm/monomorph.js +711 -0
  33. package/src/backend/wasm/optimize.js +227 -0
  34. package/src/backend/wasm/types.js +159 -35
  35. package/src/backend/wasm/wat.js +49 -2
  36. package/src/cli-core.js +275 -72
  37. package/src/compiler.js +144 -21
  38. package/src/index.js +86 -115
  39. package/src/lower/boxing.js +3 -4
  40. package/src/lower/captures.js +10 -17
  41. package/src/lower/escape.js +4 -9
  42. package/src/lower/functions.js +277 -5
  43. package/src/lower/globals.js +7 -15
  44. package/src/lower/index.js +5 -2
  45. package/src/lower/ownership.js +5 -10
  46. package/src/lower/range.js +2 -5
  47. package/src/lower/walk.js +25 -0
  48. package/src/resolver.js +2 -1
  49. package/src/test-runner.js +20 -0
  50. package/src/typechecker/assignability.js +42 -1
  51. package/src/typechecker/expressions.js +142 -2
  52. package/src/typechecker/index.js +27 -1
  53. package/src/typechecker/resolve.js +35 -6
  54. package/src/typechecker/statements.js +0 -7
  55. package/src/typechecker/stdlib/core.js +17 -19
  56. package/src/typechecker/stdlib/extended.js +18 -3
  57. package/src/typechecker/stdlib/shared.js +53 -0
  58. package/src/typechecker/stdlib.js +2 -0
  59. package/src/typechecker/types.js +206 -7
  60. package/src/vendor.js +2 -2
@@ -64,12 +64,12 @@ export function encodeF64(val) {
64
64
  return Array.from(new Uint8Array(buf));
65
65
  }
66
66
 
67
- export function encodeString(str) {
67
+ function encodeString(str) {
68
68
  const utf8 = new TextEncoder().encode(str);
69
69
  return [...encodeU32LEB(utf8.length), ...utf8];
70
70
  }
71
71
 
72
- export function encodeVector(items, encodeItem) {
72
+ function encodeVector(items, encodeItem) {
73
73
  const count = encodeU32LEB(items.length);
74
74
  const bytes = [];
75
75
  for (const item of items) {
@@ -83,7 +83,7 @@ export function encodeVector(items, encodeItem) {
83
83
 
84
84
  // ── Value Types & Heap Types ─────────────────────────────────
85
85
 
86
- export const ValType = {
86
+ const ValType = {
87
87
  i32: 0x7f,
88
88
  i64: 0x7e,
89
89
  f32: 0x7d,
@@ -101,9 +101,11 @@ export const ValType = {
101
101
  nullref: 0x71,
102
102
  nullfuncref: 0x73,
103
103
  nullexternref: 0x72,
104
+ exnref: 0x69,
105
+ nullexnref: 0x75,
104
106
  };
105
107
 
106
- export function encodeValType(type) {
108
+ function encodeValType(type) {
107
109
  if (typeof type === "number") {
108
110
  return [type];
109
111
  }
@@ -129,7 +131,7 @@ export function encodeValType(type) {
129
131
  throw new Error(`Invalid valtype specification: ${JSON.stringify(type)}`);
130
132
  }
131
133
 
132
- export function encodeBlockType(blockType) {
134
+ function encodeBlockType(blockType) {
133
135
  if (!blockType || blockType === "void") {
134
136
  return [0x40];
135
137
  }
@@ -153,6 +155,7 @@ const OPCODES = {
153
155
  if: 0x04,
154
156
  else: 0x05,
155
157
  throw: 0x08,
158
+ throw_ref: 0x0a,
156
159
  end: 0x0b,
157
160
  br: 0x0c,
158
161
  br_if: 0x0d,
@@ -164,6 +167,7 @@ const OPCODES = {
164
167
  return_call_indirect: 0x13,
165
168
  call_ref: 0x14,
166
169
  return_call_ref: 0x15,
170
+ try_table: 0x1f,
167
171
  drop: 0x1a,
168
172
  select: 0x1b,
169
173
 
@@ -173,6 +177,23 @@ const OPCODES = {
173
177
  "global.get": 0x23,
174
178
  "global.set": 0x24,
175
179
 
180
+ "i32.load": 0x28,
181
+ "i64.load": 0x29,
182
+ "f32.load": 0x2a,
183
+ "f64.load": 0x2b,
184
+ "i32.load8_s": 0x2c,
185
+ "i32.load8_u": 0x2d,
186
+ "i32.load16_s": 0x2e,
187
+ "i32.load16_u": 0x2f,
188
+ "i32.store": 0x36,
189
+ "i64.store": 0x37,
190
+ "f32.store": 0x38,
191
+ "f64.store": 0x39,
192
+ "i32.store8": 0x3a,
193
+ "i32.store16": 0x3b,
194
+ "memory.size": 0x3f,
195
+ "memory.grow": 0x40,
196
+
176
197
  "i32.const": 0x41,
177
198
  "i64.const": 0x42,
178
199
  "f32.const": 0x43,
@@ -360,12 +381,41 @@ const GC_OPCODES = {
360
381
  "extern.convert_any": 0x1b,
361
382
  };
362
383
 
363
- export function encodeInstruction(inst) {
384
+ // Natural alignment (log2) per load/store opcode; memarg = align, offset.
385
+ const MEM_ALIGN = {
386
+ "i32.load": 2,
387
+ "i64.load": 3,
388
+ "f32.load": 2,
389
+ "f64.load": 3,
390
+ "i32.load8_s": 0,
391
+ "i32.load8_u": 0,
392
+ "i32.load16_s": 1,
393
+ "i32.load16_u": 1,
394
+ "i32.store": 2,
395
+ "i64.store": 3,
396
+ "f32.store": 2,
397
+ "f64.store": 3,
398
+ "i32.store8": 0,
399
+ "i32.store16": 1,
400
+ };
401
+
402
+ function encodeInstruction(inst) {
364
403
  if (typeof inst === "string") {
365
404
  inst = { op: inst };
366
405
  }
367
406
  const { op } = inst;
368
407
 
408
+ if (MEM_ALIGN[op] !== undefined) {
409
+ return [
410
+ OPCODES[op],
411
+ ...encodeU32LEB(inst.align ?? MEM_ALIGN[op]),
412
+ ...encodeU32LEB(inst.offset ?? 0),
413
+ ];
414
+ }
415
+ if (op === "memory.size" || op === "memory.grow") {
416
+ return [OPCODES[op], 0x00];
417
+ }
418
+
369
419
  // 1. Check saturating truncations (0xFC prefix)
370
420
  if (SAT_TRUNC_OPCODES[op] !== undefined) {
371
421
  return [0xfc, ...encodeU32LEB(SAT_TRUNC_OPCODES[op])];
@@ -392,6 +442,26 @@ export function encodeInstruction(inst) {
392
442
  ) {
393
443
  return bytes;
394
444
  }
445
+ if (
446
+ op === "ref.cast" ||
447
+ op === "ref.cast_null" ||
448
+ op === "ref.test" ||
449
+ op === "ref.test_null"
450
+ ) {
451
+ const ht =
452
+ inst.heapType !== undefined
453
+ ? inst.heapType
454
+ : inst.typeIndex !== undefined
455
+ ? inst.typeIndex
456
+ : "any";
457
+ if (typeof ht === "number") {
458
+ bytes.push(...encodeI32LEB(ht));
459
+ } else {
460
+ const code = ValType[ht] ?? ValType[`${ht}ref`] ?? 0x6e;
461
+ bytes.push(code);
462
+ }
463
+ return bytes;
464
+ }
395
465
  if (inst.typeIndex !== undefined) {
396
466
  bytes.push(...encodeU32LEB(inst.typeIndex));
397
467
  }
@@ -425,6 +495,40 @@ export function encodeInstruction(inst) {
425
495
  case "throw":
426
496
  return [byte, ...encodeU32LEB(inst.tagIndex ?? inst.index ?? 0)];
427
497
 
498
+ case "throw_ref":
499
+ return [byte];
500
+
501
+ case "try_table": {
502
+ const blockTypeBytes = encodeBlockType(
503
+ inst.blockType ?? inst.resultType,
504
+ );
505
+ const catches = inst.catches ?? [];
506
+ const catchBytes = encodeVector(catches, (c) => {
507
+ const kind = c.kind ?? c.op;
508
+ switch (kind) {
509
+ case "catch":
510
+ return [
511
+ 0x00,
512
+ ...encodeU32LEB(c.tagIndex ?? c.tag ?? 0),
513
+ ...encodeU32LEB(c.label ?? c.depth ?? 0),
514
+ ];
515
+ case "catch_ref":
516
+ return [
517
+ 0x01,
518
+ ...encodeU32LEB(c.tagIndex ?? c.tag ?? 0),
519
+ ...encodeU32LEB(c.label ?? c.depth ?? 0),
520
+ ];
521
+ case "catch_all":
522
+ return [0x02, ...encodeU32LEB(c.label ?? c.depth ?? 0)];
523
+ case "catch_all_ref":
524
+ return [0x03, ...encodeU32LEB(c.label ?? c.depth ?? 0)];
525
+ default:
526
+ throw new Error(`Unknown catch kind: ${kind}`);
527
+ }
528
+ });
529
+ return [byte, ...blockTypeBytes, ...catchBytes];
530
+ }
531
+
428
532
  case "call":
429
533
  case "return_call":
430
534
  return [byte, ...encodeU32LEB(inst.funcIndex ?? inst.index ?? 0)];
@@ -494,7 +598,7 @@ function encodeSection(id, payload) {
494
598
  return [id, ...encodeU32LEB(payload.length), ...payload];
495
599
  }
496
600
 
497
- export function encodeTypeEntry(t) {
601
+ function encodeTypeEntry(t) {
498
602
  if (t.form === "rec") {
499
603
  // Recursive type group: 0x4E, count, types
500
604
  const count = encodeU32LEB(t.types.length);
@@ -533,12 +637,12 @@ export function encodeTypeEntry(t) {
533
637
  ];
534
638
  }
535
639
 
536
- export function encodeTypeSection(types) {
640
+ function encodeTypeSection(types) {
537
641
  if (!types || types.length === 0) return [];
538
642
  return encodeSection(1, encodeVector(types, encodeTypeEntry));
539
643
  }
540
644
 
541
- export function encodeImportSection(imports) {
645
+ function encodeImportSection(imports) {
542
646
  if (!imports || imports.length === 0) return [];
543
647
  return encodeSection(
544
648
  2,
@@ -581,7 +685,7 @@ export function encodeImportSection(imports) {
581
685
  );
582
686
  }
583
687
 
584
- export function encodeFunctionSection(funcs) {
688
+ function encodeFunctionSection(funcs) {
585
689
  if (!funcs || funcs.length === 0) return [];
586
690
  return encodeSection(
587
691
  3,
@@ -589,7 +693,7 @@ export function encodeFunctionSection(funcs) {
589
693
  );
590
694
  }
591
695
 
592
- export function encodeTagSection(tags) {
696
+ function encodeTagSection(tags) {
593
697
  if (!tags || tags.length === 0) return [];
594
698
  return encodeSection(
595
699
  13,
@@ -597,7 +701,22 @@ export function encodeTagSection(tags) {
597
701
  );
598
702
  }
599
703
 
600
- export function encodeGlobalSection(globals) {
704
+ // Single linear memory: `{ min, max }` in 64 KiB pages. `max` is optional;
705
+ // the shared-buffer runtime only grows the memory inside the start function,
706
+ // before any JS TypedArray view exists.
707
+ function encodeMemorySection(memory) {
708
+ if (!memory) return [];
709
+ const limits =
710
+ memory.max != null
711
+ ? [0x01, ...encodeU32LEB(memory.min), ...encodeU32LEB(memory.max)]
712
+ : [0x00, ...encodeU32LEB(memory.min)];
713
+ return encodeSection(
714
+ 5,
715
+ encodeVector([limits], (b) => b),
716
+ );
717
+ }
718
+
719
+ function encodeGlobalSection(globals) {
601
720
  if (!globals || globals.length === 0) return [];
602
721
  return encodeSection(
603
722
  6,
@@ -621,7 +740,7 @@ export function encodeGlobalSection(globals) {
621
740
  );
622
741
  }
623
742
 
624
- export function encodeExportSection(exports) {
743
+ function encodeExportSection(exports) {
625
744
  if (!exports || exports.length === 0) return [];
626
745
  return encodeSection(
627
746
  7,
@@ -670,7 +789,7 @@ function sameValType(a, b) {
670
789
  return false;
671
790
  }
672
791
 
673
- export function encodeElementSection(elements) {
792
+ function encodeElementSection(elements) {
674
793
  if (!elements || elements.length === 0) return [];
675
794
  const segment = [0x03, 0x00, ...encodeVector(elements, encodeU32LEB)];
676
795
  return encodeSection(
@@ -679,64 +798,76 @@ export function encodeElementSection(elements) {
679
798
  );
680
799
  }
681
800
 
682
- export function encodeCodeSection(funcs) {
801
+ // Encodes the code section. `bodyOffsets` (when given) receives, per
802
+ // function, the offset of the body's first instruction relative to the start
803
+ // of the section bytes returned.
804
+ function encodeCodeSection(funcs, bodyOffsets = null) {
683
805
  if (!funcs || funcs.length === 0) return [];
684
- return encodeSection(
685
- 10,
686
- encodeVector(funcs, (fn) => {
687
- // Compress locals: [type1, type1, type2] -> [{ count: 2, type: type1 }, { count: 1, type: type2 }]
688
- const rawLocals = fn.locals ?? [];
689
- const compressedLocals = [];
690
- for (const loc of rawLocals) {
691
- const locType =
692
- typeof loc === "string"
693
- ? loc
694
- : loc.type !== undefined
695
- ? loc.type
696
- : loc;
697
- if (
698
- compressedLocals.length > 0 &&
699
- sameValType(
700
- compressedLocals[compressedLocals.length - 1].type,
701
- locType,
702
- )
703
- ) {
704
- compressedLocals[compressedLocals.length - 1].count++;
705
- } else {
706
- compressedLocals.push({ count: 1, type: locType });
707
- }
708
- }
709
-
710
- const localsBytes = encodeVector(compressedLocals, (group) => [
711
- ...encodeU32LEB(group.count),
712
- ...encodeValType(group.type),
713
- ]);
714
-
715
- const bodyInsts = fn.body ?? [];
716
- const bodyBytes = [];
717
- for (const inst of bodyInsts) {
718
- const chunk = encodeInstruction(inst);
719
- for (let i = 0; i < chunk.length; i++) bodyBytes.push(chunk[i]);
720
- }
721
- // Ensure terminating end opcode (0x0B)
806
+ const relOffsets = [];
807
+ const payload = encodeVector(funcs, (fn) => {
808
+ // Compress locals: [type1, type1, type2] -> [{ count: 2, type: type1 }, { count: 1, type: type2 }]
809
+ const rawLocals = fn.locals ?? [];
810
+ const compressedLocals = [];
811
+ for (const loc of rawLocals) {
812
+ const locType =
813
+ typeof loc === "string" ? loc : loc.type !== undefined ? loc.type : loc;
722
814
  if (
723
- bodyInsts.length === 0 ||
724
- bodyInsts[bodyInsts.length - 1].op !== "end"
815
+ compressedLocals.length > 0 &&
816
+ sameValType(compressedLocals[compressedLocals.length - 1].type, locType)
725
817
  ) {
726
- bodyBytes.push(0x0b);
818
+ compressedLocals[compressedLocals.length - 1].count++;
819
+ } else {
820
+ compressedLocals.push({ count: 1, type: locType });
727
821
  }
822
+ }
728
823
 
729
- const fullBody = [...localsBytes, ...bodyBytes];
730
- return [...encodeU32LEB(fullBody.length), ...fullBody];
731
- }),
732
- );
824
+ const localsBytes = encodeVector(compressedLocals, (group) => [
825
+ ...encodeU32LEB(group.count),
826
+ ...encodeValType(group.type),
827
+ ]);
828
+
829
+ const bodyInsts = fn.body ?? [];
830
+ const bodyBytes = [];
831
+ for (const inst of bodyInsts) {
832
+ const chunk = encodeInstruction(inst);
833
+ for (let i = 0; i < chunk.length; i++) bodyBytes.push(chunk[i]);
834
+ }
835
+ // Ensure terminating end opcode (0x0B)
836
+ if (
837
+ bodyInsts.length === 0 ||
838
+ bodyInsts[bodyInsts.length - 1].op !== "end"
839
+ ) {
840
+ bodyBytes.push(0x0b);
841
+ }
842
+
843
+ const fullBody = [...localsBytes, ...bodyBytes];
844
+ const sizeBytes = encodeU32LEB(fullBody.length);
845
+ // Offset of the first instruction within this entry + entry length.
846
+ relOffsets.push([
847
+ sizeBytes.length + localsBytes.length,
848
+ sizeBytes.length + fullBody.length,
849
+ ]);
850
+ return [...sizeBytes, ...fullBody];
851
+ });
852
+ if (bodyOffsets) {
853
+ // Section-relative: id + size LEB + count LEB + preceding entries.
854
+ let cursor =
855
+ 1 +
856
+ encodeU32LEB(payload.length).length +
857
+ encodeU32LEB(funcs.length).length;
858
+ for (const [first, len] of relOffsets) {
859
+ bodyOffsets.push(cursor + first);
860
+ cursor += len;
861
+ }
862
+ }
863
+ return encodeSection(10, payload);
733
864
  }
734
865
 
735
866
  // ── Top-level Module Encoder ─────────────────────────────────
736
867
 
737
868
  // Custom section placed right after the header; `isGoFrontWasm` keys off it.
738
- export const GOFRONT_SECTION_NAME = "gofront";
739
- const GOFRONT_SECTION_BYTES = [
869
+ const GOFRONT_SECTION_NAME = "gofront";
870
+ export const GOFRONT_SECTION_BYTES = [
740
871
  0x00,
741
872
  ...encodeU32LEB(1 + GOFRONT_SECTION_NAME.length),
742
873
  ...encodeString(GOFRONT_SECTION_NAME),
@@ -753,6 +884,10 @@ export function isGoFrontWasm(bytes) {
753
884
  return true;
754
885
  }
755
886
 
887
+ // Encodes the module. When `mod.sourceMappingURL` is set a trailing
888
+ // `sourceMappingURL` custom section is appended, and `mod.codeOffsets` is
889
+ // filled with the absolute byte offset of every function body (aligned with
890
+ // `mod.funcs`) so a function-level source map can be built.
756
891
  export function encodeModule(mod) {
757
892
  const magic = [0x00, 0x61, 0x73, 0x6d]; // \0asm
758
893
  const version = [0x01, 0x00, 0x00, 0x00]; // version 1
@@ -760,6 +895,7 @@ export function encodeModule(mod) {
760
895
  const typeSec = encodeTypeSection(mod.types);
761
896
  const importSec = encodeImportSection(mod.imports);
762
897
  const funcSec = encodeFunctionSection(mod.funcs);
898
+ const memorySec = encodeMemorySection(mod.memory);
763
899
  const tagSec = encodeTagSection(mod.tags);
764
900
  const globalSec = encodeGlobalSection(mod.globals);
765
901
  const exportSec = encodeExportSection(mod.exports);
@@ -768,7 +904,8 @@ export function encodeModule(mod) {
768
904
  ? encodeSection(8, encodeU32LEB(mod.start))
769
905
  : [];
770
906
  const elemSec = encodeElementSection(mod.elements);
771
- const codeSec = encodeCodeSection(mod.funcs);
907
+ const sectionOffsets = [];
908
+ const codeSec = encodeCodeSection(mod.funcs, sectionOffsets);
772
909
 
773
910
  const allBytes = [
774
911
  ...magic,
@@ -777,13 +914,24 @@ export function encodeModule(mod) {
777
914
  ...typeSec,
778
915
  ...importSec,
779
916
  ...funcSec,
917
+ ...memorySec,
780
918
  ...tagSec,
781
919
  ...globalSec,
782
920
  ...exportSec,
783
921
  ...startSec,
784
922
  ...elemSec,
785
- ...codeSec,
786
923
  ];
924
+ const codeStart = allBytes.length;
925
+ mod.codeOffsets = sectionOffsets.map((o) => codeStart + o);
926
+ allBytes.push(...codeSec);
927
+ if (mod.sourceMappingURL) {
928
+ allBytes.push(
929
+ ...encodeSection(0, [
930
+ ...encodeString("sourceMappingURL"),
931
+ ...encodeString(mod.sourceMappingURL),
932
+ ]),
933
+ );
934
+ }
787
935
 
788
936
  return new Uint8Array(allBytes);
789
937
  }
@@ -1,14 +1,16 @@
1
1
  // src/backend/wasm/glue.js
2
2
  // Runtime imports and JS glue for executing GoFront WASM modules.
3
3
 
4
- import { WASM_IMPORTS_JS } from "./boundary.js";
4
+ import { WASM_IMPORTS_JS, WASM_STDLIB_JS } from "./boundary.js";
5
5
 
6
6
  // The import table is authored once as source text (it is spliced into the
7
7
  // production bundle by the facade); evaluate that same text here so tests
8
8
  // exercise exactly what ships. The text is a compile-time constant.
9
- const makeImports = new Function(`${WASM_IMPORTS_JS}\nreturn __gfw_imports;`)();
9
+ const makeImports = new Function(
10
+ `${WASM_IMPORTS_JS}\n${WASM_STDLIB_JS}\nreturn __gfw_imports;`,
11
+ )();
10
12
 
11
- export function createWasmImports({
13
+ function createWasmImports({
12
14
  stringTable = [],
13
15
  panicTag = null,
14
16
  stdout = null,
@@ -30,6 +32,14 @@ const NIL_DEREF_PATTERNS = [
30
32
  "null dereference", // JavaScriptCore
31
33
  ];
32
34
 
35
+ function safeString(v) {
36
+ try {
37
+ return String(v);
38
+ } catch {
39
+ return "[panic object]";
40
+ }
41
+ }
42
+
33
43
  function isNilDerefTrap(e) {
34
44
  if (
35
45
  typeof WebAssembly !== "undefined" &&
@@ -53,6 +63,7 @@ export function instantiateWasm(
53
63
 
54
64
  const module = new WebAssembly.Module(wasmBytes);
55
65
  const instance = new WebAssembly.Instance(module, imports);
66
+ imports.__bind?.(instance.exports);
56
67
 
57
68
  // The module exports its panic tag if defined internally, or uses the imported one
58
69
  const tag = instance.exports.panicTag ?? defaultPanicTag;
@@ -67,9 +78,12 @@ export function instantiateWasm(
67
78
  try {
68
79
  return val(...cur);
69
80
  } catch (e) {
81
+ if (instance.exports.__panic) {
82
+ instance.exports.__panic.value = null;
83
+ }
70
84
  if (tag && e instanceof WebAssembly.Exception && e.is(tag)) {
71
85
  const msg = e.getArg(tag, 0);
72
- throw new Error(String(msg));
86
+ throw new Error(safeString(msg));
73
87
  }
74
88
  if (isNilDerefTrap(e)) {
75
89
  throw new Error(
@@ -91,7 +105,7 @@ export function instantiateWasm(
91
105
  inner.is(tag)
92
106
  ) {
93
107
  const msg = inner.getArg(tag, 0);
94
- throw new Error(String(msg));
108
+ throw new Error(safeString(msg));
95
109
  }
96
110
  if (isNilDerefTrap(inner)) {
97
111
  throw new Error(