gofront 1.7.0 → 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 (128) hide show
  1. package/README.md +13 -13
  2. package/package.json +8 -37
  3. package/src/backend/js/expressions.js +1341 -148
  4. package/src/backend/js/fmt-runtime.js +573 -0
  5. package/src/backend/js/index.js +375 -57
  6. package/src/backend/js/runtime.js +1460 -133
  7. package/src/backend/js/statements.js +900 -86
  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 +2 -1
  12. package/src/backend/js/stdlib/fmt.js +349 -10
  13. package/src/backend/js/stdlib/index.js +8 -3
  14. package/src/backend/js/stdlib/json.js +247 -52
  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 +10 -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 +436 -80
  29. package/src/backend/wasm/emit-builtins.js +303 -13
  30. package/src/backend/wasm/emit-exprs.js +1397 -203
  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 +512 -77
  34. package/src/backend/wasm/encode.js +43 -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/prescan.js +397 -0
  41. package/src/backend/wasm/stdlib/bytes.js +1804 -127
  42. package/src/backend/wasm/stdlib/core.js +1574 -199
  43. package/src/backend/wasm/stdlib/index.js +0 -5
  44. package/src/backend/wasm/stdlib/io.js +1567 -150
  45. package/src/backend/wasm/stdlib/packages.js +2 -0
  46. package/src/backend/wasm/stdlib/simd.js +293 -28
  47. package/src/backend/wasm/stdlib/time.js +1041 -12
  48. package/src/backend/wasm/types.js +200 -15
  49. package/src/backend/wasm/variadic.js +93 -0
  50. package/src/backend/wasm/wat.js +47 -20
  51. package/src/{cli-core.js → cli/cli-core.js} +236 -10
  52. package/src/{test-runner.js → cli/test-runner.js} +332 -39
  53. package/src/{vendor.js → cli/vendor.js} +3 -4
  54. package/src/compiler.js +107 -269
  55. package/src/index.js +15 -169
  56. package/src/{lexer.js → lexer/lexer.js} +102 -55
  57. package/src/{tokens.js → lexer/tokens.js} +1 -0
  58. package/src/link/sourcemap.js +97 -0
  59. package/src/link/stdlib.js +515 -0
  60. package/src/link/wasm.js +294 -0
  61. package/src/lower/bounds.js +394 -0
  62. package/src/lower/boxing.js +1 -1
  63. package/src/lower/captures.js +176 -32
  64. package/src/lower/index.js +26 -18
  65. package/src/lower/json-codecs.js +1398 -0
  66. package/src/lower/ownership.js +22 -1
  67. package/src/lower/range.js +86 -1
  68. package/src/lower/reachability.js +132 -0
  69. package/src/parser/declarations.js +58 -13
  70. package/src/parser/expressions.js +29 -7
  71. package/src/parser/index.js +2 -1
  72. package/src/parser/statements.js +37 -22
  73. package/src/{templ-parser.js → parser/templ.js} +138 -15
  74. package/src/parser/types.js +69 -9
  75. package/src/resolver.js +22 -1
  76. package/src/typechecker/assignability.js +176 -17
  77. package/src/typechecker/const-fold.js +356 -0
  78. package/src/typechecker/constants.js +256 -0
  79. package/src/typechecker/expressions.js +931 -122
  80. package/src/typechecker/index.js +459 -67
  81. package/src/typechecker/init-order.js +330 -0
  82. package/src/typechecker/resolve.js +451 -56
  83. package/src/typechecker/statements.js +90 -9
  84. package/src/typechecker/stdlib/core.js +333 -55
  85. package/src/typechecker/stdlib/excluded.js +898 -0
  86. package/src/typechecker/stdlib/extended.js +322 -26
  87. package/src/typechecker/{stdlib.js → stdlib/index.js} +5 -5
  88. package/src/typechecker/stdlib/simd.js +65 -15
  89. package/src/typechecker/termination.js +1 -1
  90. package/src/typechecker/types.js +149 -25
  91. package/stdlib/bytes/iter.go +192 -0
  92. package/stdlib/bytes/search.go +240 -0
  93. package/stdlib/bytes/split.go +218 -0
  94. package/stdlib/cmp/cmp.go +65 -0
  95. package/stdlib/encoding/json/errors.go +31 -0
  96. package/stdlib/encoding/json/indent.go +71 -0
  97. package/stdlib/encoding/json/number.go +12 -0
  98. package/stdlib/encoding/json/stream.go +173 -0
  99. package/stdlib/errors/overlay.go +12 -0
  100. package/stdlib/hash/hash.go +53 -0
  101. package/stdlib/internal/jsonrt/dec.go +738 -0
  102. package/stdlib/internal/jsonrt/enc.go +366 -0
  103. package/stdlib/internal/jsonrt/indent.go +135 -0
  104. package/stdlib/internal/jsonrt/number.go +27 -0
  105. package/stdlib/internal/jsonrt/reader.go +73 -0
  106. package/stdlib/internal/jsonrt/scanner.go +649 -0
  107. package/stdlib/io/io.go +346 -0
  108. package/stdlib/iter/iter.go +18 -0
  109. package/stdlib/maps/iter.go +65 -0
  110. package/stdlib/math/extra.go +270 -0
  111. package/stdlib/slices/extra.go +73 -0
  112. package/stdlib/slices/iter.go +113 -0
  113. package/stdlib/sort/sort.go +561 -0
  114. package/stdlib/strconv/quote.go +341 -0
  115. package/stdlib/strings/extra.go +35 -0
  116. package/stdlib/strings/iter.go +122 -0
  117. package/stdlib/sync/extra.go +105 -0
  118. package/stdlib/unicode/unicode.go +112 -0
  119. package/stdlib/unicode/utf8/bytes.go +239 -0
  120. package/src/backend/wasm/const-fold.js +0 -205
  121. package/src/backend/wasm/emit-stdlib.js +0 -13
  122. /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
  123. /package/src/{colors.js → cli/colors.js} +0 -0
  124. /package/src/{dev-server.js → cli/dev-server.js} +0 -0
  125. /package/src/{minifier.js → cli/minifier.js} +0 -0
  126. /package/src/{project-config.js → cli/project-config.js} +0 -0
  127. /package/src/{pwa.js → cli/pwa.js} +0 -0
  128. /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
