gofront 1.5.1 → 1.6.1

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 (91) hide show
  1. package/README.md +174 -466
  2. package/package.json +16 -20
  3. package/src/backend/js/expressions.js +288 -128
  4. package/src/backend/js/index.js +138 -56
  5. package/src/backend/js/runtime.js +1055 -53
  6. package/src/backend/js/source-map.js +32 -0
  7. package/src/backend/js/statements.js +151 -83
  8. package/src/backend/js/stdlib/base64.js +12 -0
  9. package/src/backend/js/stdlib/binary.js +102 -0
  10. package/src/backend/js/stdlib/bits.js +15 -1
  11. package/src/backend/js/stdlib/builder.js +28 -9
  12. package/src/backend/js/stdlib/bytes.js +75 -18
  13. package/src/backend/js/stdlib/errors.js +22 -3
  14. package/src/backend/js/stdlib/fmt.js +95 -20
  15. package/src/backend/js/stdlib/hex.js +17 -0
  16. package/src/backend/js/stdlib/html.js +1 -1
  17. package/src/backend/js/stdlib/index.js +19 -0
  18. package/src/backend/js/stdlib/io.js +105 -3
  19. package/src/backend/js/stdlib/maps.js +1 -1
  20. package/src/backend/js/stdlib/math.js +13 -1
  21. package/src/backend/js/stdlib/path.js +20 -6
  22. package/src/backend/js/stdlib/rand.js +10 -5
  23. package/src/backend/js/stdlib/sha256.js +12 -0
  24. package/src/backend/js/stdlib/shared.js +17 -0
  25. package/src/backend/js/stdlib/slices.js +53 -27
  26. package/src/backend/js/stdlib/sort.js +1 -1
  27. package/src/backend/js/stdlib/strconv.js +52 -11
  28. package/src/backend/js/stdlib/strings.js +152 -16
  29. package/src/backend/js/stdlib/sync.js +13 -0
  30. package/src/backend/js/stdlib/testing.js +2 -2
  31. package/src/backend/js/stdlib/time.js +93 -22
  32. package/src/backend/js/stdlib/unicode.js +2 -2
  33. package/src/backend/js/templ.js +1 -1
  34. package/src/backend/wasm/boundary.js +1677 -58
  35. package/src/backend/wasm/const-fold.js +205 -0
  36. package/src/backend/wasm/emit-builtins.js +1230 -0
  37. package/src/backend/wasm/emit-exprs.js +2096 -0
  38. package/src/backend/wasm/emit-maps.js +1999 -0
  39. package/src/backend/wasm/emit-shared.js +519 -0
  40. package/src/backend/wasm/emit-stdlib.js +13 -0
  41. package/src/backend/wasm/emit-stmts.js +2466 -0
  42. package/src/backend/wasm/emit.js +656 -5031
  43. package/src/backend/wasm/encode.js +259 -85
  44. package/src/backend/wasm/glue.js +19 -5
  45. package/src/backend/wasm/index.js +2432 -86
  46. package/src/backend/wasm/monomorph.js +711 -0
  47. package/src/backend/wasm/optimize.js +236 -0
  48. package/src/backend/wasm/stdlib/bytes.js +5039 -0
  49. package/src/backend/wasm/stdlib/core.js +8903 -0
  50. package/src/backend/wasm/stdlib/index.js +40 -0
  51. package/src/backend/wasm/stdlib/io.js +2161 -0
  52. package/src/backend/wasm/stdlib/packages.js +2400 -0
  53. package/src/backend/wasm/stdlib/pure.js +720 -0
  54. package/src/backend/wasm/stdlib/time.js +1200 -0
  55. package/src/backend/wasm/types.js +312 -36
  56. package/src/backend/wasm/wat.js +49 -2
  57. package/src/cli-core.js +276 -73
  58. package/src/compiler.js +162 -25
  59. package/src/dts-parser.js +79 -126
  60. package/src/index.js +88 -121
  61. package/src/lexer.js +80 -29
  62. package/src/lower/boxing.js +4 -6
  63. package/src/lower/captures.js +12 -23
  64. package/src/lower/escape.js +4 -9
  65. package/src/lower/functions.js +277 -5
  66. package/src/lower/globals.js +7 -15
  67. package/src/lower/index.js +5 -2
  68. package/src/lower/ownership.js +35 -19
  69. package/src/lower/range.js +2 -5
  70. package/src/lower/walk.js +25 -0
  71. package/src/minifier.js +20 -68
  72. package/src/parser/declarations.js +3 -6
  73. package/src/parser/expressions.js +3 -8
  74. package/src/parser/index.js +18 -3
  75. package/src/parser/statements.js +25 -38
  76. package/src/parser/types.js +1 -2
  77. package/src/resolver.js +3 -27
  78. package/src/templ-lexer.js +29 -63
  79. package/src/templ-parser.js +22 -45
  80. package/src/test-runner.js +20 -0
  81. package/src/typechecker/assignability.js +123 -14
  82. package/src/typechecker/expressions.js +244 -4
  83. package/src/typechecker/index.js +27 -1
  84. package/src/typechecker/resolve.js +71 -12
  85. package/src/typechecker/statements.js +0 -7
  86. package/src/typechecker/stdlib/core.js +292 -103
  87. package/src/typechecker/stdlib/extended.js +471 -4
  88. package/src/typechecker/stdlib/shared.js +53 -0
  89. package/src/typechecker/stdlib.js +2 -0
  90. package/src/typechecker/types.js +545 -8
  91. package/src/vendor.js +2 -2
