gofront 1.6.1 → 1.8.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 (132) hide show
  1. package/README.md +45 -49
  2. package/package.json +12 -8
  3. package/src/backend/js/expressions.js +1627 -148
  4. package/src/backend/js/fmt-runtime.js +573 -0
  5. package/src/backend/js/index.js +508 -102
  6. package/src/backend/js/runtime.js +1997 -106
  7. package/src/backend/js/statements.js +931 -87
  8. package/src/backend/js/stdlib/bits.js +87 -14
  9. package/src/backend/js/stdlib/builder.js +41 -6
  10. package/src/backend/js/stdlib/bytes.js +253 -7
  11. package/src/backend/js/stdlib/errors.js +7 -1
  12. package/src/backend/js/stdlib/fmt.js +349 -10
  13. package/src/backend/js/stdlib/index.js +19 -2
  14. package/src/backend/js/stdlib/json.js +1167 -0
  15. package/src/backend/js/stdlib/maps.js +0 -8
  16. package/src/backend/js/stdlib/math.js +17 -1
  17. package/src/backend/js/stdlib/simd.js +34 -0
  18. package/src/backend/js/stdlib/slices.js +36 -6
  19. package/src/backend/js/stdlib/strconv.js +10 -5
  20. package/src/backend/js/stdlib/strings.js +83 -4
  21. package/src/backend/js/stdlib/testing.js +7 -0
  22. package/src/backend/js/stdlib/time.js +79 -55
  23. package/src/backend/js/stdlib/unicode.js +1 -1
  24. package/src/backend/js/stdlib/utf8.js +1 -1
  25. package/src/backend/js/templ.js +38 -76
  26. package/src/backend/js/type-info.js +272 -0
  27. package/src/backend/shared/fmt-types.js +188 -0
  28. package/src/backend/wasm/boundary.js +446 -72
  29. package/src/backend/wasm/emit-builtins.js +320 -13
  30. package/src/backend/wasm/emit-exprs.js +1404 -199
  31. package/src/backend/wasm/emit-maps.js +61 -13
  32. package/src/backend/wasm/emit-stmts.js +1359 -164
  33. package/src/backend/wasm/emit.js +518 -74
  34. package/src/backend/wasm/encode.js +128 -13
  35. package/src/backend/wasm/globals.js +452 -0
  36. package/src/backend/wasm/index.js +194 -2489
  37. package/src/backend/wasm/method-expr.js +381 -0
  38. package/src/backend/wasm/module-emitter.js +2240 -0
  39. package/src/backend/wasm/monomorph.js +62 -4
  40. package/src/backend/wasm/optimize.js +4 -2
  41. package/src/backend/wasm/prescan.js +397 -0
  42. package/src/backend/wasm/stdlib/bytes.js +1804 -127
  43. package/src/backend/wasm/stdlib/core.js +1574 -199
  44. package/src/backend/wasm/stdlib/index.js +3 -5
  45. package/src/backend/wasm/stdlib/io.js +1567 -150
  46. package/src/backend/wasm/stdlib/packages.js +2 -0
  47. package/src/backend/wasm/stdlib/simd.js +499 -0
  48. package/src/backend/wasm/stdlib/time.js +1041 -12
  49. package/src/backend/wasm/types.js +221 -15
  50. package/src/backend/wasm/variadic.js +93 -0
  51. package/src/backend/wasm/wat.js +84 -20
  52. package/src/{cli-core.js → cli/cli-core.js} +236 -10
  53. package/src/{test-runner.js → cli/test-runner.js} +332 -39
  54. package/src/{vendor.js → cli/vendor.js} +89 -15
  55. package/src/compiler.js +107 -269
  56. package/src/dts-parser.js +71 -14
  57. package/src/index.js +15 -169
  58. package/src/{lexer.js → lexer/lexer.js} +102 -55
  59. package/src/{tokens.js → lexer/tokens.js} +1 -0
  60. package/src/link/sourcemap.js +97 -0
  61. package/src/link/stdlib.js +515 -0
  62. package/src/link/wasm.js +294 -0
  63. package/src/lower/bounds.js +394 -0
  64. package/src/lower/boxing.js +1 -6
  65. package/src/lower/captures.js +176 -32
  66. package/src/lower/embedding.js +2 -1
  67. package/src/lower/index.js +26 -18
  68. package/src/lower/json-codecs.js +1398 -0
  69. package/src/lower/ownership.js +22 -1
  70. package/src/lower/range.js +86 -1
  71. package/src/lower/reachability.js +132 -0
  72. package/src/parser/declarations.js +58 -13
  73. package/src/parser/expressions.js +29 -7
  74. package/src/parser/index.js +2 -1
  75. package/src/parser/statements.js +37 -22
  76. package/src/parser/struct-tag.js +144 -0
  77. package/src/{templ-parser.js → parser/templ.js} +138 -15
  78. package/src/parser/types.js +130 -13
  79. package/src/resolver.js +22 -1
  80. package/src/typechecker/assignability.js +215 -11
  81. package/src/typechecker/const-fold.js +356 -0
  82. package/src/typechecker/constants.js +256 -0
  83. package/src/typechecker/expressions.js +960 -99
  84. package/src/typechecker/index.js +491 -72
  85. package/src/typechecker/init-order.js +330 -0
  86. package/src/typechecker/resolve.js +596 -52
  87. package/src/typechecker/statements.js +157 -17
  88. package/src/typechecker/stdlib/core.js +333 -55
  89. package/src/typechecker/stdlib/excluded.js +898 -0
  90. package/src/typechecker/stdlib/extended.js +359 -26
  91. package/src/typechecker/{stdlib.js → stdlib/index.js} +6 -4
  92. package/src/typechecker/stdlib/simd.js +123 -0
  93. package/src/typechecker/termination.js +1 -1
  94. package/src/typechecker/types.js +165 -26
  95. package/stdlib/bytes/iter.go +192 -0
  96. package/stdlib/bytes/search.go +240 -0
  97. package/stdlib/bytes/split.go +218 -0
  98. package/stdlib/cmp/cmp.go +65 -0
  99. package/stdlib/encoding/json/errors.go +31 -0
  100. package/stdlib/encoding/json/indent.go +71 -0
  101. package/stdlib/encoding/json/number.go +12 -0
  102. package/stdlib/encoding/json/stream.go +173 -0
  103. package/stdlib/errors/overlay.go +12 -0
  104. package/stdlib/hash/hash.go +53 -0
  105. package/stdlib/internal/jsonrt/dec.go +738 -0
  106. package/stdlib/internal/jsonrt/enc.go +366 -0
  107. package/stdlib/internal/jsonrt/indent.go +135 -0
  108. package/stdlib/internal/jsonrt/number.go +27 -0
  109. package/stdlib/internal/jsonrt/reader.go +73 -0
  110. package/stdlib/internal/jsonrt/scanner.go +649 -0
  111. package/stdlib/io/io.go +346 -0
  112. package/stdlib/iter/iter.go +18 -0
  113. package/stdlib/maps/iter.go +65 -0
  114. package/stdlib/math/extra.go +270 -0
  115. package/stdlib/slices/extra.go +73 -0
  116. package/stdlib/slices/iter.go +113 -0
  117. package/stdlib/sort/sort.go +561 -0
  118. package/stdlib/strconv/quote.go +341 -0
  119. package/stdlib/strings/extra.go +35 -0
  120. package/stdlib/strings/iter.go +122 -0
  121. package/stdlib/sync/extra.go +105 -0
  122. package/stdlib/unicode/unicode.go +112 -0
  123. package/stdlib/unicode/utf8/bytes.go +239 -0
  124. package/src/backend/wasm/const-fold.js +0 -205
  125. package/src/backend/wasm/emit-stdlib.js +0 -13
  126. /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
  127. /package/src/{colors.js → cli/colors.js} +0 -0
  128. /package/src/{dev-server.js → cli/dev-server.js} +0 -0
  129. /package/src/{minifier.js → cli/minifier.js} +0 -0
  130. /package/src/{project-config.js → cli/project-config.js} +0 -0
  131. /package/src/{pwa.js → cli/pwa.js} +0 -0
  132. /package/src/{templ-lexer.js → lexer/templ-lexer.js} +0 -0