@@ -418,11 +419,26 @@ const SIMD_OPCODES = {
418
419
  "i64x2.replace_lane": 30,
419
420
  "f32x4.extract_lane": 31,
420
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,
421
429
  "v128.not": 77,
422
430
  "v128.and": 78,
423
431
  "v128.andnot": 79,
424
432
  "v128.or": 80,
425
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,
426
442
  "f32x4.abs": 224,
427
443
  "f32x4.neg": 225,
428
444
  "f32x4.sqrt": 227,
@@ -579,6 +595,7 @@ function encodeInstruction(inst) {
579
595
 
580
596
  case "br":
581
597
  case "br_if":
598
+ case "br_on_null":
582
599
  return [byte, ...encodeU32LEB(inst.depth ?? inst.index ?? 0)];
583
600
 
584
601
  case "br_table": {
@@ -703,35 +720,48 @@ function encodeTypeEntry(t) {
703
720
  return [0x4e, ...count, ...innerBytes];
704
721
  }
705
722
 
723
+ let compositeBytes;
706
724
  if (t.form === "struct") {
707
725
  // Struct type: 0x5F, vector of fields [valtype, mutability]
708
726
  const fields = t.fields ?? [];
709
- return [
727
+ compositeBytes = [
710
728
  0x5f,
711
729
  ...encodeVector(fields, (f) => [
712
730
  ...encodeValType(f.type),
713
731
  f.mutable ? 0x01 : 0x00,
714
732
  ]),
715
733
  ];
716
- }
717
-
718
- if (t.form === "array") {
734
+ } else if (t.form === "array") {
719
735
  // Array type: 0x5E, field [valtype, mutability]
720
- return [
736
+ compositeBytes = [
721
737
  0x5e,
722
738
  ...encodeValType(t.elemType ?? t.type),
723
739
  t.mutable ? 0x01 : 0x00,
724
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
+ ];
725
750
  }
726
751
 
727
- // Function type: 0x60, params vector, results vector
728
- const params = t.params ?? [];
729
- const results = t.results ?? [];
730
- return [
731
- 0x60,
732
- ...encodeVector(params, encodeValType),
733
- ...encodeVector(results, encodeValType),
734
- ];
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;
735
765
  }
736
766
 
737
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
+ }