gofront 1.8.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 (177) hide show
  1. package/README.md +3 -3
  2. package/package.json +5 -4
  3. package/src/backend/js/expressions.js +849 -371
  4. package/src/backend/js/index.js +241 -147
  5. package/src/backend/js/natives.js +383 -0
  6. package/src/backend/js/runtime.js +512 -2547
  7. package/src/backend/js/statements.js +506 -103
  8. package/src/backend/js/stdlib/index.js +45 -93
  9. package/src/backend/js/templ.js +32 -45
  10. package/src/backend/js/type-info.js +177 -13
  11. package/src/backend/shared/fixed-float.js +20 -0
  12. package/src/backend/shared/fmt-types.js +63 -171
  13. package/src/backend/shared/host-unicode.js +36 -0
  14. package/src/backend/wasm/boundary.js +490 -965
  15. package/src/backend/wasm/emit-builtins.js +643 -256
  16. package/src/backend/wasm/emit-exprs.js +124 -57
  17. package/src/backend/wasm/emit-maps.js +17 -367
  18. package/src/backend/wasm/emit-stmts.js +325 -75
  19. package/src/backend/wasm/emit.js +104 -13
  20. package/src/backend/wasm/globals.js +47 -8
  21. package/src/backend/wasm/glue.js +2 -4
  22. package/src/backend/wasm/index.js +36 -530
  23. package/src/backend/wasm/method-expr.js +12 -1
  24. package/src/backend/wasm/module-emitter.js +92 -717
  25. package/src/backend/wasm/monomorph.js +103 -6
  26. package/src/backend/wasm/natives.js +596 -0
  27. package/src/backend/wasm/prescan.js +5 -153
  28. package/src/backend/wasm/prune-funcs.js +89 -0
  29. package/src/backend/wasm/stdlib/index.js +2 -32
  30. package/src/backend/wasm/types.js +68 -20
  31. package/src/backend/wasm/variadic.js +3 -3
  32. package/src/cli/cli-core.js +5 -1
  33. package/src/cli/test-runner.js +115 -342
  34. package/src/compiler.js +108 -130
  35. package/src/lexer/lexer.js +105 -11
  36. package/src/lexer/templ-lexer.js +61 -18
  37. package/src/lexer/tokens.js +1 -0
  38. package/src/link/stdlib.js +354 -132
  39. package/src/link/type-names.js +148 -0
  40. package/src/link/wasm.js +30 -6
  41. package/src/lower/bounds.js +153 -0
  42. package/src/lower/captures.js +92 -33
  43. package/src/lower/embedding.js +32 -3
  44. package/src/lower/errors-as.js +196 -0
  45. package/src/lower/fmt-formatters.js +905 -0
  46. package/src/lower/functions.js +2 -0
  47. package/src/lower/gen-types.js +274 -0
  48. package/src/lower/index.js +7 -1
  49. package/src/lower/json-codecs.js +228 -247
  50. package/src/lower/ownership.js +25 -7
  51. package/src/lower/reachability.js +69 -51
  52. package/src/lower/walk.js +12 -0
  53. package/src/parser/declarations.js +28 -5
  54. package/src/parser/expressions.js +86 -16
  55. package/src/parser/statements.js +1 -0
  56. package/src/parser/templ.js +44 -5
  57. package/src/parser/types.js +1 -0
  58. package/src/resolver.js +1 -2
  59. package/src/typechecker/assignability.js +124 -11
  60. package/src/typechecker/const-fold.js +70 -27
  61. package/src/typechecker/constants.js +32 -6
  62. package/src/typechecker/expressions.js +326 -98
  63. package/src/typechecker/index.js +208 -33
  64. package/src/typechecker/init-order.js +136 -40
  65. package/src/typechecker/operand-text.js +40 -0
  66. package/src/typechecker/resolve.js +192 -36
  67. package/src/typechecker/statements.js +122 -11
  68. package/src/typechecker/stdlib/excluded.js +9 -5
  69. package/src/typechecker/stdlib/index.js +25 -9
  70. package/src/typechecker/termination.js +10 -1
  71. package/src/typechecker/types.js +203 -573
  72. package/stdlib/bytes/buffer.go +479 -0
  73. package/stdlib/bytes/bytes.go +1010 -0
  74. package/stdlib/bytes/reader.go +161 -0
  75. package/stdlib/context/context.go +115 -0
  76. package/stdlib/crypto/rand/rand.go +34 -0
  77. package/stdlib/crypto/sha256/sha256.go +249 -0
  78. package/stdlib/encoding/base64/base64.go +331 -0
  79. package/stdlib/encoding/binary/binary.go +332 -0
  80. package/stdlib/encoding/hex/hex.go +140 -0
  81. package/stdlib/encoding/json/json.go +62 -0
  82. package/stdlib/errors/errors.go +174 -0
  83. package/stdlib/fmt/errors.go +110 -0
  84. package/stdlib/fmt/format.go +732 -0
  85. package/stdlib/fmt/print.go +1232 -0
  86. package/stdlib/gom/gom.go +293 -0
  87. package/stdlib/html/html.go +251 -0
  88. package/stdlib/internal/fmtrt/fmtrt.go +163 -0
  89. package/stdlib/internal/jsonrt/dec.go +171 -38
  90. package/stdlib/internal/jsonrt/enc.go +186 -36
  91. package/stdlib/internal/jsonrt/scanner.go +17 -8
  92. package/stdlib/io/io.go +157 -1
  93. package/stdlib/maps/maps.go +131 -0
  94. package/stdlib/math/bits/bits.go +507 -0
  95. package/stdlib/math/extra.go +16 -14
  96. package/stdlib/math/math.go +439 -0
  97. package/stdlib/math/rand/rand.go +297 -0
  98. package/stdlib/os/os.go +93 -0
  99. package/stdlib/path/filepath/filepath.go +47 -0
  100. package/stdlib/path/path.go +394 -0
  101. package/stdlib/regexp/regexp.go +231 -0
  102. package/stdlib/slices/slices.go +526 -0
  103. package/stdlib/slices/sort.go +209 -0
  104. package/stdlib/slices/zsort_cmp.go +480 -0
  105. package/stdlib/slices/zsort_ordered.go +482 -0
  106. package/stdlib/sort/sort.go +391 -452
  107. package/stdlib/sort/zsort_func.go +480 -0
  108. package/stdlib/sort/zsort_iface.go +480 -0
  109. package/stdlib/strconv/float.go +1699 -0
  110. package/stdlib/strconv/isprint.go +752 -0
  111. package/stdlib/strconv/quote.go +235 -50
  112. package/stdlib/strconv/strconv.go +616 -0
  113. package/stdlib/strings/builder.go +95 -0
  114. package/stdlib/strings/reader.go +163 -0
  115. package/stdlib/strings/replacer.go +102 -0
  116. package/stdlib/strings/strings.go +807 -0
  117. package/stdlib/sync/sync.go +268 -0
  118. package/stdlib/testing/async_js.go +61 -0
  119. package/stdlib/testing/benchmark.go +297 -0
  120. package/stdlib/testing/testing.go +523 -0
  121. package/stdlib/time/duration.go +233 -0
  122. package/stdlib/time/format.go +745 -0
  123. package/stdlib/time/parse.go +790 -0
  124. package/stdlib/time/sleep_js.go +63 -0
  125. package/stdlib/time/time.go +834 -0
  126. package/stdlib/unicode/unicode.go +141 -3
  127. package/stdlib/unicode/utf8/utf8.go +464 -0
  128. package/src/backend/js/fmt-runtime.js +0 -573
  129. package/src/backend/js/stdlib/base64.js +0 -12
  130. package/src/backend/js/stdlib/binary.js +0 -102
  131. package/src/backend/js/stdlib/bits.js +0 -119
  132. package/src/backend/js/stdlib/builder.js +0 -98
  133. package/src/backend/js/stdlib/bytes.js +0 -368
  134. package/src/backend/js/stdlib/errors.js +0 -45
  135. package/src/backend/js/stdlib/fmt.js +0 -517
  136. package/src/backend/js/stdlib/gom.js +0 -155
  137. package/src/backend/js/stdlib/hex.js +0 -17
  138. package/src/backend/js/stdlib/html.js +0 -20
  139. package/src/backend/js/stdlib/io.js +0 -137
  140. package/src/backend/js/stdlib/json.js +0 -1167
  141. package/src/backend/js/stdlib/maps.js +0 -37
  142. package/src/backend/js/stdlib/math.js +0 -85
  143. package/src/backend/js/stdlib/os.js +0 -13
  144. package/src/backend/js/stdlib/path.js +0 -48
  145. package/src/backend/js/stdlib/rand.js +0 -32
  146. package/src/backend/js/stdlib/regexp.js +0 -54
  147. package/src/backend/js/stdlib/sha256.js +0 -12
  148. package/src/backend/js/stdlib/shared.js +0 -17
  149. package/src/backend/js/stdlib/simd.js +0 -34
  150. package/src/backend/js/stdlib/slices.js +0 -107
  151. package/src/backend/js/stdlib/sort.js +0 -30
  152. package/src/backend/js/stdlib/strconv.js +0 -79
  153. package/src/backend/js/stdlib/strings.js +0 -301
  154. package/src/backend/js/stdlib/sync.js +0 -13
  155. package/src/backend/js/stdlib/testing.js +0 -41
  156. package/src/backend/js/stdlib/time.js +0 -152
  157. package/src/backend/js/stdlib/unicode.js +0 -25
  158. package/src/backend/js/stdlib/utf8.js +0 -26
  159. package/src/backend/wasm/stdlib/bytes.js +0 -6716
  160. package/src/backend/wasm/stdlib/core.js +0 -10278
  161. package/src/backend/wasm/stdlib/io.js +0 -3578
  162. package/src/backend/wasm/stdlib/packages.js +0 -2402
  163. package/src/backend/wasm/stdlib/pure.js +0 -720
  164. package/src/backend/wasm/stdlib/time.js +0 -2229
  165. package/src/typechecker/stdlib/core.js +0 -1079
  166. package/src/typechecker/stdlib/extended.js +0 -1210
  167. package/stdlib/bytes/iter.go +0 -192
  168. package/stdlib/bytes/search.go +0 -240
  169. package/stdlib/bytes/split.go +0 -218
  170. package/stdlib/errors/overlay.go +0 -12
  171. package/stdlib/maps/iter.go +0 -65
  172. package/stdlib/slices/extra.go +0 -73
  173. package/stdlib/slices/iter.go +0 -113
  174. package/stdlib/strings/extra.go +0 -35
  175. package/stdlib/strings/iter.go +0 -122
  176. package/stdlib/sync/extra.go +0 -105
  177. package/stdlib/unicode/utf8/bytes.go +0 -239
