gofront 1.4.0 → 1.6.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 (77) hide show
  1. package/README.md +174 -403
  2. package/package.json +16 -19
  3. package/src/{codegen → backend/js}/expressions.js +265 -77
  4. package/src/{codegen → backend/js}/index.js +120 -89
  5. package/src/{codegen → backend/js}/runtime.js +102 -0
  6. package/src/{codegen → backend/js}/source-map.js +32 -0
  7. package/src/{codegen → backend/js}/statements.js +139 -64
  8. package/src/backend/js/stdlib/bits.js +32 -0
  9. package/src/{codegen → backend/js}/stdlib/errors.js +2 -2
  10. package/src/{codegen → backend/js}/stdlib/fmt.js +26 -8
  11. package/src/{codegen → backend/js}/stdlib/index.js +6 -0
  12. package/src/{codegen → backend/js}/stdlib/maps.js +1 -1
  13. package/src/{codegen → backend/js}/stdlib/path.js +2 -2
  14. package/src/backend/js/stdlib/shared.js +17 -0
  15. package/src/{codegen → backend/js}/stdlib/slices.js +1 -1
  16. package/src/{codegen → backend/js}/stdlib/sort.js +1 -1
  17. package/src/backend/js/stdlib/strconv.js +58 -0
  18. package/src/{codegen → backend/js}/stdlib/strings.js +4 -1
  19. package/src/{codegen → backend/js}/stdlib/testing.js +2 -2
  20. package/src/{codegen → backend/js}/stdlib/time.js +3 -3
  21. package/src/{codegen → backend/js}/templ.js +3 -3
  22. package/src/backend/wasm/boundary.js +1798 -0
  23. package/src/backend/wasm/emit-builtins.js +1103 -0
  24. package/src/backend/wasm/emit-exprs.js +1908 -0
  25. package/src/backend/wasm/emit-maps.js +1863 -0
  26. package/src/backend/wasm/emit-shared.js +519 -0
  27. package/src/backend/wasm/emit-stdlib.js +3006 -0
  28. package/src/backend/wasm/emit-stmts.js +2472 -0
  29. package/src/backend/wasm/emit.js +1376 -0
  30. package/src/backend/wasm/encode.js +937 -0
  31. package/src/backend/wasm/glue.js +144 -0
  32. package/src/backend/wasm/index.js +2790 -0
  33. package/src/backend/wasm/monomorph.js +711 -0
  34. package/src/backend/wasm/optimize.js +227 -0
  35. package/src/backend/wasm/types.js +544 -0
  36. package/src/backend/wasm/wat.js +302 -0
  37. package/src/cli-core.js +323 -75
  38. package/src/compiler.js +447 -14
  39. package/src/dev-server.js +56 -15
  40. package/src/index.js +99 -120
  41. package/src/lexer.js +18 -0
  42. package/src/lower/boxing.js +35 -0
  43. package/src/lower/captures.js +172 -0
  44. package/src/lower/embedding.js +30 -0
  45. package/src/lower/escape.js +164 -0
  46. package/src/lower/functions.js +302 -0
  47. package/src/lower/globals.js +316 -0
  48. package/src/lower/index.js +118 -0
  49. package/src/lower/ownership.js +137 -0
  50. package/src/lower/range.js +67 -0
  51. package/src/lower/walk.js +25 -0
  52. package/src/minifier.js +2 -0
  53. package/src/parser/index.js +8 -3
  54. package/src/resolver.js +3 -1
  55. package/src/test-runner.js +94 -20
  56. package/src/typechecker/assignability.js +42 -1
  57. package/src/typechecker/expressions.js +182 -3
  58. package/src/typechecker/index.js +261 -5
  59. package/src/typechecker/resolve.js +35 -6
  60. package/src/typechecker/stdlib/core.js +34 -19
  61. package/src/typechecker/stdlib/extended.js +18 -3
  62. package/src/typechecker/stdlib/shared.js +53 -0
  63. package/src/typechecker/stdlib.js +2 -0
  64. package/src/typechecker/types.js +259 -6
  65. package/src/vendor.js +2 -2
  66. package/src/codegen/stdlib/strconv.js +0 -33
  67. /package/src/{codegen → backend/js}/stdlib/builder.js +0 -0
  68. /package/src/{codegen → backend/js}/stdlib/bytes.js +0 -0
  69. /package/src/{codegen → backend/js}/stdlib/gom.js +0 -0
  70. /package/src/{codegen → backend/js}/stdlib/html.js +0 -0
  71. /package/src/{codegen → backend/js}/stdlib/io.js +0 -0
  72. /package/src/{codegen → backend/js}/stdlib/math.js +0 -0
  73. /package/src/{codegen → backend/js}/stdlib/os.js +0 -0
  74. /package/src/{codegen → backend/js}/stdlib/rand.js +0 -0
  75. /package/src/{codegen → backend/js}/stdlib/regexp.js +0 -0
  76. /package/src/{codegen → backend/js}/stdlib/unicode.js +0 -0
  77. /package/src/{codegen → backend/js}/stdlib/utf8.js +0 -0
