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
@@ -0,0 +1,1615 @@
1
+ // src/backend/wasm/module-emitter.js
2
+ // ModuleEmitter: Module IR builder that registers types, imports, globals and functions.
3
+
4
+ import { wTypeKey } from "./boundary.js";
5
+ import { hasErrorUsage } from "./prescan.js";
6
+ import {
7
+ ERROR_STRUCT,
8
+ getFuncSignature,
9
+ getSliceElemType,
10
+ goTypeKey,
11
+ isScalarType,
12
+ isSliceType,
13
+ toWasmType,
14
+ } from "./types.js";
15
+
16
+ export class ModuleEmitter {
17
+ constructor(checker, lowerResult = null, programs = [], options = {}) {
18
+ this.checker = checker;
19
+ this.lowerResult = lowerResult;
20
+ // Local package names whose code is linked into this module; selectors
21
+ // like `mathx.NewVec3` are de-qualified to `NewVec3` by the emitter.
22
+ this.bundledPackages = options.bundledPackages ?? new Set();
23
+ this.natives = options.natives !== false; // replace native calls with host code
24
+ this.constCache = new Map(); // name -> BasicLit (package-level literal consts)
25
+ this.nonLiteralConsts = new Set(); // package-level consts the backend cannot evaluate yet
26
+
27
+ this.types = []; // type entries
28
+ this.typeCache = new Map(); // signature string -> index
29
+ this._openRec = null; // rec group receiving new types while structs are resolved
30
+
31
+ this.structTypes = new Map(); // name -> struct info
32
+ this.complexTypes = new Map(); // key -> complex struct info
33
+ this.ptrTypes = new Map(); // key -> ptr supertype info
34
+ this.boxTypes = new Map(); // wType key -> box info
35
+ this.ifaceBoxTypes = new Map(); // key -> iface box info
36
+ this.nilBoxTypes = new Map(); // key -> typed-nil marker info
37
+ this.arrayTypes = new Map(); // wType key -> array info
38
+ this.sliceTypes = new Map(); // wType key -> slice info
39
+ this.sliceTypesByIndex = new Map(); // typeIndex -> slice info
40
+ this.taggedSliceTypes = new Map(); // Go slice type key -> tagged info
41
+ this.mapTypes = new Map(); // wType key -> map info
42
+ this.mapTypesByIndex = new Map(); // typeIndex -> map info
43
+ this.mapBucketsType = null; // shared (array (mut i32))
44
+ this.closureTypes = new Map(); // key -> closure info
45
+ this.envTypes = new Map(); // key -> env info
46
+
47
+ this.elements = []; // declarative elements for ref.func
48
+ this.elementSet = new Set();
49
+
50
+ this.imports = []; // import entries
51
+ this.importCache = new Map(); // key -> func index
52
+
53
+ this.tags = []; // exception tags
54
+ this.globals = []; // global entries
55
+ this.globalCache = new Map(); // name -> index
56
+
57
+ this.funcs = []; // internal func definitions
58
+ this.funcMap = new Map(); // name -> global func index
59
+ this.funcParamTypes = new Map(); // name -> param types array
60
+ this.funcParamGoTypes = new Map(); // name -> param Go types array
61
+ this.funcReturnTypesByIdx = new Map(); // global func index -> result wasm types
62
+
63
+ this.exports = []; // export entries
64
+ this.stringTable = []; // string literals
65
+ this.stringCache = new Map();
66
+
67
+ // `errors.New` / `fmt.Errorf` / strconv parse failures allocate the
68
+ // runtime's own error struct; it joins the struct rec group so the
69
+ // `error.Error` dispatcher can `ref.test` it like any user type.
70
+ this.hasErrorType = hasErrorUsage(programs);
71
+
72
+ if (programs && programs.length > 0) {
73
+ this.initTypes(programs);
74
+ }
75
+
76
+ this._importsLocked = false;
77
+ this._initRuntimeImports();
78
+ }
79
+
80
+ // The runtime error struct is not declared in Go source: give the checker a
81
+ // named type with its method set (`Error() string`) so type assertions to
82
+ // `interface{ Error() string }` find it as a candidate.
83
+ _registerRuntimeErrorType() {
84
+ const types = this.checker?.types;
85
+ if (!types || types.has(ERROR_STRUCT)) return;
86
+ const STRING = { kind: "basic", name: "string" };
87
+ types.set(ERROR_STRUCT, {
88
+ kind: "named",
89
+ name: ERROR_STRUCT,
90
+ underlying: {
91
+ kind: "struct",
92
+ fields: new Map(),
93
+ methods: new Map([
94
+ ["Error", { kind: "func", params: [], returns: [STRING] }],
95
+ ]),
96
+ },
97
+ });
98
+ }
99
+
100
+ initTypes(programs) {
101
+ const progs = Array.isArray(programs) ? programs : [programs];
102
+ const rawStructs = [];
103
+ const seen = new Set();
104
+ const aliases = []; // qualified key (pkg.T) -> unqualified struct name
105
+
106
+ if (this.hasErrorType) {
107
+ this._registerRuntimeErrorType();
108
+ seen.add(ERROR_STRUCT);
109
+ rawStructs.push({
110
+ name: ERROR_STRUCT,
111
+ structType: {
112
+ kind: "struct",
113
+ fields: new Map([["msg", { kind: "basic", name: "string" }]]),
114
+ },
115
+ });
116
+ }
117
+
118
+ for (const p of progs) {
119
+ for (const d of p.decls ?? []) {
120
+ if (
121
+ d.kind === "TypeDecl" &&
122
+ !d.isAlias &&
123
+ d.type?.kind === "StructType"
124
+ ) {
125
+ if (!seen.has(d.name)) {
126
+ seen.add(d.name);
127
+ rawStructs.push({
128
+ name: d.name,
129
+ astFields: d.type.fields ?? [],
130
+ pkgName: p.pkg?.name ?? "main",
131
+ pkgTarget: p.target ?? "wasm",
132
+ });
133
+ }
134
+ }
135
+ }
136
+ }
137
+
138
+ if (this.checker?.types) {
139
+ for (const [key, t] of this.checker.types.entries()) {
140
+ if (t.kind === "named" && t.underlying?.kind === "struct") {
141
+ const name = t.name ?? key;
142
+ if (!seen.has(name)) {
143
+ seen.add(name);
144
+ rawStructs.push({ name, structType: t.underlying });
145
+ }
146
+ if (key !== name) aliases.push([key, name]);
147
+ }
148
+ }
149
+ }
150
+
151
+ if (rawStructs.length === 0) return;
152
+
153
+ // Field resolution may create array/slice/box types; they join this rec
154
+ // group so struct indices (base + i) stay valid and may be referenced
155
+ // from the auxiliary types in any order.
156
+ const base = this._getTotalTypeCount();
157
+ const structEntries = rawStructs.map(() => ({
158
+ form: "struct",
159
+ fields: [],
160
+ }));
161
+ this.types.push({ form: "rec", types: structEntries });
162
+ this._openRec = structEntries;
163
+
164
+ for (let i = 0; i < rawStructs.length; i++) {
165
+ const s = rawStructs[i];
166
+ this.structTypes.set(s.name, {
167
+ name: s.name,
168
+ typeIndex: base + i,
169
+ fields: [],
170
+ fieldIndexMap: new Map(),
171
+ embeds: [],
172
+ pkgName: s.pkgName ?? null,
173
+ pkgTarget: s.pkgTarget ?? "wasm",
174
+ });
175
+ }
176
+ // `mathx.Vec3` resolves to the same struct info as `Vec3` once linked.
177
+ for (const [key, name] of aliases) {
178
+ if (!this.structTypes.has(key))
179
+ this.structTypes.set(key, this.structTypes.get(name));
180
+ }
181
+
182
+ for (let i = 0; i < rawStructs.length; i++) {
183
+ const s = rawStructs[i];
184
+ const info = this.structTypes.get(s.name);
185
+ const resolvedStruct =
186
+ s.structType ?? this.checker?.types?.get(s.name)?.underlying;
187
+
188
+ const fields = [];
189
+ const fieldIndexMap = new Map();
190
+ const embeds = [];
191
+
192
+ if (s.astFields && s.astFields.length > 0) {
193
+ let fIdx = 0;
194
+ for (const f of s.astFields) {
195
+ const names = f.embedded
196
+ ? [
197
+ (
198
+ f.embeddedName ??
199
+ (f.type?.kind === "PointerType"
200
+ ? f.type.base?.name
201
+ : f.type?.name)
202
+ )
203
+ ?.split(".")
204
+ .pop(),
205
+ ]
206
+ : f.names?.length
207
+ ? f.names
208
+ : f.name
209
+ ? [f.name]
210
+ : [];
211
+ for (const n of names) {
212
+ const wType = toWasmType(f.type, this.checker, this);
213
+ fields.push({
214
+ name: n,
215
+ goType: f.type,
216
+ wType,
217
+ embedded: Boolean(f.embedded),
218
+ });
219
+ fieldIndexMap.set(n, fIdx);
220
+ if (f.embedded) {
221
+ // The embedded type, when it differs from the field
222
+ // name (`Mutex$$sync` for the field `Mutex`).
223
+ const ref =
224
+ f.type?.kind === "PointerType"
225
+ ? f.type.base?.name
226
+ : f.type?.name;
227
+ const typeName = this.checker?.types?.get(ref)?.name;
228
+ embeds.push({
229
+ name: n,
230
+ ...(typeName && typeName !== n && { typeName }),
231
+ fieldIndex: fIdx,
232
+ });
233
+ }
234
+ fIdx++;
235
+ }
236
+ }
237
+ } else if (resolvedStruct?.fields) {
238
+ let fIdx = 0;
239
+ // `fields` also holds promoted fields; the declared fields
240
+ // (`_structFields`) give the real layout of a defined struct.
241
+ const declared = resolvedStruct._structFields
242
+ ? resolvedStruct._structFields.flatMap((sf) => {
243
+ if (!sf.embedded) return sf.names.map((n) => [n, sf.type]);
244
+ const inner = sf.type.kind === "pointer" ? sf.type.base : sf.type;
245
+ const n = (inner?.name ?? "").split(".").pop();
246
+ return n ? [[n, sf.type]] : [];
247
+ })
248
+ : [...resolvedStruct.fields.entries()];
249
+ for (const [fName, fType] of declared) {
250
+ const wType = toWasmType(fType, this.checker, this);
251
+ fields.push({ name: fName, goType: fType, wType });
252
+ fieldIndexMap.set(fName, fIdx);
253
+ fIdx++;
254
+ }
255
+ if (resolvedStruct?._embeds) {
256
+ for (const embed of resolvedStruct._embeds) {
257
+ const inner = embed.kind === "pointer" ? embed.base : embed;
258
+ const embedName = inner?.kind === "named" ? inner.name : null;
259
+ if (embedName && fieldIndexMap.has(embedName)) {
260
+ embeds.push({
261
+ name: embedName,
262
+ fieldIndex: fieldIndexMap.get(embedName),
263
+ });
264
+ }
265
+ }
266
+ }
267
+ }
268
+
269
+ info.fields = fields;
270
+ info.fieldIndexMap = fieldIndexMap;
271
+ info.embeds = embeds;
272
+
273
+ const typeEntry = structEntries[i];
274
+ typeEntry.fields = fields.map((f) => ({ type: f.wType, mutable: true }));
275
+ info.typeEntry = typeEntry;
276
+ }
277
+
278
+ this._openRec = null;
279
+ }
280
+
281
+ _pushType(typeEntry) {
282
+ (this._openRec ?? this.types).push(typeEntry);
283
+ }
284
+
285
+ getStructType(name) {
286
+ return this.structTypes.get(name) ?? null;
287
+ }
288
+
289
+ getAnonStructType(fields) {
290
+ const key = fields.map((f) => `${f.name}:${goTypeKey(f.goType)}`).join(";");
291
+ const name = `__anon_${key.replace(/[^a-zA-Z0-9_]/g, "_")}`;
292
+ if (this.structTypes.has(name)) {
293
+ return this.structTypes.get(name);
294
+ }
295
+ const fieldWTypes = fields.map((f) =>
296
+ toWasmType(f.goType, this.checker, this),
297
+ );
298
+ const typeIndex = this._getTotalTypeCount();
299
+ const typeEntry = {
300
+ form: "struct",
301
+ fields: fieldWTypes.map((t) => ({ type: t, mutable: true })),
302
+ };
303
+ this._pushType(typeEntry);
304
+ const info = {
305
+ name,
306
+ typeIndex,
307
+ typeEntry,
308
+ fields: fields.map((f, i) => ({
309
+ name: f.name,
310
+ wType: fieldWTypes[i],
311
+ goType: f.goType,
312
+ fieldIndex: i,
313
+ })),
314
+ fieldIndexMap: new Map(fields.map((f, i) => [f.name, i])),
315
+ embeds: [],
316
+ pkgName: null,
317
+ pkgTarget: "wasm",
318
+ isAnon: true,
319
+ };
320
+ this.structTypes.set(name, info);
321
+ return info;
322
+ }
323
+
324
+ getComplexType(name = "complex128") {
325
+ const key = name === "complex64" ? "complex64" : "complex128";
326
+ if (this.complexTypes.has(key)) {
327
+ return this.complexTypes.get(key);
328
+ }
329
+ const isF32 = key === "complex64";
330
+ const elemWType = isF32 ? "f32" : "f64";
331
+ const elemGoType = { kind: "basic", name: isF32 ? "float32" : "float64" };
332
+ const typeName = `$${key}`;
333
+ const typeIndex = this._getTotalTypeCount();
334
+ const typeEntry = {
335
+ form: "struct",
336
+ name: typeName,
337
+ // Mutable so the facade's per-field setters for an exported struct
338
+ // that holds a complex value validate.
339
+ fields: [
340
+ { type: elemWType, mutable: true },
341
+ { type: elemWType, mutable: true },
342
+ ],
343
+ };
344
+ this._pushType(typeEntry);
345
+ const fields = [
346
+ { name: "re", fieldIndex: 0, wType: elemWType, goType: elemGoType },
347
+ { name: "im", fieldIndex: 1, wType: elemWType, goType: elemGoType },
348
+ ];
349
+ const info = {
350
+ name: typeName,
351
+ typeIndex,
352
+ typeEntry,
353
+ fields,
354
+ fieldIndexMap: new Map([
355
+ ["re", 0],
356
+ ["im", 1],
357
+ ]),
358
+ embeds: [],
359
+ pkgName: null,
360
+ pkgTarget: "wasm",
361
+ isComplex: true,
362
+ elemWType,
363
+ elemGoType,
364
+ };
365
+ this.complexTypes.set(key, info);
366
+ this.structTypes.set(key, info);
367
+ this.structTypes.set(typeName, info);
368
+ return info;
369
+ }
370
+
371
+ getPtrType(goType) {
372
+ const key = goTypeKey(goType);
373
+ if (this.ptrTypes.has(key)) {
374
+ return this.ptrTypes.get(key);
375
+ }
376
+ const typeIndex = this._getTotalTypeCount();
377
+ const name = `$ptr_${key}`;
378
+ const typeEntry = {
379
+ form: "struct",
380
+ name,
381
+ sub: true,
382
+ fields: [],
383
+ };
384
+ this._pushType(typeEntry);
385
+ const ptrInfo = { key, name, typeIndex, typeEntry, goType };
386
+ this.ptrTypes.set(key, ptrInfo);
387
+ return ptrInfo;
388
+ }
389
+
390
+ getBoxType(goType) {
391
+ const key = goTypeKey(goType);
392
+ if (this.boxTypes.has(key)) {
393
+ return this.boxTypes.get(key);
394
+ }
395
+ const ptrInfo = this.getPtrType(goType);
396
+ const wType = toWasmType(goType, this.checker, this);
397
+ const typeIndex = this._getTotalTypeCount();
398
+ const name = `$box_${key}`;
399
+ const typeEntry = {
400
+ form: "struct",
401
+ name,
402
+ sub: true,
403
+ superType: ptrInfo.name,
404
+ superTypeIndex: ptrInfo.typeIndex,
405
+ fields: [{ type: wType, mutable: true }],
406
+ };
407
+ this._pushType(typeEntry);
408
+ const boxInfo = {
409
+ key,
410
+ name,
411
+ typeIndex,
412
+ typeEntry,
413
+ ptrType: ptrInfo,
414
+ ptrTypeIndex: ptrInfo.typeIndex,
415
+ wType,
416
+ goType,
417
+ };
418
+ this.boxTypes.set(key, boxInfo);
419
+ return boxInfo;
420
+ }
421
+
422
+ getIfaceBoxType(goType) {
423
+ const key = goTypeKey(goType);
424
+ if (this.ifaceBoxTypes.has(key)) {
425
+ return this.ifaceBoxTypes.get(key);
426
+ }
427
+ const wType = toWasmType(goType, this.checker, this);
428
+ const tagIndex = this.ifaceBoxTypes.size;
429
+ const tagTypeIndex = this._getTotalTypeCount();
430
+ const tagFields = [];
431
+ for (let i = 0; i < tagIndex; i++) {
432
+ tagFields.push({ type: "i32", mutable: false });
433
+ }
434
+ this._pushType({
435
+ form: "struct",
436
+ fields: tagFields,
437
+ });
438
+ const typeIndex = this._getTotalTypeCount();
439
+ const typeEntry = {
440
+ form: "struct",
441
+ fields: [
442
+ { type: wType, mutable: true },
443
+ {
444
+ type: {
445
+ kind: "ref",
446
+ nullable: true,
447
+ typeIndex: tagTypeIndex,
448
+ },
449
+ mutable: false,
450
+ },
451
+ ],
452
+ };
453
+ this._pushType(typeEntry);
454
+ const boxInfo = { key, typeIndex, typeEntry, wType, goType, tagTypeIndex };
455
+ this.ifaceBoxTypes.set(key, boxInfo);
456
+ return boxInfo;
457
+ }
458
+
459
+ // The value a nil map, func or `*T` takes inside an interface: a marker
460
+ // object per Go type, so `any(nilMap) != nil` and a type switch still sees
461
+ // the dynamic type. Struct types are structural, so each marker gets a
462
+ // distinct field count to keep them apart.
463
+ getNilBoxType(goType) {
464
+ const key = goTypeKey(goType);
465
+ if (this.nilBoxTypes.has(key)) return this.nilBoxTypes.get(key);
466
+ const n = this.nilBoxTypes.size + 1;
467
+ const typeIndex = this._getTotalTypeCount();
468
+ this._pushType({
469
+ form: "struct",
470
+ fields: Array.from({ length: n }, () => ({
471
+ type: "i32",
472
+ mutable: true,
473
+ })),
474
+ });
475
+ const globalIndex = this.globals.length;
476
+ this.globals.push({
477
+ type: { kind: "ref", nullable: true, typeIndex },
478
+ mutable: false,
479
+ init: [
480
+ ...Array.from({ length: n }, () => ({ op: "i32.const", value: 0 })),
481
+ { op: "struct.new", typeIndex },
482
+ ],
483
+ });
484
+ const info = { key, goType, typeIndex, globalIndex };
485
+ this.nilBoxTypes.set(key, info);
486
+ return info;
487
+ }
488
+
489
+ getArrayType(elemGoType) {
490
+ const elemWType = toWasmType(elemGoType, this.checker, this);
491
+ const key =
492
+ typeof elemWType === "string" ? elemWType : JSON.stringify(elemWType);
493
+ if (this.arrayTypes.has(key)) {
494
+ return this.arrayTypes.get(key);
495
+ }
496
+ const typeIndex = this._getTotalTypeCount();
497
+ const typeEntry = {
498
+ form: "array",
499
+ elemType: elemWType,
500
+ mutable: true,
501
+ };
502
+ this._pushType(typeEntry);
503
+ const arrInfo = { typeIndex, typeEntry, elemWType, elemGoType };
504
+ this.arrayTypes.set(key, arrInfo);
505
+ return arrInfo;
506
+ }
507
+
508
+ getSliceType(elemGoType) {
509
+ const elemWType = toWasmType(elemGoType, this.checker, this);
510
+ const key =
511
+ typeof elemWType === "string" ? elemWType : JSON.stringify(elemWType);
512
+ if (this.sliceTypes.has(key)) {
513
+ return this.sliceTypes.get(key);
514
+ }
515
+ const arrInfo = this.getArrayType(elemGoType);
516
+ const typeIndex = this._getTotalTypeCount();
517
+ const typeEntry = {
518
+ form: "struct",
519
+ // Slice types are open: each defined slice type, and each Go type among
520
+ // the 32-bit-or-smaller elements, gets a tagged subtype when it
521
+ // reaches an interface.
522
+ sub: true,
523
+ fields: [
524
+ {
525
+ type: {
526
+ kind: "ref",
527
+ nullable: true,
528
+ typeIndex: arrInfo.typeIndex,
529
+ },
530
+ mutable: false,
531
+ },
532
+ { type: "i32", mutable: false }, // offset
533
+ { type: "i32", mutable: false }, // len
534
+ { type: "i32", mutable: false }, // cap
535
+ ],
536
+ };
537
+ this._pushType(typeEntry);
538
+
539
+ const emptyGlobalIndex = this.globals.length;
540
+ this.globals.push({
541
+ type: {
542
+ kind: "ref",
543
+ nullable: true,
544
+ typeIndex,
545
+ },
546
+ mutable: false,
547
+ init: [
548
+ { op: "ref.null", heapType: arrInfo.typeIndex },
549
+ { op: "i32.const", value: 0 },
550
+ { op: "i32.const", value: 0 },
551
+ { op: "i32.const", value: 0 },
552
+ { op: "struct.new", typeIndex },
553
+ ],
554
+ });
555
+
556
+ const sliceInfo = {
557
+ typeIndex,
558
+ typeEntry,
559
+ arrInfo,
560
+ elemWType,
561
+ elemGoType,
562
+ emptyGlobalIndex,
563
+ };
564
+ this.sliceTypes.set(key, sliceInfo);
565
+ this.sliceTypesByIndex.set(typeIndex, sliceInfo);
566
+ return sliceInfo;
567
+ }
568
+
569
+ // The subtype of the slice struct a value of Go slice type `sliceGoType`
570
+ // takes inside an interface. Slices of booleans and small integers share
571
+ // one WebAssembly type, so the extra tag field (a nullable ref to a struct
572
+ // with one immutable i32 field per tag index) is what tells `[]byte` from
573
+ // `[]bool` and `json.RawMessage` from `[]byte` in a type test.
574
+ getTaggedSliceType(sliceGoType, elemGoType) {
575
+ const key = goTypeKey(sliceGoType);
576
+ const known = this.taggedSliceTypes.get(key);
577
+ if (known) return known;
578
+ const base = this.getSliceType(elemGoType);
579
+ const tagIndex = this.taggedSliceTypes.size + 1;
580
+ const tagTypeIndex = this._getTotalTypeCount();
581
+ this._pushType({
582
+ form: "struct",
583
+ fields: Array.from({ length: tagIndex }, () => ({
584
+ type: "i32",
585
+ mutable: false,
586
+ })),
587
+ });
588
+ const typeIndex = this._getTotalTypeCount();
589
+ this._pushType({
590
+ form: "struct",
591
+ sub: true,
592
+ superTypeIndex: base.typeIndex,
593
+ fields: [
594
+ ...base.typeEntry.fields,
595
+ {
596
+ type: { kind: "ref", nullable: true, typeIndex: tagTypeIndex },
597
+ mutable: false,
598
+ },
599
+ ],
600
+ });
601
+ const info = { key, typeIndex, tagTypeIndex, base, goType: sliceGoType };
602
+ this.taggedSliceTypes.set(key, info);
603
+ return info;
604
+ }
605
+
606
+ getSliceTypeByIndex(typeIndex) {
607
+ return this.sliceTypesByIndex?.get(typeIndex) ?? null;
608
+ }
609
+
610
+ getMapType(keyGoType, valGoType) {
611
+ const keyWType = toWasmType(keyGoType, this.checker, this);
612
+ const valWType = toWasmType(valGoType, this.checker, this);
613
+ // Vector keys share one wasm type but compare differently (floats vs
614
+ // mask words), so the Go type is part of the key.
615
+ const keyPart =
616
+ keyWType === "v128" ? `v128_${goTypeKey(keyGoType)}` : wTypeKey(keyWType);
617
+ const key = `${keyPart}:${wTypeKey(valWType)}`;
618
+ if (this.mapTypes.has(key)) {
619
+ return this.mapTypes.get(key);
620
+ }
621
+
622
+ if (!this.mapBucketsType) {
623
+ const typeIndex = this._getTotalTypeCount();
624
+ this._pushType({
625
+ form: "array",
626
+ elemType: "i32",
627
+ mutable: true,
628
+ });
629
+ this.mapBucketsType = { typeIndex };
630
+ }
631
+
632
+ const entryTypeIndex = this._getTotalTypeCount();
633
+ this._pushType({
634
+ form: "struct",
635
+ fields: [
636
+ { type: keyWType, mutable: true }, // 0: key
637
+ { type: valWType, mutable: true }, // 1: val
638
+ { type: "i32", mutable: true }, // 2: next (bucket chain)
639
+ { type: "i32", mutable: true }, // 3: order_prev (insertion order)
640
+ { type: "i32", mutable: true }, // 4: order_next (insertion order)
641
+ { type: "i32", mutable: true }, // 5: active (1 = active, 0 = deleted)
642
+ ],
643
+ });
644
+
645
+ const entriesTypeIndex = this._getTotalTypeCount();
646
+ this._pushType({
647
+ form: "array",
648
+ elemType: { kind: "ref", nullable: true, typeIndex: entryTypeIndex },
649
+ mutable: true,
650
+ });
651
+
652
+ const mapTypeIndex = this._getTotalTypeCount();
653
+ this._pushType({
654
+ form: "struct",
655
+ fields: [
656
+ {
657
+ type: {
658
+ kind: "ref",
659
+ nullable: true,
660
+ typeIndex: this.mapBucketsType.typeIndex,
661
+ },
662
+ mutable: true,
663
+ }, // 0: buckets
664
+ {
665
+ type: {
666
+ kind: "ref",
667
+ nullable: true,
668
+ typeIndex: entriesTypeIndex,
669
+ },
670
+ mutable: true,
671
+ }, // 1: entries
672
+ { type: "i32", mutable: true }, // 2: len
673
+ { type: "i32", mutable: true }, // 3: cap
674
+ { type: "i32", mutable: true }, // 4: count
675
+ { type: "i32", mutable: true }, // 5: head
676
+ { type: "i32", mutable: true }, // 6: tail
677
+ { type: "i32", mutable: true }, // 7: free_head
678
+ { type: "i32", mutable: true }, // 8: num_buckets
679
+ ],
680
+ });
681
+
682
+ const suffix = key.replace(/[^a-zA-Z0-9_]/g, "_");
683
+ const mapInfo = {
684
+ key,
685
+ suffix,
686
+ typeIndex: mapTypeIndex,
687
+ entryTypeIndex,
688
+ entriesTypeIndex,
689
+ bucketsTypeIndex: this.mapBucketsType.typeIndex,
690
+ keyWType,
691
+ valWType,
692
+ keyGoType,
693
+ valGoType,
694
+ wType: { kind: "ref", nullable: true, typeIndex: mapTypeIndex },
695
+ makeFuncName: `__map_make_${suffix}`,
696
+ getFuncName: `__map_get_${suffix}`,
697
+ getOkFuncName: `__map_get_ok_${suffix}`,
698
+ setFuncName: `__map_set_${suffix}`,
699
+ deleteFuncName: `__map_delete_${suffix}`,
700
+ lenFuncName: `__map_len_${suffix}`,
701
+ clearFuncName: `__map_clear_${suffix}`,
702
+ keysFuncName: `__map_keys_${suffix}`,
703
+ valuesFuncName: `__map_values_${suffix}`,
704
+ };
705
+ this.mapTypes.set(key, mapInfo);
706
+ this.mapTypesByIndex.set(mapTypeIndex, mapInfo);
707
+ return mapInfo;
708
+ }
709
+
710
+ getClosureType(goType) {
711
+ const sig = getFuncSignature(goType, this.checker);
712
+ const paramWTypes = sig.params
713
+ .map((p) => toWasmType(p, this.checker, this))
714
+ .filter(Boolean);
715
+ const returnWTypes = sig.returns
716
+ .map((r) => toWasmType(r, this.checker, this))
717
+ .filter(Boolean);
718
+
719
+ const key = `${paramWTypes.map((p) => (typeof p === "object" ? JSON.stringify(p) : p)).join(",")}=>${returnWTypes.map((r) => (typeof r === "object" ? JSON.stringify(r) : r)).join(",")}`;
720
+ if (this.closureTypes.has(key)) {
721
+ return this.closureTypes.get(key);
722
+ }
723
+
724
+ // 1. Function signature type index: (param anyref, ...paramWTypes) -> (...returnWTypes)
725
+ const funcTypeIndex = this.getTypeIndex(
726
+ ["anyref", ...paramWTypes],
727
+ returnWTypes,
728
+ );
729
+
730
+ // 2. Closure struct type index: struct { fn: (ref funcTypeIndex), env: anyref }
731
+ const typeIndex = this._getTotalTypeCount();
732
+ const typeEntry = {
733
+ form: "struct",
734
+ fields: [
735
+ {
736
+ type: {
737
+ kind: "ref",
738
+ nullable: false,
739
+ typeIndex: funcTypeIndex,
740
+ },
741
+ mutable: false,
742
+ },
743
+ {
744
+ type: "anyref",
745
+ mutable: true,
746
+ },
747
+ ],
748
+ };
749
+ this._pushType(typeEntry);
750
+
751
+ const closureInfo = {
752
+ typeIndex,
753
+ typeEntry,
754
+ funcTypeIndex,
755
+ paramWTypes,
756
+ returnWTypes,
757
+ sig,
758
+ };
759
+ this.closureTypes.set(key, closureInfo);
760
+ return closureInfo;
761
+ }
762
+
763
+ getEnvType(fieldTypes) {
764
+ const key = fieldTypes
765
+ .map((f) =>
766
+ typeof f.wType === "object" ? JSON.stringify(f.wType) : f.wType,
767
+ )
768
+ .join(",");
769
+ if (this.envTypes.has(key)) {
770
+ return this.envTypes.get(key);
771
+ }
772
+ const typeIndex = this._getTotalTypeCount();
773
+ const typeEntry = {
774
+ form: "struct",
775
+ fields: fieldTypes.map((f) => ({ type: f.wType, mutable: true })),
776
+ };
777
+ this._pushType(typeEntry);
778
+ const envInfo = { typeIndex, typeEntry, fieldTypes };
779
+ this.envTypes.set(key, envInfo);
780
+ return envInfo;
781
+ }
782
+
783
+ addRefFuncElement(funcIdx) {
784
+ if (!this.elementSet.has(funcIdx)) {
785
+ this.elementSet.add(funcIdx);
786
+ this.elements.push(funcIdx);
787
+ }
788
+ }
789
+
790
+ _getTotalTypeCount() {
791
+ let count = 0;
792
+ for (const entry of this.types) {
793
+ if (entry.form === "rec") count += entry.types.length;
794
+ else count += 1;
795
+ }
796
+ return count;
797
+ }
798
+
799
+ _initRuntimeImports() {
800
+ this._initPanicTag();
801
+ this._initPanicGlobal();
802
+ this.getPanicImportIndex(); // env.panic
803
+ this.internString("runtime error: index out of range");
804
+ this.internString("runtime error: slice bounds out of range");
805
+ this.internString("assignment to entry in nil map");
806
+
807
+ // String built-ins
808
+ this.getStringImportIndex(); // env.str
809
+ this.getStringLenImportIndex(); // env.str_len
810
+ this.getStringConcatImportIndex(); // env.str_concat
811
+ this.getStringCmpImportIndex("=="); // env.str_eq
812
+ this.getStringCmpImportIndex("!="); // env.str_ne
813
+ this.getStringCmpImportIndex("<"); // env.str_lt
814
+ this.getStringCmpImportIndex("<="); // env.str_le
815
+ this.getStringCmpImportIndex(">"); // env.str_gt
816
+ this.getStringCmpImportIndex(">="); // env.str_ge
817
+ this.getStringGetImportIndex(); // env.str_get
818
+ this.getStringSliceImportIndex(); // env.str_slice
819
+ this.getStringFromCodePointImportIndex(); // env.str_from_code_point
820
+ this.getStringCodePointAtImportIndex(); // env.str_code_point_at
821
+ this.getStringHashImportIndex(); // env.str_hash
822
+ this.getStrFromI64ImportIndex();
823
+ this.getStrFromI32ImportIndex();
824
+ this.getStrFromF64ImportIndex();
825
+ this.getIsStringImportIndex(); // env.is_string
826
+ this.getStrToBytesImportIndex();
827
+ this.getStrByteAtImportIndex();
828
+ this.getBytesToStrInitImportIndex();
829
+ this.getBytesToStrPushImportIndex();
830
+ this.getBytesToStrFinishImportIndex();
831
+
832
+ // Logging built-ins
833
+ this.getPrintlnEmptyIndex(); // env.println_empty
834
+ const logTypes = ["i32", "i64", "f32", "f64", "externref", "anyref"];
835
+ for (const t of logTypes) {
836
+ this.getLogImportIndex(t, false, false); // env.print_*
837
+ this.getLogImportIndex(t, true, false); // env.println_*
838
+ }
839
+ this.getLogImportIndex("i32", false, true); // env.print_bool
840
+ this.getLogImportIndex("i32", true, true); // env.println_bool
841
+ }
842
+
843
+ _initPanicTag() {
844
+ // Type: (externref) -> ()
845
+ const typeIdx = this.getTypeIndex(["externref"], []);
846
+ // Tag 0
847
+ this.tags.push({ typeIndex: typeIdx });
848
+ // Export tag 0 as "panicTag"
849
+ this.exports.push({ name: "panicTag", kind: "tag", index: 0 });
850
+ }
851
+
852
+ getPanicNodeTypeIndex() {
853
+ if (this.panicNodeTypeIndex !== undefined) {
854
+ return this.panicNodeTypeIndex;
855
+ }
856
+ const typeIndex = this._getTotalTypeCount();
857
+ const typeEntry = {
858
+ form: "rec",
859
+ types: [
860
+ {
861
+ form: "struct",
862
+ fields: [
863
+ {
864
+ type: "anyref",
865
+ mutable: true,
866
+ },
867
+ {
868
+ type: "i32",
869
+ mutable: true,
870
+ },
871
+ {
872
+ type: {
873
+ kind: "ref",
874
+ nullable: true,
875
+ typeIndex,
876
+ },
877
+ mutable: true,
878
+ },
879
+ ],
880
+ },
881
+ ],
882
+ };
883
+ this._pushType(typeEntry);
884
+ this.panicNodeTypeIndex = typeIndex;
885
+ return typeIndex;
886
+ }
887
+
888
+ _initPanicGlobal() {
889
+ const pType = this.getPanicNodeTypeIndex();
890
+ const gIdx = this.globals.length;
891
+ this.globals.push({
892
+ type: { kind: "ref", nullable: true, typeIndex: pType },
893
+ mutable: true,
894
+ init: [{ op: "ref.null", typeIndex: pType }],
895
+ });
896
+ this.panicGlobalIndex = gIdx;
897
+ this.exports.push({ name: "__panic", kind: "global", index: gIdx });
898
+ }
899
+
900
+ getPanicGlobalIndex() {
901
+ return this.panicGlobalIndex;
902
+ }
903
+
904
+ // `__deferArmed` is set to 1 immediately before a deferred closure is
905
+ // invoked and cleared by that closure's prologue. recover() only takes
906
+ // effect when the current frame observed the flag, which gives Go's
907
+ // "recover must be called directly by the deferred function" rule.
908
+ getDeferArmedGlobalIndex() {
909
+ if (this.deferArmedGlobalIndex !== undefined) {
910
+ return this.deferArmedGlobalIndex;
911
+ }
912
+ const gIdx = this.globals.length;
913
+ this.globals.push({
914
+ type: "i32",
915
+ mutable: true,
916
+ init: [{ op: "i32.const", value: 0 }],
917
+ });
918
+ this.deferArmedGlobalIndex = gIdx;
919
+ return gIdx;
920
+ }
921
+
922
+ getDeferNodeTypeIndex() {
923
+ if (this.deferNodeTypeIndex !== undefined) {
924
+ return this.deferNodeTypeIndex;
925
+ }
926
+ const deferClosureSig = { kind: "Signature", params: [], results: [] };
927
+ const deferClosureInfo = this.getClosureType(deferClosureSig);
928
+ const typeIndex = this._getTotalTypeCount();
929
+ const typeEntry = {
930
+ form: "rec",
931
+ types: [
932
+ {
933
+ form: "struct",
934
+ fields: [
935
+ {
936
+ type: {
937
+ kind: "ref",
938
+ nullable: true,
939
+ typeIndex: deferClosureInfo.typeIndex,
940
+ },
941
+ mutable: false,
942
+ },
943
+ {
944
+ type: {
945
+ kind: "ref",
946
+ nullable: true,
947
+ typeIndex,
948
+ },
949
+ mutable: true,
950
+ },
951
+ ],
952
+ },
953
+ ],
954
+ };
955
+ this._pushType(typeEntry);
956
+ this.deferNodeTypeIndex = typeIndex;
957
+ return typeIndex;
958
+ }
959
+
960
+ getTypeIndex(params, results) {
961
+ const key = `${params.map((p) => (typeof p === "object" ? JSON.stringify(p) : p)).join(",")}=>${results.map((r) => (typeof r === "object" ? JSON.stringify(r) : r)).join(",")}`;
962
+ if (this.typeCache.has(key)) {
963
+ return this.typeCache.get(key);
964
+ }
965
+ const idx = this._getTotalTypeCount();
966
+ this._pushType({ form: "func", params, results });
967
+ this.typeCache.set(key, idx);
968
+ return idx;
969
+ }
970
+
971
+ internString(str) {
972
+ if (this.stringCache.has(str)) {
973
+ return this.stringCache.get(str);
974
+ }
975
+ const idx = this.stringTable.length;
976
+ this.stringTable.push(str);
977
+ this.stringCache.set(str, idx);
978
+ return idx;
979
+ }
980
+
981
+ getOrAddFuncImport(module, name, params, results) {
982
+ const key = `${module}.${name}`;
983
+ if (this.importCache.has(key)) {
984
+ return this.importCache.get(key);
985
+ }
986
+ if (this._importsLocked) {
987
+ throw new Error(`Unexpected import added after lock: ${key}`);
988
+ }
989
+ const typeIndex = this.getTypeIndex(params, results);
990
+ const globalFuncIdx = this.imports.filter((i) => i.kind === "func").length;
991
+ this.imports.push({
992
+ module,
993
+ name,
994
+ kind: "func",
995
+ typeIndex,
996
+ });
997
+ this.importCache.set(key, globalFuncIdx);
998
+ return globalFuncIdx;
999
+ }
1000
+
1001
+ getStringImportIndex() {
1002
+ return this.getOrAddFuncImport("env", "str", ["i32"], ["externref"]);
1003
+ }
1004
+
1005
+ // Panics are raised by a JS import that throws a plain `Error`, so wasm
1006
+ // frames unwind without a wasm `throw` and the JS caller needs no
1007
+ // try/catch guard around exports. (V8 cannot inline JS→wasm calls made
1008
+ // inside a try block, and a non-inlined call boxes every float argument.)
1009
+ getPanicImportIndex() {
1010
+ return this.getOrAddFuncImport("env", "panic", ["externref"], []);
1011
+ }
1012
+
1013
+ getBoundsPanicFuncIndex() {
1014
+ return this.resolveFuncIndex("__bounds_panic");
1015
+ }
1016
+
1017
+ getIndexPanicFuncIndex() {
1018
+ return this.resolveFuncIndex("__index_panic");
1019
+ }
1020
+
1021
+ getNilPanicFuncIndex() {
1022
+ return this.resolveFuncIndex("__nil_panic");
1023
+ }
1024
+
1025
+ // `gofront/shared` buffers are `(base i32, len i32)` views over the single
1026
+ // exported linear memory, bump-allocated by `__shared_new` during start.
1027
+ getSharedType() {
1028
+ if (this.sharedType) return this.sharedType;
1029
+ const typeIndex = this._getTotalTypeCount();
1030
+ const typeEntry = {
1031
+ form: "struct",
1032
+ fields: [
1033
+ { type: "i32", mutable: false }, // byte base
1034
+ { type: "i32", mutable: false }, // element count
1035
+ ],
1036
+ };
1037
+ this._pushType(typeEntry);
1038
+ this.memory = { min: 1 };
1039
+ this.exports.push({ name: "memory", kind: "memory", index: 0 });
1040
+ const topGlobalIndex = this.globals.length;
1041
+ this.globals.push({
1042
+ type: "i32",
1043
+ mutable: true,
1044
+ init: [{ op: "i32.const", value: 0 }],
1045
+ });
1046
+ const wType = { kind: "ref", nullable: true, typeIndex };
1047
+ this.sharedType = { typeIndex, typeEntry, wType, topGlobalIndex };
1048
+ return this.sharedType;
1049
+ }
1050
+
1051
+ getSliceBoundsPanicFuncIndex() {
1052
+ return this.resolveFuncIndex("__slice_bounds_panic");
1053
+ }
1054
+
1055
+ getPrintlnEmptyIndex() {
1056
+ return this.getOrAddFuncImport("env", "println_empty", [], []);
1057
+ }
1058
+
1059
+ getPrintResetIndex() {
1060
+ return this.getOrAddFuncImport("env", "print_reset", [], []);
1061
+ }
1062
+
1063
+ getLogImportIndex(wType, isLast = false, isBool = false) {
1064
+ const prefix = isLast ? "println" : "print";
1065
+ if (isBool) {
1066
+ return this.getOrAddFuncImport("env", `${prefix}_bool`, ["i32"], []);
1067
+ }
1068
+ switch (wType) {
1069
+ case "i32":
1070
+ return this.getOrAddFuncImport("env", `${prefix}_i32`, ["i32"], []);
1071
+ case "i64":
1072
+ return this.getOrAddFuncImport("env", `${prefix}_i64`, ["i64"], []);
1073
+ case "u64":
1074
+ return this.getOrAddFuncImport("env", `${prefix}_u64`, ["i64"], []);
1075
+ case "f32":
1076
+ return this.getOrAddFuncImport("env", `${prefix}_f32`, ["f32"], []);
1077
+ case "f64":
1078
+ return this.getOrAddFuncImport("env", `${prefix}_f64`, ["f64"], []);
1079
+ case "externref":
1080
+ return this.getOrAddFuncImport(
1081
+ "env",
1082
+ `${prefix}_str`,
1083
+ ["externref"],
1084
+ [],
1085
+ );
1086
+ default:
1087
+ if (typeof wType === "object") {
1088
+ return this.getOrAddFuncImport(
1089
+ "env",
1090
+ `${prefix}_any`,
1091
+ ["anyref"],
1092
+ [],
1093
+ );
1094
+ }
1095
+ return this.getOrAddFuncImport("env", `${prefix}_i32`, ["i32"], []);
1096
+ }
1097
+ }
1098
+
1099
+ getStringLenImportIndex() {
1100
+ return this.getOrAddFuncImport("env", "str_len", ["externref"], ["i32"]);
1101
+ }
1102
+
1103
+ getStringConcatImportIndex() {
1104
+ return this.getOrAddFuncImport(
1105
+ "env",
1106
+ "str_concat",
1107
+ ["externref", "externref"],
1108
+ ["externref"],
1109
+ );
1110
+ }
1111
+
1112
+ getStringCmpImportIndex(op) {
1113
+ const opMap = {
1114
+ "==": "str_eq",
1115
+ "!=": "str_ne",
1116
+ "<": "str_lt",
1117
+ "<=": "str_le",
1118
+ ">": "str_gt",
1119
+ ">=": "str_ge",
1120
+ };
1121
+ const fn = opMap[op] ?? "str_eq";
1122
+ return this.getOrAddFuncImport(
1123
+ "env",
1124
+ fn,
1125
+ ["externref", "externref"],
1126
+ ["i32"],
1127
+ );
1128
+ }
1129
+
1130
+ getStringGetImportIndex() {
1131
+ return this.getOrAddFuncImport(
1132
+ "env",
1133
+ "str_get",
1134
+ ["externref", "i32"],
1135
+ ["i32"],
1136
+ );
1137
+ }
1138
+
1139
+ getStringSliceImportIndex() {
1140
+ return this.getOrAddFuncImport(
1141
+ "env",
1142
+ "str_slice",
1143
+ ["externref", "i32", "i32"],
1144
+ ["externref"],
1145
+ );
1146
+ }
1147
+
1148
+ getStringFromCodePointImportIndex() {
1149
+ return this.getOrAddFuncImport(
1150
+ "env",
1151
+ "str_from_code_point",
1152
+ ["i32"],
1153
+ ["externref"],
1154
+ );
1155
+ }
1156
+
1157
+ getStringCodePointAtImportIndex() {
1158
+ return this.getOrAddFuncImport(
1159
+ "env",
1160
+ "str_code_point_at",
1161
+ ["externref", "i32"],
1162
+ ["i32"],
1163
+ );
1164
+ }
1165
+
1166
+ getIsStringImportIndex() {
1167
+ return this.getOrAddFuncImport("env", "is_string", ["anyref"], ["i32"]);
1168
+ }
1169
+
1170
+ getStringHashImportIndex() {
1171
+ return this.getOrAddFuncImport("env", "str_hash", ["externref"], ["i32"]);
1172
+ }
1173
+
1174
+ getStrFromI64ImportIndex() {
1175
+ return this.getOrAddFuncImport(
1176
+ "env",
1177
+ "str_from_i64",
1178
+ ["i64"],
1179
+ ["externref"],
1180
+ );
1181
+ }
1182
+
1183
+ getStrFromI32ImportIndex() {
1184
+ return this.getOrAddFuncImport(
1185
+ "env",
1186
+ "str_from_i32",
1187
+ ["i32"],
1188
+ ["externref"],
1189
+ );
1190
+ }
1191
+
1192
+ getStrFromF64ImportIndex() {
1193
+ return this.getOrAddFuncImport(
1194
+ "env",
1195
+ "str_from_f64",
1196
+ ["f64"],
1197
+ ["externref"],
1198
+ );
1199
+ }
1200
+
1201
+ getStrToBytesImportIndex() {
1202
+ return this.getOrAddFuncImport(
1203
+ "env",
1204
+ "str_to_bytes",
1205
+ ["externref"],
1206
+ ["i32"],
1207
+ );
1208
+ }
1209
+
1210
+ getStrByteAtImportIndex() {
1211
+ return this.getOrAddFuncImport("env", "str_byte_at", ["i32"], ["i32"]);
1212
+ }
1213
+
1214
+ getBytesToStrInitImportIndex() {
1215
+ return this.getOrAddFuncImport("env", "bytes_to_str_init", ["i32"], []);
1216
+ }
1217
+
1218
+ getBytesToStrPushImportIndex() {
1219
+ return this.getOrAddFuncImport("env", "bytes_to_str_push", ["i32"], []);
1220
+ }
1221
+
1222
+ getBytesToStrFinishImportIndex() {
1223
+ return this.getOrAddFuncImport(
1224
+ "env",
1225
+ "bytes_to_str_finish",
1226
+ [],
1227
+ ["externref"],
1228
+ );
1229
+ }
1230
+
1231
+ getMathImportIndex(jsName, isBinary = false) {
1232
+ const params = isBinary ? ["f64", "f64"] : ["f64"];
1233
+ return this.getOrAddFuncImport("Math", jsName, params, ["f64"]);
1234
+ }
1235
+
1236
+ getMathRandomImportIndex() {
1237
+ return this.getOrAddFuncImport("Math", "random", [], ["f64"]);
1238
+ }
1239
+
1240
+ resolveFuncIndex(name) {
1241
+ return this.funcMap.get(name) ?? null;
1242
+ }
1243
+
1244
+ getFuncParamTypes(name) {
1245
+ return this.funcParamTypes.get(name) ?? [];
1246
+ }
1247
+
1248
+ getFuncParamGoTypes(name) {
1249
+ return this.funcParamGoTypes.get(name) ?? [];
1250
+ }
1251
+
1252
+ resolveGlobal(name) {
1253
+ return this.globalCache.get(name) ?? null;
1254
+ }
1255
+
1256
+ resolveConst(name) {
1257
+ return this.constCache.get(name) ?? null;
1258
+ }
1259
+
1260
+ isNonLiteralConst(name) {
1261
+ return this.nonLiteralConsts.has(name);
1262
+ }
1263
+
1264
+ // Interface method dispatch is a `ref.test` chain over every concrete type
1265
+ // that declares `methodName` (no itabs); the static interface type is not
1266
+ // needed to build the candidate list.
1267
+ findInterfaceCandidates(_ifaceType, methodName) {
1268
+ const candidates = [];
1269
+ const seenKeys = new Set();
1270
+
1271
+ // 1. Struct types
1272
+ for (const [sName, sInfo] of this.structTypes.entries()) {
1273
+ const directMethodName = `${sName}.${methodName}`;
1274
+ if (this.funcMap.has(directMethodName)) {
1275
+ const fIdx = this.resolveFuncIndex(directMethodName);
1276
+ const paramGoTypes = this.getFuncParamGoTypes(directMethodName);
1277
+ const paramWTypes = this.getFuncParamTypes(directMethodName);
1278
+ const recvGoType = paramGoTypes[0];
1279
+ const isPtrRecv = recvGoType?.kind === "pointer";
1280
+
1281
+ if (isPtrRecv) {
1282
+ const key = `ptr:${sName}`;
1283
+ if (!seenKeys.has(key)) {
1284
+ seenKeys.add(key);
1285
+ candidates.push({
1286
+ recvTypeName: sName,
1287
+ testTypeIndex: sInfo.typeIndex,
1288
+ isBoxedValue: false,
1289
+ needsValueDeref: false,
1290
+ embedPath: null,
1291
+ funcIndex: fIdx,
1292
+ targetParamTypes: paramWTypes,
1293
+ structInfo: sInfo,
1294
+ });
1295
+ }
1296
+ } else {
1297
+ // Value receiver: both *sName and sName match
1298
+ const ptrKey = `ptr:${sName}`;
1299
+ if (!seenKeys.has(ptrKey)) {
1300
+ seenKeys.add(ptrKey);
1301
+ candidates.push({
1302
+ recvTypeName: sName,
1303
+ testTypeIndex: sInfo.typeIndex,
1304
+ isBoxedValue: false,
1305
+ needsValueDeref: true,
1306
+ embedPath: null,
1307
+ funcIndex: fIdx,
1308
+ targetParamTypes: paramWTypes,
1309
+ targetRecvWType: paramWTypes[0],
1310
+ structInfo: sInfo,
1311
+ });
1312
+ }
1313
+ const box = this.getIfaceBoxType({ kind: "named", name: sName });
1314
+ const valKey = `val:${sName}`;
1315
+ if (box && !seenKeys.has(valKey)) {
1316
+ seenKeys.add(valKey);
1317
+ candidates.push({
1318
+ recvTypeName: sName,
1319
+ testTypeIndex: box.typeIndex,
1320
+ isBoxedValue: true,
1321
+ needsValueDeref: false,
1322
+ embedPath: null,
1323
+ funcIndex: fIdx,
1324
+ targetParamTypes: paramWTypes,
1325
+ targetRecvWType: paramWTypes[0],
1326
+ structInfo: sInfo,
1327
+ });
1328
+ }
1329
+ }
1330
+ }
1331
+
1332
+ // Promoted methods on sName
1333
+ if (sInfo.embeds && sInfo.embeds.length > 0) {
1334
+ for (const embed of sInfo.embeds) {
1335
+ const embedType = embed.typeName ?? embed.name;
1336
+ const embedMethodName = `${embedType}.${methodName}`;
1337
+ if (this.funcMap.has(embedMethodName)) {
1338
+ const fIdx = this.resolveFuncIndex(embedMethodName);
1339
+ const paramGoTypes = this.getFuncParamGoTypes(embedMethodName);
1340
+ const paramWTypes = this.getFuncParamTypes(embedMethodName);
1341
+ const isPtrRecv = paramGoTypes[0]?.kind === "pointer";
1342
+ const embedSInfo = this.getStructType(embedType);
1343
+
1344
+ const ptrKey = `ptr:${sName}`;
1345
+ if (!seenKeys.has(ptrKey)) {
1346
+ seenKeys.add(ptrKey);
1347
+ candidates.push({
1348
+ recvTypeName: sName,
1349
+ testTypeIndex: sInfo.typeIndex,
1350
+ isBoxedValue: false,
1351
+ needsValueDeref: !isPtrRecv,
1352
+ embedPath: [
1353
+ {
1354
+ parentTypeIndex: sInfo.typeIndex,
1355
+ fieldIndex: embed.fieldIndex,
1356
+ },
1357
+ ],
1358
+ funcIndex: fIdx,
1359
+ targetParamTypes: paramWTypes,
1360
+ targetRecvWType: paramWTypes[0],
1361
+ structInfo: embedSInfo,
1362
+ });
1363
+ }
1364
+
1365
+ if (!isPtrRecv) {
1366
+ const box = this.getIfaceBoxType({ kind: "named", name: sName });
1367
+ const valKey = `val:${sName}`;
1368
+ if (box && !seenKeys.has(valKey)) {
1369
+ seenKeys.add(valKey);
1370
+ candidates.push({
1371
+ recvTypeName: sName,
1372
+ testTypeIndex: box.typeIndex,
1373
+ isBoxedValue: true,
1374
+ needsValueDeref: false,
1375
+ embedPath: [
1376
+ {
1377
+ parentTypeIndex: sInfo.typeIndex,
1378
+ fieldIndex: embed.fieldIndex,
1379
+ },
1380
+ ],
1381
+ funcIndex: fIdx,
1382
+ targetParamTypes: paramWTypes,
1383
+ targetRecvWType: paramWTypes[0],
1384
+ structInfo: embedSInfo,
1385
+ });
1386
+ }
1387
+ }
1388
+ }
1389
+ }
1390
+ }
1391
+ }
1392
+
1393
+ // 2. Named non-struct types
1394
+ for (const [fnName, fIdx] of this.funcMap.entries()) {
1395
+ if (fnName.endsWith(`.${methodName}`)) {
1396
+ const rName = fnName.slice(0, fnName.length - methodName.length - 1);
1397
+ if (!this.structTypes.has(rName)) {
1398
+ const rType = this.checker?.types?.get(rName) ?? {
1399
+ kind: "named",
1400
+ name: rName,
1401
+ };
1402
+ const paramGoTypes = this.getFuncParamGoTypes(fnName);
1403
+ const paramWTypes = this.getFuncParamTypes(fnName);
1404
+ const isPtrRecv = paramGoTypes[0]?.kind === "pointer";
1405
+ const isSlice = isSliceType(rType, this.checker);
1406
+
1407
+ if (isSlice) {
1408
+ const elemType = getSliceElemType(rType, this.checker);
1409
+ const sliceInfo = this.getSliceType(elemType);
1410
+ const ptrIfaceBox = this.getIfaceBoxType({
1411
+ kind: "pointer",
1412
+ base: rType,
1413
+ });
1414
+ const box = this.getBoxType(rType);
1415
+ if (isPtrRecv) {
1416
+ const ptrKey = `ptr:${rName}`;
1417
+ if (ptrIfaceBox && !seenKeys.has(ptrKey)) {
1418
+ seenKeys.add(ptrKey);
1419
+ candidates.push({
1420
+ recvTypeName: rName,
1421
+ testTypeIndex: ptrIfaceBox.typeIndex,
1422
+ isBoxedValue: true,
1423
+ needsValueDeref: false,
1424
+ embedPath: null,
1425
+ funcIndex: fIdx,
1426
+ targetParamTypes: paramWTypes,
1427
+ });
1428
+ }
1429
+ } else {
1430
+ const ptrKey = `ptr:${rName}`;
1431
+ if (ptrIfaceBox && box && !seenKeys.has(ptrKey)) {
1432
+ seenKeys.add(ptrKey);
1433
+ candidates.push({
1434
+ recvTypeName: rName,
1435
+ testTypeIndex: ptrIfaceBox.typeIndex,
1436
+ isBoxedValue: true,
1437
+ nilPtrPanic: true,
1438
+ needsValueDeref: false,
1439
+ embedPath: [
1440
+ {
1441
+ castTypeIndex: box.typeIndex,
1442
+ parentTypeIndex: box.typeIndex,
1443
+ fieldIndex: 0,
1444
+ },
1445
+ ],
1446
+ funcIndex: fIdx,
1447
+ targetParamTypes: paramWTypes,
1448
+ });
1449
+ }
1450
+ const key = `slice:${rName}`;
1451
+ if (sliceInfo && !seenKeys.has(key)) {
1452
+ seenKeys.add(key);
1453
+ candidates.push({
1454
+ recvTypeName: rName,
1455
+ testTypeIndex: sliceInfo.typeIndex,
1456
+ isBoxedValue: false,
1457
+ needsValueDeref: false,
1458
+ embedPath: null,
1459
+ funcIndex: fIdx,
1460
+ targetParamTypes: paramWTypes,
1461
+ });
1462
+ }
1463
+ }
1464
+ } else {
1465
+ const box = isPtrRecv
1466
+ ? this.getIfaceBoxType({ kind: "pointer", base: rType })
1467
+ : this.getIfaceBoxType(rType);
1468
+ const key = `${isPtrRecv ? "ptr" : "named"}:${rName}`;
1469
+ if (box && !seenKeys.has(key)) {
1470
+ seenKeys.add(key);
1471
+ candidates.push({
1472
+ recvTypeName: rName,
1473
+ testTypeIndex: box.typeIndex,
1474
+ isBoxedValue: true,
1475
+ needsValueDeref: false,
1476
+ embedPath: null,
1477
+ funcIndex: fIdx,
1478
+ targetParamTypes: paramWTypes,
1479
+ });
1480
+ }
1481
+ if (!isPtrRecv)
1482
+ this._addPtrScalarCandidate(
1483
+ candidates,
1484
+ seenKeys,
1485
+ rName,
1486
+ rType,
1487
+ fIdx,
1488
+ paramWTypes,
1489
+ );
1490
+ }
1491
+ }
1492
+ }
1493
+ }
1494
+
1495
+ return candidates;
1496
+ }
1497
+
1498
+ // A value-receiver method of a named scalar is also in the method set of
1499
+ // the pointer: `var g Getter = &x` holds a pointer box whose target is read
1500
+ // before the call.
1501
+ _addPtrScalarCandidate(
1502
+ candidates,
1503
+ seenKeys,
1504
+ rName,
1505
+ rType,
1506
+ fIdx,
1507
+ paramWTypes,
1508
+ ) {
1509
+ if (!isScalarType(rType, this.checker, this)) return;
1510
+ const ptrBox = this.getIfaceBoxType({ kind: "pointer", base: rType });
1511
+ const target = this.getBoxType(rType);
1512
+ const ptrKey = `ptr:${rName}`;
1513
+ if (!ptrBox || seenKeys.has(ptrKey)) return;
1514
+ seenKeys.add(ptrKey);
1515
+ candidates.push({
1516
+ recvTypeName: rName,
1517
+ testTypeIndex: ptrBox.typeIndex,
1518
+ isBoxedValue: true,
1519
+ nilPtrPanic: true,
1520
+ needsValueDeref: false,
1521
+ embedPath: [
1522
+ {
1523
+ castTypeIndex: target.typeIndex,
1524
+ parentTypeIndex: target.typeIndex,
1525
+ fieldIndex: 0,
1526
+ },
1527
+ ],
1528
+ funcIndex: fIdx,
1529
+ targetParamTypes: paramWTypes,
1530
+ });
1531
+ }
1532
+
1533
+ getTypesImplementingInterface(ifaceType) {
1534
+ const candidates = [];
1535
+ const seen = new Set();
1536
+ const ifaceResolved =
1537
+ this.checker?.resolveType?.(
1538
+ ifaceType?.kind === "named" ? ifaceType.underlying : ifaceType,
1539
+ ) ?? ifaceType;
1540
+
1541
+ // 1. Struct types
1542
+ for (const [sName, sInfo] of this.structTypes.entries()) {
1543
+ const namedType = this.checker?.types?.get(sName) ?? {
1544
+ kind: "named",
1545
+ name: sName,
1546
+ };
1547
+ const ptrType = { kind: "pointer", base: namedType };
1548
+
1549
+ // Check pointer to struct
1550
+ if (this.checker?.implements?.(ptrType, ifaceResolved)) {
1551
+ if (!seen.has(sInfo.typeIndex)) {
1552
+ seen.add(sInfo.typeIndex);
1553
+ // A typed nil `*S` is a marker object, which also satisfies it.
1554
+ candidates.push({
1555
+ name: `*${sName}`,
1556
+ typeIndex: sInfo.typeIndex,
1557
+ nilTypeIndex: this.getNilBoxType(ptrType).typeIndex,
1558
+ });
1559
+ }
1560
+ }
1561
+
1562
+ // Check struct value (boxed)
1563
+ if (this.checker?.implements?.(namedType, ifaceResolved)) {
1564
+ const box = this.getIfaceBoxType(namedType);
1565
+ if (box && !seen.has(box.typeIndex)) {
1566
+ seen.add(box.typeIndex);
1567
+ candidates.push({ name: sName, typeIndex: box.typeIndex });
1568
+ }
1569
+ }
1570
+ }
1571
+
1572
+ // 2. Named non-struct types that have methods
1573
+ if (this.checker?.types) {
1574
+ for (const [tName, tVal] of this.checker.types.entries()) {
1575
+ if (
1576
+ tVal?.kind === "named" &&
1577
+ tVal.underlying?.kind !== "struct" &&
1578
+ tVal.underlying?.kind !== "interface"
1579
+ ) {
1580
+ if (this.checker?.implements?.(tVal, ifaceResolved)) {
1581
+ const box = this.getIfaceBoxType(tVal);
1582
+ if (box && !seen.has(box.typeIndex)) {
1583
+ seen.add(box.typeIndex);
1584
+ candidates.push({ name: tName, typeIndex: box.typeIndex });
1585
+ }
1586
+ }
1587
+ this._addPtrNamedImplementor(
1588
+ tVal,
1589
+ tName,
1590
+ ifaceResolved,
1591
+ seen,
1592
+ candidates,
1593
+ );
1594
+ }
1595
+ }
1596
+ }
1597
+
1598
+ return candidates;
1599
+ }
1600
+
1601
+ // `*T` of a named scalar or slice is boxed apart from `T`.
1602
+ _addPtrNamedImplementor(tVal, tName, ifaceResolved, seen, candidates) {
1603
+ const ptrType = { kind: "pointer", base: tVal };
1604
+ if (
1605
+ !isScalarType(tVal, this.checker, this) &&
1606
+ !isSliceType(tVal, this.checker)
1607
+ )
1608
+ return;
1609
+ if (!this.checker?.implements?.(ptrType, ifaceResolved)) return;
1610
+ const box = this.getIfaceBoxType(ptrType);
1611
+ if (!box || seen.has(box.typeIndex)) return;
1612
+ seen.add(box.typeIndex);
1613
+ candidates.push({ name: `*${tName}`, typeIndex: box.typeIndex });
1614
+ }
1615
+ }