@@ -55,6 +55,18 @@ function sameSignature(c, params, paramTypes, want, got) {
55
55
  return want === undefined || got === undefined || sameTypes(got, want);
56
56
  }
57
57
 
58
+ // True when a struct type embeds a pointer, directly or through an embedded
59
+ // struct: promoted method calls read through it.
60
+ function _embedsPointer(t, seen = new Set()) {
61
+ const base = t?.kind === "pointer" ? t.base : t;
62
+ const st = base?.underlying ?? base;
63
+ if (!st || seen.has(st)) return false;
64
+ seen.add(st);
65
+ return (st._embeds ?? []).some(
66
+ (e) => e.kind === "pointer" || _embedsPointer(e, seen),
67
+ );
68
+ }
69
+
58
70
  export class FunctionEmitter {
59
71
  constructor(moduleEmitter, funcDecl, funcIndex) {
60
72
  this.mod = moduleEmitter;
@@ -781,7 +793,22 @@ export class FunctionEmitter {
781
793
  // A pointer dereference or a call through a func value: sites whose nil
782
794
  // operand raises Go's panic through the `nil` block.
783
795
  _isNilTrapSite(node) {
784
- if (node.kind === "CallExpr") return this._isFuncValueCall(node);
796
+ if (node.kind === "CallExpr")
797
+ return (
798
+ this._isFuncValueCall(node) ||
799
+ (node.func?.kind === "SelectorExpr" &&
800
+ _embedsPointer(node.func.expr?._type))
801
+ );
802
+ // Binding a method value on an interface checks it for nil.
803
+ if (node.kind === "NilCheckStmt") return true;
804
+ // A pointer to an array: indexing, slicing and ranging with a value
805
+ // read the array.
806
+ if (
807
+ node.kind === "IndexExpr" ||
808
+ node.kind === "SliceExpr" ||
809
+ node.kind === "RangeExpr"
810
+ )
811
+ return node.expr?._type?.kind === "pointer";
785
812
  const star =
786
813
  (node.kind === "UnaryExpr" && node.op === "*") ||
787
814
  node.kind === "StarExpr";
@@ -893,11 +920,12 @@ export class FunctionEmitter {
893
920
  return null;
894
921
  }
895
922
 
896
- _buildEnvFields(capturedNames) {
923
+ _buildEnvFields(capturedNames, funcLit) {
897
924
  const envFields = [];
925
+ const types = this.captureAnalysis?.captureTypes?.get(funcLit);
898
926
  for (const name of capturedNames) {
899
927
  const isBoxed = this.mutatedCaptures.has(name);
900
- const goType = this._findCapturedVarGoType(name);
928
+ const goType = types?.get(name) ?? this._findCapturedVarGoType(name);
901
929
  const boxInfo = isBoxed ? this.mod.getBoxType(goType) : null;
902
930
  envFields.push({
903
931
  name,
@@ -913,7 +941,10 @@ export class FunctionEmitter {
913
941
  }
914
942
 
915
943
  _unpackClosureEnv() {
916
- const envFields = this._buildEnvFields(this.capturedNames);
944
+ const envFields = this._buildEnvFields(
945
+ this.capturedNames,
946
+ this.funcDecl._funcLit,
947
+ );
917
948
  const envInfo = this.mod.getEnvType(envFields);
918
949
  this.envInfo = envInfo;
919
950
  this.envFields = envFields;
@@ -1126,7 +1157,7 @@ export class FunctionEmitter {
1126
1157
  const instName = (ft?.kind === "PointerType" ? ft.base : ft)?.name;
1127
1158
  const embedInfo =
1128
1159
  (instName && this.mod.structTypes.get(instName)) ??
1129
- this.mod.structTypes.get(embed.name);
1160
+ this.mod.structTypes.get(embed.typeName ?? embed.name);
1130
1161
  if (embedInfo) {
1131
1162
  const sub = this._resolveFieldPath(embedInfo, fieldName);
1132
1163
  if (sub) {
@@ -1381,6 +1412,7 @@ export class FunctionEmitter {
1381
1412
 
1382
1413
  // 2. Iterate candidates
1383
1414
  for (const cand of candidates) {
1415
+ this._emitNilPtrCandidate(cand, fn, recvLocal);
1384
1416
  this.pushInstruction({ op: "block", blockType: "void" });
1385
1417
  this.pushInstruction({ op: "local.get", index: recvLocal });
1386
1418
  this.pushInstruction({ op: "ref.test", typeIndex: cand.testTypeIndex });
@@ -1445,6 +1477,36 @@ export class FunctionEmitter {
1445
1477
  emitMiss("interface conversion: nil or unmatched type for method call");
1446
1478
  }
1447
1479
 
1480
+ // A typed nil `*S` inside the interface is a marker object (see
1481
+ // getNilBoxType): a pointer-receiver method runs with a nil receiver, as in
1482
+ // Go; a value-receiver or promoted method must read through the nil pointer.
1483
+ _emitNilPtrCandidate(cand, fn, recvLocal) {
1484
+ if (cand.isBoxedValue || !cand.structInfo) return;
1485
+ if (cand.testTypeIndex !== cand.structInfo.typeIndex) return;
1486
+ const nilBox = this.mod.getNilBoxType({
1487
+ kind: "pointer",
1488
+ base: { kind: "named", name: cand.recvTypeName },
1489
+ });
1490
+ this.pushInstruction({ op: "block", blockType: "void" });
1491
+ this.pushInstruction({ op: "local.get", index: recvLocal });
1492
+ this.pushInstruction({ op: "ref.test", typeIndex: nilBox.typeIndex });
1493
+ this.pushInstruction("i32.eqz");
1494
+ this.pushInstruction({ op: "br_if", depth: 0 });
1495
+ if (cand.needsValueDeref || cand.embedPath) {
1496
+ this.emitPanic(
1497
+ "runtime error: invalid memory address or nil pointer dereference",
1498
+ );
1499
+ } else {
1500
+ this.pushInstruction({ op: "ref.null", heapType: cand.testTypeIndex });
1501
+ for (let p = 1; p < fn.params.length; p++) {
1502
+ this.pushInstruction({ op: "local.get", index: p });
1503
+ }
1504
+ this.pushInstruction({ op: "call", funcIndex: cand.funcIndex });
1505
+ this.pushInstruction("return");
1506
+ }
1507
+ this.pushInstruction("end");
1508
+ }
1509
+
1448
1510
  // A value-receiver method reached through a pointer box reads the pointee
1449
1511
  // first, so a nil pointer inside the interface is a catchable panic.
1450
1512
  _emitNilPtrBoxPanic(cand, recvLocal) {
@@ -1570,11 +1632,9 @@ export class FunctionEmitter {
1570
1632
  size,
1571
1633
  });
1572
1634
  } else {
1573
- this.pushInstruction({ op: "i32.const", value: size });
1574
- this.pushInstruction({
1575
- op: "array.new_default",
1576
- typeIndex: arrInfo.typeIndex,
1577
- });
1635
+ this._emitNewArray(elemType, arrInfo, () =>
1636
+ this.pushInstruction({ op: "i32.const", value: size }),
1637
+ );
1578
1638
  }
1579
1639
  return;
1580
1640
  }
@@ -1616,8 +1676,24 @@ export class FunctionEmitter {
1616
1676
  }
1617
1677
  }
1618
1678
 
1619
- allocTemp(wasmType) {
1620
- return this.acquireTemp(wasmType);
1679
+ // Pushes a new array of `arrInfo` with the length `emitLen` pushes. Elements
1680
+ // start at their zero value: strings are the empty string, not a null
1681
+ // reference, so the array reads, compares and prints like Go's.
1682
+ _emitNewArray(elemType, arrInfo, emitLen) {
1683
+ if (
1684
+ arrInfo.elemWType === "externref" &&
1685
+ isStringType(elemType, this.mod.checker)
1686
+ ) {
1687
+ this.emitZeroValue(elemType, "externref");
1688
+ emitLen();
1689
+ this.pushInstruction({ op: "array.new", typeIndex: arrInfo.typeIndex });
1690
+ return;
1691
+ }
1692
+ emitLen();
1693
+ this.pushInstruction({
1694
+ op: "array.new_default",
1695
+ typeIndex: arrInfo.typeIndex,
1696
+ });
1621
1697
  }
1622
1698
 
1623
1699
  // Temps are keyed by a canonical string so ref-typed temps (fresh object
@@ -1796,10 +1872,25 @@ export class FunctionEmitter {
1796
1872
  this.emitExprStmt(stmt);
1797
1873
  break;
1798
1874
 
1875
+ // Evaluates `stmt.expr` and panics (recoverably) when it is nil.
1876
+ case "NilCheckStmt":
1877
+ this.emitExpr(stmt.expr, "anyref");
1878
+ this._emitNilBranch();
1879
+ this.pushInstruction("drop");
1880
+ break;
1881
+
1799
1882
  case "LabeledStmt":
1800
1883
  if (stmt.body) {
1801
- if (stmt.body.kind === "ForStmt") {
1884
+ // Stacked labels (`a: b: for`) all target the loop: outer
1885
+ // labels alias the innermost one.
1886
+ if (stmt.body.kind !== "LabeledStmt") {
1802
1887
  stmt.body.label = stmt.label;
1888
+ this._labelAliases?.delete(stmt.label);
1889
+ } else {
1890
+ let inner = stmt.body;
1891
+ while (inner.body?.kind === "LabeledStmt") inner = inner.body;
1892
+ this._labelAliases ??= new Map();
1893
+ this._labelAliases.set(stmt.label, inner.label);
1803
1894
  }
1804
1895
  this.emitStmt(stmt.body);
1805
1896
  }
@@ -5,7 +5,12 @@
5
5
  import { foldConstExpr } from "../../typechecker/const-fold.js";
6
6
  import { orderPackageInits } from "../../typechecker/init-order.js";
7
7
  import { _someNode } from "./prescan.js";
8
- import { isStructType, toWasmType, variadicAsSlice } from "./types.js";
8
+ import {
9
+ isStringType,
10
+ isStructType,
11
+ toWasmType,
12
+ variadicAsSlice,
13
+ } from "./types.js";
9
14
 
10
15
  // Package-level `const` declarations (iota is already substituted by the
11
16
  // parser). Literals and expressions the folder can reduce become BasicLits in
@@ -169,6 +174,36 @@ function _zeroArrayConst(goType, mod, depth = 0) {
169
174
  return out;
170
175
  }
171
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
+
172
207
  export function _collectPackageGlobals(progs, mod, checker) {
173
208
  const pending = []; // non-constant initializers, run by the start function
174
209
  const pendingConsts = []; // unfoldable consts; they run before any var
@@ -183,11 +218,12 @@ export function _collectPackageGlobals(progs, mod, checker) {
183
218
  const name = names[i];
184
219
  // Foldable consts live in `constCache`, not in a global.
185
220
  if (isConst && !mod.isNonLiteralConst(name)) continue;
221
+ const spreadTypes =
222
+ values.length === 1 && names.length > 1
223
+ ? _spreadValueTypes(spec, values[0])
224
+ : null;
186
225
  const rawType =
187
- spec.type ??
188
- (values.length === 1 && names.length > 1
189
- ? values[0]?._type?.types?.[i]
190
- : values[i]?._type);
226
+ spec.type ?? (spreadTypes ? spreadTypes[i] : values[i]?._type);
191
227
  const wType = toWasmType(rawType, checker, mod);
192
228
  const gIdx = mod.globals.length;
193
229
  let needsRuntimeZero = false;
@@ -252,6 +288,9 @@ export function _collectPackageGlobals(progs, mod, checker) {
252
288
  }
253
289
  }
254
290
 
291
+ if (values.length === 0 && _zeroHasString(rawType, mod))
292
+ needsRuntimeZero = true;
293
+
255
294
  let litNode = values?.[i];
256
295
  let sign = 1;
257
296
  if (
@@ -302,7 +341,7 @@ export function _collectPackageGlobals(progs, mod, checker) {
302
341
  if (needsRuntimeZero) pending.push({ name, zeroType: rawType });
303
342
  if (!isConst && i === 0 && values.length === 1 && names.length > 1) {
304
343
  // `var q, r = pair()`: one call fills every name.
305
- pending.push({ name, names, expr: values[0] });
344
+ pending.push({ name, names, expr: values[0], types: spreadTypes });
306
345
  }
307
346
  if (values[i] && !isConstInit && names.length === values.length) {
308
347
  (isConst ? pendingConsts : pending).push({
@@ -355,7 +394,7 @@ export function _orderConstInits(entries) {
355
394
  // initializer in declaration order and invoking package `init()` functions;
356
395
  // registered as the module start function.
357
396
  export function _makeGlobalInitFunc(pending, initNames = []) {
358
- const stmts = pending.map(({ name, names, expr, zeroType }) =>
397
+ const stmts = pending.map(({ name, names, expr, types, zeroType }) =>
359
398
  zeroType
360
399
  ? { kind: "GlobalZeroInit", name, goType: zeroType }
361
400
  : {
@@ -364,7 +403,7 @@ export function _makeGlobalInitFunc(pending, initNames = []) {
364
403
  ? names.map((n, i) => ({
365
404
  kind: "Ident",
366
405
  name: n,
367
- _type: expr._type?.types?.[i],
406
+ _type: types?.[i] ?? expr._type?.types?.[i],
368
407
  }))
369
408
  : [{ kind: "Ident", name, _type: expr._type }],
370
409
  op: "=",
@@ -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 = [],