gofront 1.8.0 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/README.md +3 -3
  2. package/package.json +6 -5
  3. package/src/backend/js/expressions.js +1157 -408
  4. package/src/backend/js/index.js +327 -153
  5. package/src/backend/js/natives.js +383 -0
  6. package/src/backend/js/runtime.js +559 -2552
  7. package/src/backend/js/statements.js +670 -158
  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 +298 -19
  11. package/src/backend/shared/fixed-float.js +20 -0
  12. package/src/backend/shared/fmt-types.js +91 -170
  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 +884 -256
  16. package/src/backend/wasm/emit-exprs.js +185 -77
  17. package/src/backend/wasm/emit-maps.js +17 -367
  18. package/src/backend/wasm/emit-shared.js +4 -1
  19. package/src/backend/wasm/emit-stmts.js +524 -240
  20. package/src/backend/wasm/emit.js +176 -38
  21. package/src/backend/wasm/globals.js +48 -8
  22. package/src/backend/wasm/glue.js +2 -4
  23. package/src/backend/wasm/index.js +44 -530
  24. package/src/backend/wasm/method-expr.js +12 -1
  25. package/src/backend/wasm/module-emitter.js +299 -770
  26. package/src/backend/wasm/monomorph.js +139 -8
  27. package/src/backend/wasm/natives.js +600 -0
  28. package/src/backend/wasm/prescan.js +5 -153
  29. package/src/backend/wasm/prune-funcs.js +89 -0
  30. package/src/backend/wasm/stdlib/index.js +2 -32
  31. package/src/backend/wasm/types.js +97 -20
  32. package/src/backend/wasm/variadic.js +9 -4
  33. package/src/cli/cli-core.js +5 -1
  34. package/src/cli/test-runner.js +115 -342
  35. package/src/compiler.js +113 -130
  36. package/src/lexer/lexer.js +106 -11
  37. package/src/lexer/templ-lexer.js +61 -18
  38. package/src/lexer/tokens.js +1 -0
  39. package/src/link/stdlib.js +355 -132
  40. package/src/link/type-names.js +204 -0
  41. package/src/link/wasm.js +30 -6
  42. package/src/lower/bounds.js +153 -0
  43. package/src/lower/captures.js +92 -33
  44. package/src/lower/embedding.js +62 -5
  45. package/src/lower/errors-as.js +196 -0
  46. package/src/lower/fmt-formatters.js +929 -0
  47. package/src/lower/functions.js +225 -7
  48. package/src/lower/gen-types.js +293 -0
  49. package/src/lower/index.js +14 -2
  50. package/src/lower/json-codecs.js +334 -245
  51. package/src/lower/ownership.js +25 -7
  52. package/src/lower/reachability.js +69 -51
  53. package/src/lower/walk.js +12 -0
  54. package/src/parser/declarations.js +30 -5
  55. package/src/parser/expressions.js +111 -57
  56. package/src/parser/statements.js +108 -0
  57. package/src/parser/templ.js +44 -5
  58. package/src/parser/types.js +12 -0
  59. package/src/resolver.js +1 -2
  60. package/src/typechecker/assignability.js +214 -19
  61. package/src/typechecker/const-fold.js +70 -27
  62. package/src/typechecker/constants.js +156 -8
  63. package/src/typechecker/expressions.js +490 -108
  64. package/src/typechecker/index.js +346 -42
  65. package/src/typechecker/init-order.js +136 -40
  66. package/src/typechecker/operand-text.js +41 -0
  67. package/src/typechecker/resolve.js +490 -65
  68. package/src/typechecker/statements.js +288 -27
  69. package/src/typechecker/stdlib/excluded.js +9 -5
  70. package/src/typechecker/stdlib/index.js +25 -9
  71. package/src/typechecker/termination.js +10 -1
  72. package/src/typechecker/types.js +287 -575
  73. package/stdlib/bytes/buffer.go +479 -0
  74. package/stdlib/bytes/bytes.go +1010 -0
  75. package/stdlib/bytes/reader.go +161 -0
  76. package/stdlib/context/context.go +115 -0
  77. package/stdlib/crypto/rand/rand.go +34 -0
  78. package/stdlib/crypto/sha256/sha256.go +249 -0
  79. package/stdlib/encoding/base64/base64.go +331 -0
  80. package/stdlib/encoding/binary/binary.go +332 -0
  81. package/stdlib/encoding/hex/hex.go +140 -0
  82. package/stdlib/encoding/json/json.go +62 -0
  83. package/stdlib/errors/errors.go +174 -0
  84. package/stdlib/fmt/errors.go +110 -0
  85. package/stdlib/fmt/format.go +732 -0
  86. package/stdlib/fmt/print.go +1232 -0
  87. package/stdlib/gom/gom.go +293 -0
  88. package/stdlib/html/html.go +251 -0
  89. package/stdlib/internal/fmtrt/fmtrt.go +163 -0
  90. package/stdlib/internal/jsonrt/dec.go +171 -38
  91. package/stdlib/internal/jsonrt/enc.go +186 -36
  92. package/stdlib/internal/jsonrt/scanner.go +17 -8
  93. package/stdlib/io/io.go +157 -1
  94. package/stdlib/maps/maps.go +131 -0
  95. package/stdlib/math/bits/bits.go +507 -0
  96. package/stdlib/math/extra.go +16 -14
  97. package/stdlib/math/math.go +439 -0
  98. package/stdlib/math/rand/rand.go +297 -0
  99. package/stdlib/os/os.go +93 -0
  100. package/stdlib/path/filepath/filepath.go +47 -0
  101. package/stdlib/path/path.go +394 -0
  102. package/stdlib/regexp/regexp.go +231 -0
  103. package/stdlib/slices/slices.go +526 -0
  104. package/stdlib/slices/sort.go +209 -0
  105. package/stdlib/slices/zsort_cmp.go +480 -0
  106. package/stdlib/slices/zsort_ordered.go +482 -0
  107. package/stdlib/sort/sort.go +391 -452
  108. package/stdlib/sort/zsort_func.go +480 -0
  109. package/stdlib/sort/zsort_iface.go +480 -0
  110. package/stdlib/strconv/float.go +1699 -0
  111. package/stdlib/strconv/isprint.go +752 -0
  112. package/stdlib/strconv/quote.go +235 -50
  113. package/stdlib/strconv/strconv.go +616 -0
  114. package/stdlib/strings/builder.go +95 -0
  115. package/stdlib/strings/reader.go +163 -0
  116. package/stdlib/strings/replacer.go +102 -0
  117. package/stdlib/strings/strings.go +807 -0
  118. package/stdlib/sync/sync.go +268 -0
  119. package/stdlib/testing/async_js.go +61 -0
  120. package/stdlib/testing/benchmark.go +297 -0
  121. package/stdlib/testing/testing.go +523 -0
  122. package/stdlib/time/duration.go +233 -0
  123. package/stdlib/time/format.go +745 -0
  124. package/stdlib/time/parse.go +790 -0
  125. package/stdlib/time/sleep_js.go +63 -0
  126. package/stdlib/time/time.go +834 -0
  127. package/stdlib/unicode/unicode.go +141 -3
  128. package/stdlib/unicode/utf8/utf8.go +464 -0
  129. package/src/backend/js/fmt-runtime.js +0 -573
  130. package/src/backend/js/stdlib/base64.js +0 -12
  131. package/src/backend/js/stdlib/binary.js +0 -102
  132. package/src/backend/js/stdlib/bits.js +0 -119
  133. package/src/backend/js/stdlib/builder.js +0 -98
  134. package/src/backend/js/stdlib/bytes.js +0 -368
  135. package/src/backend/js/stdlib/errors.js +0 -45
  136. package/src/backend/js/stdlib/fmt.js +0 -517
  137. package/src/backend/js/stdlib/gom.js +0 -155
  138. package/src/backend/js/stdlib/hex.js +0 -17
  139. package/src/backend/js/stdlib/html.js +0 -20
  140. package/src/backend/js/stdlib/io.js +0 -137
  141. package/src/backend/js/stdlib/json.js +0 -1167
  142. package/src/backend/js/stdlib/maps.js +0 -37
  143. package/src/backend/js/stdlib/math.js +0 -85
  144. package/src/backend/js/stdlib/os.js +0 -13
  145. package/src/backend/js/stdlib/path.js +0 -48
  146. package/src/backend/js/stdlib/rand.js +0 -32
  147. package/src/backend/js/stdlib/regexp.js +0 -54
  148. package/src/backend/js/stdlib/sha256.js +0 -12
  149. package/src/backend/js/stdlib/shared.js +0 -17
  150. package/src/backend/js/stdlib/simd.js +0 -34
  151. package/src/backend/js/stdlib/slices.js +0 -107
  152. package/src/backend/js/stdlib/sort.js +0 -30
  153. package/src/backend/js/stdlib/strconv.js +0 -79
  154. package/src/backend/js/stdlib/strings.js +0 -301
  155. package/src/backend/js/stdlib/sync.js +0 -13
  156. package/src/backend/js/stdlib/testing.js +0 -41
  157. package/src/backend/js/stdlib/time.js +0 -152
  158. package/src/backend/js/stdlib/unicode.js +0 -25
  159. package/src/backend/js/stdlib/utf8.js +0 -26
  160. package/src/backend/wasm/stdlib/bytes.js +0 -6716
  161. package/src/backend/wasm/stdlib/core.js +0 -10278
  162. package/src/backend/wasm/stdlib/io.js +0 -3578
  163. package/src/backend/wasm/stdlib/packages.js +0 -2402
  164. package/src/backend/wasm/stdlib/pure.js +0 -720
  165. package/src/backend/wasm/stdlib/time.js +0 -2229
  166. package/src/typechecker/stdlib/core.js +0 -1079
  167. package/src/typechecker/stdlib/extended.js +0 -1210
  168. package/stdlib/bytes/iter.go +0 -192
  169. package/stdlib/bytes/search.go +0 -240
  170. package/stdlib/bytes/split.go +0 -218
  171. package/stdlib/errors/overlay.go +0 -12
  172. package/stdlib/maps/iter.go +0 -65
  173. package/stdlib/slices/extra.go +0 -73
  174. package/stdlib/slices/iter.go +0 -113
  175. package/stdlib/strings/extra.go +0 -35
  176. package/stdlib/strings/iter.go +0 -122
  177. package/stdlib/sync/extra.go +0 -105
  178. package/stdlib/unicode/utf8/bytes.go +0 -239