@@ -52,24 +52,36 @@ export function encodeI64LEB(val) {
52
52
  return bytes;
53
53
  }
54
54
 
55
+ const _f32Arr = new Float32Array(1);
56
+ const _f32Bytes = new Uint8Array(_f32Arr.buffer);
55
57
  export function encodeF32(val) {
56
- const buf = new ArrayBuffer(4);
57
- new DataView(buf).setFloat32(0, Number(val), true); // little-endian
58
- return Array.from(new Uint8Array(buf));
58
+ _f32Arr[0] = Number(val);
59
+ return [_f32Bytes[0], _f32Bytes[1], _f32Bytes[2], _f32Bytes[3]];
59
60
  }
60
61
 
62
+ const _f64Arr = new Float64Array(1);
63
+ const _f64Bytes = new Uint8Array(_f64Arr.buffer);
61
64
  export function encodeF64(val) {
62
- const buf = new ArrayBuffer(8);
63
- new DataView(buf).setFloat64(0, Number(val), true); // little-endian
64
- return Array.from(new Uint8Array(buf));
65
+ _f64Arr[0] = Number(val);
66
+ return [
67
+ _f64Bytes[0],
68
+ _f64Bytes[1],
69
+ _f64Bytes[2],
70
+ _f64Bytes[3],
71
+ _f64Bytes[4],
72
+ _f64Bytes[5],
73
+ _f64Bytes[6],
74
+ _f64Bytes[7],
75
+ ];
65
76
  }
66
77
 