@@ -0,0 +1,1103 @@
1
+ // src/backend/wasm/emit-builtins.js
2
+ // FunctionEmitter mixin: panic/recover, Go builtins (len, append, make, …) and
3
+ // call expression dispatch (static, method, closure, package).
4
+
5
+ import {
6
+ getMapKeyValTypes,
7
+ isArrayType,
8
+ isMapType,
9
+ isNonEmptyInterface,
10
+ isSliceType,
11
+ isStringType,
12
+ isStructType,
13
+ isTestingT,
14
+ toWasmType,
15
+ } from "./types.js";
16
+
17
+ export class BuiltinsEmitter {
18
+ emitPanic(msg) {
19
+ const strIdx = this.mod.internString(msg);
20
+ const funcIdx = this.mod.getStringImportIndex();
21
+ this.pushInstruction({ op: "i32.const", value: strIdx });
22
+ this.pushInstruction({ op: "call", funcIndex: funcIdx });
23
+ this.pushInstruction("any.convert_extern");
24
+ this.emitPanicThrow();
25
+ }
26
+
27
+ // Consumes the anyref message on the stack, pushes a PanicNode, and raises the panic via
28
+ // `env.panic`; `unreachable` tells the validator control never returns.
29
+ emitPanicThrow() {
30
+ const panicNodeTypeIndex = this.mod.getPanicNodeTypeIndex();
31
+ const panicGlobal = this.mod.getPanicGlobalIndex();
32
+ const tmpVal = this.acquireTemp("anyref");
33
+ this.pushInstruction({ op: "local.set", index: tmpVal });
34
+
35
+ // Push PanicNode: (struct.new $PanicNode val (i32.const 0) (global.get panicGlobal))
36
+ this.pushInstruction({ op: "local.get", index: tmpVal });
37
+ this.pushInstruction({ op: "i32.const", value: 0 }); // recovered = 0
38
+ this.pushInstruction({ op: "global.get", index: panicGlobal }); // prev
39
+ this.pushInstruction({
40
+ op: "struct.new",
41
+ typeIndex: panicNodeTypeIndex,
42
+ });
43
+ this.pushInstruction({ op: "global.set", index: panicGlobal });
44
+
45
+ // Prepare externref for env.panic
46
+ this.pushInstruction({ op: "local.get", index: tmpVal });
47
+ this.pushInstruction("extern.convert_any");
48
+ this.pushInstruction({
49
+ op: "call",
50
+ funcIndex: this.mod.getPanicImportIndex(),
51
+ });
52
+ this.pushInstruction("unreachable");
53
+ this.releaseTemp(tmpVal, "anyref");
54
+ }
55
+
56
+ _getSliceElemType(t) {
57
+ if (!t) return null;
58
+ if (t.elem) return t.elem;
59
+ if (t.underlying?.elem) return t.underlying.elem;
60
+ if (t.kind === "SliceType") return t.elem;
61
+ if (t.name && this.mod.checker?.types?.has(t.name)) {
62
+ const resolved = this.mod.checker.types.get(t.name);
63
+ return this._getSliceElemType(resolved);
64
+ }
65
+ return null;
66
+ }
67
+
68
+ _getArrayElemType(t) {
69
+ if (!t) return null;
70
+ if (t.elem) return t.elem;
71
+ if (t.underlying?.elem) return t.underlying.elem;
72
+ if (t.kind === "ArrayType") return t.elem;
73
+ if (t.name && this.mod.checker?.types?.has(t.name)) {
74
+ const resolved = this.mod.checker.types.get(t.name);
75
+ return this._getArrayElemType(resolved);
76
+ }
77
+ return null;
78
+ }
79
+
80
+ _emitBuiltinLen(call, targetWasmType) {
81
+ const arg = call.args[0];
82
+ const argType = arg._type ?? this._resolveExprGoType(arg);
83
+ if (this._sharedInfo(argType)) {
84
+ this.emitSharedLen(arg, targetWasmType);
85
+ return;
86
+ }
87
+ if (isMapType(argType, this.mod.checker)) {
88
+ const { keyType, valType } = getMapKeyValTypes(argType, this.mod.checker);
89
+ const mapInfo = this.mod.getMapType(keyType, valType);
90
+ this.emitExpr(arg, mapInfo.wType);
91
+ this.pushInstruction({
92
+ op: "call",
93
+ funcIndex: this.mod.resolveFuncIndex(mapInfo.lenFuncName),
94
+ });
95
+ if ((targetWasmType ?? "i64") === "i64") {
96
+ this.pushInstruction("i64.extend_i32_u");
97
+ }
98
+ return;
99
+ }
100
+ if (isSliceType(argType, this.mod.checker)) {
101
+ const elemType = this._getSliceElemType(argType);
102
+ const sliceInfo = this.mod.getSliceType(elemType);
103
+ const sliceWType = {
104
+ kind: "ref",
105
+ nullable: true,
106
+ typeIndex: sliceInfo.typeIndex,
107
+ };
108
+ this.emitExpr(arg, sliceWType);
109
+ this.pushInstruction({
110
+ op: "struct.get",
111
+ typeIndex: sliceInfo.typeIndex,
112
+ fieldIndex: 2,
113
+ });
114
+ if ((targetWasmType ?? "i64") === "i64") {
115
+ this.pushInstruction("i64.extend_i32_u");
116
+ }
117
+ return;
118
+ }
119
+ if (
120
+ isArrayType(argType, this.mod.checker) ||
121
+ (argType?.kind === "pointer" &&
122
+ isArrayType(argType.base, this.mod.checker))
123
+ ) {
124
+ const elemType = this._getArrayElemType(argType?.base ?? argType);
125
+ const arrInfo = this.mod.getArrayType(elemType);
126
+ const arrWType = {
127
+ kind: "ref",
128
+ nullable: true,
129
+ typeIndex: arrInfo.typeIndex,
130
+ };
131
+ this.emitExpr(arg, arrWType);
132
+ this.pushInstruction("array.len");
133
+ if ((targetWasmType ?? "i64") === "i64") {
134
+ this.pushInstruction("i64.extend_i32_u");
135
+ }
136
+ return;
137
+ }
138
+ if (isStringType(argType, this.mod.checker)) {
139
+ this.emitExpr(arg, "externref");
140
+ const lenIdx = this.mod.getStringLenImportIndex();
141
+ this.pushInstruction({ op: "call", funcIndex: lenIdx });
142
+ if ((targetWasmType ?? "i64") === "i64") {
143
+ this.pushInstruction("i64.extend_i32_u");
144
+ }
145
+ return;
146
+ }
147
+ }
148
+
149
+ _emitBuiltinString(call) {
150
+ const arg = call.args[0];
151
+ const argType = arg._type;
152
+ const argWType = toWasmType(argType, this.mod.checker);
153
+ if (argWType === "i64") {
154
+ this.emitExpr(arg, "i64");
155
+ this.pushInstruction("i32.wrap_i64");
156
+ } else {
157
+ this.emitExpr(arg, "i32");
158
+ }
159
+ const funcIdx = this.mod.getStringFromCodePointImportIndex();
160
+ this.pushInstruction({ op: "call", funcIndex: funcIdx });
161
+ }
162
+
163
+ _emitBuiltinCap(call, targetWasmType) {
164
+ const arg = call.args[0];
165
+ const argType = arg._type;
166
+ if (isSliceType(argType, this.mod.checker)) {
167
+ const elemType = this._getSliceElemType(argType);
168
+ const sliceInfo = this.mod.getSliceType(elemType);
169
+ const sliceWType = {
170
+ kind: "ref",
171
+ nullable: true,
172
+ typeIndex: sliceInfo.typeIndex,
173
+ };
174
+ this.emitExpr(arg, sliceWType);
175
+ this.pushInstruction({
176
+ op: "struct.get",
177
+ typeIndex: sliceInfo.typeIndex,
178
+ fieldIndex: 3,
179
+ });
180
+ if ((targetWasmType ?? "i64") === "i64") {
181
+ this.pushInstruction("i64.extend_i32_u");
182
+ }
183
+ return;
184
+ }
185
+ if (
186
+ isArrayType(argType, this.mod.checker) ||
187
+ (argType?.kind === "pointer" &&
188
+ isArrayType(argType.base, this.mod.checker))
189
+ ) {
190
+ const elemType = this._getArrayElemType(argType?.base ?? argType);
191
+ const arrInfo = this.mod.getArrayType(elemType);
192
+ const arrWType = {
193
+ kind: "ref",
194
+ nullable: true,
195
+ typeIndex: arrInfo.typeIndex,
196
+ };
197
+ this.emitExpr(arg, arrWType);
198
+ this.pushInstruction("array.len");
199
+ if ((targetWasmType ?? "i64") === "i64") {
200
+ this.pushInstruction("i64.extend_i32_u");
201
+ }
202
+ return;
203
+ }
204
+ }
205
+
206
+ _emitBuiltinMake(call) {
207
+ const { args } = call;
208
+ const typeArg = args[0];
209
+ const targetMapType =
210
+ call._type ??
211
+ (typeArg.kind === "TypeExpr" ? typeArg.type : typeArg._type) ??
212
+ typeArg;
213
+ if (isMapType(targetMapType, this.mod.checker)) {
214
+ const { keyType, valType } = getMapKeyValTypes(
215
+ targetMapType,
216
+ this.mod.checker,
217
+ );
218
+ const mapInfo = this.mod.getMapType(keyType, valType);
219
+ if (args[1]) {
220
+ const hintTmp = this.acquireTemp("i32");
221
+ this._emitIndexExprToI32(args[1], hintTmp);
222
+ this.pushInstruction({ op: "local.get", index: hintTmp });
223
+ this.releaseTemp(hintTmp, "i32");
224
+ } else {
225
+ this.pushInstruction({ op: "i32.const", value: 0 });
226
+ }
227
+ this.pushInstruction({
228
+ op: "call",
229
+ funcIndex: this.mod.resolveFuncIndex(mapInfo.makeFuncName),
230
+ });
231
+ return;
232
+ }
233
+ const elemGoType =
234
+ this._getSliceElemType(call._type) ??
235
+ this._getSliceElemType(
236
+ typeArg.kind === "TypeExpr" ? typeArg.type : typeArg,
237
+ ) ??
238
+ this._getSliceElemType(typeArg._type);
239
+ if (elemGoType) {
240
+ const arrInfo = this.mod.getArrayType(elemGoType);
241
+ const sliceInfo = this.mod.getSliceType(elemGoType);
242
+
243
+ const lenTmp = this.acquireTemp("i32");
244
+ this._emitIndexExprToI32(args[1], lenTmp);
245
+
246
+ const capTmp = this.acquireTemp("i32");
247
+ if (args[2]) {
248
+ this._emitIndexExprToI32(args[2], capTmp);
249
+ } else {
250
+ this.pushInstruction({ op: "local.get", index: lenTmp });
251
+ this.pushInstruction({ op: "local.set", index: capTmp });
252
+ }
253
+
254
+ this.pushInstruction({ op: "local.get", index: lenTmp });
255
+ this.pushInstruction({ op: "local.get", index: capTmp });
256
+ this.pushInstruction("i32.gt_u");
257
+ this.pushInstruction({ op: "local.get", index: capTmp });
258
+ this.pushInstruction({ op: "i32.const", value: 0 });
259
+ this.pushInstruction("i32.lt_s");
260
+ this.pushInstruction("i32.or");
261
+ this.pushInstruction({ op: "if", blockType: "void" });
262
+ this.emitPanic("runtime error: makeslice: len out of range");
263
+ this.pushInstruction("end");
264
+
265
+ if (isStructType(elemGoType, this.mod.checker, this.mod)) {
266
+ const arrTmp = this.acquireTemp({
267
+ kind: "ref",
268
+ nullable: true,
269
+ typeIndex: arrInfo.typeIndex,
270
+ });
271
+ this.pushInstruction({ op: "local.get", index: capTmp });
272
+ this.pushInstruction({
273
+ op: "array.new_default",
274
+ typeIndex: arrInfo.typeIndex,
275
+ });
276
+ this.pushInstruction({ op: "local.set", index: arrTmp });
277
+
278
+ const iTmp = this.acquireTemp("i32");
279
+ this.pushInstruction({ op: "i32.const", value: 0 });
280
+ this.pushInstruction({ op: "local.set", index: iTmp });
281
+
282
+ this.pushInstruction({ op: "block", blockType: "void" });
283
+ this.pushInstruction({ op: "loop", blockType: "void" });
284
+ this.pushInstruction({ op: "local.get", index: iTmp });
285
+ this.pushInstruction({ op: "local.get", index: capTmp });
286
+ this.pushInstruction("i32.ge_s");
287
+ this.pushInstruction({ op: "br_if", depth: 1 });
288
+
289
+ this.pushInstruction({ op: "local.get", index: arrTmp });
290
+ this.pushInstruction({ op: "local.get", index: iTmp });
291
+ this.emitZeroValue(elemGoType, arrInfo.elemWType);
292
+ this.pushInstruction({
293
+ op: "array.set",
294
+ typeIndex: arrInfo.typeIndex,
295
+ });
296
+
297
+ this.pushInstruction({ op: "local.get", index: iTmp });
298
+ this.pushInstruction({ op: "i32.const", value: 1 });
299
+ this.pushInstruction("i32.add");
300
+ this.pushInstruction({ op: "local.set", index: iTmp });
301
+ this.pushInstruction({ op: "br", depth: 0 });
302
+ this.pushInstruction("end");
303
+ this.pushInstruction("end");
304
+ this.releaseTemp(iTmp, "i32");
305
+
306
+ this.pushInstruction({ op: "local.get", index: arrTmp });
307
+ this.pushInstruction({ op: "i32.const", value: 0 });
308
+ this.pushInstruction({ op: "local.get", index: lenTmp });
309
+ this.pushInstruction({ op: "local.get", index: capTmp });
310
+ this.pushInstruction({
311
+ op: "struct.new",
312
+ typeIndex: sliceInfo.typeIndex,
313
+ });
314
+ this.releaseTemp(arrTmp, {
315
+ kind: "ref",
316
+ nullable: true,
317
+ typeIndex: arrInfo.typeIndex,
318
+ });
319
+ } else {
320
+ this.pushInstruction({ op: "local.get", index: capTmp });
321
+ this.pushInstruction({
322
+ op: "array.new_default",
323
+ typeIndex: arrInfo.typeIndex,
324
+ });
325
+ this.pushInstruction({ op: "i32.const", value: 0 });
326
+ this.pushInstruction({ op: "local.get", index: lenTmp });
327
+ this.pushInstruction({ op: "local.get", index: capTmp });
328
+ this.pushInstruction({
329
+ op: "struct.new",
330
+ typeIndex: sliceInfo.typeIndex,
331
+ });
332
+ }
333
+
334
+ this.releaseTemp(capTmp, "i32");
335
+ this.releaseTemp(lenTmp, "i32");
336
+ }
337
+ }
338
+
339
+ // With `newLen` already computed: when it exceeds the old capacity, allocate
340
+ // a doubled (min 2, at least newLen) backing array and copy the old elements
341
+ // into it at offset 0; otherwise reuse the old array, offset and capacity.
342
+ _emitGrowSlice({
343
+ arrInfo,
344
+ elemGoType,
345
+ oldArrTmp,
346
+ oldOffTmp,
347
+ oldLenTmp,
348
+ oldCapTmp,
349
+ newArrTmp,
350
+ newOffTmp,
351
+ newLenTmp,
352
+ newCapTmp,
353
+ }) {
354
+ this.pushInstruction({ op: "local.get", index: newLenTmp });
355
+ this.pushInstruction({ op: "local.get", index: oldCapTmp });
356
+ this.pushInstruction("i32.gt_u");
357
+ this.pushInstruction({ op: "if", blockType: "void" });
358
+
359
+ this.pushInstruction({ op: "local.get", index: oldCapTmp });
360
+ this.pushInstruction({ op: "i32.const", value: 1 });
361
+ this.pushInstruction("i32.shl");
362
+ this.pushInstruction({ op: "local.set", index: newCapTmp });
363
+
364
+ this.pushInstruction({ op: "local.get", index: newLenTmp });
365
+ this.pushInstruction({ op: "local.get", index: newCapTmp });
366
+ this.pushInstruction("i32.gt_u");
367
+ this.pushInstruction({ op: "if", blockType: "void" });
368
+ this.pushInstruction({ op: "local.get", index: newLenTmp });
369
+ this.pushInstruction({ op: "local.set", index: newCapTmp });
370
+ this.pushInstruction("end");
371
+
372
+ this.pushInstruction({ op: "local.get", index: newCapTmp });
373
+ this.pushInstruction({ op: "i32.const", value: 2 });
374
+ this.pushInstruction("i32.lt_u");
375
+ this.pushInstruction({ op: "if", blockType: "void" });
376
+ this.pushInstruction({ op: "i32.const", value: 2 });
377
+ this.pushInstruction({ op: "local.set", index: newCapTmp });
378
+ this.pushInstruction("end");
379
+
380
+ this.pushInstruction({ op: "local.get", index: newCapTmp });
381
+ this.pushInstruction({
382
+ op: "array.new_default",
383
+ typeIndex: arrInfo.typeIndex,
384
+ });
385
+ this.pushInstruction({ op: "local.set", index: newArrTmp });
386
+ this.pushInstruction({ op: "i32.const", value: 0 });
387
+ this.pushInstruction({ op: "local.set", index: newOffTmp });
388
+
389
+ this.pushInstruction({ op: "local.get", index: oldLenTmp });
390
+ this.pushInstruction({ op: "i32.const", value: 0 });
391
+ this.pushInstruction("i32.gt_u");
392
+ this.pushInstruction({ op: "if", blockType: "void" });
393
+ this.pushInstruction({ op: "local.get", index: newArrTmp });
394
+ this.pushInstruction({ op: "i32.const", value: 0 });
395
+ this.pushInstruction({ op: "local.get", index: oldArrTmp });
396
+ this.pushInstruction({ op: "local.get", index: oldOffTmp });
397
+ this.pushInstruction({ op: "local.get", index: oldLenTmp });
398
+ this._emitArrayCopy(arrInfo, elemGoType);
399
+ this.pushInstruction("end");
400
+
401
+ this.pushInstruction("else");
402
+ this.pushInstruction({ op: "local.get", index: oldArrTmp });
403
+ this.pushInstruction({ op: "local.set", index: newArrTmp });
404
+ this.pushInstruction({ op: "local.get", index: oldOffTmp });
405
+ this.pushInstruction({ op: "local.set", index: newOffTmp });
406
+ this.pushInstruction({ op: "local.get", index: oldCapTmp });
407
+ this.pushInstruction({ op: "local.set", index: newCapTmp });
408
+ this.pushInstruction("end");
409
+ }
410
+
411
+ _emitBuiltinAppend(call) {
412
+ const { args } = call;
413
+ const sNode = args[0];
414
+ const sliceGoType = sNode._type ?? call._type;
415
+ const elemGoType = this._getSliceElemType(sliceGoType);
416
+ const sliceInfo = this.mod.getSliceType(elemGoType);
417
+ const arrInfo = sliceInfo.arrInfo;
418
+ const sliceWType = {
419
+ kind: "ref",
420
+ nullable: true,
421
+ typeIndex: sliceInfo.typeIndex,
422
+ };
423
+ const arrWType = {
424
+ kind: "ref",
425
+ nullable: true,
426
+ typeIndex: arrInfo.typeIndex,
427
+ };
428
+
429
+ const sTmp = this.acquireTemp(sliceWType);
430
+ this.emitExpr(sNode, sliceWType);
431
+ this.pushInstruction({ op: "local.set", index: sTmp });
432
+
433
+ const oldArrTmp = this.acquireTemp(arrWType);
434
+ const oldOffTmp = this.acquireTemp("i32");
435
+ const oldLenTmp = this.acquireTemp("i32");
436
+ const oldCapTmp = this.acquireTemp("i32");
437
+
438
+ this._emitSliceUnpack(sTmp, sliceInfo, {
439
+ arr: oldArrTmp,
440
+ off: oldOffTmp,
441
+ len: oldLenTmp,
442
+ cap: oldCapTmp,
443
+ });
444
+ this.releaseTemp(sTmp, sliceWType);
445
+
446
+ const newArrTmp = this.acquireTemp(arrWType);
447
+ const newOffTmp = this.acquireTemp("i32");
448
+ const newCapTmp = this.acquireTemp("i32");
449
+ const newLenTmp = this.acquireTemp("i32");
450
+ const growArgs = {
451
+ arrInfo,
452
+ elemGoType,
453
+ oldArrTmp,
454
+ oldOffTmp,
455
+ oldLenTmp,
456
+ oldCapTmp,
457
+ newArrTmp,
458
+ newOffTmp,
459
+ newLenTmp,
460
+ newCapTmp,
461
+ };
462
+
463
+ if (args.length === 2 && args[1]._spread) {
464
+ const s2Tmp = this.acquireTemp(sliceWType);
465
+ this.emitExpr(args[1], sliceWType);
466
+ this.pushInstruction({ op: "local.set", index: s2Tmp });
467
+
468
+ const s2ArrTmp = this.acquireTemp(arrWType);
469
+ const s2OffTmp = this.acquireTemp("i32");
470
+ const s2LenTmp = this.acquireTemp("i32");
471
+
472
+ this._emitSliceUnpack(s2Tmp, sliceInfo, {
473
+ arr: s2ArrTmp,
474
+ off: s2OffTmp,
475
+ len: s2LenTmp,
476
+ });
477
+ this.releaseTemp(s2Tmp, sliceWType);
478
+
479
+ this.pushInstruction({ op: "local.get", index: oldLenTmp });
480
+ this.pushInstruction({ op: "local.get", index: s2LenTmp });
481
+ this.pushInstruction("i32.add");
482
+ this.pushInstruction({ op: "local.set", index: newLenTmp });
483
+
484
+ this._emitGrowSlice(growArgs);
485
+
486
+ this.pushInstruction({ op: "local.get", index: s2LenTmp });
487
+ this.pushInstruction({ op: "i32.const", value: 0 });
488
+ this.pushInstruction("i32.gt_u");
489
+ this.pushInstruction({ op: "if", blockType: "void" });
490
+ this.pushInstruction({ op: "local.get", index: newArrTmp });
491
+ this.pushInstruction({ op: "local.get", index: newOffTmp });
492
+ this.pushInstruction({ op: "local.get", index: oldLenTmp });
493
+ this.pushInstruction("i32.add");
494
+ this.pushInstruction({ op: "local.get", index: s2ArrTmp });
495
+ this.pushInstruction({ op: "local.get", index: s2OffTmp });
496
+ this.pushInstruction({ op: "local.get", index: s2LenTmp });
497
+ this._emitArrayCopy(arrInfo, elemGoType);
498
+ this.pushInstruction("end");
499
+
500
+ this.releaseTemp(s2LenTmp, "i32");
501
+ this.releaseTemp(s2OffTmp, "i32");
502
+ this.releaseTemp(s2ArrTmp, arrWType);
503
+ } else {
504
+ const k = args.length - 1;
505
+ this.pushInstruction({ op: "local.get", index: oldLenTmp });
506
+ this.pushInstruction({ op: "i32.const", value: k });
507
+ this.pushInstruction("i32.add");
508
+ this.pushInstruction({ op: "local.set", index: newLenTmp });
509
+
510
+ this._emitGrowSlice(growArgs);
511
+
512
+ for (let i = 0; i < k; i++) {
513
+ const elemNode = args[1 + i];
514
+
515
+ this.pushInstruction({ op: "local.get", index: newArrTmp });
516
+ this.pushInstruction({ op: "local.get", index: newOffTmp });
517
+ this.pushInstruction({ op: "local.get", index: oldLenTmp });
518
+ this.pushInstruction("i32.add");
519
+ if (i > 0) {
520
+ this.pushInstruction({ op: "i32.const", value: i });
521
+ this.pushInstruction("i32.add");
522
+ }
523
+ this.emitExpr(elemNode, arrInfo.elemWType);
524
+ this._emitCopyIfValueStruct(elemNode, elemGoType, arrInfo.elemWType);
525
+ this.pushInstruction({
526
+ op: "array.set",
527
+ typeIndex: arrInfo.typeIndex,
528
+ });
529
+ }
530
+ }
531
+
532
+ this.pushInstruction({ op: "local.get", index: newArrTmp });
533
+ this.pushInstruction({ op: "local.get", index: newOffTmp });
534
+ this.pushInstruction({ op: "local.get", index: newLenTmp });
535
+ this.pushInstruction({ op: "local.get", index: newCapTmp });
536
+ this.pushInstruction({
537
+ op: "struct.new",
538
+ typeIndex: sliceInfo.typeIndex,
539
+ });
540
+
541
+ this.releaseTemp(newLenTmp, "i32");
542
+ this.releaseTemp(newCapTmp, "i32");
543
+ this.releaseTemp(newOffTmp, "i32");
544
+ this.releaseTemp(newArrTmp, arrWType);
545
+ this.releaseTemp(oldCapTmp, "i32");
546
+ this.releaseTemp(oldLenTmp, "i32");
547
+ this.releaseTemp(oldOffTmp, "i32");
548
+ this.releaseTemp(oldArrTmp, arrWType);
549
+ }
550
+
551
+ _emitBuiltinCopy(call, targetWasmType) {
552
+ const { args } = call;
553
+ const dstNode = args[0];
554
+ const srcNode = args[1];
555
+ if (this._sharedInfo(dstNode._type) || this._sharedInfo(srcNode._type)) {
556
+ this.emitSharedCopy(dstNode, srcNode, targetWasmType);
557
+ return;
558
+ }
559
+ const elemGoType =
560
+ this._getSliceElemType(dstNode._type) ??
561
+ this._getSliceElemType(srcNode._type);
562
+ const sliceInfo = this.mod.getSliceType(elemGoType);
563
+ const arrInfo = sliceInfo.arrInfo;
564
+ const sliceWType = {
565
+ kind: "ref",
566
+ nullable: true,
567
+ typeIndex: sliceInfo.typeIndex,
568
+ };
569
+
570
+ const dstTmp = this.acquireTemp(sliceWType);
571
+ this.emitExpr(dstNode, sliceWType);
572
+ this.pushInstruction({ op: "local.set", index: dstTmp });
573
+
574
+ const srcTmp = this.acquireTemp(sliceWType);
575
+ this.emitExpr(srcNode, sliceWType);
576
+ this.pushInstruction({ op: "local.set", index: srcTmp });
577
+
578
+ const nTmp = this.acquireTemp("i32");
579
+ this.pushInstruction({ op: "i32.const", value: 0 });
580
+ this.pushInstruction({ op: "local.set", index: nTmp });
581
+
582
+ const dstLenTmp = this.acquireTemp("i32");
583
+ this.pushInstruction({ op: "local.get", index: dstTmp });
584
+ this.pushInstruction({
585
+ op: "struct.get",
586
+ typeIndex: sliceInfo.typeIndex,
587
+ fieldIndex: 2,
588
+ });
589
+ this.pushInstruction({ op: "local.set", index: dstLenTmp });
590
+
591
+ const srcLenTmp = this.acquireTemp("i32");
592
+ this.pushInstruction({ op: "local.get", index: srcTmp });
593
+ this.pushInstruction({
594
+ op: "struct.get",
595
+ typeIndex: sliceInfo.typeIndex,
596
+ fieldIndex: 2,
597
+ });
598
+ this.pushInstruction({ op: "local.set", index: srcLenTmp });
599
+
600
+ this.pushInstruction({ op: "local.get", index: dstLenTmp });
601
+ this.pushInstruction({ op: "local.get", index: srcLenTmp });
602
+ this.pushInstruction("i32.lt_u");
603
+ this.pushInstruction({ op: "if", blockType: "i32" });
604
+ this.pushInstruction({ op: "local.get", index: dstLenTmp });
605
+ this.pushInstruction("else");
606
+ this.pushInstruction({ op: "local.get", index: srcLenTmp });
607
+ this.pushInstruction("end");
608
+ this.pushInstruction({ op: "local.set", index: nTmp });
609
+
610
+ this.releaseTemp(srcLenTmp, "i32");
611
+ this.releaseTemp(dstLenTmp, "i32");
612
+
613
+ this.pushInstruction({ op: "local.get", index: nTmp });
614
+ this.pushInstruction({ op: "i32.const", value: 0 });
615
+ this.pushInstruction("i32.gt_u");
616
+ this.pushInstruction({ op: "if", blockType: "void" });
617
+
618
+ this.pushInstruction({ op: "local.get", index: dstTmp });
619
+ this.pushInstruction({
620
+ op: "struct.get",
621
+ typeIndex: sliceInfo.typeIndex,
622
+ fieldIndex: 0,
623
+ });
624
+ this.pushInstruction({ op: "local.get", index: dstTmp });
625
+ this.pushInstruction({
626
+ op: "struct.get",
627
+ typeIndex: sliceInfo.typeIndex,
628
+ fieldIndex: 1,
629
+ });
630
+ this.pushInstruction({ op: "local.get", index: srcTmp });
631
+ this.pushInstruction({
632
+ op: "struct.get",
633
+ typeIndex: sliceInfo.typeIndex,
634
+ fieldIndex: 0,
635
+ });
636
+ this.pushInstruction({ op: "local.get", index: srcTmp });
637
+ this.pushInstruction({
638
+ op: "struct.get",
639
+ typeIndex: sliceInfo.typeIndex,
640
+ fieldIndex: 1,
641
+ });
642
+ this.pushInstruction({ op: "local.get", index: nTmp });
643
+ this._emitArrayCopy(arrInfo, elemGoType);
644
+
645
+ this.pushInstruction("end");
646
+
647
+ this.releaseTemp(srcTmp, sliceWType);
648
+ this.releaseTemp(dstTmp, sliceWType);
649
+
650
+ this.pushInstruction({ op: "local.get", index: nTmp });
651
+ this.releaseTemp(nTmp, "i32");
652
+ if ((targetWasmType ?? "i64") === "i64") {
653
+ this.pushInstruction("i64.extend_i32_u");
654
+ }
655
+ }
656
+
657
+ _emitBuiltinDelete(call) {
658
+ const { args } = call;
659
+ const mArg = args[0];
660
+ const kArg = args[1];
661
+ const mType = mArg._type ?? this._resolveExprGoType(mArg);
662
+ const { keyType, valType } = getMapKeyValTypes(mType, this.mod.checker);
663
+ const mapInfo = this.mod.getMapType(keyType, valType);
664
+ this.emitExpr(mArg, mapInfo.wType);
665
+ this.emitExpr(kArg, mapInfo.keyWType);
666
+ this.pushInstruction({
667
+ op: "call",
668
+ funcIndex: this.mod.resolveFuncIndex(mapInfo.deleteFuncName),
669
+ });
670
+ }
671
+
672
+ _emitBuiltinClear(call) {
673
+ const { args } = call;
674
+ const arg = args[0];
675
+ const argType = arg._type ?? this._resolveExprGoType(arg);
676
+ if (isMapType(argType, this.mod.checker)) {
677
+ const { keyType, valType } = getMapKeyValTypes(argType, this.mod.checker);
678
+ const mapInfo = this.mod.getMapType(keyType, valType);
679
+ this.emitExpr(arg, mapInfo.wType);
680
+ this.pushInstruction({
681
+ op: "call",
682
+ funcIndex: this.mod.resolveFuncIndex(mapInfo.clearFuncName),
683
+ });
684
+ return;
685
+ }
686
+ if (isSliceType(argType, this.mod.checker)) {
687
+ this.emitSliceClear(arg, argType);
688
+ return;
689
+ }
690
+ }
691
+
692
+ _emitBuiltinPrint(call) {
693
+ const { func, args } = call;
694
+ const isPrintln = func.name === "println";
695
+ if (args.length === 0 && isPrintln) {
696
+ const logIdx = this.mod.getPrintlnEmptyIndex();
697
+ this.pushInstruction({ op: "call", funcIndex: logIdx });
698
+ return;
699
+ }
700
+ for (let i = 0; i < args.length; i++) {
701
+ const arg = args[i];
702
+ const isLast = i === args.length - 1 && isPrintln;
703
+ const isBool =
704
+ arg._type?.name === "bool" ||
705
+ (arg.kind === "BasicLit" && arg.litKind === "BOOL");
706
+ const wType = toWasmType(arg._type, this.mod.checker);
707
+ this.emitExpr(arg, wType);
708
+ const logFuncIdx = this.mod.getLogImportIndex(wType, isLast, isBool);
709
+ this.pushInstruction({ op: "call", funcIndex: logFuncIdx });
710
+ }
711
+ }
712
+
713
+ _emitBuiltinPanic(call) {
714
+ const { args } = call;
715
+ const arg = args?.[0];
716
+ if (!arg) {
717
+ const strIdx = this.mod.internString("");
718
+ const funcIdx = this.mod.getStringImportIndex();
719
+ this.pushInstruction({ op: "i32.const", value: strIdx });
720
+ this.pushInstruction({ op: "call", funcIndex: funcIdx });
721
+ this.pushInstruction("any.convert_extern");
722
+ } else {
723
+ const goType = this._resolveExprGoType(arg);
724
+ const wType = toWasmType(goType, this.mod.checker);
725
+ if (
726
+ isStringType(goType, this.mod.checker) ||
727
+ (arg.kind === "BasicLit" && arg.litKind === "STRING") ||
728
+ wType === "externref"
729
+ ) {
730
+ this.emitExpr(arg, "externref");
731
+ this.pushInstruction("any.convert_extern");
732
+ } else if (wType === "i64") {
733
+ this.emitExpr(arg, "i64");
734
+ this.pushInstruction({
735
+ op: "call",
736
+ funcIndex: this.mod.getStrFromI64ImportIndex(),
737
+ });
738
+ this.pushInstruction("any.convert_extern");
739
+ } else if (wType === "i32") {
740
+ this.emitExpr(arg, "i32");
741
+ this.pushInstruction({
742
+ op: "call",
743
+ funcIndex: this.mod.getStrFromI32ImportIndex(),
744
+ });
745
+ this.pushInstruction("any.convert_extern");
746
+ } else if (wType === "f64" || wType === "f32") {
747
+ if (wType === "f32") {
748
+ this.emitExpr(arg, "f32");
749
+ this.pushInstruction("f64.promote_f32");
750
+ } else {
751
+ this.emitExpr(arg, "f64");
752
+ }
753
+ this.pushInstruction({
754
+ op: "call",
755
+ funcIndex: this.mod.getStrFromF64ImportIndex(),
756
+ });
757
+ this.pushInstruction("any.convert_extern");
758
+ } else {
759
+ this.emitExpr(arg, "anyref");
760
+ }
761
+ }
762
+ this.emitPanicThrow();
763
+ }
764
+
765
+ _emitBuiltinRecover(_call, targetWasmType) {
766
+ const panicNodeTypeIndex = this.mod.getPanicNodeTypeIndex();
767
+ const panicGlobal = this.mod.getPanicGlobalIndex();
768
+ const tmp = this.acquireTemp("anyref");
769
+
770
+ // tmp = null
771
+ this.pushInstruction({ op: "ref.null", heapType: "any" });
772
+ this.pushInstruction({ op: "local.set", index: tmp });
773
+
774
+ // if (__recoverOk && __panic != null)
775
+ if (this.recoverOkLocalIndex != null) {
776
+ this.pushInstruction({
777
+ op: "local.get",
778
+ index: this.recoverOkLocalIndex,
779
+ });
780
+ } else {
781
+ // Not a deferred frame and no direct recover prologue: always nil.
782
+ this.pushInstruction({ op: "i32.const", value: 0 });
783
+ }
784
+ this.pushInstruction({ op: "global.get", index: panicGlobal });
785
+ this.pushInstruction({ op: "ref.is_null" });
786
+ this.pushInstruction({ op: "i32.eqz" });
787
+ this.pushInstruction({ op: "i32.and" });
788
+ this.pushInstruction({ op: "if", blockType: "void" });
789
+ this.pushControl("ifHasPanicNode");
790
+
791
+ // if (__panic.recovered == 0)
792
+ this.pushInstruction({ op: "global.get", index: panicGlobal });
793
+ this.pushInstruction({
794
+ op: "struct.get",
795
+ typeIndex: panicNodeTypeIndex,
796
+ fieldIndex: 1, // recovered
797
+ });
798
+ this.pushInstruction({ op: "i32.eqz" });
799
+ this.pushInstruction({ op: "if", blockType: "void" });
800
+ this.pushControl("ifNotYetRecovered");
801
+
802
+ // __panic.recovered = 1
803
+ this.pushInstruction({ op: "global.get", index: panicGlobal });
804
+ this.pushInstruction({ op: "i32.const", value: 1 });
805
+ this.pushInstruction({
806
+ op: "struct.set",
807
+ typeIndex: panicNodeTypeIndex,
808
+ fieldIndex: 1,
809
+ });
810
+
811
+ // tmp = __panic.val
812
+ this.pushInstruction({ op: "global.get", index: panicGlobal });
813
+ this.pushInstruction({
814
+ op: "struct.get",
815
+ typeIndex: panicNodeTypeIndex,
816
+ fieldIndex: 0, // val
817
+ });
818
+ this.pushInstruction({ op: "local.set", index: tmp });
819
+
820
+ this.pushInstruction("end"); // ifNotYetRecovered
821
+
822
+ this.pushInstruction("end"); // ifHasPanicNode
823
+
824
+ this.pushInstruction({ op: "local.get", index: tmp });
825
+ this.releaseTemp(tmp, "anyref");
826
+
827
+ if (targetWasmType === "externref") {
828
+ this.pushInstruction("extern.convert_any");
829
+ }
830
+ }
831
+
832
+ _emitBuiltinCall(call, targetWasmType) {
833
+ const { func } = call;
834
+ if (func.kind !== "Ident") return false;
835
+ switch (func.name) {
836
+ case "len":
837
+ this._emitBuiltinLen(call, targetWasmType);
838
+ return true;
839
+ case "string":
840
+ this._emitBuiltinString(call);
841
+ return true;
842
+ case "cap":
843
+ this._emitBuiltinCap(call, targetWasmType);
844
+ return true;
845
+ case "make":
846
+ this._emitBuiltinMake(call);
847
+ return true;
848
+ case "append":
849
+ this._emitBuiltinAppend(call);
850
+ return true;
851
+ case "copy":
852
+ this._emitBuiltinCopy(call, targetWasmType);
853
+ return true;
854
+ case "delete":
855
+ this._emitBuiltinDelete(call);
856
+ return true;
857
+ case "clear":
858
+ this._emitBuiltinClear(call);
859
+ return true;
860
+ case "print":
861
+ case "println":
862
+ this._emitBuiltinPrint(call);
863
+ return true;
864
+ case "panic":
865
+ this._emitBuiltinPanic(call);
866
+ return true;
867
+ case "recover":
868
+ this._emitBuiltinRecover(call, targetWasmType);
869
+ return true;
870
+ default:
871
+ return false;
872
+ }
873
+ }
874
+
875
+ _emitPackageCall(call, targetWasmType) {
876
+ const { func, args } = call;
877
+ if (func.kind !== "SelectorExpr") return false;
878
+ if (isTestingT(func.expr?._type)) {
879
+ this.emitTestingCall(func, args);
880
+ return true;
881
+ }
882
+ const pkgName = func.expr?.name;
883
+ switch (pkgName) {
884
+ case "math":
885
+ this.emitMathCall(func.field, args);
886
+ return true;
887
+ case "bits":
888
+ this.emitBitsCall(func.field, args);
889
+ return true;
890
+ case "maps":
891
+ this.emitMapsCall(func.field, args, targetWasmType);
892
+ return true;
893
+ case "slices":
894
+ this.emitSlicesCall(func.field, args, targetWasmType);
895
+ return true;
896
+ case "strings":
897
+ this.emitStringsCall(func.field, args, targetWasmType);
898
+ return true;
899
+ case "strconv":
900
+ this.emitStrconvCall(func.field, args);
901
+ return true;
902
+ case "fmt":
903
+ this.emitFmtCall(func.field, args);
904
+ return true;
905
+ case "errors":
906
+ this.emitErrorsCall(func.field, args);
907
+ return true;
908
+ case "utf8":
909
+ this.emitUtf8Call(func.field, args, targetWasmType);
910
+ return true;
911
+ case "sort":
912
+ this.emitSortCall(func.field, args, targetWasmType);
913
+ return true;
914
+ case "shared":
915
+ this.emitSharedNew(func.field, args);
916
+ return true;
917
+ default:
918
+ return false;
919
+ }
920
+ }
921
+
922
+ _emitMethodCall(call) {
923
+ const { func, args } = call;
924
+ if (func.kind !== "SelectorExpr") return false;
925
+
926
+ const recvType = func.expr._type ?? this._resolveExprGoType(func.expr);
927
+ const recvShared = this._sharedInfo(recvType);
928
+ if (recvShared && func.field === "Subarray") {
929
+ this.emitSharedSubarray(func.expr, args, recvShared);
930
+ return true;
931
+ }
932
+ const recvTypeName = this._getReceiverTypeName(recvType, func.expr);
933
+ if (isNonEmptyInterface(recvType, this.mod.checker)) {
934
+ const ifaceName = recvTypeName ?? "anon";
935
+ const dispatchName = `__dispatch_${ifaceName}_${func.field}`;
936
+ const targetFuncIdx = this.mod.resolveFuncIndex(dispatchName);
937
+ if (targetFuncIdx !== null) {
938
+ const targetParamTypes = this.mod.getFuncParamTypes(dispatchName);
939
+ const paramGoTypes = this.mod.getFuncParamGoTypes(dispatchName);
940
+ this.emitExpr(func.expr, "anyref");
941
+ for (let i = 0; i < args.length; i++) {
942
+ const pType = targetParamTypes[i + 1] ?? null;
943
+ const pGoType = paramGoTypes[i + 1] ?? null;
944
+ this.emitExpr(args[i], pType);
945
+ this._emitCopyIfValueStruct(args[i], pGoType, pType);
946
+ }
947
+ this.pushInstruction({ op: "call", funcIndex: targetFuncIdx });
948
+ return true;
949
+ }
950
+ }
951
+ if (recvTypeName) {
952
+ const methodName = `${recvTypeName}.${func.field}`;
953
+ let targetFuncIdx = this.mod.resolveFuncIndex(methodName);
954
+ let targetParamTypes = this.mod.getFuncParamTypes(methodName);
955
+
956
+ // If not found directly, check embedded structs
957
+ if (targetFuncIdx === null) {
958
+ const structInfo = this.mod.getStructType(recvTypeName);
959
+ if (structInfo) {
960
+ for (const embed of structInfo.embeds) {
961
+ const embedMethodName = `${embed.name}.${func.field}`;
962
+ const subIdx = this.mod.resolveFuncIndex(embedMethodName);
963
+ if (subIdx !== null) {
964
+ targetFuncIdx = subIdx;
965
+ targetParamTypes = this.mod.getFuncParamTypes(embedMethodName);
966
+ const baseWType = this.toWasmType(recvType);
967
+ this.emitExpr(func.expr, baseWType);
968
+ this.pushInstruction({
969
+ op: "struct.get",
970
+ typeIndex: structInfo.typeIndex,
971
+ fieldIndex: embed.fieldIndex,
972
+ });
973
+ for (let i = 0; i < args.length; i++) {
974
+ const pType = targetParamTypes[i + 1] ?? null;
975
+ this.emitExpr(args[i], pType);
976
+ }
977
+ this.pushInstruction({
978
+ op: "call",
979
+ funcIndex: targetFuncIdx,
980
+ });
981
+ return true;
982
+ }
983
+ }
984
+ }
985
+ }
986
+
987
+ if (targetFuncIdx !== null) {
988
+ const recvWType = targetParamTypes[0] ?? null;
989
+ const recvGoType = this.mod.getFuncParamGoTypes(methodName)[0] ?? null;
990
+ this.emitExpr(func.expr, recvWType);
991
+ this._emitCopyIfValueStruct(func.expr, recvGoType, recvWType);
992
+ const paramGoTypes = this.mod.getFuncParamGoTypes(methodName);
993
+ for (let i = 0; i < args.length; i++) {
994
+ const pType = targetParamTypes[i + 1] ?? null;
995
+ const pGoType = paramGoTypes[i + 1] ?? null;
996
+ this.emitExpr(args[i], pType);
997
+ this._emitCopyIfValueStruct(args[i], pGoType, pType);
998
+ }
999
+ this.pushInstruction({ op: "call", funcIndex: targetFuncIdx });
1000
+ return true;
1001
+ }
1002
+ }
1003
+ return false;
1004
+ }
1005
+
1006
+ _emitStaticCall(call) {
1007
+ const { func, args } = call;
1008
+ if (func.kind !== "Ident") return false;
1009
+
1010
+ const isLocal = this.resolveLocal(func.name) !== null;
1011
+ if (!isLocal) {
1012
+ const targetFuncIdx = this.mod.resolveFuncIndex(func.name);
1013
+ if (targetFuncIdx !== null) {
1014
+ const paramTypes = this.mod.getFuncParamTypes(func.name);
1015
+ const paramGoTypes = this.mod.getFuncParamGoTypes(func.name);
1016
+ for (let i = 0; i < args.length; i++) {
1017
+ const arg = args[i];
1018
+ const pType = paramTypes[i] ?? null;
1019
+ const pGoType = paramGoTypes[i] ?? null;
1020
+ this.emitExpr(arg, pType);
1021
+ this._emitCopyIfValueStruct(arg, pGoType, pType);
1022
+ }
1023
+ this.pushInstruction({ op: "call", funcIndex: targetFuncIdx });
1024
+ return true;
1025
+ }
1026
+ }
1027
+ return false;
1028
+ }
1029
+
1030
+ _emitClosureCall(call) {
1031
+ const { func, args } = call;
1032
+ const funcGoType = this._resolveExprGoType(func) ?? func._type;
1033
+ const closureInfo = this.mod.getClosureType(funcGoType);
1034
+ const closureWType = {
1035
+ kind: "ref",
1036
+ nullable: true,
1037
+ typeIndex: closureInfo.typeIndex,
1038
+ };
1039
+
1040
+ const closureTmp = this.acquireTemp(closureWType);
1041
+ this.emitExpr(func, closureWType);
1042
+ this.pushInstruction({ op: "local.set", index: closureTmp });
1043
+
1044
+ // Push param 0: env (anyref)
1045
+ this.pushInstruction({ op: "local.get", index: closureTmp });
1046
+ this.pushInstruction({
1047
+ op: "struct.get",
1048
+ typeIndex: closureInfo.typeIndex,
1049
+ fieldIndex: 1,
1050
+ });
1051
+
1052
+ // Push user arguments
1053
+ for (let i = 0; i < args.length; i++) {
1054
+ const arg = args[i];
1055
+ const pWType = closureInfo.paramWTypes[i] ?? null;
1056
+ const pGoType = closureInfo.sig.params[i] ?? null;
1057
+ this.emitExpr(arg, pWType);
1058
+ this._emitCopyIfValueStruct(arg, pGoType, pWType);
1059
+ }
1060
+
1061
+ // Push funcref
1062
+ this.pushInstruction({ op: "local.get", index: closureTmp });
1063
+ this.pushInstruction({
1064
+ op: "struct.get",
1065
+ typeIndex: closureInfo.typeIndex,
1066
+ fieldIndex: 0,
1067
+ });
1068
+
1069
+ // Call via call_ref
1070
+ this.pushInstruction({
1071
+ op: "call_ref",
1072
+ typeIndex: closureInfo.funcTypeIndex,
1073
+ });
1074
+
1075
+ this.releaseTemp(closureTmp, closureWType);
1076
+ }
1077
+
1078
+ emitCallExpr(call, targetWasmType = null) {
1079
+ const deqFunc = this._dequalify(call.func);
1080
+ if (deqFunc) {
1081
+ this.emitCallExpr({ ...call, func: deqFunc }, targetWasmType);
1082
+ return;
1083
+ }
1084
+
1085
+ if (this._emitBuiltinCall(call, targetWasmType)) {
1086
+ return;
1087
+ }
1088
+
1089
+ if (this._emitPackageCall(call, targetWasmType)) {
1090
+ return;
1091
+ }
1092
+
1093
+ if (this._emitMethodCall(call)) {
1094
+ return;
1095
+ }
1096
+
1097
+ if (this._emitStaticCall(call)) {
1098
+ return;
1099
+ }
1100
+
1101
+ this._emitClosureCall(call);
1102
+ }
1103
+ }