gofront 1.7.0 → 1.9.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 (194) hide show
  1. package/README.md +14 -14
  2. package/package.json +10 -38
  3. package/src/backend/js/expressions.js +2060 -389
  4. package/src/backend/js/index.js +586 -174
  5. package/src/backend/js/natives.js +383 -0
  6. package/src/backend/js/runtime.js +936 -1644
  7. package/src/backend/js/statements.js +1349 -132
  8. package/src/backend/js/stdlib/index.js +51 -94
  9. package/src/backend/js/templ.js +67 -118
  10. package/src/backend/js/type-info.js +436 -0
  11. package/src/backend/shared/fixed-float.js +20 -0
  12. package/src/backend/shared/fmt-types.js +80 -0
  13. package/src/backend/shared/host-unicode.js +36 -0
  14. package/src/backend/wasm/boundary.js +753 -872
  15. package/src/backend/wasm/emit-builtins.js +914 -237
  16. package/src/backend/wasm/emit-exprs.js +1456 -195
  17. package/src/backend/wasm/emit-maps.js +78 -380
  18. package/src/backend/wasm/emit-stmts.js +1659 -214
  19. package/src/backend/wasm/emit.js +614 -88
  20. package/src/backend/wasm/encode.js +43 -13
  21. package/src/backend/wasm/globals.js +491 -0
  22. package/src/backend/wasm/glue.js +2 -4
  23. package/src/backend/wasm/index.js +406 -3195
  24. package/src/backend/wasm/method-expr.js +392 -0
  25. package/src/backend/wasm/module-emitter.js +1615 -0
  26. package/src/backend/wasm/monomorph.js +164 -9
  27. package/src/backend/wasm/natives.js +596 -0
  28. package/src/backend/wasm/prescan.js +249 -0
  29. package/src/backend/wasm/prune-funcs.js +89 -0
  30. package/src/backend/wasm/stdlib/index.js +2 -37
  31. package/src/backend/wasm/stdlib/simd.js +293 -28
  32. package/src/backend/wasm/types.js +260 -27
  33. package/src/backend/wasm/variadic.js +93 -0
  34. package/src/backend/wasm/wat.js +47 -20
  35. package/src/{cli-core.js → cli/cli-core.js} +241 -11
  36. package/src/{test-runner.js → cli/test-runner.js} +188 -122
  37. package/src/{vendor.js → cli/vendor.js} +3 -4
  38. package/src/compiler.js +202 -386
  39. package/src/index.js +15 -169
  40. package/src/{lexer.js → lexer/lexer.js} +203 -62
  41. package/src/{templ-lexer.js → lexer/templ-lexer.js} +61 -18
  42. package/src/{tokens.js → lexer/tokens.js} +2 -0
  43. package/src/link/sourcemap.js +97 -0
  44. package/src/link/stdlib.js +737 -0
  45. package/src/link/type-names.js +148 -0
  46. package/src/link/wasm.js +318 -0
  47. package/src/lower/bounds.js +547 -0
  48. package/src/lower/boxing.js +1 -1
  49. package/src/lower/captures.js +267 -64
  50. package/src/lower/embedding.js +32 -3
  51. package/src/lower/errors-as.js +196 -0
  52. package/src/lower/fmt-formatters.js +905 -0
  53. package/src/lower/functions.js +2 -0
  54. package/src/lower/gen-types.js +274 -0
  55. package/src/lower/index.js +33 -19
  56. package/src/lower/json-codecs.js +1379 -0
  57. package/src/lower/ownership.js +47 -8
  58. package/src/lower/range.js +86 -1
  59. package/src/lower/reachability.js +150 -0
  60. package/src/lower/walk.js +12 -0
  61. package/src/parser/declarations.js +83 -15
  62. package/src/parser/expressions.js +110 -18
  63. package/src/parser/index.js +2 -1
  64. package/src/parser/statements.js +38 -22
  65. package/src/{templ-parser.js → parser/templ.js} +179 -17
  66. package/src/parser/types.js +70 -9
  67. package/src/resolver.js +21 -1
  68. package/src/typechecker/assignability.js +291 -19
  69. package/src/typechecker/const-fold.js +399 -0
  70. package/src/typechecker/constants.js +282 -0
  71. package/src/typechecker/expressions.js +1220 -183
  72. package/src/typechecker/index.js +659 -92
  73. package/src/typechecker/init-order.js +426 -0
  74. package/src/typechecker/operand-text.js +40 -0
  75. package/src/typechecker/resolve.js +613 -62
  76. package/src/typechecker/statements.js +212 -20
  77. package/src/typechecker/stdlib/excluded.js +902 -0
  78. package/src/typechecker/stdlib/index.js +38 -0
  79. package/src/typechecker/stdlib/simd.js +65 -15
  80. package/src/typechecker/termination.js +11 -2
  81. package/src/typechecker/types.js +268 -514
  82. package/stdlib/bytes/buffer.go +479 -0
  83. package/stdlib/bytes/bytes.go +1010 -0
  84. package/stdlib/bytes/reader.go +161 -0
  85. package/stdlib/cmp/cmp.go +65 -0
  86. package/stdlib/context/context.go +115 -0
  87. package/stdlib/crypto/rand/rand.go +34 -0
  88. package/stdlib/crypto/sha256/sha256.go +249 -0
  89. package/stdlib/encoding/base64/base64.go +331 -0
  90. package/stdlib/encoding/binary/binary.go +332 -0
  91. package/stdlib/encoding/hex/hex.go +140 -0
  92. package/stdlib/encoding/json/errors.go +31 -0
  93. package/stdlib/encoding/json/indent.go +71 -0
  94. package/stdlib/encoding/json/json.go +62 -0
  95. package/stdlib/encoding/json/number.go +12 -0
  96. package/stdlib/encoding/json/stream.go +173 -0
  97. package/stdlib/errors/errors.go +174 -0
  98. package/stdlib/fmt/errors.go +110 -0
  99. package/stdlib/fmt/format.go +732 -0
  100. package/stdlib/fmt/print.go +1232 -0
  101. package/stdlib/gom/gom.go +293 -0
  102. package/stdlib/hash/hash.go +53 -0
  103. package/stdlib/html/html.go +251 -0
  104. package/stdlib/internal/fmtrt/fmtrt.go +163 -0
  105. package/stdlib/internal/jsonrt/dec.go +871 -0
  106. package/stdlib/internal/jsonrt/enc.go +516 -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 +658 -0
  111. package/stdlib/io/io.go +502 -0
  112. package/stdlib/iter/iter.go +18 -0
  113. package/stdlib/maps/maps.go +131 -0
  114. package/stdlib/math/bits/bits.go +507 -0
  115. package/stdlib/math/extra.go +272 -0
  116. package/stdlib/math/math.go +439 -0
  117. package/stdlib/math/rand/rand.go +297 -0
  118. package/stdlib/os/os.go +93 -0
  119. package/stdlib/path/filepath/filepath.go +47 -0
  120. package/stdlib/path/path.go +394 -0
  121. package/stdlib/regexp/regexp.go +231 -0
  122. package/stdlib/slices/slices.go +526 -0
  123. package/stdlib/slices/sort.go +209 -0
  124. package/stdlib/slices/zsort_cmp.go +480 -0
  125. package/stdlib/slices/zsort_ordered.go +482 -0
  126. package/stdlib/sort/sort.go +500 -0
  127. package/stdlib/sort/zsort_func.go +480 -0
  128. package/stdlib/sort/zsort_iface.go +480 -0
  129. package/stdlib/strconv/float.go +1699 -0
  130. package/stdlib/strconv/isprint.go +752 -0
  131. package/stdlib/strconv/quote.go +526 -0
  132. package/stdlib/strconv/strconv.go +616 -0
  133. package/stdlib/strings/builder.go +95 -0
  134. package/stdlib/strings/reader.go +163 -0
  135. package/stdlib/strings/replacer.go +102 -0
  136. package/stdlib/strings/strings.go +807 -0
  137. package/stdlib/sync/sync.go +268 -0
  138. package/stdlib/testing/async_js.go +61 -0
  139. package/stdlib/testing/benchmark.go +297 -0
  140. package/stdlib/testing/testing.go +523 -0
  141. package/stdlib/time/duration.go +233 -0
  142. package/stdlib/time/format.go +745 -0
  143. package/stdlib/time/parse.go +790 -0
  144. package/stdlib/time/sleep_js.go +63 -0
  145. package/stdlib/time/time.go +834 -0
  146. package/stdlib/unicode/unicode.go +250 -0
  147. package/stdlib/unicode/utf8/utf8.go +464 -0
  148. package/src/backend/js/stdlib/base64.js +0 -12
  149. package/src/backend/js/stdlib/binary.js +0 -102
  150. package/src/backend/js/stdlib/bits.js +0 -46
  151. package/src/backend/js/stdlib/builder.js +0 -63
  152. package/src/backend/js/stdlib/bytes.js +0 -122
  153. package/src/backend/js/stdlib/errors.js +0 -44
  154. package/src/backend/js/stdlib/fmt.js +0 -178
  155. package/src/backend/js/stdlib/gom.js +0 -155
  156. package/src/backend/js/stdlib/hex.js +0 -17
  157. package/src/backend/js/stdlib/html.js +0 -20
  158. package/src/backend/js/stdlib/io.js +0 -137
  159. package/src/backend/js/stdlib/json.js +0 -972
  160. package/src/backend/js/stdlib/maps.js +0 -45
  161. package/src/backend/js/stdlib/math.js +0 -69
  162. package/src/backend/js/stdlib/os.js +0 -13
  163. package/src/backend/js/stdlib/path.js +0 -48
  164. package/src/backend/js/stdlib/rand.js +0 -32
  165. package/src/backend/js/stdlib/regexp.js +0 -54
  166. package/src/backend/js/stdlib/sha256.js +0 -12
  167. package/src/backend/js/stdlib/shared.js +0 -17
  168. package/src/backend/js/stdlib/simd.js +0 -24
  169. package/src/backend/js/stdlib/slices.js +0 -77
  170. package/src/backend/js/stdlib/sort.js +0 -30
  171. package/src/backend/js/stdlib/strconv.js +0 -74
  172. package/src/backend/js/stdlib/strings.js +0 -222
  173. package/src/backend/js/stdlib/sync.js +0 -13
  174. package/src/backend/js/stdlib/testing.js +0 -34
  175. package/src/backend/js/stdlib/time.js +0 -128
  176. package/src/backend/js/stdlib/unicode.js +0 -25
  177. package/src/backend/js/stdlib/utf8.js +0 -26
  178. package/src/backend/wasm/const-fold.js +0 -205
  179. package/src/backend/wasm/emit-stdlib.js +0 -13
  180. package/src/backend/wasm/stdlib/bytes.js +0 -5039
  181. package/src/backend/wasm/stdlib/core.js +0 -8903
  182. package/src/backend/wasm/stdlib/io.js +0 -2161
  183. package/src/backend/wasm/stdlib/packages.js +0 -2400
  184. package/src/backend/wasm/stdlib/pure.js +0 -720
  185. package/src/backend/wasm/stdlib/time.js +0 -1200
  186. package/src/typechecker/stdlib/core.js +0 -801
  187. package/src/typechecker/stdlib/extended.js +0 -914
  188. package/src/typechecker/stdlib.js +0 -22
  189. /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
  190. /package/src/{colors.js → cli/colors.js} +0 -0
  191. /package/src/{dev-server.js → cli/dev-server.js} +0 -0
  192. /package/src/{minifier.js → cli/minifier.js} +0 -0
  193. /package/src/{project-config.js → cli/project-config.js} +0 -0
  194. /package/src/{pwa.js → cli/pwa.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,491 @@
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 {
9
+ isStringType,
10
+ isStructType,
11
+ toWasmType,
12
+ variadicAsSlice,
13
+ } from "./types.js";
14
+
15
+ // Package-level `const` declarations (iota is already substituted by the
16
+ // parser). Literals and expressions the folder can reduce become BasicLits in
17
+ // `constCache`; whatever is left is recorded in `nonLiteralConsts` and later
18
+ // becomes a global initialised by the start function.
19
+ export function _collectPackageConsts(progs, mod, checker = null) {
20
+ const entries = [];
21
+ for (const p of progs) {
22
+ for (const d of p.decls ?? []) {
23
+ if (d.kind !== "ConstDecl") continue;
24
+ for (const spec of d.decls ?? []) {
25
+ for (let i = 0; i < spec.names.length; i++) {
26
+ if (spec.value?.[i])
27
+ entries.push({
28
+ name: spec.names[i],
29
+ expr: spec.value[i],
30
+ filename: p._filename,
31
+ source: p._source,
32
+ });
33
+ }
34
+ }
35
+ }
36
+ }
37
+ const resolve = (name) => mod.constCache.get(name) ?? null;
38
+ // Iterate to a fixpoint so a constant may refer to one declared later.
39
+ let pending = entries;
40
+ let progress = true;
41
+ while (progress && pending.length > 0) {
42
+ progress = false;
43
+ const rest = [];
44
+ for (const entry of pending) {
45
+ let lit = null;
46
+ try {
47
+ lit = foldConstExpr(entry.expr, resolve);
48
+ } catch (err) {
49
+ if (checker?.err) {
50
+ const savedFile = checker._currentFile;
51
+ const savedSource = checker._currentSource;
52
+ if (entry.filename) checker._currentFile = entry.filename;
53
+ if (entry.source) checker._currentSource = entry.source;
54
+ checker.err(err.message, entry.expr);
55
+ checker._currentFile = savedFile;
56
+ checker._currentSource = savedSource;
57
+ } else {
58
+ throw new Error(
59
+ `Constant expression error for '${entry.name}': ${err.message}`,
60
+ );
61
+ }
62
+ continue;
63
+ }
64
+ if (lit) {
65
+ mod.constCache.set(entry.name, lit);
66
+ progress = true;
67
+ } else {
68
+ rest.push(entry);
69
+ }
70
+ }
71
+ pending = rest;
72
+ }
73
+ for (const entry of pending) mod.nonLiteralConsts.add(entry.name);
74
+ }
75
+
76
+ function _isPointerGoType(t, mod) {
77
+ if (!t) return false;
78
+ if (t.kind === "pointer" || t.kind === "PointerType" || t.kind === "StarExpr")
79
+ return true;
80
+ // `type P *T` used as a field type: look through the name.
81
+ const named =
82
+ t.kind === "named" || t.kind === "TypeName" || t.kind === "Ident";
83
+ const under = named
84
+ ? (t.underlying ?? mod.checker?.types?.get(t.name)?.underlying)
85
+ : null;
86
+ return under ? _isPointerGoType(under, mod) : false;
87
+ }
88
+
89
+ // Constant-expression zero value of a struct global. Nested struct values are
90
+ // built in place: a null ref would trap on the first field access.
91
+ // Null when a nested array is too large to build as a constant expression.
92
+ function _zeroStructConst(structInfo, mod) {
93
+ const out = [];
94
+ for (const f of structInfo.fields) {
95
+ const zero = _zeroFieldConst(f, mod);
96
+ if (!zero) return null;
97
+ out.push(...zero);
98
+ }
99
+ out.push({ op: "struct.new", typeIndex: structInfo.typeIndex });
100
+ return out;
101
+ }
102
+
103
+ function _zeroFieldConst(f, mod) {
104
+ const w = f.wType;
105
+ if (w === "i64") return [{ op: "i64.const", value: 0n }];
106
+ if (w === "f32" || w === "f64") return [{ op: `${w}.const`, value: 0 }];
107
+ if (w === "externref") return [{ op: "ref.null", heapType: "extern" }];
108
+ if (w === "anyref") return [{ op: "ref.null", heapType: "any" }];
109
+ if (typeof w !== "object" || w === null)
110
+ return [{ op: "i32.const", value: 0 }];
111
+ const ti = w.typeIndex;
112
+ const slice = typeof ti === "number" ? mod.getSliceTypeByIndex(ti) : null;
113
+ if (slice) return [{ op: "global.get", index: slice.emptyGlobalIndex }];
114
+ if (typeof ti === "number" && !_isPointerGoType(f.goType, mod)) {
115
+ for (const s of mod.structTypes.values())
116
+ if (s.typeIndex === ti) return _zeroStructConst(s, mod);
117
+ // An array field is a value: a null ref would trap on first use.
118
+ if (_arrayShape(f.goType, mod.checker))
119
+ return _zeroArrayConst(f.goType, mod);
120
+ }
121
+ return [{ op: "ref.null", heapType: ti ?? "any" }];
122
+ }
123
+
124
+ // Length and element type of an array type (checker type, AST node or name).
125
+ function _arrayShape(t, checker) {
126
+ if (!t) return null;
127
+ if (t.kind === "array" || t.kind === "ArrayType") {
128
+ const size = typeof t.size === "number" ? t.size : Number(t.size?.value);
129
+ return Number.isInteger(size) ? { size, elem: t.elem } : null;
130
+ }
131
+ if (t.kind === "named" && t.underlying)
132
+ return _arrayShape(t.underlying, checker);
133
+ if (t.kind === "TypeName" || t.kind === "Ident")
134
+ return _arrayShape(checker?.types?.get(t.name), checker);
135
+ return null;
136
+ }
137
+
138
+ // Constant-expression zero value of a package-level array global (a null ref
139
+ // would trap on the first element access); null when it cannot be built.
140
+ function _zeroArrayConst(goType, mod, depth = 0) {
141
+ const checker = mod.checker;
142
+ if (!goType || goType.kind === "pointer" || depth > 4) return null;
143
+ const shape = _arrayShape(goType, checker);
144
+ if (!shape) return null;
145
+ const { size, elem } = shape;
146
+ const arrInfo = mod.getArrayType(elem);
147
+ const elemW = toWasmType(elem, checker, mod);
148
+ let one = null;
149
+ if (_arrayShape(elem, checker)) {
150
+ one = _zeroArrayConst(elem, mod, depth + 1);
151
+ if (!one) return null;
152
+ } else if (
153
+ elem?.kind !== "pointer" &&
154
+ elem?.kind !== "StarExpr" &&
155
+ typeof elemW === "object" &&
156
+ isStructType(elem, checker, mod)
157
+ ) {
158
+ const info = Array.from(mod.structTypes.values()).find(
159
+ (x) => x.typeIndex === elemW.typeIndex,
160
+ );
161
+ if (!info) return null;
162
+ one = _zeroStructConst(info, mod);
163
+ if (!one) return null;
164
+ }
165
+ if (!one)
166
+ return [
167
+ { op: "i32.const", value: size },
168
+ { op: "array.new_default", typeIndex: arrInfo.typeIndex },
169
+ ];
170
+ if (size > 64) return null;
171
+ const out = [];
172
+ for (let i = 0; i < size; i++) out.push(...one);
173
+ out.push({ op: "array.new_fixed", typeIndex: arrInfo.typeIndex, size });
174
+ return out;
175
+ }
176
+
177
+ // True when the zero value of `t` holds a string (a string, or a struct or
178
+ // array containing one). A constant expression cannot build the empty string,
179
+ // so such globals get their zero value from the start function.
180
+ function _zeroHasString(t, mod, depth = 0) {
181
+ if (!t || depth > 4 || _isPointerGoType(t, mod)) return false;
182
+ if (isStringType(t, mod.checker)) return true;
183
+ const shape = _arrayShape(t, mod.checker);
184
+ if (shape) return _zeroHasString(shape.elem, mod, depth + 1);
185
+ const w = toWasmType(t, mod.checker, mod);
186
+ if (typeof w === "object" && typeof w?.typeIndex === "number") {
187
+ const info = Array.from(mod.structTypes.values()).find(
188
+ (x) => x.typeIndex === w.typeIndex,
189
+ );
190
+ if (info)
191
+ return info.fields.some((f) => _zeroHasString(f.goType, mod, depth + 1));
192
+ }
193
+ return false;
194
+ }
195
+
196
+ // Types of the names in `var a, b = f()` / `var v, ok = m[k]`: one value fills
197
+ // every name.
198
+ function _spreadValueTypes(spec, value) {
199
+ if (spec._commaOk)
200
+ return [
201
+ value._mapValueType ?? value._type,
202
+ { kind: "basic", name: "bool" },
203
+ ];
204
+ return value?._type?.types ?? [];
205
+ }
206
+
207
+ export function _collectPackageGlobals(progs, mod, checker) {
208
+ const pending = []; // non-constant initializers, run by the start function
209
+ const pendingConsts = []; // unfoldable consts; they run before any var
210
+ for (const p of progs) {
211
+ for (const d of p.decls ?? []) {
212
+ const isConst = d.kind === "ConstDecl";
213
+ if (d.kind !== "VarDecl" && !isConst) continue;
214
+ for (const spec of d.decls ?? d.specs ?? [d]) {
215
+ const names = spec.names ?? (spec.name ? [spec.name] : []);
216
+ const values = spec.value ?? (spec.init ? [spec.init] : []);
217
+ for (let i = 0; i < names.length; i++) {
218
+ const name = names[i];
219
+ // Foldable consts live in `constCache`, not in a global.
220
+ if (isConst && !mod.isNonLiteralConst(name)) continue;
221
+ const spreadTypes =
222
+ values.length === 1 && names.length > 1
223
+ ? _spreadValueTypes(spec, values[0])
224
+ : null;
225
+ const rawType =
226
+ spec.type ?? (spreadTypes ? spreadTypes[i] : values[i]?._type);
227
+ const wType = toWasmType(rawType, checker, mod);
228
+ const gIdx = mod.globals.length;
229
+ let needsRuntimeZero = false;
230
+ let initInsts = [{ op: `${wType}.const`, value: 0 }];
231
+ if (wType === "i64") initInsts = [{ op: "i64.const", value: 0n }];
232
+ else if (wType === "f32" || wType === "f64")
233
+ initInsts = [{ op: `${wType}.const`, value: 0.0 }];
234
+ else if (
235
+ wType === "externref" ||
236
+ wType === "anyref" ||
237
+ typeof wType === "object"
238
+ ) {
239
+ const sliceInfo =
240
+ typeof wType === "object" && typeof wType.typeIndex === "number"
241
+ ? mod.getSliceTypeByIndex(wType.typeIndex)
242
+ : null;
243
+ if (sliceInfo) {
244
+ initInsts = [
245
+ { op: "global.get", index: sliceInfo.emptyGlobalIndex },
246
+ ];
247
+ } else if (
248
+ typeof wType === "object" &&
249
+ typeof wType.typeIndex === "number" &&
250
+ rawType?.kind !== "pointer"
251
+ ) {
252
+ const structInfo = Array.from(mod.structTypes.values()).find(
253
+ (s) => s.typeIndex === wType.typeIndex,
254
+ );
255
+ const arrZero = structInfo ? null : _zeroArrayConst(rawType, mod);
256
+ const structZero = structInfo
257
+ ? _zeroStructConst(structInfo, mod)
258
+ : null;
259
+ if (structZero) {
260
+ initInsts = structZero;
261
+ } else if (arrZero) {
262
+ initInsts = arrZero;
263
+ } else if (
264
+ (structInfo || _arrayShape(rawType, checker)) &&
265
+ !values[i]
266
+ ) {
267
+ // Too large for a constant expression: the start
268
+ // function builds the zero value.
269
+ needsRuntimeZero = true;
270
+ initInsts = [
271
+ { op: "ref.null", heapType: wType.typeIndex ?? "any" },
272
+ ];
273
+ } else {
274
+ initInsts = [
275
+ { op: "ref.null", heapType: wType.typeIndex ?? "any" },
276
+ ];
277
+ }
278
+ } else {
279
+ initInsts = [
280
+ {
281
+ op: "ref.null",
282
+ heapType:
283
+ typeof wType === "object"
284
+ ? (wType.heapType ?? wType.typeIndex ?? "any")
285
+ : wType.replace("ref", ""),
286
+ },
287
+ ];
288
+ }
289
+ }
290
+
291
+ if (values.length === 0 && _zeroHasString(rawType, mod))
292
+ needsRuntimeZero = true;
293
+
294
+ let litNode = values?.[i];
295
+ let sign = 1;
296
+ if (
297
+ litNode &&
298
+ litNode.kind === "UnaryExpr" &&
299
+ litNode.op === "-" &&
300
+ litNode.operand?.kind === "BasicLit"
301
+ ) {
302
+ litNode = litNode.operand;
303
+ sign = -1;
304
+ }
305
+
306
+ let isConstInit = false;
307
+ if (litNode && litNode.kind === "BasicLit") {
308
+ if (litNode.litKind === "INT") {
309
+ isConstInit = true;
310
+ const s = String(litNode.value);
311
+ const clean = /^0[0-7]+$/.test(s) ? `0o${s.slice(1)}` : s;
312
+ initInsts = [
313
+ wType === "i64"
314
+ ? {
315
+ op: "i64.const",
316
+ value: BigInt(sign) * BigInt(clean),
317
+ }
318
+ : {
319
+ op: "i32.const",
320
+ value: (sign * Number(clean)) | 0,
321
+ },
322
+ ];
323
+ } else if (litNode.litKind === "FLOAT") {
324
+ isConstInit = true;
325
+ initInsts = [
326
+ {
327
+ op: `${wType}.const`,
328
+ value: sign * Number(litNode.value),
329
+ },
330
+ ];
331
+ } else if (litNode.litKind === "BOOL") {
332
+ isConstInit = true;
333
+ initInsts = [
334
+ {
335
+ op: "i32.const",
336
+ value: litNode.value === "true" ? 1 : 0,
337
+ },
338
+ ];
339
+ }
340
+ }
341
+ if (needsRuntimeZero) pending.push({ name, zeroType: rawType });
342
+ if (!isConst && i === 0 && values.length === 1 && names.length > 1) {
343
+ // `var q, r = pair()`: one call fills every name.
344
+ pending.push({ name, names, expr: values[0], types: spreadTypes });
345
+ }
346
+ if (values[i] && !isConstInit && names.length === values.length) {
347
+ (isConst ? pendingConsts : pending).push({
348
+ name,
349
+ expr: values[i],
350
+ });
351
+ }
352
+ mod.globals.push({
353
+ type: wType,
354
+ mutable: true,
355
+ init: initInsts,
356
+ });
357
+ mod.globalCache.set(name, {
358
+ index: gIdx,
359
+ type: wType,
360
+ goType: rawType,
361
+ });
362
+ }
363
+ }
364
+ }
365
+ }
366
+ // Initialisers run in dependency order (Go spec), not declaration order.
367
+ const rank = new Map();
368
+ for (const e of orderPackageInits(progs).vars)
369
+ for (const n of e.names) rank.set(n, rank.size);
370
+ pending.sort((a, b) => (rank.get(a.name) ?? 0) - (rank.get(b.name) ?? 0));
371
+ return [..._orderConstInits(pendingConsts), ...pending];
372
+ }
373
+
374
+ // Orders runtime-initialised consts so each runs after the consts it
375
+ // references (Go allows a constant to use one declared later).
376
+ export function _orderConstInits(entries) {
377
+ const byName = new Map(entries.map((e) => [e.name, e]));
378
+ const ordered = [];
379
+ const state = new Map();
380
+ const visit = (entry) => {
381
+ if (state.has(entry.name)) return;
382
+ state.set(entry.name, true);
383
+ _someNode(entry.expr, (n) => {
384
+ if (n.kind === "Ident" && byName.has(n.name)) visit(byName.get(n.name));
385
+ return false;
386
+ });
387
+ ordered.push(entry);
388
+ };
389
+ for (const e of entries) visit(e);
390
+ return ordered;
391
+ }
392
+
393
+ // Synthesises `__init_globals()` assigning every non-constant package-level
394
+ // initializer in declaration order and invoking package `init()` functions;
395
+ // registered as the module start function.
396
+ export function _makeGlobalInitFunc(pending, initNames = []) {
397
+ const stmts = pending.map(({ name, names, expr, types, zeroType }) =>
398
+ zeroType
399
+ ? { kind: "GlobalZeroInit", name, goType: zeroType }
400
+ : {
401
+ kind: "AssignStmt",
402
+ lhs: names
403
+ ? names.map((n, i) => ({
404
+ kind: "Ident",
405
+ name: n,
406
+ _type: types?.[i] ?? expr._type?.types?.[i],
407
+ }))
408
+ : [{ kind: "Ident", name, _type: expr._type }],
409
+ op: "=",
410
+ rhs: [expr],
411
+ },
412
+ );
413
+ for (const initName of initNames) {
414
+ stmts.push({
415
+ kind: "ExprStmt",
416
+ expr: {
417
+ kind: "CallExpr",
418
+ func: { kind: "Ident", name: initName },
419
+ args: [],
420
+ },
421
+ });
422
+ }
423
+ stmts.push({ kind: "ReturnStmt", values: [] });
424
+ return {
425
+ kind: "FuncDecl",
426
+ name: "__init_globals",
427
+ params: [],
428
+ returnType: null,
429
+ body: { kind: "Block", stmts, list: stmts },
430
+ _isGlobalInit: true,
431
+ };
432
+ }
433
+
434
+ export function _makeInterfaceDispatcher(info, checker) {
435
+ let methodSig = info.methodSig;
436
+ if (!methodSig) {
437
+ const raw =
438
+ info.ifaceType?.kind === "named" && info.ifaceType.underlying
439
+ ? info.ifaceType.underlying
440
+ : info.ifaceType;
441
+ const ifaceResolved = checker?.resolveType?.(raw) ?? raw;
442
+ const methods =
443
+ ifaceResolved?.methods ?? ifaceResolved?.underlying?.methods;
444
+ if (methods instanceof Map) {
445
+ methodSig = methods.get(info.methodName);
446
+ } else if (Array.isArray(methods)) {
447
+ methodSig = methods.find((m) => (m.name ?? m) === info.methodName);
448
+ }
449
+ }
450
+
451
+ const params = [{ name: "__recv", type: { kind: "basic", name: "any" } }];
452
+ if (methodSig?.params) {
453
+ const sigParams = variadicAsSlice(methodSig.params, methodSig.variadic);
454
+ for (let p = 0; p < sigParams.length; p++) {
455
+ params.push({ name: `__arg${p}`, type: sigParams[p] });
456
+ }
457
+ } else if (info.callNode?.args) {
458
+ for (let p = 0; p < info.callNode.args.length; p++) {
459
+ params.push({
460
+ name: `__arg${p}`,
461
+ type: info.callNode.args[p]._type ?? {
462
+ kind: "basic",
463
+ name: "any",
464
+ },
465
+ });
466
+ }
467
+ }
468
+
469
+ let returnType = info.callNode?._type ?? null;
470
+ if (methodSig?.returns) {
471
+ returnType =
472
+ methodSig.returns.length === 1
473
+ ? methodSig.returns[0]
474
+ : methodSig.returns.length > 1
475
+ ? { kind: "tuple", types: methodSig.returns }
476
+ : null;
477
+ }
478
+
479
+ return {
480
+ kind: "FuncDecl",
481
+ name: `__dispatch_${info.ifaceName}_${info.methodName}`,
482
+ params,
483
+ returnType,
484
+ body: { kind: "Block", stmts: [], list: [] },
485
+ _isInterfaceDispatcher: true,
486
+ _ifaceName: info.ifaceName,
487
+ _ifaceType: info.ifaceType,
488
+ _methodName: info.methodName,
489
+ _rootFuncDecl: null,
490
+ };
491
+ }
@@ -1,14 +1,12 @@
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, WASM_STDLIB_JS } from "./boundary.js";
4
+ import { WASM_IMPORTS_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(
10
- `${WASM_IMPORTS_JS}\n${WASM_STDLIB_JS}\nreturn __gfw_imports;`,
11
- )();
9
+ const makeImports = new Function(`${WASM_IMPORTS_JS}\nreturn __gfw_imports;`)();
12
10
 
13
11
  function createWasmImports({
14
12
  stringTable = [],