67
- export function encodeString(str) {
68
- const utf8 = new TextEncoder().encode(str);
78
+ const _textEncoder = new TextEncoder();
79
+ function encodeString(str) {
80
+ const utf8 = _textEncoder.encode(str);
69
81
  return [...encodeU32LEB(utf8.length), ...utf8];
70
82
  }
71
83
 
72
- export function encodeVector(items, encodeItem) {
84
+ function encodeVector(items, encodeItem) {
73
85
  const count = encodeU32LEB(items.length);
74
86
  const bytes = [];
75
87
  for (const item of items) {
@@ -83,7 +95,7 @@ export function encodeVector(items, encodeItem) {
83
95
 
84
96
  // ── Value Types & Heap Types ─────────────────────────────────
85
97
 
86
- export const ValType = {
98
+ const ValType = {
87
99
  i32: 0x7f,
88
100
  i64: 0x7e,
89
101
  f32: 0x7d,
@@ -101,9 +113,11 @@ export const ValType = {
101
113
  nullref: 0x71,
102
114
  nullfuncref: 0x73,
103
115
  nullexternref: 0x72,
116
+ exnref: 0x69,
117
+ nullexnref: 0x75,
104
118
  };
105
119
 
106
- export function encodeValType(type) {
120
+ function encodeValType(type) {
107
121
  if (typeof type === "number") {
108
122
  return [type];
109
123
  }
@@ -129,7 +143,7 @@ export function encodeValType(type) {
129
143
  throw new Error(`Invalid valtype specification: ${JSON.stringify(type)}`);
130
144
  }
131
145
 
132
- export function encodeBlockType(blockType) {
146
+ function encodeBlockType(blockType) {
133
147
  if (!blockType || blockType === "void") {
134
148
  return [0x40];
135
149
  }
@@ -153,6 +167,7 @@ const OPCODES = {
153
167
  if: 0x04,
154
168
  else: 0x05,
155
169
  throw: 0x08,
170
+ throw_ref: 0x0a,
156
171
  end: 0x0b,
157
172
  br: 0x0c,
158
173
  br_if: 0x0d,
@@ -164,6 +179,7 @@ const OPCODES = {
164
179
  return_call_indirect: 0x13,
165
180
  call_ref: 0x14,
166
181
  return_call_ref: 0x15,
182
+ try_table: 0x1f,
167
183
  drop: 0x1a,
168
184
  select: 0x1b,
169
185
 
@@ -173,6 +189,23 @@ const OPCODES = {
173
189
  "global.get": 0x23,
174
190
  "global.set": 0x24,
175
191
 
192
+ "i32.load": 0x28,
193
+ "i64.load": 0x29,
194
+ "f32.load": 0x2a,
195
+ "f64.load": 0x2b,
196
+ "i32.load8_s": 0x2c,
197
+ "i32.load8_u": 0x2d,
198
+ "i32.load16_s": 0x2e,
199
+ "i32.load16_u": 0x2f,
200
+ "i32.store": 0x36,
201
+ "i64.store": 0x37,
202
+ "f32.store": 0x38,
203
+ "f64.store": 0x39,
204
+ "i32.store8": 0x3a,
205
+ "i32.store16": 0x3b,
206
+ "memory.size": 0x3f,
207
+ "memory.grow": 0x40,
208
+
176
209
  "i32.const": 0x41,
177
210
  "i64.const": 0x42,
178
211
  "f32.const": 0x43,
@@ -360,12 +393,41 @@ const GC_OPCODES = {
360
393
  "extern.convert_any": 0x1b,
361
394
  };
362
395
 
363
- export function encodeInstruction(inst) {
396
+ // Natural alignment (log2) per load/store opcode; memarg = align, offset.
397
+ const MEM_ALIGN = {
398
+ "i32.load": 2,
399
+ "i64.load": 3,
400
+ "f32.load": 2,
401
+ "f64.load": 3,
402
+ "i32.load8_s": 0,
403
+ "i32.load8_u": 0,
404
+ "i32.load16_s": 1,
405
+ "i32.load16_u": 1,
406
+ "i32.store": 2,
407
+ "i64.store": 3,
408
+ "f32.store": 2,
409
+ "f64.store": 3,
410
+ "i32.store8": 0,
411
+ "i32.store16": 1,
412
+ };
413
+
414
+ function encodeInstruction(inst) {
364
415
  if (typeof inst === "string") {
365
416
  inst = { op: inst };
366
417
  }
367
418
  const { op } = inst;
368
419
 
420
+ if (MEM_ALIGN[op] !== undefined) {
421
+ return [
422
+ OPCODES[op],
423
+ ...encodeU32LEB(inst.align ?? MEM_ALIGN[op]),
424
+ ...encodeU32LEB(inst.offset ?? 0),
425
+ ];
426
+ }
427
+ if (op === "memory.size" || op === "memory.grow") {
428
+ return [OPCODES[op], 0x00];
429
+ }
430
+
369
431
  // 1. Check saturating truncations (0xFC prefix)
370
432
  if (SAT_TRUNC_OPCODES[op] !== undefined) {
371
433
  return [0xfc, ...encodeU32LEB(SAT_TRUNC_OPCODES[op])];
@@ -392,6 +454,26 @@ export function encodeInstruction(inst) {
392
454
  ) {
393
455
  return bytes;
394
456
  }
457
+ if (
458
+ op === "ref.cast" ||
459
+ op === "ref.cast_null" ||
460
+ op === "ref.test" ||
461
+ op === "ref.test_null"
462
+ ) {
463
+ const ht =
464
+ inst.heapType !== undefined
465
+ ? inst.heapType
466
+ : inst.typeIndex !== undefined
467
+ ? inst.typeIndex
468
+ : "any";
469
+ if (typeof ht === "number") {
470
+ bytes.push(...encodeI32LEB(ht));
471
+ } else {
472
+ const code = ValType[ht] ?? ValType[`${ht}ref`] ?? 0x6e;
473
+ bytes.push(code);
474
+ }
475
+ return bytes;
476
+ }
395
477
  if (inst.typeIndex !== undefined) {
396
478
  bytes.push(...encodeU32LEB(inst.typeIndex));
397
479
  }
@@ -425,6 +507,40 @@ export function encodeInstruction(inst) {
425
507
  case "throw":
426
508
  return [byte, ...encodeU32LEB(inst.tagIndex ?? inst.index ?? 0)];
427
509
 
510
+ case "throw_ref":
511
+ return [byte];
512
+
513
+ case "try_table": {
514
+ const blockTypeBytes = encodeBlockType(
515
+ inst.blockType ?? inst.resultType,
516
+ );
517
+ const catches = inst.catches ?? [];
518
+ const catchBytes = encodeVector(catches, (c) => {
519
+ const kind = c.kind ?? c.op;
520
+ switch (kind) {
521
+ case "catch":
522
+ return [
523
+ 0x00,
524
+ ...encodeU32LEB(c.tagIndex ?? c.tag ?? 0),
525
+ ...encodeU32LEB(c.label ?? c.depth ?? 0),
526
+ ];
527
+ case "catch_ref":
528
+ return [
529
+ 0x01,
530
+ ...encodeU32LEB(c.tagIndex ?? c.tag ?? 0),
531
+ ...encodeU32LEB(c.label ?? c.depth ?? 0),
532
+ ];
533
+ case "catch_all":
534
+ return [0x02, ...encodeU32LEB(c.label ?? c.depth ?? 0)];
535
+ case "catch_all_ref":
536
+ return [0x03, ...encodeU32LEB(c.label ?? c.depth ?? 0)];
537
+ default:
538
+ throw new Error(`Unknown catch kind: ${kind}`);
539
+ }
540
+ });
541
+ return [byte, ...blockTypeBytes, ...catchBytes];
542
+ }
543
+
428
544
  case "call":
429
545
  case "return_call":
430
546
  return [byte, ...encodeU32LEB(inst.funcIndex ?? inst.index ?? 0)];
@@ -494,7 +610,7 @@ function encodeSection(id, payload) {
494
610
  return [id, ...encodeU32LEB(payload.length), ...payload];
495
611
  }
496
612
 
497
- export function encodeTypeEntry(t) {
613
+ function encodeTypeEntry(t) {
498
614
  if (t.form === "rec") {
499
615
  // Recursive type group: 0x4E, count, types
500
616
  const count = encodeU32LEB(t.types.length);
@@ -533,12 +649,12 @@ export function encodeTypeEntry(t) {
533
649
  ];
534
650
  }
535
651
 
536
- export function encodeTypeSection(types) {
652
+ function encodeTypeSection(types) {
537
653
  if (!types || types.length === 0) return [];
538
654
  return encodeSection(1, encodeVector(types, encodeTypeEntry));
539
655
  }
540
656
 
541
- export function encodeImportSection(imports) {
657
+ function encodeImportSection(imports) {
542
658
  if (!imports || imports.length === 0) return [];
543
659
  return encodeSection(
544
660
  2,
@@ -581,7 +697,7 @@ export function encodeImportSection(imports) {
581
697
  );
582
698
  }
583
699
 
584
- export function encodeFunctionSection(funcs) {
700
+ function encodeFunctionSection(funcs) {
585
701
  if (!funcs || funcs.length === 0) return [];
586
702
  return encodeSection(
587
703
  3,
@@ -589,7 +705,7 @@ export function encodeFunctionSection(funcs) {
589
705
  );
590
706
  }
591
707
 
592
- export function encodeTagSection(tags) {
708
+ function encodeTagSection(tags) {
593
709
  if (!tags || tags.length === 0) return [];
594
710
  return encodeSection(
595
711
  13,
@@ -597,7 +713,22 @@ export function encodeTagSection(tags) {
597
713
  );
598
714
  }
599
715
 
600
- export function encodeGlobalSection(globals) {
716
+ // Single linear memory: `{ min, max }` in 64 KiB pages. `max` is optional;
717
+ // the shared-buffer runtime only grows the memory inside the start function,
718
+ // before any JS TypedArray view exists.
719
+ function encodeMemorySection(memory) {
720
+ if (!memory) return [];
721
+ const limits =
722
+ memory.max != null
723
+ ? [0x01, ...encodeU32LEB(memory.min), ...encodeU32LEB(memory.max)]
724
+ : [0x00, ...encodeU32LEB(memory.min)];
725
+ return encodeSection(
726
+ 5,
727
+ encodeVector([limits], (b) => b),
728
+ );
729
+ }
730
+
731
+ function encodeGlobalSection(globals) {
601
732
  if (!globals || globals.length === 0) return [];
602
733
  return encodeSection(
603
734
  6,
@@ -621,7 +752,7 @@ export function encodeGlobalSection(globals) {
621
752
  );
622
753
  }
623
754
 
624
- export function encodeExportSection(exports) {
755
+ function encodeExportSection(exports) {
625
756
  if (!exports || exports.length === 0) return [];
626
757
  return encodeSection(
627
758
  7,
@@ -670,7 +801,7 @@ function sameValType(a, b) {
670
801
  return false;
671
802
  }
672
803
 
673
- export function encodeElementSection(elements) {
804
+ function encodeElementSection(elements) {
674
805
  if (!elements || elements.length === 0) return [];
675
806
  const segment = [0x03, 0x00, ...encodeVector(elements, encodeU32LEB)];
676
807
  return encodeSection(
@@ -679,64 +810,76 @@ export function encodeElementSection(elements) {
679
810
  );
680
811
  }
681
812
 
682
- export function encodeCodeSection(funcs) {
813
+ // Encodes the code section. `bodyOffsets` (when given) receives, per
814
+ // function, the offset of the body's first instruction relative to the start
815
+ // of the section bytes returned.
816
+ function encodeCodeSection(funcs, bodyOffsets = null) {
683
817
  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)
818
+ const relOffsets = [];
819
+ const payload = encodeVector(funcs, (fn) => {
820
+ // Compress locals: [type1, type1, type2] -> [{ count: 2, type: type1 }, { count: 1, type: type2 }]
821
+ const rawLocals = fn.locals ?? [];
822
+ const compressedLocals = [];
823
+ for (const loc of rawLocals) {
824
+ const locType =
825
+ typeof loc === "string" ? loc : loc.type !== undefined ? loc.type : loc;
722
826
  if (
723
- bodyInsts.length === 0 ||
724
- bodyInsts[bodyInsts.length - 1].op !== "end"
827
+ compressedLocals.length > 0 &&
828
+ sameValType(compressedLocals[compressedLocals.length - 1].type, locType)
725
829
  ) {
726
- bodyBytes.push(0x0b);
830
+ compressedLocals[compressedLocals.length - 1].count++;
831
+ } else {
832
+ compressedLocals.push({ count: 1, type: locType });
727
833
  }
834
+ }
728
835
 
729
- const fullBody = [...localsBytes, ...bodyBytes];
730
- return [...encodeU32LEB(fullBody.length), ...fullBody];
731
- }),
732
- );
836
+ const localsBytes = encodeVector(compressedLocals, (group) => [
837
+ ...encodeU32LEB(group.count),
838
+ ...encodeValType(group.type),
839
+ ]);
840
+
841
+ const bodyInsts = fn.body ?? [];
842
+ const bodyBytes = [];
843
+ for (const inst of bodyInsts) {
844
+ const chunk = encodeInstruction(inst);
845
+ for (let i = 0; i < chunk.length; i++) bodyBytes.push(chunk[i]);
846
+ }
847
+ // Ensure terminating end opcode (0x0B)
848
+ if (
849
+ bodyInsts.length === 0 ||
850
+ bodyInsts[bodyInsts.length - 1].op !== "end"
851
+ ) {
852
+ bodyBytes.push(0x0b);
853
+ }
854
+
855
+ const fullBody = [...localsBytes, ...bodyBytes];
856
+ const sizeBytes = encodeU32LEB(fullBody.length);
857
+ // Offset of the first instruction within this entry + entry length.
858
+ relOffsets.push([
859
+ sizeBytes.length + localsBytes.length,
860
+ sizeBytes.length + fullBody.length,
861
+ ]);
862
+ return [...sizeBytes, ...fullBody];
863
+ });
864
+ if (bodyOffsets) {
865
+ // Section-relative: id + size LEB + count LEB + preceding entries.
866
+ let cursor =
867
+ 1 +
868
+ encodeU32LEB(payload.length).length +
869
+ encodeU32LEB(funcs.length).length;
870
+ for (const [first, len] of relOffsets) {
871
+ bodyOffsets.push(cursor + first);
872
+ cursor += len;
873
+ }
874
+ }
875
+ return encodeSection(10, payload);
733
876
  }
734
877
 
735
878
  // ── Top-level Module Encoder ─────────────────────────────────
736
879
 
737
880
  // Custom section placed right after the header; `isGoFrontWasm` keys off it.
738
- export const GOFRONT_SECTION_NAME = "gofront";
739
- const GOFRONT_SECTION_BYTES = [
881
+ const GOFRONT_SECTION_NAME = "gofront";
882
+ export const GOFRONT_SECTION_BYTES = [
740
883
  0x00,
741
884
  ...encodeU32LEB(1 + GOFRONT_SECTION_NAME.length),
742
885
  ...encodeString(GOFRONT_SECTION_NAME),
@@ -753,6 +896,10 @@ export function isGoFrontWasm(bytes) {
753
896
  return true;
754
897
  }
755
898
 
899
+ // Encodes the module. When `mod.sourceMappingURL` is set a trailing
900
+ // `sourceMappingURL` custom section is appended, and `mod.codeOffsets` is
901
+ // filled with the absolute byte offset of every function body (aligned with
902
+ // `mod.funcs`) so a function-level source map can be built.
756
903
  export function encodeModule(mod) {
757
904
  const magic = [0x00, 0x61, 0x73, 0x6d]; // \0asm
758
905
  const version = [0x01, 0x00, 0x00, 0x00]; // version 1
@@ -760,6 +907,7 @@ export function encodeModule(mod) {
760
907
  const typeSec = encodeTypeSection(mod.types);
761
908
  const importSec = encodeImportSection(mod.imports);
762
909
  const funcSec = encodeFunctionSection(mod.funcs);
910
+ const memorySec = encodeMemorySection(mod.memory);
763
911
  const tagSec = encodeTagSection(mod.tags);
764
912
  const globalSec = encodeGlobalSection(mod.globals);
765
913
  const exportSec = encodeExportSection(mod.exports);
@@ -768,22 +916,48 @@ export function encodeModule(mod) {
768
916
  ? encodeSection(8, encodeU32LEB(mod.start))
769
917
  : [];
770
918
  const elemSec = encodeElementSection(mod.elements);
771
- const codeSec = encodeCodeSection(mod.funcs);
772
-
773
- const allBytes = [
774
- ...magic,
775
- ...version,
776
- ...GOFRONT_SECTION_BYTES,
777
- ...typeSec,
778
- ...importSec,
779
- ...funcSec,
780
- ...tagSec,
781
- ...globalSec,
782
- ...exportSec,
783
- ...startSec,
784
- ...elemSec,
785
- ...codeSec,
919
+ const sectionOffsets = [];
920
+ const codeSec = encodeCodeSection(mod.funcs, sectionOffsets);
921
+
922
+ const sections = [
923
+ magic,
924
+ version,
925
+ GOFRONT_SECTION_BYTES,
926
+ typeSec,
927
+ importSec,
928
+ funcSec,
929
+ memorySec,
930
+ tagSec,
931
+ globalSec,
932
+ exportSec,
933
+ startSec,
934
+ elemSec,
786
935
  ];
936
+ let totalLen = 0;
937
+ for (const s of sections) totalLen += s.length;
938
+ const codeStart = totalLen;
939
+ mod.codeOffsets = sectionOffsets.map((o) => codeStart + o);
940
+ totalLen += codeSec.length;
941
+ let smSec = null;
942
+ if (mod.sourceMappingURL) {
943
+ smSec = encodeSection(0, [
944
+ ...encodeString("sourceMappingURL"),
945
+ ...encodeString(mod.sourceMappingURL),
946
+ ]);
947
+ totalLen += smSec.length;
948
+ }
949
+
950
+ const out = new Uint8Array(totalLen);
951
+ let offset = 0;
952
+ for (const s of sections) {
953
+ out.set(s, offset);
954
+ offset += s.length;
955
+ }
956
+ out.set(codeSec, offset);
957
+ offset += codeSec.length;
958
+ if (smSec) {
959
+ out.set(smSec, offset);
960
+ }
787
961
 
788
- return new Uint8Array(allBytes);
962
+ return out;
789
963
  }
@@ -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(