gofront 1.5.1 → 1.6.1

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