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
@@ -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" ||
@@ -542,6 +522,47 @@ export function isSliceType(goType, checker = null) {
542
522
  return goType.kind === "slice" || goType.kind === "SliceType";
543
523
  }
544
524
 
525
+ // Element kinds whose slices share one WebAssembly representation (a slice of
526
+ // i32), so `[]bool`, `[]byte` and `[]rune` cannot be told apart by `ref.test`.
527
+ const TAGGED_SLICE_ELEMS = new Set([
528
+ "bool",
529
+ "int8",
530
+ "int16",
531
+ "int32",
532
+ "uint8",
533
+ "uint16",
534
+ "uint32",
535
+ "byte",
536
+ "rune",
537
+ ]);
538
+
539
+ // True for a defined slice type or a slice of booleans or integers of at
540
+ // most 32 bits. In an
541
+ // interface such a value carries a per-Go-type tag (see getTaggedSliceType).
542
+ export function isTaggedSliceType(goType, checker = null) {
543
+ if (!isSliceType(goType, checker)) return false;
544
+ // A defined slice type (`type S []int`) is tagged whatever its element, so
545
+ // `case S` does not match an unnamed `[]int`.
546
+ const declared =
547
+ goType.kind === "TypeName" || goType.kind === "Ident"
548
+ ? (checker?.types?.get(goType.name) ?? goType)
549
+ : goType;
550
+ if (declared?.kind === "named") return true;
551
+ let elem = getSliceElemType(goType, checker);
552
+ // A source-level element (`uint8` in a case list) is a TypeName node.
553
+ for (let i = 0; i < 16 && elem; i++) {
554
+ if (elem.kind === "named")
555
+ elem = elem.underlying ?? checker?.types?.get(elem.name);
556
+ else if (elem.kind === "TypeName" || elem.kind === "Ident")
557
+ elem = checker?.types?.get(elem.name) ?? {
558
+ kind: "basic",
559
+ name: elem.name,
560
+ };
561
+ else break;
562
+ }
563
+ return elem?.kind === "basic" && TAGGED_SLICE_ELEMS.has(elem.name);
564
+ }
565
+
545
566
  export function getSliceElemType(t, checker = null) {
546
567
  if (!t) return null;
547
568
  if (t.elem) return t.elem;
@@ -765,6 +786,33 @@ export function isSigned(goType) {
765
786
  return true;
766
787
  }
767
788
 
789
+ // How a builtin `print`/`println` operand is handed to the host. The host
790
+ // prints an i32 or i64 as signed, so a uint32 widens to i64 and a uint64 uses
791
+ // its own unsigned import (`wType` "u64").
792
+ export function printOperandKind(arg, checker) {
793
+ const isBool =
794
+ isBoolGoType(arg._type) ||
795
+ _namedBasic(arg._type) === "bool" ||
796
+ (arg.kind === "BasicLit" && arg.litKind === "BOOL");
797
+ let wType = toWasmType(arg._type, checker);
798
+ let widen = false;
799
+ if (!isBool && (wType === "i32" || wType === "i64") && !isSigned(arg._type)) {
800
+ if (wType === "i64") wType = "u64";
801
+ else if (_namedBasic(arg._type) === "uint32") {
802
+ wType = "i64";
803
+ widen = true;
804
+ }
805
+ }
806
+ return { wType, isBool, widen };
807
+ }
808
+
809
+ function _namedBasic(t) {
810
+ while (t && (t.kind === "named" || t.kind === "TypeName") && t.underlying) {
811
+ t = t.underlying;
812
+ }
813
+ return t?.name;
814
+ }
815
+
768
816
  export function isFloat(goType) {
769
817
  if (!goType) return false;
770
818
  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;
@@ -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
  });