@@ -2,28 +2,17 @@
2
2
  // Pre-scan helpers: walk the lowered AST to decide which imports, runtime helpers
3
3
  // and interface dispatchers a module needs before emission starts.
4
4
 
5
- import { BINARY_MATH_IMPORTS } from "./stdlib/core.js";
5
+ import { prepareWasmNative } from "./natives.js";
6
6
  import {
7
7
  anonIfaceName,
8
8
  getInterfaceMethodList,
9
9
  getReceiverTypeName,
10
- isAnyType,
11
- isBoolGoType,
12
10
  isInterfaceType,
13
11
  isNonEmptyInterface,
14
- isTestingT,
12
+ printOperandKind,
15
13
  toWasmType,
16
14
  } from "./types.js";
17
15
 
18
- export function importsAnyOf(programs, pkgs) {
19
- const has = (p) => (pkgs instanceof Set ? pkgs.has(p) : pkgs.includes(p));
20
- return (programs ?? []).some((p) =>
21
- (p.imports ?? []).some((decl) =>
22
- (decl.imports ?? [decl]).some((imp) => has(imp.path)),
23
- ),
24
- );
25
- }
26
-
27
16
  export function hasErrorUsage(programs) {
28
17
  return (programs ?? []).some((p) => {
29
18
  let found = false;
@@ -64,28 +53,6 @@ export function _someNode(node, pred) {
64
53
  return false;
65
54
  }
66
55
 
67
- export function _usesTestingRun(node) {
68
- return _someNode(
69
- node,
70
- (n) =>
71
- n.kind === "CallExpr" &&
72
- n.func?.kind === "SelectorExpr" &&
73
- n.func.field === "Run" &&
74
- isTestingT(n.func.expr?._type),
75
- );
76
- }
77
-
78
- export function _usesTestingCleanup(node) {
79
- return _someNode(
80
- node,
81
- (n) =>
82
- n.kind === "CallExpr" &&
83
- n.func?.kind === "SelectorExpr" &&
84
- n.func.field === "Cleanup" &&
85
- isTestingT(n.func.expr?._type),
86
- );
87
- }
88
-
89
56
  // `__any_eq` is only needed when some `==`/`!=` (or switch case) compares
90
57
  // anyref operands, a struct with an anyref field is compared, or the error
91
58
  // runtime (`errors.Is`) is present. `_emitAnyEq` throws if this misses a
@@ -110,68 +77,12 @@ export function _usesAnyEq(funcDecls, mod) {
110
77
  );
111
78
  }
112
79
 
113
- // An `fmt` call with an `any`/interface operand or a `xs...` spread needs
114
- // `__any_fmt`. `emitFmtCall` falls back to scalar-only dispatch without it.
115
- export function _usesFmtAny(funcDecls, mod) {
116
- return funcDecls.some((fn) =>
117
- _someNode(
118
- fn.body,
119
- (n) =>
120
- n.kind === "CallExpr" &&
121
- n.func?.kind === "SelectorExpr" &&
122
- n.func.expr?.kind === "Ident" &&
123
- n.func.expr.name === "fmt" &&
124
- n.args?.some(
125
- (a) =>
126
- a._spread ||
127
- isAnyType(a._type, mod.checker) ||
128
- isInterfaceType(a._type, mod.checker),
129
- ),
130
- ),
131
- );
132
- }
133
-
134
- // Whether a value of this static type may print a 64-bit unsigned integer
135
- // (directly, inside a container or struct, or behind `any`/an interface).
136
- export function typeMayHoldU64(t, mod, seen = new Set(), depth = 0) {
137
- if (!t || typeof t !== "object" || seen.has(t) || depth > 8) return false;
138
- seen.add(t);
139
- if (isAnyType(t, mod.checker) || isInterfaceType(t, mod.checker)) return true;
140
- const sub = (x) => typeMayHoldU64(x, mod, seen, depth + 1);
141
- switch (t.kind) {
142
- case "basic":
143
- return t.name === "uint64" || t.name === "uint" || t.name === "uintptr";
144
- case "TypeName":
145
- case "Ident":
146
- return sub(
147
- mod.checker?.resolveType?.(t) ?? { kind: "basic", name: t.name },
148
- );
149
- case "named":
150
- return sub(t.underlying);
151
- case "tuple":
152
- return (t.types ?? []).some(sub);
153
- case "pointer":
154
- return sub(t.base);
155
- case "slice":
156
- case "array":
157
- return sub(t.elem);
158
- case "map":
159
- return sub(t.key) || sub(t.value);
160
- case "struct":
161
- return [...(t.fields?.values?.() ?? [])].some(sub);
162
- default:
163
- return false;
164
- }
165
- }
166
-
167
80
  export function _scanImportsInBody(node, mod) {
168
81
  if (!node || typeof node !== "object") return;
169
82
 
170
83
  if (node.kind === "CallExpr") {
171
84
  const { func, args } = node;
172
- if ((args ?? []).some((a) => a._spread || typeMayHoldU64(a._type, mod))) {
173
- mod.getFmtBoxU64Index();
174
- }
85
+ prepareWasmNative(mod, node);
175
86
  if (
176
87
  func.kind === "Ident" &&
177
88
  (func.name === "print" || func.name === "println")
@@ -181,69 +92,10 @@ export function _scanImportsInBody(node, mod) {
181
92
  mod.getPrintlnEmptyIndex();
182
93
  }
183
94
  for (let i = 0; i < args.length; i++) {
184
- const arg = args[i];
185
95
  const isLast = i === args.length - 1 && isPrintln;
186
- const isBool =
187
- isBoolGoType(arg._type) ||
188
- (arg.kind === "BasicLit" && arg.litKind === "BOOL");
189
- const wType = toWasmType(arg._type, mod.checker);
96
+ const { wType, isBool } = printOperandKind(args[i], mod.checker);
190
97
  mod.getLogImportIndex(wType, isLast, isBool);
191
- }
192
- } else if (
193
- func.kind === "SelectorExpr" &&
194
- func.expr?.name === "math" &&
195
- func.field
196
- ) {
197
- const jsMathMap = {
198
- Sin: "sin",
199
- Cos: "cos",
200
- Tan: "tan",
201
- Asin: "asin",
202
- Acos: "acos",
203
- Atan: "atan",
204
- Atan2: "atan2",
205
- Pow: "pow",
206
- Mod: "mod",
207
- Hypot: "hypot",
208
- Cbrt: "cbrt",
209
- Exp: "exp",
210
- Log: "log",
211
- Log1p: "log1p",
212
- Log2: "log2",
213
- Log10: "log10",
214
- Expm1: "expm1",
215
- Round: "round",
216
- Sinh: "sinh",
217
- Cosh: "cosh",
218
- Tanh: "tanh",
219
- };
220
- if (jsMathMap[func.field]) {
221
- const isBinary = BINARY_MATH_IMPORTS.has(jsMathMap[func.field]);
222
- mod.getMathImportIndex(jsMathMap[func.field], isBinary);
223
- }
224
- } else if (
225
- func.kind === "SelectorExpr" &&
226
- isTestingT(func.expr?._type) &&
227
- func.field
228
- ) {
229
- mod.internString(func.field);
230
- if (func.field === "Name") mod.getTestingNameImportIndex();
231
- else if (func.field === "Failed" || func.field === "Skipped")
232
- mod.getTestingFlagImportIndex();
233
- else if (func.field === "Run") {
234
- // closure type for func(*testing.T) must exist before bodies are emitted
235
- mod.getTestingRunClosureType();
236
- } else if (func.field === "Cleanup") {
237
- mod.getTestingCleanupClosureType();
238
- } else {
239
- mod.getTestingCallImportIndex();
240
- for (const arg of args) {
241
- mod.getTestingArgImportIndex(
242
- toWasmType(arg._type, mod.checker),
243
- isBoolGoType(arg._type) ||
244
- (arg.kind === "BasicLit" && arg.litKind === "BOOL"),
245
- );
246
- }
98
+ if (!isPrintln) mod.getPrintResetIndex();
247
99
  }
248
100
  }
249
101
  }
@@ -0,0 +1,89 @@
1
+ // src/backend/was./prune-funcs.js
2
+ // Drops function imports and defined functions that no export, element
3
+ // segment, global initialiser or start function reaches, and renumbers the function index space to match. Imports
4
+ // are registered eagerly because the table is locked before bodies are
5
+ // emitted, so most modules declare far more than they call.
6
+
7
+ const FUNC_REF_OPS = new Set(["call", "return_call", "ref.func"]);
8
+
9
+ const funcRef = (inst) => inst.funcIndex ?? inst.index ?? 0;
10
+
11
+ function funcRefsOf(insts, visit) {
12
+ for (const inst of insts ?? []) {
13
+ if (FUNC_REF_OPS.has(inst.op)) visit(funcRef(inst));
14
+ }
15
+ }
16
+
17
+ // Calls `visit` for every function the module reaches without a body: exports,
18
+ // element segments, global initialisers and the start function.
19
+ function forEachRoot(moduleIR, visit) {
20
+ for (const g of moduleIR.globals ?? []) funcRefsOf(g.init, visit);
21
+ for (const idx of moduleIR.elements ?? []) visit(idx);
22
+ for (const exp of moduleIR.exports ?? []) {
23
+ if (exp.kind === "func") visit(exp.index);
24
+ }
25
+ if (typeof moduleIR.start === "number") visit(moduleIR.start);
26
+ }
27
+
28
+ function remapInst(inst, remap) {
29
+ if (!FUNC_REF_OPS.has(inst.op)) return inst;
30
+ const next = remap[funcRef(inst)];
31
+ return "funcIndex" in inst
32
+ ? { ...inst, funcIndex: next }
33
+ : { ...inst, index: next };
34
+ }
35
+
36
+ // Marks every function index reachable from the roots through call bodies.
37
+ function reachableFuncs(moduleIR, count, total) {
38
+ const used = new Array(total).fill(false);
39
+ const work = [];
40
+ const mark = (idx) => {
41
+ if (idx < total && !used[idx]) {
42
+ used[idx] = true;
43
+ work.push(idx);
44
+ }
45
+ };
46
+ forEachRoot(moduleIR, mark);
47
+ while (work.length > 0) {
48
+ const idx = work.pop();
49
+ if (idx >= count) funcRefsOf(moduleIR.funcs[idx - count].body, mark);
50
+ }
51
+ return used;
52
+ }
53
+
54
+ function applyRemap(moduleIR, remap) {
55
+ for (const fn of moduleIR.funcs) {
56
+ fn.body = fn.body.map((inst) => remapInst(inst, remap));
57
+ }
58
+ for (const g of moduleIR.globals ?? []) {
59
+ if (g.init) g.init = g.init.map((inst) => remapInst(inst, remap));
60
+ }
61
+ if (moduleIR.elements) {
62
+ moduleIR.elements = moduleIR.elements.map((idx) => remap[idx]);
63
+ }
64
+ for (const exp of moduleIR.exports ?? []) {
65
+ if (exp.kind === "func") exp.index = remap[exp.index];
66
+ }
67
+ if (typeof moduleIR.start === "number") {
68
+ moduleIR.start = remap[moduleIR.start];
69
+ }
70
+ }
71
+
72
+ export function pruneUnusedFuncs(moduleIR) {
73
+ const imports = moduleIR.imports ?? [];
74
+ const funcImports = imports.filter((imp) => imp.kind === "func");
75
+ const count = funcImports.length;
76
+ const total = count + moduleIR.funcs.length;
77
+
78
+ const used = reachableFuncs(moduleIR, count, total);
79
+ if (used.every(Boolean)) return;
80
+
81
+ const remap = new Array(total);
82
+ let next = 0;
83
+ for (let i = 0; i < total; i++) if (used[i]) remap[i] = next++;
84
+
85
+ const dropped = new Set(funcImports.filter((_, i) => !used[i]));
86
+ moduleIR.imports = imports.filter((imp) => !dropped.has(imp));
87
+ moduleIR.funcs = moduleIR.funcs.filter((_, i) => used[count + i]);
88
+ applyRemap(moduleIR, remap);
89
+ }
@@ -1,38 +1,8 @@
1
1
  // src/backend/wasm/stdlib/index.js
2
2
  // Standard library emitters for the WebAssembly backend.
3
- //
4
- // Organized by domain:
5
- // - core.js: math, math/bits, maps, slices, strings, strconv, fmt, errors, sort, unicode/utf8, testing
6
- // - bytes.js: bytes package functions and bytes.Buffer methods
7
- // - pure.js: stateless utilities (unicode, math/rand, html, path/filepath)
8
- // - io.js: io constants/functions, readers/writers, fmt Fprint/Sscan, synthetic IO methods
9
- // - time.js: time package (Duration, Time struct, components, Format/Parse, clock)
10
3
 
11
- import { BytesEmitter } from "./bytes.js";
12
- import { BINARY_MATH_IMPORTS, StdlibCoreEmitter } from "./core.js";
13
- import { IoEmitter } from "./io.js";
14
- import { ExtendedPackagesEmitter } from "./packages.js";
15
- import { PurePackagesEmitter } from "./pure.js";
16
4
  import { SimdEmitter } from "./simd.js";
17
- import { TimeEmitter } from "./time.js";
18
5
 
19
- export {
20
- BINARY_MATH_IMPORTS,
21
- BytesEmitter,
22
- ExtendedPackagesEmitter,
23
- IoEmitter,
24
- PurePackagesEmitter,
25
- SimdEmitter,
26
- StdlibCoreEmitter,
27
- TimeEmitter,
28
- };
6
+ export { SimdEmitter };
29
7
 
30
- export const stdlibEmitters = [
31
- StdlibCoreEmitter,
32
- BytesEmitter,
33
- PurePackagesEmitter,
34
- IoEmitter,
35
- TimeEmitter,
36
- ExtendedPackagesEmitter,
37
- SimdEmitter,
38
- ];
8
+ export const stdlibEmitters = [SimdEmitter];
@@ -9,25 +9,6 @@ export { isComplex, isComplex64 };
9
9
  // Name of the runtime's error struct (`errors.New`, `fmt.Errorf`, strconv).
10
10
  export const ERROR_STRUCT = "__errorString";
11
11
 
12
- // `*testing.T` is a JS-side object (the harness's __GoFront_T) passed through
13
- // the boundary as an opaque externref.
14
- export function isTestingT(goType) {
15
- if (!goType) return false;
16
- const base =
17
- goType.kind === "pointer" || goType.kind === "PointerType"
18
- ? goType.base
19
- : goType.kind === "StarExpr"
20
- ? (goType.expr ?? goType.operand)
21
- : goType;
22
- const name = base?.name ?? (typeof base === "string" ? base : null);
23
- return (
24
- name === "testing.T" ||
25
- name === "testing.B" ||
26
- name === "testing.M" ||
27
- name === "testing.TB"
28
- );
29
- }
30
-
31
12
  export function toWasmType(goType, checker = null, mod = null) {
32
13
  if (!goType) return "i32";
33
14
  if (typeof goType === "string") {
@@ -47,7 +28,6 @@ export function toWasmType(goType, checker = null, mod = null) {
47
28
  if (typeof goType === "object" && goType.kind === "ref") {
48
29
  return goType;
49
30
  }
50
- if (isTestingT(goType)) return "externref";
51
31
 
52
32
  if (
53
33
  goType.kind === "simd" ||
@@ -427,6 +407,24 @@ export function goTypeKey(t) {
427
407
  if (t.kind === "map" || t.kind === "MapType") {
428
408
  return `map_${goTypeKey(t.key ?? t.keyType)}_${goTypeKey(t.value ?? t.val ?? t.elem ?? t.valueType ?? t.valType)}`;
429
409
  }
410
+ // Func types differ by signature (`[2]func()` is not `[2]func(int) bool`).
411
+ // A checker func lists the element type of a variadic parameter, like a
412
+ // parsed `FuncType`.
413
+ if (t.kind === "func" || t.kind === "FuncType") {
414
+ const parsed = t.kind === "FuncType";
415
+ const params = (t.params ?? []).map((p) => goTypeKey(parsed ? p.type : p));
416
+ const variadic = parsed ? t.params?.at(-1)?.variadic : t.variadic;
417
+ const rets = parsed
418
+ ? t.returnType
419
+ ? [t.returnType]
420
+ : []
421
+ : (t.returns ?? []).filter(
422
+ (r) => !(r?.kind === "basic" && r.name === "void"),
423
+ );
424
+ return `fn_${variadic ? "v" : ""}${params.join(",")}_${rets.map(goTypeKey).join(",")}`;
425
+ }
426
+ if (t.kind === "tuple" || t.kind === "TupleType")
427
+ return `tup_${(t.types ?? []).map(goTypeKey).join(",")}`;
430
428
  if (t.kind === "struct" || t.kind === "StructType") {
431
429
  if (t.name) return t.name;
432
430
  const rawFields = [];
@@ -500,6 +498,7 @@ function isReplaceableRef(goType, checker) {
500
498
  return (
501
499
  isSliceType(goType, checker) ||
502
500
  isMapType(goType, checker) ||
501
+ isComplex(goType) ||
503
502
  t?.kind === "pointer" ||
504
503
  t?.kind === "PointerType"
505
504
  );
@@ -542,6 +541,57 @@ export function isSliceType(goType, checker = null) {
542
541
  return goType.kind === "slice" || goType.kind === "SliceType";
543
542
  }
544
543
 
544
+ // Element kinds whose slices share one WebAssembly representation (a slice of
545
+ // i32, or of i64 for the 64-bit integers), so `[]bool`, `[]byte`, `[]rune` and
546
+ // `[]int` vs `[]int64` cannot be told apart by `ref.test`.
547
+ const TAGGED_SLICE_ELEMS = new Set([
548
+ "int",
549
+ "int64",
550
+ "uint",
551
+ "uint64",
552
+ "uintptr",
553
+ "bool",
554
+ "int8",
555
+ "int16",
556
+ "int32",
557
+ "uint8",
558
+ "uint16",
559
+ "uint32",
560
+ "byte",
561
+ "rune",
562
+ ]);
563
+
564
+ // True for a defined slice type, a slice of booleans or integers, or a slice
565
+ // of a method interface. In an
566
+ // interface such a value carries a per-Go-type tag (see getTaggedSliceType).
567
+ export function isTaggedSliceType(goType, checker = null) {
568
+ if (!isSliceType(goType, checker)) return false;
569
+ // A defined slice type (`type S []int`) is tagged whatever its element, so
570
+ // `case S` does not match an unnamed `[]int`.
571
+ const declared =
572
+ goType.kind === "TypeName" || goType.kind === "Ident"
573
+ ? (checker?.types?.get(goType.name) ?? goType)
574
+ : goType;
575
+ if (declared?.kind === "named") return true;
576
+ let elem = getSliceElemType(goType, checker);
577
+ // A source-level element (`uint8` in a case list) is a TypeName node.
578
+ for (let i = 0; i < 16 && elem; i++) {
579
+ if (elem.kind === "named")
580
+ elem = elem.underlying ?? checker?.types?.get(elem.name);
581
+ else if (elem.kind === "TypeName" || elem.kind === "Ident")
582
+ elem = checker?.types?.get(elem.name) ?? {
583
+ kind: "basic",
584
+ name: elem.name,
585
+ };
586
+ else break;
587
+ }
588
+ // A slice of a method interface (`[]error`) is a slice of `anyref` like
589
+ // `[]any`, and `[]fmt.Stringer` another one. `[]any` and `[]interface{}`
590
+ // are the same type, so neither is tagged.
591
+ if (isNonEmptyInterface(elem, checker)) return true;
592
+ return elem?.kind === "basic" && TAGGED_SLICE_ELEMS.has(elem.name);
593
+ }
594
+
545
595
  export function getSliceElemType(t, checker = null) {
546
596
  if (!t) return null;
547
597
  if (t.elem) return t.elem;
@@ -765,6 +815,33 @@ export function isSigned(goType) {
765
815
  return true;
766
816
  }
767
817
 
818
+ // How a builtin `print`/`println` operand is handed to the host. The host
819
+ // prints an i32 or i64 as signed, so a uint32 widens to i64 and a uint64 uses
820
+ // its own unsigned import (`wType` "u64").
821
+ export function printOperandKind(arg, checker) {
822
+ const isBool =
823
+ isBoolGoType(arg._type) ||
824
+ _namedBasic(arg._type) === "bool" ||
825
+ (arg.kind === "BasicLit" && arg.litKind === "BOOL");
826
+ let wType = toWasmType(arg._type, checker);
827
+ let widen = false;
828
+ if (!isBool && (wType === "i32" || wType === "i64") && !isSigned(arg._type)) {
829
+ if (wType === "i64") wType = "u64";
830
+ else if (_namedBasic(arg._type) === "uint32") {
831
+ wType = "i64";
832
+ widen = true;
833
+ }
834
+ }
835
+ return { wType, isBool, widen };
836
+ }
837
+
838
+ function _namedBasic(t) {
839
+ while (t && (t.kind === "named" || t.kind === "TypeName") && t.underlying) {
840
+ t = t.underlying;
841
+ }
842
+ return t?.name;
843
+ }
844
+
768
845
  export function isFloat(goType) {
769
846
  if (!goType) return false;
770
847
  if (goType.kind === "TypeName" || goType.kind === "Ident") {
@@ -13,9 +13,9 @@ function variadicFuncType(t) {
13
13
  return fn?.kind === "func" && fn.variadic ? fn : null;
14
14
  }
15
15
 
16
- // `fmt.Sprintf(...)` or a method of an emitter-backed type (`t.Errorf(...)`
17
- // on `*testing.T`): those emitters take the arguments unpacked. Emitter
18
- // types are the dotted names; GoFront and Go-source types are not.
16
+ // A call of a package member or a method of an emitter-backed type: those
17
+ // emitters take the arguments unpacked. Emitter types are the dotted names;
18
+ // GoFront and Go-source types are not.
19
19
  function isStdlibCall(call) {
20
20
  const f = call.func;
21
21
  if (f?.kind !== "SelectorExpr") return false;
@@ -26,13 +26,17 @@ function isStdlibCall(call) {
26
26
  }
27
27
 
28
28
  function packCallArgs(call) {
29
- if (call._multiForward || isStdlibCall(call)) return false;
29
+ // A call node can be reachable twice (`defer f(x)` lowers to a wrapper
30
+ // whose body lists the same call under `stmts` and `list`): pack it once.
31
+ if (call._variadicPacked || call._multiForward || isStdlibCall(call))
32
+ return false;
30
33
  const fn = variadicFuncType(call.func?._type);
31
34
  if (!fn) return false;
32
35
  const n = fn.params.length;
33
36
  const args = call.args ?? [];
34
37
  if (args.length === n && args[n - 1]?._spread) {
35
38
  args[n - 1]._spread = false;
39
+ call._variadicPacked = true;
36
40
  return true;
37
41
  }
38
42
  if (args.length < n - 1) return false;
@@ -62,6 +66,7 @@ function packCallArgs(call) {
62
66
  _col: call._col,
63
67
  },
64
68
  ];
69
+ call._variadicPacked = true;
65
70
  return true;
66
71
  }
67
72
 
@@ -45,11 +45,12 @@ export function runCompile(inputPath, isDir, options) {
45
45
  dumpAst = false,
46
46
  emitWat = false,
47
47
  wasmOpt = false,
48
+ natives = true,
48
49
  forceTarget = null,
49
50
  targetOverrides = null,
50
51
  rootDir = null,
51
52
  } = options ?? {};
52
- const targetOpts = {};
53
+ const targetOpts = { natives };
53
54
  if (forceTarget) targetOpts.forceTarget = forceTarget;
54
55
  if (targetOverrides) targetOpts.targetOverrides = targetOverrides;
55
56
  if (rootDir) targetOpts.rootDir = rootDir;
@@ -632,6 +633,7 @@ export function parseBuildArgs(argv) {
632
633
  const noWasmOpt = argv.includes("--no-wasm-opt");
633
634
  const wasmOpt = !noWasmOpt && (argv.includes("--wasm-opt") || release);
634
635
  const forceTarget = parseTargetFlag(argv);
636
+ const natives = !argv.includes("--no-natives");
635
637
 
636
638
  for (let i = 0; i < argv.length; i++) {
637
639
  const arg = argv[i];
@@ -656,6 +658,7 @@ export function parseBuildArgs(argv) {
656
658
  mangle: !noMangle && !noMinify,
657
659
  release,
658
660
  wasmOpt,
661
+ natives,
659
662
  forceTarget,
660
663
  };
661
664
  }
@@ -858,6 +861,7 @@ export async function handleBuild(targetDir = ".", options = {}) {
858
861
  sourceMap: options.sourceMap ?? false,
859
862
  emitWat: options.emitWat ?? false,
860
863
  wasmOpt: options.wasmOpt ?? false,
864
+ natives: options.natives ?? true,
861
865
  forceTarget: options.forceTarget ?? null,
862
866
  ...projectTargetOptions(project, options),
863
867
  });