@@ -354,6 +354,7 @@ const OPCODES = {
354
354
  "ref.func": 0xd2,
355
355
  "ref.eq": 0xd3,
356
356
  "ref.as_non_null": 0xd4,
357
+ br_on_null: 0xd5,
357
358
  };
358
359
 
359
360
  // 0xFC prefixed saturating truncations
@@ -393,6 +394,64 @@ const GC_OPCODES = {
393
394
  "extern.convert_any": 0x1b,
394
395
  };
395
396
 
397
+ // 0xFD prefixed SIMD instructions (Wasm SIMD / v128)
398
+ const SIMD_OPCODES = {
399
+ "v128.load": 0,
400
+ "v128.store": 11,
401
+ "v128.const": 12,
402
+ "i8x16.shuffle": 13,
403
+ "i8x16.swizzle": 14,
404
+ "i8x16.splat": 15,
405
+ "i16x8.splat": 16,
406
+ "i32x4.splat": 17,
407
+ "i64x2.splat": 18,
408
+ "f32x4.splat": 19,
409
+ "f64x2.splat": 20,
410
+ "i8x16.extract_lane_s": 21,
411
+ "i8x16.extract_lane_u": 22,
412
+ "i8x16.replace_lane": 23,
413
+ "i16x8.extract_lane_s": 24,
414
+ "i16x8.extract_lane_u": 25,
415
+ "i16x8.replace_lane": 26,
416
+ "i32x4.extract_lane": 27,
417
+ "i32x4.replace_lane": 28,
418
+ "i64x2.extract_lane": 29,
419
+ "i64x2.replace_lane": 30,
420
+ "f32x4.extract_lane": 31,
421
+ "f32x4.replace_lane": 32,
422
+ "i32x4.eq": 55,
423
+ "f32x4.eq": 65,
424
+ "f32x4.ne": 66,
425
+ "f32x4.lt": 67,
426
+ "f32x4.gt": 68,
427
+ "f32x4.le": 69,
428
+ "f32x4.ge": 70,
429
+ "v128.not": 77,
430
+ "v128.and": 78,
431
+ "v128.andnot": 79,
432
+ "v128.or": 80,
433
+ "v128.xor": 81,
434
+ "v128.bitselect": 82,
435
+ "v128.any_true": 83,
436
+ "i32x4.all_true": 163,
437
+ "i32x4.bitmask": 164,
438
+ "f32x4.ceil": 103,
439
+ "f32x4.floor": 104,
440
+ "f32x4.trunc": 105,
441
+ "f32x4.nearest": 106,
442
+ "f32x4.abs": 224,
443
+ "f32x4.neg": 225,
444
+ "f32x4.sqrt": 227,
445
+ "f32x4.add": 228,
446
+ "f32x4.sub": 229,
447
+ "f32x4.mul": 230,
448
+ "f32x4.div": 231,
449
+ "f32x4.min": 232,
450
+ "f32x4.max": 233,
451
+ "f32x4.pmin": 234,
452
+ "f32x4.pmax": 235,
453
+ };
454
+
396
455
  // Natural alignment (log2) per load/store opcode; memarg = align, offset.
397
456
  const MEM_ALIGN = {
398
457
  "i32.load": 2,
@@ -433,6 +492,48 @@ function encodeInstruction(inst) {
433
492
  return [0xfc, ...encodeU32LEB(SAT_TRUNC_OPCODES[op])];
434
493
  }
435
494
 
495
+ // 2. Check SIMD opcodes (0xFD prefix)
496
+ if (SIMD_OPCODES[op] !== undefined) {
497
+ const subOp = SIMD_OPCODES[op];
498
+ const bytes = [0xfd, ...encodeU32LEB(subOp)];
499
+ if (op === "v128.load" || op === "v128.store") {
500
+ bytes.push(...encodeU32LEB(inst.align ?? 2));
501
+ bytes.push(...encodeU32LEB(inst.offset ?? 0));
502
+ return bytes;
503
+ }
504
+ if (op === "v128.const") {
505
+ const prefixLen = bytes.length;
506
+ if (inst.bytes) {
507
+ for (let i = 0; i < 16; i++) {
508
+ bytes.push(inst.bytes[i] ?? 0);
509
+ }
510
+ } else if (inst.values) {
511
+ const vals = inst.values.slice(0, 4);
512
+ for (const val of vals) {
513
+ bytes.push(...encodeF32(val));
514
+ }
515
+ while (bytes.length < prefixLen + 16) {
516
+ bytes.push(0);
517
+ }
518
+ } else {
519
+ for (let i = 0; i < 16; i++) bytes.push(0);
520
+ }
521
+ return bytes;
522
+ }
523
+ if (op === "i8x16.shuffle") {
524
+ const lanes = inst.lanes ?? inst.indices ?? [];
525
+ for (let i = 0; i < 16; i++) {
526
+ bytes.push(lanes[i] ?? 0);
527
+ }
528
+ return bytes;
529
+ }
530
+ if (op.includes("extract_lane") || op.includes("replace_lane")) {
531
+ bytes.push((inst.lane ?? inst.index ?? 0) & 0xff);
532
+ return bytes;
533
+ }
534
+ return bytes;
535
+ }
536
+
436
537
  // 2. Check WasmGC opcodes (0xFB prefix)
437
538
  if (GC_OPCODES[op] !== undefined) {
438
539
  const subOp = GC_OPCODES[op];
@@ -494,6 +595,7 @@ function encodeInstruction(inst) {
494
595
 
495
596
  case "br":
496
597
  case "br_if":
598
+ case "br_on_null":
497
599
  return [byte, ...encodeU32LEB(inst.depth ?? inst.index ?? 0)];
498
600
 
499
601
  case "br_table": {
@@ -618,35 +720,48 @@ function encodeTypeEntry(t) {
618
720
  return [0x4e, ...count, ...innerBytes];
619
721
  }
620
722
 
723
+ let compositeBytes;
621
724
  if (t.form === "struct") {
622
725
  // Struct type: 0x5F, vector of fields [valtype, mutability]
623
726
  const fields = t.fields ?? [];
624
- return [
727
+ compositeBytes = [
625
728
  0x5f,
626
729
  ...encodeVector(fields, (f) => [
627
730
  ...encodeValType(f.type),
628
731
  f.mutable ? 0x01 : 0x00,
629
732
  ]),
630
733
  ];
631
- }
632
-
633
- if (t.form === "array") {
734
+ } else if (t.form === "array") {
634
735
  // Array type: 0x5E, field [valtype, mutability]
635
- return [
736
+ compositeBytes = [
636
737
  0x5e,
637
738
  ...encodeValType(t.elemType ?? t.type),
638
739
  t.mutable ? 0x01 : 0x00,
639
740
  ];
741
+ } else {
742
+ // Function type: 0x60, params vector, results vector
743
+ const params = t.params ?? [];
744
+ const results = t.results ?? [];
745
+ compositeBytes = [
746
+ 0x60,
747
+ ...encodeVector(params, encodeValType),
748
+ ...encodeVector(results, encodeValType),
749
+ ];
640
750
  }
641
751
 
642
- // Function type: 0x60, params vector, results vector
643
- const params = t.params ?? [];
644
- const results = t.results ?? [];
645
- return [
646
- 0x60,
647
- ...encodeVector(params, encodeValType),
648
- ...encodeVector(results, encodeValType),
649
- ];
752
+ if (t.sub) {
753
+ const superIndices =
754
+ t.superTypeIndex !== undefined
755
+ ? [t.superTypeIndex]
756
+ : (t.superTypeIndices ?? []);
757
+ return [
758
+ 0x50,
759
+ ...encodeVector(superIndices, encodeU32LEB),
760
+ ...compositeBytes,
761
+ ];
762
+ }
763
+
764
+ return compositeBytes;
650
765
  }
651
766
 
652
767
  function encodeTypeSection(types) {
@@ -0,0 +1,452 @@
1
+ // src/backend/wasm/globals.js
2
+ // Package-level constants and variables: constant folding, zero values and the
3
+ // synthesised `__init_globals` start function; plus interface dispatcher synthesis.
4
+
5
+ import { foldConstExpr } from "../../typechecker/const-fold.js";
6
+ import { orderPackageInits } from "../../typechecker/init-order.js";
7
+ import { _someNode } from "./prescan.js";
8
+ import { isStructType, toWasmType, variadicAsSlice } from "./types.js";
9
+
10
+ // Package-level `const` declarations (iota is already substituted by the
11
+ // parser). Literals and expressions the folder can reduce become BasicLits in
12
+ // `constCache`; whatever is left is recorded in `nonLiteralConsts` and later
13
+ // becomes a global initialised by the start function.
14
+ export function _collectPackageConsts(progs, mod, checker = null) {
15
+ const entries = [];
16
+ for (const p of progs) {
17
+ for (const d of p.decls ?? []) {
18
+ if (d.kind !== "ConstDecl") continue;
19
+ for (const spec of d.decls ?? []) {
20
+ for (let i = 0; i < spec.names.length; i++) {
21
+ if (spec.value?.[i])
22
+ entries.push({
23
+ name: spec.names[i],
24
+ expr: spec.value[i],
25
+ filename: p._filename,
26
+ source: p._source,
27
+ });
28
+ }
29
+ }
30
+ }
31
+ }
32
+ const resolve = (name) => mod.constCache.get(name) ?? null;
33
+ // Iterate to a fixpoint so a constant may refer to one declared later.
34
+ let pending = entries;
35
+ let progress = true;
36
+ while (progress && pending.length > 0) {
37
+ progress = false;
38
+ const rest = [];
39
+ for (const entry of pending) {
40
+ let lit = null;
41
+ try {
42
+ lit = foldConstExpr(entry.expr, resolve);
43
+ } catch (err) {
44
+ if (checker?.err) {
45
+ const savedFile = checker._currentFile;
46
+ const savedSource = checker._currentSource;
47
+ if (entry.filename) checker._currentFile = entry.filename;
48
+ if (entry.source) checker._currentSource = entry.source;
49
+ checker.err(err.message, entry.expr);
50
+ checker._currentFile = savedFile;
51
+ checker._currentSource = savedSource;
52
+ } else {
53
+ throw new Error(
54
+ `Constant expression error for '${entry.name}': ${err.message}`,
55
+ );
56
+ }
57
+ continue;
58
+ }
59
+ if (lit) {
60
+ mod.constCache.set(entry.name, lit);
61
+ progress = true;
62
+ } else {
63
+ rest.push(entry);
64
+ }
65
+ }
66
+ pending = rest;
67
+ }
68
+ for (const entry of pending) mod.nonLiteralConsts.add(entry.name);
69
+ }
70
+
71
+ function _isPointerGoType(t, mod) {
72
+ if (!t) return false;
73
+ if (t.kind === "pointer" || t.kind === "PointerType" || t.kind === "StarExpr")
74
+ return true;
75
+ // `type P *T` used as a field type: look through the name.
76
+ const named =
77
+ t.kind === "named" || t.kind === "TypeName" || t.kind === "Ident";
78
+ const under = named
79
+ ? (t.underlying ?? mod.checker?.types?.get(t.name)?.underlying)
80
+ : null;
81
+ return under ? _isPointerGoType(under, mod) : false;
82
+ }
83
+
84
+ // Constant-expression zero value of a struct global. Nested struct values are
85
+ // built in place: a null ref would trap on the first field access.
86
+ // Null when a nested array is too large to build as a constant expression.
87
+ function _zeroStructConst(structInfo, mod) {
88
+ const out = [];
89
+ for (const f of structInfo.fields) {
90
+ const zero = _zeroFieldConst(f, mod);
91
+ if (!zero) return null;
92
+ out.push(...zero);
93
+ }
94
+ out.push({ op: "struct.new", typeIndex: structInfo.typeIndex });
95
+ return out;
96
+ }
97
+
98
+ function _zeroFieldConst(f, mod) {
99
+ const w = f.wType;
100
+ if (w === "i64") return [{ op: "i64.const", value: 0n }];
101
+ if (w === "f32" || w === "f64") return [{ op: `${w}.const`, value: 0 }];
102
+ if (w === "externref") return [{ op: "ref.null", heapType: "extern" }];
103
+ if (w === "anyref") return [{ op: "ref.null", heapType: "any" }];
104
+ if (typeof w !== "object" || w === null)
105
+ return [{ op: "i32.const", value: 0 }];
106
+ const ti = w.typeIndex;
107
+ const slice = typeof ti === "number" ? mod.getSliceTypeByIndex(ti) : null;
108
+ if (slice) return [{ op: "global.get", index: slice.emptyGlobalIndex }];
109
+ if (typeof ti === "number" && !_isPointerGoType(f.goType, mod)) {
110
+ for (const s of mod.structTypes.values())
111
+ if (s.typeIndex === ti) return _zeroStructConst(s, mod);
112
+ // An array field is a value: a null ref would trap on first use.
113
+ if (_arrayShape(f.goType, mod.checker))
114
+ return _zeroArrayConst(f.goType, mod);
115
+ }
116
+ return [{ op: "ref.null", heapType: ti ?? "any" }];
117
+ }
118
+
119
+ // Length and element type of an array type (checker type, AST node or name).
120
+ function _arrayShape(t, checker) {
121
+ if (!t) return null;
122
+ if (t.kind === "array" || t.kind === "ArrayType") {
123
+ const size = typeof t.size === "number" ? t.size : Number(t.size?.value);
124
+ return Number.isInteger(size) ? { size, elem: t.elem } : null;
125
+ }
126
+ if (t.kind === "named" && t.underlying)
127
+ return _arrayShape(t.underlying, checker);
128
+ if (t.kind === "TypeName" || t.kind === "Ident")
129
+ return _arrayShape(checker?.types?.get(t.name), checker);
130
+ return null;
131
+ }
132
+
133
+ // Constant-expression zero value of a package-level array global (a null ref
134
+ // would trap on the first element access); null when it cannot be built.
135
+ function _zeroArrayConst(goType, mod, depth = 0) {
136
+ const checker = mod.checker;
137
+ if (!goType || goType.kind === "pointer" || depth > 4) return null;
138
+ const shape = _arrayShape(goType, checker);
139
+ if (!shape) return null;
140
+ const { size, elem } = shape;
141
+ const arrInfo = mod.getArrayType(elem);
142
+ const elemW = toWasmType(elem, checker, mod);
143
+ let one = null;
144
+ if (_arrayShape(elem, checker)) {
145
+ one = _zeroArrayConst(elem, mod, depth + 1);
146
+ if (!one) return null;
147
+ } else if (
148
+ elem?.kind !== "pointer" &&
149
+ elem?.kind !== "StarExpr" &&
150
+ typeof elemW === "object" &&
151
+ isStructType(elem, checker, mod)
152
+ ) {
153
+ const info = Array.from(mod.structTypes.values()).find(
154
+ (x) => x.typeIndex === elemW.typeIndex,
155
+ );
156
+ if (!info) return null;
157
+ one = _zeroStructConst(info, mod);
158
+ if (!one) return null;
159
+ }
160
+ if (!one)
161
+ return [
162
+ { op: "i32.const", value: size },
163
+ { op: "array.new_default", typeIndex: arrInfo.typeIndex },
164
+ ];
165
+ if (size > 64) return null;
166
+ const out = [];
167
+ for (let i = 0; i < size; i++) out.push(...one);
168
+ out.push({ op: "array.new_fixed", typeIndex: arrInfo.typeIndex, size });
169
+ return out;
170
+ }
171
+
172
+ export function _collectPackageGlobals(progs, mod, checker) {
173
+ const pending = []; // non-constant initializers, run by the start function
174
+ const pendingConsts = []; // unfoldable consts; they run before any var
175
+ for (const p of progs) {
176
+ for (const d of p.decls ?? []) {
177
+ const isConst = d.kind === "ConstDecl";
178
+ if (d.kind !== "VarDecl" && !isConst) continue;
179
+ for (const spec of d.decls ?? d.specs ?? [d]) {
180
+ const names = spec.names ?? (spec.name ? [spec.name] : []);
181
+ const values = spec.value ?? (spec.init ? [spec.init] : []);
182
+ for (let i = 0; i < names.length; i++) {
183
+ const name = names[i];
184
+ // Foldable consts live in `constCache`, not in a global.
185
+ if (isConst && !mod.isNonLiteralConst(name)) continue;
186
+ const rawType =
187
+ spec.type ??
188
+ (values.length === 1 && names.length > 1
189
+ ? values[0]?._type?.types?.[i]
190
+ : values[i]?._type);
191
+ const wType = toWasmType(rawType, checker, mod);
192
+ const gIdx = mod.globals.length;
193
+ let needsRuntimeZero = false;
194
+ let initInsts = [{ op: `${wType}.const`, value: 0 }];
195
+ if (wType === "i64") initInsts = [{ op: "i64.const", value: 0n }];
196
+ else if (wType === "f32" || wType === "f64")
197
+ initInsts = [{ op: `${wType}.const`, value: 0.0 }];
198
+ else if (
199
+ wType === "externref" ||
200
+ wType === "anyref" ||
201
+ typeof wType === "object"
202
+ ) {
203
+ const sliceInfo =
204
+ typeof wType === "object" && typeof wType.typeIndex === "number"
205
+ ? mod.getSliceTypeByIndex(wType.typeIndex)
206
+ : null;
207
+ if (sliceInfo) {
208
+ initInsts = [
209
+ { op: "global.get", index: sliceInfo.emptyGlobalIndex },
210
+ ];
211
+ } else if (
212
+ typeof wType === "object" &&
213
+ typeof wType.typeIndex === "number" &&
214
+ rawType?.kind !== "pointer"
215
+ ) {
216
+ const structInfo = Array.from(mod.structTypes.values()).find(
217
+ (s) => s.typeIndex === wType.typeIndex,
218
+ );
219
+ const arrZero = structInfo ? null : _zeroArrayConst(rawType, mod);
220
+ const structZero = structInfo
221
+ ? _zeroStructConst(structInfo, mod)
222
+ : null;
223
+ if (structZero) {
224
+ initInsts = structZero;
225
+ } else if (arrZero) {
226
+ initInsts = arrZero;
227
+ } else if (
228
+ (structInfo || _arrayShape(rawType, checker)) &&
229
+ !values[i]
230
+ ) {
231
+ // Too large for a constant expression: the start
232
+ // function builds the zero value.
233
+ needsRuntimeZero = true;
234
+ initInsts = [
235
+ { op: "ref.null", heapType: wType.typeIndex ?? "any" },
236
+ ];
237
+ } else {
238
+ initInsts = [
239
+ { op: "ref.null", heapType: wType.typeIndex ?? "any" },
240
+ ];
241
+ }
242
+ } else {
243
+ initInsts = [
244
+ {
245
+ op: "ref.null",
246
+ heapType:
247
+ typeof wType === "object"
248
+ ? (wType.heapType ?? wType.typeIndex ?? "any")
249
+ : wType.replace("ref", ""),
250
+ },
251
+ ];
252
+ }
253
+ }
254
+
255
+ let litNode = values?.[i];
256
+ let sign = 1;
257
+ if (
258
+ litNode &&
259
+ litNode.kind === "UnaryExpr" &&
260
+ litNode.op === "-" &&
261
+ litNode.operand?.kind === "BasicLit"
262
+ ) {
263
+ litNode = litNode.operand;
264
+ sign = -1;
265
+ }
266
+
267
+ let isConstInit = false;
268
+ if (litNode && litNode.kind === "BasicLit") {
269
+ if (litNode.litKind === "INT") {
270
+ isConstInit = true;
271
+ const s = String(litNode.value);
272
+ const clean = /^0[0-7]+$/.test(s) ? `0o${s.slice(1)}` : s;
273
+ initInsts = [
274
+ wType === "i64"
275
+ ? {
276
+ op: "i64.const",
277
+ value: BigInt(sign) * BigInt(clean),
278
+ }
279
+ : {
280
+ op: "i32.const",
281
+ value: (sign * Number(clean)) | 0,
282
+ },
283
+ ];
284
+ } else if (litNode.litKind === "FLOAT") {
285
+ isConstInit = true;
286
+ initInsts = [
287
+ {
288
+ op: `${wType}.const`,
289
+ value: sign * Number(litNode.value),
290
+ },
291
+ ];
292
+ } else if (litNode.litKind === "BOOL") {
293
+ isConstInit = true;
294
+ initInsts = [
295
+ {
296
+ op: "i32.const",
297
+ value: litNode.value === "true" ? 1 : 0,
298
+ },
299
+ ];
300
+ }
301
+ }
302
+ if (needsRuntimeZero) pending.push({ name, zeroType: rawType });
303
+ if (!isConst && i === 0 && values.length === 1 && names.length > 1) {
304
+ // `var q, r = pair()`: one call fills every name.
305
+ pending.push({ name, names, expr: values[0] });
306
+ }
307
+ if (values[i] && !isConstInit && names.length === values.length) {
308
+ (isConst ? pendingConsts : pending).push({
309
+ name,
310
+ expr: values[i],
311
+ });
312
+ }
313
+ mod.globals.push({
314
+ type: wType,
315
+ mutable: true,
316
+ init: initInsts,
317
+ });
318
+ mod.globalCache.set(name, {
319
+ index: gIdx,
320
+ type: wType,
321
+ goType: rawType,
322
+ });
323
+ }
324
+ }
325
+ }
326
+ }
327
+ // Initialisers run in dependency order (Go spec), not declaration order.
328
+ const rank = new Map();
329
+ for (const e of orderPackageInits(progs).vars)
330
+ for (const n of e.names) rank.set(n, rank.size);
331
+ pending.sort((a, b) => (rank.get(a.name) ?? 0) - (rank.get(b.name) ?? 0));
332
+ return [..._orderConstInits(pendingConsts), ...pending];
333
+ }
334
+
335
+ // Orders runtime-initialised consts so each runs after the consts it
336
+ // references (Go allows a constant to use one declared later).
337
+ export function _orderConstInits(entries) {
338
+ const byName = new Map(entries.map((e) => [e.name, e]));
339
+ const ordered = [];
340
+ const state = new Map();
341
+ const visit = (entry) => {
342
+ if (state.has(entry.name)) return;
343
+ state.set(entry.name, true);
344
+ _someNode(entry.expr, (n) => {
345
+ if (n.kind === "Ident" && byName.has(n.name)) visit(byName.get(n.name));
346
+ return false;
347
+ });
348
+ ordered.push(entry);
349
+ };
350
+ for (const e of entries) visit(e);
351
+ return ordered;
352
+ }
353
+
354
+ // Synthesises `__init_globals()` assigning every non-constant package-level
355
+ // initializer in declaration order and invoking package `init()` functions;
356
+ // registered as the module start function.
357
+ export function _makeGlobalInitFunc(pending, initNames = []) {
358
+ const stmts = pending.map(({ name, names, expr, zeroType }) =>
359
+ zeroType
360
+ ? { kind: "GlobalZeroInit", name, goType: zeroType }
361
+ : {
362
+ kind: "AssignStmt",
363
+ lhs: names
364
+ ? names.map((n, i) => ({
365
+ kind: "Ident",
366
+ name: n,
367
+ _type: expr._type?.types?.[i],
368
+ }))
369
+ : [{ kind: "Ident", name, _type: expr._type }],
370
+ op: "=",
371
+ rhs: [expr],
372
+ },
373
+ );
374
+ for (const initName of initNames) {
375
+ stmts.push({
376
+ kind: "ExprStmt",
377
+ expr: {
378
+ kind: "CallExpr",
379
+ func: { kind: "Ident", name: initName },
380
+ args: [],
381
+ },
382
+ });
383
+ }
384
+ stmts.push({ kind: "ReturnStmt", values: [] });
385
+ return {
386
+ kind: "FuncDecl",
387
+ name: "__init_globals",
388
+ params: [],
389
+ returnType: null,
390
+ body: { kind: "Block", stmts, list: stmts },
391
+ _isGlobalInit: true,
392
+ };
393
+ }
394
+
395
+ export function _makeInterfaceDispatcher(info, checker) {
396
+ let methodSig = info.methodSig;
397
+ if (!methodSig) {
398
+ const raw =
399
+ info.ifaceType?.kind === "named" && info.ifaceType.underlying
400
+ ? info.ifaceType.underlying
401
+ : info.ifaceType;
402
+ const ifaceResolved = checker?.resolveType?.(raw) ?? raw;
403
+ const methods =
404
+ ifaceResolved?.methods ?? ifaceResolved?.underlying?.methods;
405
+ if (methods instanceof Map) {
406
+ methodSig = methods.get(info.methodName);
407
+ } else if (Array.isArray(methods)) {
408
+ methodSig = methods.find((m) => (m.name ?? m) === info.methodName);
409
+ }
410
+ }
411
+
412
+ const params = [{ name: "__recv", type: { kind: "basic", name: "any" } }];
413
+ if (methodSig?.params) {
414
+ const sigParams = variadicAsSlice(methodSig.params, methodSig.variadic);
415
+ for (let p = 0; p < sigParams.length; p++) {
416
+ params.push({ name: `__arg${p}`, type: sigParams[p] });
417
+ }
418
+ } else if (info.callNode?.args) {
419
+ for (let p = 0; p < info.callNode.args.length; p++) {
420
+ params.push({
421
+ name: `__arg${p}`,
422
+ type: info.callNode.args[p]._type ?? {
423
+ kind: "basic",
424
+ name: "any",
425
+ },
426
+ });
427
+ }
428
+ }
429
+
430
+ let returnType = info.callNode?._type ?? null;
431
+ if (methodSig?.returns) {
432
+ returnType =
433
+ methodSig.returns.length === 1
434
+ ? methodSig.returns[0]
435
+ : methodSig.returns.length > 1
436
+ ? { kind: "tuple", types: methodSig.returns }
437
+ : null;
438
+ }
439
+
440
+ return {
441
+ kind: "FuncDecl",
442
+ name: `__dispatch_${info.ifaceName}_${info.methodName}`,
443
+ params,
444
+ returnType,
445
+ body: { kind: "Block", stmts: [], list: [] },
446
+ _isInterfaceDispatcher: true,
447
+ _ifaceName: info.ifaceName,
448
+ _ifaceType: info.ifaceType,
449
+ _methodName: info.methodName,
450
+ _rootFuncDecl: null,
451
+ };
452
+ }