gofront 1.6.0 → 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 (68) hide show
  1. package/package.json +1 -1
  2. package/src/backend/js/expressions.js +238 -109
  3. package/src/backend/js/index.js +38 -1
  4. package/src/backend/js/runtime.js +953 -53
  5. package/src/backend/js/statements.js +55 -67
  6. package/src/backend/js/stdlib/base64.js +12 -0
  7. package/src/backend/js/stdlib/binary.js +102 -0
  8. package/src/backend/js/stdlib/bits.js +15 -1
  9. package/src/backend/js/stdlib/builder.js +28 -9
  10. package/src/backend/js/stdlib/bytes.js +75 -18
  11. package/src/backend/js/stdlib/errors.js +20 -1
  12. package/src/backend/js/stdlib/fmt.js +70 -13
  13. package/src/backend/js/stdlib/hex.js +17 -0
  14. package/src/backend/js/stdlib/html.js +1 -1
  15. package/src/backend/js/stdlib/index.js +16 -0
  16. package/src/backend/js/stdlib/io.js +105 -3
  17. package/src/backend/js/stdlib/math.js +13 -1
  18. package/src/backend/js/stdlib/path.js +18 -4
  19. package/src/backend/js/stdlib/rand.js +10 -5
  20. package/src/backend/js/stdlib/sha256.js +12 -0
  21. package/src/backend/js/stdlib/slices.js +52 -26
  22. package/src/backend/js/stdlib/strconv.js +21 -5
  23. package/src/backend/js/stdlib/strings.js +148 -15
  24. package/src/backend/js/stdlib/sync.js +13 -0
  25. package/src/backend/js/stdlib/time.js +90 -19
  26. package/src/backend/js/stdlib/unicode.js +2 -2
  27. package/src/backend/wasm/boundary.js +1207 -41
  28. package/src/backend/wasm/const-fold.js +205 -0
  29. package/src/backend/wasm/emit-builtins.js +203 -76
  30. package/src/backend/wasm/emit-exprs.js +464 -276
  31. package/src/backend/wasm/emit-maps.js +144 -8
  32. package/src/backend/wasm/emit-stdlib.js +12 -3005
  33. package/src/backend/wasm/emit-stmts.js +264 -270
  34. package/src/backend/wasm/emit.js +115 -87
  35. package/src/backend/wasm/encode.js +55 -29
  36. package/src/backend/wasm/index.js +1001 -32
  37. package/src/backend/wasm/optimize.js +13 -4
  38. package/src/backend/wasm/stdlib/bytes.js +5039 -0
  39. package/src/backend/wasm/stdlib/core.js +8903 -0
  40. package/src/backend/wasm/stdlib/index.js +40 -0
  41. package/src/backend/wasm/stdlib/io.js +2161 -0
  42. package/src/backend/wasm/stdlib/packages.js +2400 -0
  43. package/src/backend/wasm/stdlib/pure.js +720 -0
  44. package/src/backend/wasm/stdlib/time.js +1200 -0
  45. package/src/backend/wasm/types.js +153 -1
  46. package/src/cli-core.js +1 -1
  47. package/src/compiler.js +18 -4
  48. package/src/dts-parser.js +79 -126
  49. package/src/index.js +2 -6
  50. package/src/lexer.js +80 -29
  51. package/src/lower/boxing.js +1 -2
  52. package/src/lower/captures.js +2 -6
  53. package/src/lower/ownership.js +30 -9
  54. package/src/minifier.js +20 -68
  55. package/src/parser/declarations.js +3 -6
  56. package/src/parser/expressions.js +3 -8
  57. package/src/parser/index.js +18 -3
  58. package/src/parser/statements.js +25 -38
  59. package/src/parser/types.js +1 -2
  60. package/src/resolver.js +2 -27
  61. package/src/templ-lexer.js +29 -63
  62. package/src/templ-parser.js +22 -45
  63. package/src/typechecker/assignability.js +81 -13
  64. package/src/typechecker/expressions.js +102 -2
  65. package/src/typechecker/resolve.js +36 -6
  66. package/src/typechecker/stdlib/core.js +280 -89
  67. package/src/typechecker/stdlib/extended.js +453 -1
  68. package/src/typechecker/types.js +344 -6
@@ -1,3006 +1,13 @@
1
1
  // src/backend/wasm/emit-stdlib.js
2
- // FunctionEmitter mixin: stdlib packages implemented natively (math, math/bits,
3
- // maps, slices, strings, strconv, fmt, errors, sort, unicode/utf8, testing)
4
- // plus the runtime helper bodies they rely on.
5
-
6
- import {
7
- ERROR_STRUCT,
8
- getMapKeyValTypes,
9
- isErrorType,
10
- toWasmType,
11
- } from "./types.js";
12
-
13
- // `unicode/utf8` package constants.
14
- const UTF8_CONSTS = {
15
- RuneError: 0xfffd,
16
- MaxRune: 0x10ffff,
17
- UTFMax: 4,
18
- RuneSelf: 0x80,
19
- };
20
-
21
- // Go `math` package constants; BigInt entries are integer-typed. Float
22
- // literals are the shortest decimal that round-trips to Go's float64 value.
23
- const MATH_CONSTS = {
24
- E: Math.E,
25
- Pi: Math.PI,
26
- Phi: 1.618033988749895,
27
- Sqrt2: Math.SQRT2,
28
- SqrtE: 1.6487212707001282,
29
- SqrtPi: 1.7724538509055159,
30
- SqrtPhi: 1.272019649514069,
31
- Ln2: Math.LN2,
32
- Log2E: Math.LOG2E,
33
- Ln10: Math.LN10,
34
- Log10E: Math.LOG10E,
35
- MaxFloat32: 3.4028234663852886e38,
36
- SmallestNonzeroFloat32: 1.401298464324817e-45,
37
- MaxFloat64: Number.MAX_VALUE,
38
- SmallestNonzeroFloat64: Number.MIN_VALUE,
39
- MaxInt: 9223372036854775807n,
40
- MinInt: -9223372036854775808n,
41
- MaxInt8: 127n,
42
- MinInt8: -128n,
43
- MaxInt16: 32767n,
44
- MinInt16: -32768n,
45
- MaxInt32: 2147483647n,
46
- MinInt32: -2147483648n,
47
- MaxInt64: 9223372036854775807n,
48
- MinInt64: -9223372036854775808n,
49
- MaxUint8: 255n,
50
- MaxUint16: 65535n,
51
- MaxUint32: 4294967295n,
52
- MaxUint: 18446744073709551615n,
53
- MaxUint64: 18446744073709551615n,
54
- };
55
-
56
- // `math` functions that map 1:1 onto a single-operand f64 instruction.
57
- const MATH_F64_UNARY = {
58
- Sqrt: "f64.sqrt",
59
- Floor: "f64.floor",
60
- Ceil: "f64.ceil",
61
- Trunc: "f64.trunc",
62
- Abs: "f64.abs",
63
- };
64
-
65
- // Imported `Math.*` functions taking two f64 operands.
66
- export const BINARY_MATH_IMPORTS = new Set(["atan2", "pow", "mod", "hypot"]);
67
-
68
- export class StdlibEmitter {
69
- _isMathConst(expr) {
70
- return (
71
- expr.expr?.kind === "Ident" &&
72
- expr.expr.name === "math" &&
73
- !this.resolveLocal("math") &&
74
- !this.mod.resolveGlobal("math") &&
75
- MATH_CONSTS[expr.field] !== undefined
76
- );
77
- }
78
-
79
- _isUtf8Const(expr) {
80
- return (
81
- expr.expr?.kind === "Ident" &&
82
- expr.expr.name === "utf8" &&
83
- !this.resolveLocal("utf8") &&
84
- !this.mod.resolveGlobal("utf8") &&
85
- UTF8_CONSTS[expr.field] !== undefined
86
- );
87
- }
88
-
89
- emitUtf8Const(name, targetWasmType) {
90
- const v = UTF8_CONSTS[name];
91
- if ((targetWasmType ?? "i32") === "i64") {
92
- this.pushInstruction({ op: "i64.const", value: BigInt(v) });
93
- } else {
94
- this.pushInstruction({ op: "i32.const", value: v });
95
- }
96
- }
97
-
98
- emitMathConst(name, targetWasmType) {
99
- const v = MATH_CONSTS[name];
100
- const wType = targetWasmType ?? (typeof v === "bigint" ? "i64" : "f64");
101
- switch (wType) {
102
- case "i32":
103
- this.pushInstruction({ op: "i32.const", value: Number(v) | 0 });
104
- return;
105
- case "i64":
106
- this.pushInstruction({
107
- op: "i64.const",
108
- value: BigInt.asIntN(
109
- 64,
110
- typeof v === "bigint" ? v : BigInt(Math.trunc(v)),
111
- ),
112
- });
113
- return;
114
- case "f32":
115
- this.pushInstruction({
116
- op: "f32.const",
117
- value: Math.fround(Number(v)),
118
- });
119
- return;
120
- default:
121
- this.pushInstruction({ op: "f64.const", value: Number(v) });
122
- }
123
- }
124
-
125
- emitMathCall(name, args) {
126
- // Native WASM instructions
127
- const unaryOp = MATH_F64_UNARY[name];
128
- if (unaryOp) {
129
- this.emitExpr(args[0], "f64");
130
- this.pushInstruction(unaryOp);
131
- return;
132
- }
133
- switch (name) {
134
- case "Min":
135
- this.emitExpr(args[0], "f64");
136
- this.emitExpr(args[1], "f64");
137
- this.pushInstruction("f64.min");
138
- return;
139
- case "Max":
140
- this.emitExpr(args[0], "f64");
141
- this.emitExpr(args[1], "f64");
142
- this.pushInstruction("f64.max");
143
- return;
144
- case "Copysign":
145
- this.emitExpr(args[0], "f64");
146
- this.emitExpr(args[1], "f64");
147
- this.pushInstruction("f64.copysign");
148
- return;
149
- case "Dim":
150
- // max(x-y, 0)
151
- this.emitExpr(args[0], "f64");
152
- this.emitExpr(args[1], "f64");
153
- this.pushInstruction("f64.sub");
154
- this.pushInstruction({ op: "f64.const", value: 0 });
155
- this.pushInstruction("f64.max");
156
- return;
157
- case "Signbit":
158
- this.emitExpr(args[0], "f64");
159
- this.pushInstruction("i64.reinterpret_f64");
160
- this.pushInstruction({ op: "i64.const", value: 0n });
161
- this.pushInstruction("i64.lt_s");
162
- return;
163
- case "Exp2":
164
- this.pushInstruction({ op: "f64.const", value: 2 });
165
- this.emitExpr(args[0], "f64");
166
- this.pushInstruction({
167
- op: "call",
168
- funcIndex: this.mod.getMathImportIndex("pow", true),
169
- });
170
- return;
171
- case "Inf":
172
- // Go: Inf(sign) is +Inf when sign >= 0; copysign with the converted
173
- // sign gives exactly that (0 converts to +0.0).
174
- this.pushInstruction({ op: "f64.const", value: Infinity });
175
- this.emitExpr(args[0], "i64");
176
- this.pushInstruction("f64.convert_i64_s");
177
- this.pushInstruction("f64.copysign");
178
- return;
179
- case "NaN":
180
- this.pushInstruction({ op: "f64.const", value: NaN });
181
- return;
182
- case "IsNaN": {
183
- const tmp = this.acquireTemp("f64");
184
- this.emitExpr(args[0], "f64");
185
- this.pushInstruction({ op: "local.tee", index: tmp });
186
- this.pushInstruction({ op: "local.get", index: tmp });
187
- this.pushInstruction("f64.ne");
188
- this.releaseTemp(tmp, "f64");
189
- return;
190
- }
191
- case "IsInf": {
192
- // IsInf(f, sign): sign > 0 → f == +Inf, sign < 0 → f == -Inf, else |f| == Inf.
193
- const f = this.acquireTemp("f64");
194
- const s = this.acquireTemp("i64");
195
- this.emitExpr(args[0], "f64");
196
- this.pushInstruction({ op: "local.set", index: f });
197
- this.emitExpr(args[1], "i64");
198
- this.pushInstruction({ op: "local.set", index: s });
199
- // (s >= 0 && f == +Inf) || (s <= 0 && f == -Inf)
200
- this.pushInstruction({ op: "local.get", index: s });
201
- this.pushInstruction({ op: "i64.const", value: 0n });
202
- this.pushInstruction("i64.ge_s");
203
- this.pushInstruction({ op: "local.get", index: f });
204
- this.pushInstruction({ op: "f64.const", value: Infinity });
205
- this.pushInstruction("f64.eq");
206
- this.pushInstruction("i32.and");
207
- this.pushInstruction({ op: "local.get", index: s });
208
- this.pushInstruction({ op: "i64.const", value: 0n });
209
- this.pushInstruction("i64.le_s");
210
- this.pushInstruction({ op: "local.get", index: f });
211
- this.pushInstruction({ op: "f64.const", value: -Infinity });
212
- this.pushInstruction("f64.eq");
213
- this.pushInstruction("i32.and");
214
- this.pushInstruction("i32.or");
215
- this.releaseTemp(s, "i64");
216
- this.releaseTemp(f, "f64");
217
- return;
218
- }
219
- }
220
-
221
- // Imported JS Math functions
222
- const jsMathMap = {
223
- Sin: "sin",
224
- Cos: "cos",
225
- Tan: "tan",
226
- Asin: "asin",
227
- Acos: "acos",
228
- Atan: "atan",
229
- Atan2: "atan2",
230
- Pow: "pow",
231
- Mod: "mod",
232
- Hypot: "hypot",
233
- Cbrt: "cbrt",
234
- Exp: "exp",
235
- Log: "log",
236
- Log2: "log2",
237
- Log10: "log10",
238
- Round: "round",
239
- };
240
-
241
- if (jsMathMap[name]) {
242
- const jsName = jsMathMap[name];
243
- const isBinary = BINARY_MATH_IMPORTS.has(jsName);
244
- this.emitExpr(args[0], "f64");
245
- if (isBinary) this.emitExpr(args[1], "f64");
246
- const funcIdx = this.mod.getMathImportIndex(jsName, isBinary);
247
- this.pushInstruction({ op: "call", funcIndex: funcIdx });
248
- return;
249
- }
250
-
251
- throw new Error(`Unsupported math function: math.${name}`);
252
- }
253
-
254
- // `t.Method(args...)` on a `*testing.T`: the receiver is a JS object held
255
- // as externref. Arguments are pushed to a JS-side buffer one at a time
256
- // (typed imports), then `testing_call(t, nameIdx)` dispatches by name.
257
- emitTestingCall(func, args) {
258
- const method = func.field;
259
- const nameIdx = this.mod.internString(method);
260
- if (method === "Run") {
261
- this.emitExpr(func.expr, "externref");
262
- this.emitExpr(args[0], "externref");
263
- this.emitExpr(args[1], "anyref");
264
- this.pushInstruction({
265
- op: "call",
266
- funcIndex: this.mod.getStdlibImportIndex("testing_run"),
267
- });
268
- return;
269
- }
270
- if (method === "Name") {
271
- this.emitExpr(func.expr, "externref");
272
- this.pushInstruction({
273
- op: "call",
274
- funcIndex: this.mod.getTestingNameImportIndex(),
275
- });
276
- return;
277
- }
278
- if (method === "Failed" || method === "Skipped") {
279
- this.emitExpr(func.expr, "externref");
280
- this.pushInstruction({ op: "i32.const", value: nameIdx });
281
- this.pushInstruction({
282
- op: "call",
283
- funcIndex: this.mod.getTestingFlagImportIndex(),
284
- });
285
- return;
286
- }
287
- const temps = this._emitArgBuffer(args);
288
- this.emitExpr(func.expr, "externref");
289
- this.pushInstruction({ op: "i32.const", value: nameIdx });
290
- this.pushInstruction({
291
- op: "call",
292
- funcIndex: this.mod.getTestingCallImportIndex(),
293
- });
294
- this._releaseArgBuffer(temps);
295
- }
296
-
297
- // Evaluates every argument into a temp first, then pushes them in order via
298
- // the typed `testing_arg_*` imports. Anything that may run user code (the
299
- // argument itself, or `Error()` on an `error` operand) happens in the first
300
- // phase: such code can call Sprintf and reset the JS-side argument buffer,
301
- // so nothing may be pushed until every operand is fully evaluated.
302
- // Returns the temps so the caller can still read an argument (fmt.Errorf's
303
- // %w cause keeps the original error value in `errTmp`).
304
- _emitArgBuffer(args) {
305
- const temps = [];
306
- for (const arg of args) {
307
- const goType = arg._type ?? this._resolveExprGoType(arg);
308
- const isBool =
309
- goType?.name === "bool" ||
310
- (arg.kind === "BasicLit" && arg.litKind === "BOOL");
311
- const isErr = isErrorType(goType) && this.mod.hasErrorType;
312
- const valW = toWasmType(goType, this.mod.checker);
313
- if (isErr) {
314
- const errTmp = this.acquireTemp("anyref");
315
- const tmp = this.acquireTemp("externref");
316
- this.emitExpr(arg, "anyref");
317
- this.pushInstruction({ op: "local.tee", index: errTmp });
318
- this.pushInstruction({
319
- op: "call",
320
- funcIndex: this.mod.resolveFuncIndex("__error_str"),
321
- });
322
- this.pushInstruction({ op: "local.set", index: tmp });
323
- temps.push({ tmp, wType: "externref", isBool: false, errTmp });
324
- continue;
325
- }
326
- const tmp = this.acquireTemp(valW);
327
- this.emitExpr(arg, valW);
328
- this.pushInstruction({ op: "local.set", index: tmp });
329
- temps.push({ tmp, wType: valW, isBool, errTmp: null });
330
- }
331
- for (const { tmp, wType, isBool } of temps) {
332
- this.pushInstruction({ op: "local.get", index: tmp });
333
- this.pushInstruction({
334
- op: "call",
335
- funcIndex: this.mod.getTestingArgImportIndex(wType, isBool),
336
- });
337
- }
338
- return temps;
339
- }
340
-
341
- _releaseArgBuffer(temps) {
342
- for (let i = temps.length - 1; i >= 0; i--) {
343
- this.releaseTemp(temps[i].tmp, temps[i].wType);
344
- if (temps[i].errTmp !== null) this.releaseTemp(temps[i].errTmp, "anyref");
345
- }
346
- }
347
-
348
- emitBitsCall(name, args) {
349
- switch (name) {
350
- case "LeadingZeros32":
351
- this.emitExpr(args[0], "i32");
352
- this.pushInstruction("i32.clz");
353
- this.pushInstruction("i64.extend_i32_u");
354
- return;
355
- case "TrailingZeros32":
356
- this.emitExpr(args[0], "i32");
357
- this.pushInstruction("i32.ctz");
358
- this.pushInstruction("i64.extend_i32_u");
359
- return;
360
- case "OnesCount32":
361
- this.emitExpr(args[0], "i32");
362
- this.pushInstruction("i32.popcnt");
363
- this.pushInstruction("i64.extend_i32_u");
364
- return;
365
- case "RotateLeft32": {
366
- this.emitExpr(args[0], "i32");
367
- const arg1WType = toWasmType(args[1]?._type, this.mod.checker);
368
- if (arg1WType === "i64") {
369
- this.emitExpr(args[1], "i64");
370
- this.pushInstruction("i32.wrap_i64");
371
- } else {
372
- this.emitExpr(args[1], "i32");
373
- }
374
- this.pushInstruction("i32.rotl");
375
- return;
376
- }
377
-
378
- case "LeadingZeros64":
379
- this.emitExpr(args[0], "i64");
380
- this.pushInstruction("i64.clz");
381
- return;
382
- case "TrailingZeros64":
383
- this.emitExpr(args[0], "i64");
384
- this.pushInstruction("i64.ctz");
385
- return;
386
- case "OnesCount64":
387
- this.emitExpr(args[0], "i64");
388
- this.pushInstruction("i64.popcnt");
389
- return;
390
- case "RotateLeft64": {
391
- this.emitExpr(args[0], "i64");
392
- const arg1WType = toWasmType(args[1]?._type, this.mod.checker);
393
- if (arg1WType === "i32") {
394
- this.emitExpr(args[1], "i32");
395
- this.pushInstruction("i64.extend_i32_s");
396
- } else {
397
- this.emitExpr(args[1], "i64");
398
- }
399
- this.pushInstruction("i64.rotl");
400
- return;
401
- }
402
-
403
- default:
404
- throw new Error(`Unsupported bits function: bits.${name}`);
405
- }
406
- }
407
-
408
- emitSliceClear(arg, argType) {
409
- const elemGoType = this._getSliceElemType(argType);
410
- const sliceInfo = this.mod.getSliceType(elemGoType);
411
- const arrInfo = sliceInfo.arrInfo;
412
-
413
- const sliceTmp = this.acquireTemp({
414
- kind: "ref",
415
- nullable: true,
416
- typeIndex: sliceInfo.typeIndex,
417
- });
418
- this.emitExpr(arg, {
419
- kind: "ref",
420
- nullable: true,
421
- typeIndex: sliceInfo.typeIndex,
422
- });
423
- this.pushInstruction({ op: "local.set", index: sliceTmp });
424
-
425
- this.pushInstruction({ op: "block", blockType: "void" });
426
- this.pushInstruction({ op: "local.get", index: sliceTmp });
427
- this.pushInstruction("ref.is_null");
428
- this.pushInstruction({ op: "br_if", depth: 0 });
429
-
430
- const arrTmp = this.acquireTemp({
431
- kind: "ref",
432
- nullable: true,
433
- typeIndex: arrInfo.typeIndex,
434
- });
435
- const offTmp = this.acquireTemp("i32");
436
- const lenTmp = this.acquireTemp("i32");
437
- const iLoc = this.acquireTemp("i32");
438
-
439
- this._emitSliceUnpack(sliceTmp, sliceInfo, {
440
- arr: arrTmp,
441
- off: offTmp,
442
- len: lenTmp,
443
- });
444
-
445
- this.pushInstruction({ op: "i32.const", value: 0 });
446
- this.pushInstruction({ op: "local.set", index: iLoc });
447
-
448
- this.pushInstruction({ op: "block", blockType: "void" });
449
- this.pushInstruction({ op: "loop", blockType: "void" });
450
- this.pushInstruction({ op: "local.get", index: iLoc });
451
- this.pushInstruction({ op: "local.get", index: lenTmp });
452
- this.pushInstruction("i32.ge_s");
453
- this.pushInstruction({ op: "br_if", depth: 1 });
454
-
455
- this.pushInstruction({ op: "local.get", index: arrTmp });
456
- this.pushInstruction({ op: "local.get", index: offTmp });
457
- this.pushInstruction({ op: "local.get", index: iLoc });
458
- this.pushInstruction("i32.add");
459
- this.emitZeroValue(elemGoType, arrInfo.elemWType);
460
- this.pushInstruction({ op: "array.set", typeIndex: arrInfo.typeIndex });
461
-
462
- this.pushInstruction({ op: "local.get", index: iLoc });
463
- this.pushInstruction({ op: "i32.const", value: 1 });
464
- this.pushInstruction("i32.add");
465
- this.pushInstruction({ op: "local.set", index: iLoc });
466
- this.pushInstruction({ op: "br", depth: 0 });
467
- this.pushInstruction("end");
468
- this.pushInstruction("end");
469
-
470
- this.releaseTemp(iLoc, "i32");
471
- this.releaseTemp(lenTmp, "i32");
472
- this.releaseTemp(offTmp, "i32");
473
- this.releaseTemp(arrTmp, {
474
- kind: "ref",
475
- nullable: true,
476
- typeIndex: arrInfo.typeIndex,
477
- });
478
-
479
- this.pushInstruction("end");
480
- this.releaseTemp(sliceTmp, {
481
- kind: "ref",
482
- nullable: true,
483
- typeIndex: sliceInfo.typeIndex,
484
- });
485
- }
486
-
487
- emitMapsCall(name, args, _targetWasmType) {
488
- switch (name) {
489
- case "Keys": {
490
- const mArg = args[0];
491
- const mType = mArg._type ?? this._resolveExprGoType(mArg);
492
- const { keyType, valType } = getMapKeyValTypes(mType, this.mod.checker);
493
- const mapInfo = this.mod.getMapType(keyType, valType);
494
- this.emitExpr(mArg, mapInfo.wType);
495
- this.pushInstruction({
496
- op: "call",
497
- funcIndex: this.mod.resolveFuncIndex(mapInfo.keysFuncName),
498
- });
499
- return;
500
- }
501
- case "Values": {
502
- const mArg = args[0];
503
- const mType = mArg._type ?? this._resolveExprGoType(mArg);
504
- const { keyType, valType } = getMapKeyValTypes(mType, this.mod.checker);
505
- const mapInfo = this.mod.getMapType(keyType, valType);
506
- this.emitExpr(mArg, mapInfo.wType);
507
- this.pushInstruction({
508
- op: "call",
509
- funcIndex: this.mod.resolveFuncIndex(mapInfo.valuesFuncName),
510
- });
511
- return;
512
- }
513
- case "Clone": {
514
- const mArg = args[0];
515
- const mType = mArg._type ?? this._resolveExprGoType(mArg);
516
- const { keyType, valType } = getMapKeyValTypes(mType, this.mod.checker);
517
- const mapInfo = this.mod.getMapType(keyType, valType);
518
- this.emitExpr(mArg, mapInfo.wType);
519
- this.pushInstruction({
520
- op: "call",
521
- funcIndex: this.mod.resolveFuncIndex(mapInfo.cloneFuncName),
522
- });
523
- return;
524
- }
525
- case "Copy":
526
- this.emitMapsCopy(args);
527
- return;
528
- case "Equal":
529
- this.emitMapsEqual(args, false);
530
- return;
531
- case "EqualFunc":
532
- this.emitMapsEqual(args, true);
533
- return;
534
- case "Delete": {
535
- const mArg = args[0];
536
- const kArg = args[1];
537
- const mType = mArg._type ?? this._resolveExprGoType(mArg);
538
- const { keyType, valType } = getMapKeyValTypes(mType, this.mod.checker);
539
- const mapInfo = this.mod.getMapType(keyType, valType);
540
- this.emitExpr(mArg, mapInfo.wType);
541
- this.emitExpr(kArg, mapInfo.keyWType);
542
- this.pushInstruction({
543
- op: "call",
544
- funcIndex: this.mod.resolveFuncIndex(mapInfo.deleteFuncName),
545
- });
546
- return;
547
- }
548
- case "DeleteFunc":
549
- this.emitMapsDeleteFunc(args);
550
- return;
551
- default:
552
- throw new Error(`Unsupported maps function: maps.${name}`);
553
- }
554
- }
555
-
556
- emitMapsCopy(args) {
557
- const dstNode = args[0];
558
- const srcNode = args[1];
559
- const dstType = dstNode._type ?? this._resolveExprGoType(dstNode);
560
- const { keyType, valType } = getMapKeyValTypes(dstType, this.mod.checker);
561
- const mapInfo = this.mod.getMapType(keyType, valType);
562
-
563
- const dstTmp = this.acquireTemp(mapInfo.wType);
564
- const srcTmp = this.acquireTemp(mapInfo.wType);
565
- this.emitExpr(dstNode, mapInfo.wType);
566
- this.pushInstruction({ op: "local.set", index: dstTmp });
567
- this.emitExpr(srcNode, mapInfo.wType);
568
- this.pushInstruction({ op: "local.set", index: srcTmp });
569
-
570
- this.pushInstruction({ op: "block", blockType: "void" });
571
- // if src == null -> return
572
- this.pushInstruction({ op: "local.get", index: srcTmp });
573
- this.pushInstruction("ref.is_null");
574
- this.pushInstruction({ op: "br_if", depth: 0 });
575
-
576
- // if dst == null -> panic
577
- this.pushInstruction({ op: "local.get", index: dstTmp });
578
- this.pushInstruction("ref.is_null");
579
- this.pushInstruction({ op: "if", blockType: "void" });
580
- this.emitPanic("assignment to entry in nil map");
581
- this.pushInstruction("end");
582
-
583
- const idxTmp = this.acquireTemp("i32");
584
- const entryTmp = this.acquireTemp({
585
- kind: "ref",
586
- nullable: true,
587
- typeIndex: mapInfo.entryTypeIndex,
588
- });
589
-
590
- // idx = src.head
591
- this.pushInstruction({ op: "local.get", index: srcTmp });
592
- this.pushInstruction({
593
- op: "struct.get",
594
- typeIndex: mapInfo.typeIndex,
595
- fieldIndex: 5,
596
- });
597
- this.pushInstruction({ op: "local.set", index: idxTmp });
598
-
599
- this.pushInstruction({ op: "block", blockType: "void" });
600
- this.pushInstruction({ op: "loop", blockType: "void" });
601
- this.pushInstruction({ op: "local.get", index: idxTmp });
602
- this.pushInstruction({ op: "i32.const", value: -1 });
603
- this.pushInstruction("i32.eq");
604
- this.pushInstruction({ op: "br_if", depth: 1 });
605
-
606
- this.pushInstruction({ op: "local.get", index: srcTmp });
607
- this.pushInstruction({
608
- op: "struct.get",
609
- typeIndex: mapInfo.typeIndex,
610
- fieldIndex: 1,
611
- });
612
- this.pushInstruction({ op: "local.get", index: idxTmp });
613
- this.pushInstruction({
614
- op: "array.get",
615
- typeIndex: mapInfo.entriesTypeIndex,
616
- });
617
- this.pushInstruction({ op: "local.set", index: entryTmp });
618
-
619
- this.pushInstruction({ op: "local.get", index: entryTmp });
620
- this.pushInstruction({
621
- op: "struct.get",
622
- typeIndex: mapInfo.entryTypeIndex,
623
- fieldIndex: 5,
624
- });
625
- this.pushInstruction({ op: "i32.const", value: 1 });
626
- this.pushInstruction("i32.eq");
627
- this.pushInstruction({ op: "if", blockType: "void" });
628
-
629
- // dst[k] = v
630
- this.pushInstruction({ op: "local.get", index: dstTmp });
631
- this.pushInstruction({ op: "local.get", index: entryTmp });
632
- this.pushInstruction({
633
- op: "struct.get",
634
- typeIndex: mapInfo.entryTypeIndex,
635
- fieldIndex: 0,
636
- });
637
- this.pushInstruction({ op: "local.get", index: entryTmp });
638
- this.pushInstruction({
639
- op: "struct.get",
640
- typeIndex: mapInfo.entryTypeIndex,
641
- fieldIndex: 1,
642
- });
643
- this.pushInstruction({
644
- op: "call",
645
- funcIndex: this.mod.resolveFuncIndex(mapInfo.setFuncName),
646
- });
647
-
648
- this.pushInstruction("end");
649
-
650
- // idx = entry.order_next
651
- this.pushInstruction({ op: "local.get", index: entryTmp });
652
- this.pushInstruction({
653
- op: "struct.get",
654
- typeIndex: mapInfo.entryTypeIndex,
655
- fieldIndex: 4,
656
- });
657
- this.pushInstruction({ op: "local.set", index: idxTmp });
658
- this.pushInstruction({ op: "br", depth: 0 });
659
- this.pushInstruction("end");
660
- this.pushInstruction("end");
661
-
662
- this.releaseTemp(entryTmp, {
663
- kind: "ref",
664
- nullable: true,
665
- typeIndex: mapInfo.entryTypeIndex,
666
- });
667
- this.releaseTemp(idxTmp, "i32");
668
-
669
- this.pushInstruction("end");
670
- this.releaseTemp(srcTmp, mapInfo.wType);
671
- this.releaseTemp(dstTmp, mapInfo.wType);
672
- }
673
-
674
- emitMapsEqual(args, isFunc = false) {
675
- const m1Node = args[0];
676
- const m2Node = args[1];
677
- const eqFn = isFunc ? args[2] : null;
678
-
679
- const m1Type = m1Node._type ?? this._resolveExprGoType(m1Node);
680
- const { keyType, valType } = getMapKeyValTypes(m1Type, this.mod.checker);
681
- const mapInfo = this.mod.getMapType(keyType, valType);
682
-
683
- const m1Tmp = this.acquireTemp(mapInfo.wType);
684
- const m2Tmp = this.acquireTemp(mapInfo.wType);
685
- this.emitExpr(m1Node, mapInfo.wType);
686
- this.pushInstruction({ op: "local.set", index: m1Tmp });
687
- this.emitExpr(m2Node, mapInfo.wType);
688
- this.pushInstruction({ op: "local.set", index: m2Tmp });
689
-
690
- const resTmp = this.acquireTemp("i32");
691
- this.pushInstruction({ op: "i32.const", value: 0 });
692
- this.pushInstruction({ op: "local.set", index: resTmp });
693
-
694
- this.pushInstruction({ op: "block", blockType: "void" }); // exit block 0
695
-
696
- // if m1 == m2 -> res = 1; br 0
697
- this.pushInstruction({ op: "local.get", index: m1Tmp });
698
- this.pushInstruction({ op: "local.get", index: m2Tmp });
699
- this.pushInstruction("ref.eq");
700
- this.pushInstruction({ op: "if", blockType: "void" });
701
- this.pushInstruction({ op: "i32.const", value: 1 });
702
- this.pushInstruction({ op: "local.set", index: resTmp });
703
- this.pushInstruction({ op: "br", depth: 1 });
704
- this.pushInstruction("end");
705
-
706
- // if m1 == null || m2 == null -> br 0
707
- this.pushInstruction({ op: "local.get", index: m1Tmp });
708
- this.pushInstruction("ref.is_null");
709
- this.pushInstruction({ op: "local.get", index: m2Tmp });
710
- this.pushInstruction("ref.is_null");
711
- this.pushInstruction("i32.or");
712
- this.pushInstruction({ op: "br_if", depth: 0 });
713
-
714
- // if m1.len != m2.len -> br 0
715
- this.pushInstruction({ op: "local.get", index: m1Tmp });
716
- this.pushInstruction({
717
- op: "struct.get",
718
- typeIndex: mapInfo.typeIndex,
719
- fieldIndex: 2,
720
- });
721
- this.pushInstruction({ op: "local.get", index: m2Tmp });
722
- this.pushInstruction({
723
- op: "struct.get",
724
- typeIndex: mapInfo.typeIndex,
725
- fieldIndex: 2,
726
- });
727
- this.pushInstruction("i32.ne");
728
- this.pushInstruction({ op: "br_if", depth: 0 });
729
-
730
- // Loop m1 entries
731
- const idxTmp = this.acquireTemp("i32");
732
- const entryTmp = this.acquireTemp({
733
- kind: "ref",
734
- nullable: true,
735
- typeIndex: mapInfo.entryTypeIndex,
736
- });
737
- const val1Tmp = this.acquireTemp(mapInfo.valWType);
738
- const val2Tmp = this.acquireTemp(mapInfo.valWType);
739
- const okTmp = this.acquireTemp("i32");
740
-
741
- this.pushInstruction({ op: "local.get", index: m1Tmp });
742
- this.pushInstruction({
743
- op: "struct.get",
744
- typeIndex: mapInfo.typeIndex,
745
- fieldIndex: 5,
746
- });
747
- this.pushInstruction({ op: "local.set", index: idxTmp });
748
-
749
- this.pushInstruction({ op: "block", blockType: "void" });
750
- this.pushInstruction({ op: "loop", blockType: "void" });
751
- this.pushInstruction({ op: "local.get", index: idxTmp });
752
- this.pushInstruction({ op: "i32.const", value: -1 });
753
- this.pushInstruction("i32.eq");
754
- this.pushInstruction({ op: "br_if", depth: 1 });
755
-
756
- this.pushInstruction({ op: "local.get", index: m1Tmp });
757
- this.pushInstruction({
758
- op: "struct.get",
759
- typeIndex: mapInfo.typeIndex,
760
- fieldIndex: 1,
761
- });
762
- this.pushInstruction({ op: "local.get", index: idxTmp });
763
- this.pushInstruction({
764
- op: "array.get",
765
- typeIndex: mapInfo.entriesTypeIndex,
766
- });
767
- this.pushInstruction({ op: "local.set", index: entryTmp });
768
-
769
- this.pushInstruction({ op: "local.get", index: entryTmp });
770
- this.pushInstruction({
771
- op: "struct.get",
772
- typeIndex: mapInfo.entryTypeIndex,
773
- fieldIndex: 5,
774
- });
775
- this.pushInstruction({ op: "i32.const", value: 1 });
776
- this.pushInstruction("i32.eq");
777
- this.pushInstruction({ op: "if", blockType: "void" });
778
-
779
- // val2, ok = getOk(m2, entry.key)
780
- this.pushInstruction({ op: "local.get", index: m2Tmp });
781
- this.pushInstruction({ op: "local.get", index: entryTmp });
782
- this.pushInstruction({
783
- op: "struct.get",
784
- typeIndex: mapInfo.entryTypeIndex,
785
- fieldIndex: 0,
786
- });
787
- this.pushInstruction({
788
- op: "call",
789
- funcIndex: this.mod.resolveFuncIndex(mapInfo.getOkFuncName),
790
- });
791
- this.pushInstruction({ op: "local.set", index: okTmp });
792
- this.pushInstruction({ op: "local.set", index: val2Tmp });
793
-
794
- // if !ok -> br 3 (exit outer block with res=0)
795
- this.pushInstruction({ op: "local.get", index: okTmp });
796
- this.pushInstruction("i32.eqz");
797
- this.pushInstruction({ op: "br_if", depth: 3 });
798
-
799
- // check entry.val == val2
800
- this.pushInstruction({ op: "local.get", index: entryTmp });
801
- this.pushInstruction({
802
- op: "struct.get",
803
- typeIndex: mapInfo.entryTypeIndex,
804
- fieldIndex: 1,
805
- });
806
- this.pushInstruction({ op: "local.set", index: val1Tmp });
807
-
808
- if (isFunc) {
809
- const eqClosureType = this._resolveExprGoType(eqFn);
810
- const closureInfo = this.mod.getClosureType(eqClosureType);
811
- const closureTmp = this.acquireTemp({
812
- kind: "ref",
813
- nullable: true,
814
- typeIndex: closureInfo.typeIndex,
815
- });
816
- this.emitExpr(eqFn, {
817
- kind: "ref",
818
- nullable: true,
819
- typeIndex: closureInfo.typeIndex,
820
- });
821
- this.pushInstruction({ op: "local.set", index: closureTmp });
822
-
823
- // env
824
- this.pushInstruction({ op: "local.get", index: closureTmp });
825
- this.pushInstruction({
826
- op: "struct.get",
827
- typeIndex: closureInfo.typeIndex,
828
- fieldIndex: 1,
829
- });
830
- // arg 0: val1
831
- this.pushInstruction({ op: "local.get", index: val1Tmp });
832
- // arg 1: val2
833
- this.pushInstruction({ op: "local.get", index: val2Tmp });
834
- // funcref
835
- this.pushInstruction({ op: "local.get", index: closureTmp });
836
- this.pushInstruction({
837
- op: "struct.get",
838
- typeIndex: closureInfo.typeIndex,
839
- fieldIndex: 0,
840
- });
841
- this.pushInstruction({
842
- op: "call_ref",
843
- typeIndex: closureInfo.funcTypeIndex,
844
- });
845
- this.releaseTemp(closureTmp, {
846
- kind: "ref",
847
- nullable: true,
848
- typeIndex: closureInfo.typeIndex,
849
- });
850
-
851
- this.pushInstruction("i32.eqz");
852
- this.pushInstruction({ op: "br_if", depth: 3 });
853
- } else {
854
- this.emitKeyEq(val1Tmp, val2Tmp, valType);
855
- this.pushInstruction("i32.eqz");
856
- this.pushInstruction({ op: "br_if", depth: 3 });
857
- }
858
-
859
- this.pushInstruction("end"); // end if active
860
-
861
- // idx = entry.order_next
862
- this.pushInstruction({ op: "local.get", index: entryTmp });
863
- this.pushInstruction({
864
- op: "struct.get",
865
- typeIndex: mapInfo.entryTypeIndex,
866
- fieldIndex: 4,
867
- });
868
- this.pushInstruction({ op: "local.set", index: idxTmp });
869
- this.pushInstruction({ op: "br", depth: 0 });
870
- this.pushInstruction("end");
871
- this.pushInstruction("end");
872
-
873
- // All matched: res = 1
874
- this.pushInstruction({ op: "i32.const", value: 1 });
875
- this.pushInstruction({ op: "local.set", index: resTmp });
876
-
877
- this.releaseTemp(okTmp, "i32");
878
- this.releaseTemp(val2Tmp, mapInfo.valWType);
879
- this.releaseTemp(val1Tmp, mapInfo.valWType);
880
- this.releaseTemp(entryTmp, {
881
- kind: "ref",
882
- nullable: true,
883
- typeIndex: mapInfo.entryTypeIndex,
884
- });
885
- this.releaseTemp(idxTmp, "i32");
886
-
887
- this.pushInstruction("end"); // end outer block 0
888
-
889
- this.pushInstruction({ op: "local.get", index: resTmp });
890
- this.releaseTemp(resTmp, "i32");
891
- this.releaseTemp(m2Tmp, mapInfo.wType);
892
- this.releaseTemp(m1Tmp, mapInfo.wType);
893
- }
894
-
895
- emitMapsDeleteFunc(args) {
896
- const mNode = args[0];
897
- const delFn = args[1];
898
- const mType = mNode._type ?? this._resolveExprGoType(mNode);
899
- const { keyType, valType } = getMapKeyValTypes(mType, this.mod.checker);
900
- const mapInfo = this.mod.getMapType(keyType, valType);
901
-
902
- const mTmp = this.acquireTemp(mapInfo.wType);
903
- this.emitExpr(mNode, mapInfo.wType);
904
- this.pushInstruction({ op: "local.set", index: mTmp });
905
-
906
- this.pushInstruction({ op: "block", blockType: "void" });
907
- this.pushInstruction({ op: "local.get", index: mTmp });
908
- this.pushInstruction("ref.is_null");
909
- this.pushInstruction({ op: "br_if", depth: 0 });
910
-
911
- const idxTmp = this.acquireTemp("i32");
912
- const nextTmp = this.acquireTemp("i32");
913
- const entryTmp = this.acquireTemp({
914
- kind: "ref",
915
- nullable: true,
916
- typeIndex: mapInfo.entryTypeIndex,
917
- });
918
- const keyTmp = this.acquireTemp(mapInfo.keyWType);
919
- const valTmp = this.acquireTemp(mapInfo.valWType);
920
-
921
- const delClosureType = this._resolveExprGoType(delFn);
922
- const closureInfo = this.mod.getClosureType(delClosureType);
923
- const closureTmp = this.acquireTemp({
924
- kind: "ref",
925
- nullable: true,
926
- typeIndex: closureInfo.typeIndex,
927
- });
928
- this.emitExpr(delFn, {
929
- kind: "ref",
930
- nullable: true,
931
- typeIndex: closureInfo.typeIndex,
932
- });
933
- this.pushInstruction({ op: "local.set", index: closureTmp });
934
-
935
- this.pushInstruction({ op: "local.get", index: mTmp });
936
- this.pushInstruction({
937
- op: "struct.get",
938
- typeIndex: mapInfo.typeIndex,
939
- fieldIndex: 5,
940
- });
941
- this.pushInstruction({ op: "local.set", index: idxTmp });
942
-
943
- this.pushInstruction({ op: "block", blockType: "void" });
944
- this.pushInstruction({ op: "loop", blockType: "void" });
945
- this.pushInstruction({ op: "local.get", index: idxTmp });
946
- this.pushInstruction({ op: "i32.const", value: -1 });
947
- this.pushInstruction("i32.eq");
948
- this.pushInstruction({ op: "br_if", depth: 1 });
949
-
950
- this.pushInstruction({ op: "local.get", index: mTmp });
951
- this.pushInstruction({
952
- op: "struct.get",
953
- typeIndex: mapInfo.typeIndex,
954
- fieldIndex: 1,
955
- });
956
- this.pushInstruction({ op: "local.get", index: idxTmp });
957
- this.pushInstruction({
958
- op: "array.get",
959
- typeIndex: mapInfo.entriesTypeIndex,
960
- });
961
- this.pushInstruction({ op: "local.set", index: entryTmp });
962
-
963
- this.pushInstruction({ op: "local.get", index: entryTmp });
964
- this.pushInstruction({
965
- op: "struct.get",
966
- typeIndex: mapInfo.entryTypeIndex,
967
- fieldIndex: 4,
968
- });
969
- this.pushInstruction({ op: "local.set", index: nextTmp });
970
-
971
- this.pushInstruction({ op: "local.get", index: entryTmp });
972
- this.pushInstruction({
973
- op: "struct.get",
974
- typeIndex: mapInfo.entryTypeIndex,
975
- fieldIndex: 5,
976
- });
977
- this.pushInstruction({ op: "i32.const", value: 1 });
978
- this.pushInstruction("i32.eq");
979
- this.pushInstruction({ op: "if", blockType: "void" });
980
-
981
- this.pushInstruction({ op: "local.get", index: entryTmp });
982
- this.pushInstruction({
983
- op: "struct.get",
984
- typeIndex: mapInfo.entryTypeIndex,
985
- fieldIndex: 0,
986
- });
987
- this.pushInstruction({ op: "local.set", index: keyTmp });
988
-
989
- this.pushInstruction({ op: "local.get", index: entryTmp });
990
- this.pushInstruction({
991
- op: "struct.get",
992
- typeIndex: mapInfo.entryTypeIndex,
993
- fieldIndex: 1,
994
- });
995
- this.pushInstruction({ op: "local.set", index: valTmp });
996
-
997
- // call delFn(key, val)
998
- this.pushInstruction({ op: "local.get", index: closureTmp });
999
- this.pushInstruction({
1000
- op: "struct.get",
1001
- typeIndex: closureInfo.typeIndex,
1002
- fieldIndex: 1,
1003
- });
1004
- this.pushInstruction({ op: "local.get", index: keyTmp });
1005
- this.pushInstruction({ op: "local.get", index: valTmp });
1006
- this.pushInstruction({ op: "local.get", index: closureTmp });
1007
- this.pushInstruction({
1008
- op: "struct.get",
1009
- typeIndex: closureInfo.typeIndex,
1010
- fieldIndex: 0,
1011
- });
1012
- this.pushInstruction({
1013
- op: "call_ref",
1014
- typeIndex: closureInfo.funcTypeIndex,
1015
- });
1016
-
1017
- // if delFn returned true -> delete(m, key)
1018
- this.pushInstruction({ op: "if", blockType: "void" });
1019
- this.pushInstruction({ op: "local.get", index: mTmp });
1020
- this.pushInstruction({ op: "local.get", index: keyTmp });
1021
- this.pushInstruction({
1022
- op: "call",
1023
- funcIndex: this.mod.resolveFuncIndex(mapInfo.deleteFuncName),
1024
- });
1025
- this.pushInstruction("end");
1026
-
1027
- this.pushInstruction("end"); // end if active
1028
-
1029
- this.pushInstruction({ op: "local.get", index: nextTmp });
1030
- this.pushInstruction({ op: "local.set", index: idxTmp });
1031
- this.pushInstruction({ op: "br", depth: 0 });
1032
- this.pushInstruction("end");
1033
- this.pushInstruction("end");
1034
-
1035
- this.releaseTemp(closureTmp, {
1036
- kind: "ref",
1037
- nullable: true,
1038
- typeIndex: closureInfo.typeIndex,
1039
- });
1040
- this.releaseTemp(valTmp, mapInfo.valWType);
1041
- this.releaseTemp(keyTmp, mapInfo.keyWType);
1042
- this.releaseTemp(entryTmp, {
1043
- kind: "ref",
1044
- nullable: true,
1045
- typeIndex: mapInfo.entryTypeIndex,
1046
- });
1047
- this.releaseTemp(nextTmp, "i32");
1048
- this.releaseTemp(idxTmp, "i32");
1049
-
1050
- this.pushInstruction("end");
1051
- this.releaseTemp(mTmp, mapInfo.wType);
1052
- }
1053
-
1054
- emitSlicesCall(name, args, targetWasmType) {
1055
- switch (name) {
1056
- case "Sort":
1057
- this.emitSlicesSort(args);
1058
- return;
1059
- case "Reverse":
1060
- this.emitSlicesReverse(args);
1061
- return;
1062
- case "Contains":
1063
- this.emitSlicesContains(args);
1064
- return;
1065
- case "Index":
1066
- this.emitSlicesIndex(args, targetWasmType);
1067
- return;
1068
- case "Equal":
1069
- this.emitSlicesEqual(args);
1070
- return;
1071
- case "Clone":
1072
- this.emitSlicesClone(args);
1073
- return;
1074
- default:
1075
- throw new Error(`Unsupported slices function: slices.${name}`);
1076
- }
1077
- }
1078
-
1079
- emitSlicesSort(args) {
1080
- const sNode = args[0];
1081
- const sType = sNode._type ?? this._resolveExprGoType(sNode);
1082
- const elemGoType = this._getSliceElemType(sType);
1083
- const sliceInfo = this.mod.getSliceType(elemGoType);
1084
- const arrInfo = sliceInfo.arrInfo;
1085
-
1086
- const sliceTmp = this.acquireTemp({
1087
- kind: "ref",
1088
- nullable: true,
1089
- typeIndex: sliceInfo.typeIndex,
1090
- });
1091
- this.emitExpr(sNode, {
1092
- kind: "ref",
1093
- nullable: true,
1094
- typeIndex: sliceInfo.typeIndex,
1095
- });
1096
- this.pushInstruction({ op: "local.set", index: sliceTmp });
1097
-
1098
- this.pushInstruction({ op: "block", blockType: "void" });
1099
-
1100
- this.pushInstruction({ op: "local.get", index: sliceTmp });
1101
- this.pushInstruction("ref.is_null");
1102
- this.pushInstruction({ op: "br_if", depth: 0 });
1103
-
1104
- const arrTmp = this.acquireTemp({
1105
- kind: "ref",
1106
- nullable: true,
1107
- typeIndex: arrInfo.typeIndex,
1108
- });
1109
- const offTmp = this.acquireTemp("i32");
1110
- const lenTmp = this.acquireTemp("i32");
1111
-
1112
- this._emitSliceUnpack(sliceTmp, sliceInfo, {
1113
- arr: arrTmp,
1114
- off: offTmp,
1115
- len: lenTmp,
1116
- });
1117
-
1118
- // if len <= 1 -> br 0
1119
- this.pushInstruction({ op: "local.get", index: lenTmp });
1120
- this.pushInstruction({ op: "i32.const", value: 1 });
1121
- this.pushInstruction("i32.le_s");
1122
- this.pushInstruction({ op: "br_if", depth: 0 });
1123
-
1124
- const iLoc = this.acquireTemp("i32");
1125
- const jLoc = this.acquireTemp("i32");
1126
- const keyLoc = this.acquireTemp(arrInfo.elemWType);
1127
- const testLoc = this.acquireTemp(arrInfo.elemWType);
1128
-
1129
- // i = 1
1130
- this.pushInstruction({ op: "i32.const", value: 1 });
1131
- this.pushInstruction({ op: "local.set", index: iLoc });
1132
-
1133
- // Outer loop
1134
- this.pushInstruction({ op: "loop", blockType: "void" });
1135
-
1136
- // key = arr[off + i]
1137
- this.pushInstruction({ op: "local.get", index: arrTmp });
1138
- this.pushInstruction({ op: "local.get", index: offTmp });
1139
- this.pushInstruction({ op: "local.get", index: iLoc });
1140
- this.pushInstruction("i32.add");
1141
- this.pushInstruction({ op: "array.get", typeIndex: arrInfo.typeIndex });
1142
- this.pushInstruction({ op: "local.set", index: keyLoc });
1143
-
1144
- // j = i - 1
1145
- this.pushInstruction({ op: "local.get", index: iLoc });
1146
- this.pushInstruction({ op: "i32.const", value: 1 });
1147
- this.pushInstruction("i32.sub");
1148
- this.pushInstruction({ op: "local.set", index: jLoc });
1149
-
1150
- // Inner loop
1151
- this.pushInstruction({ op: "block", blockType: "void" });
1152
- this.pushInstruction({ op: "loop", blockType: "void" });
1153
-
1154
- // if j < 0 -> break inner loop
1155
- this.pushInstruction({ op: "local.get", index: jLoc });
1156
- this.pushInstruction({ op: "i32.const", value: 0 });
1157
- this.pushInstruction("i32.lt_s");
1158
- this.pushInstruction({ op: "br_if", depth: 1 });
1159
-
1160
- // test = arr[off + j]
1161
- this.pushInstruction({ op: "local.get", index: arrTmp });
1162
- this.pushInstruction({ op: "local.get", index: offTmp });
1163
- this.pushInstruction({ op: "local.get", index: jLoc });
1164
- this.pushInstruction("i32.add");
1165
- this.pushInstruction({ op: "array.get", typeIndex: arrInfo.typeIndex });
1166
- this.pushInstruction({ op: "local.set", index: testLoc });
1167
-
1168
- // if !(key < test) -> break inner loop
1169
- this.emitElemLt(keyLoc, testLoc, elemGoType);
1170
- this.pushInstruction("i32.eqz");
1171
- this.pushInstruction({ op: "br_if", depth: 1 });
1172
-
1173
- // arr[off + j + 1] = test
1174
- this.pushInstruction({ op: "local.get", index: arrTmp });
1175
- this.pushInstruction({ op: "local.get", index: offTmp });
1176
- this.pushInstruction({ op: "local.get", index: jLoc });
1177
- this.pushInstruction("i32.add");
1178
- this.pushInstruction({ op: "i32.const", value: 1 });
1179
- this.pushInstruction("i32.add");
1180
- this.pushInstruction({ op: "local.get", index: testLoc });
1181
- this.pushInstruction({ op: "array.set", typeIndex: arrInfo.typeIndex });
1182
-
1183
- // j--
1184
- this.pushInstruction({ op: "local.get", index: jLoc });
1185
- this.pushInstruction({ op: "i32.const", value: 1 });
1186
- this.pushInstruction("i32.sub");
1187
- this.pushInstruction({ op: "local.set", index: jLoc });
1188
-
1189
- this.pushInstruction({ op: "br", depth: 0 });
1190
- this.pushInstruction("end");
1191
- this.pushInstruction("end");
1192
-
1193
- // arr[off + j + 1] = key
1194
- this.pushInstruction({ op: "local.get", index: arrTmp });
1195
- this.pushInstruction({ op: "local.get", index: offTmp });
1196
- this.pushInstruction({ op: "local.get", index: jLoc });
1197
- this.pushInstruction("i32.add");
1198
- this.pushInstruction({ op: "i32.const", value: 1 });
1199
- this.pushInstruction("i32.add");
1200
- this.pushInstruction({ op: "local.get", index: keyLoc });
1201
- this.pushInstruction({ op: "array.set", typeIndex: arrInfo.typeIndex });
1202
-
1203
- // i++
1204
- this.pushInstruction({ op: "local.get", index: iLoc });
1205
- this.pushInstruction({ op: "i32.const", value: 1 });
1206
- this.pushInstruction("i32.add");
1207
- this.pushInstruction({ op: "local.tee", index: iLoc });
1208
-
1209
- // if i < len -> continue outer loop
1210
- this.pushInstruction({ op: "local.get", index: lenTmp });
1211
- this.pushInstruction("i32.lt_s");
1212
- this.pushInstruction({ op: "br_if", depth: 0 });
1213
-
1214
- this.pushInstruction("end"); // end outer loop
1215
-
1216
- this.releaseTemp(testLoc, arrInfo.elemWType);
1217
- this.releaseTemp(keyLoc, arrInfo.elemWType);
1218
- this.releaseTemp(jLoc, "i32");
1219
- this.releaseTemp(iLoc, "i32");
1220
- this.releaseTemp(lenTmp, "i32");
1221
- this.releaseTemp(offTmp, "i32");
1222
- this.releaseTemp(arrTmp, {
1223
- kind: "ref",
1224
- nullable: true,
1225
- typeIndex: arrInfo.typeIndex,
1226
- });
1227
-
1228
- this.pushInstruction("end"); // end exit block
1229
- this.releaseTemp(sliceTmp, {
1230
- kind: "ref",
1231
- nullable: true,
1232
- typeIndex: sliceInfo.typeIndex,
1233
- });
1234
- }
1235
-
1236
- emitSlicesReverse(args) {
1237
- const sNode = args[0];
1238
- const sType = sNode._type ?? this._resolveExprGoType(sNode);
1239
- const elemGoType = this._getSliceElemType(sType);
1240
- const sliceInfo = this.mod.getSliceType(elemGoType);
1241
- const arrInfo = sliceInfo.arrInfo;
1242
-
1243
- const sliceTmp = this.acquireTemp({
1244
- kind: "ref",
1245
- nullable: true,
1246
- typeIndex: sliceInfo.typeIndex,
1247
- });
1248
- this.emitExpr(sNode, {
1249
- kind: "ref",
1250
- nullable: true,
1251
- typeIndex: sliceInfo.typeIndex,
1252
- });
1253
- this.pushInstruction({ op: "local.set", index: sliceTmp });
1254
-
1255
- this.pushInstruction({ op: "block", blockType: "void" });
1256
-
1257
- this.pushInstruction({ op: "local.get", index: sliceTmp });
1258
- this.pushInstruction("ref.is_null");
1259
- this.pushInstruction({ op: "br_if", depth: 0 });
1260
-
1261
- const arrTmp = this.acquireTemp({
1262
- kind: "ref",
1263
- nullable: true,
1264
- typeIndex: arrInfo.typeIndex,
1265
- });
1266
- const offTmp = this.acquireTemp("i32");
1267
- const lenTmp = this.acquireTemp("i32");
1268
-
1269
- this._emitSliceUnpack(sliceTmp, sliceInfo, {
1270
- arr: arrTmp,
1271
- off: offTmp,
1272
- len: lenTmp,
1273
- });
1274
-
1275
- const iLoc = this.acquireTemp("i32");
1276
- const jLoc = this.acquireTemp("i32");
1277
- const tmp1 = this.acquireTemp(arrInfo.elemWType);
1278
- const tmp2 = this.acquireTemp(arrInfo.elemWType);
1279
-
1280
- // i = 0, j = len - 1
1281
- this.pushInstruction({ op: "i32.const", value: 0 });
1282
- this.pushInstruction({ op: "local.set", index: iLoc });
1283
- this.pushInstruction({ op: "local.get", index: lenTmp });
1284
- this.pushInstruction({ op: "i32.const", value: 1 });
1285
- this.pushInstruction("i32.sub");
1286
- this.pushInstruction({ op: "local.set", index: jLoc });
1287
-
1288
- this.pushInstruction({ op: "block", blockType: "void" });
1289
- this.pushInstruction({ op: "loop", blockType: "void" });
1290
-
1291
- // if i >= j -> break
1292
- this.pushInstruction({ op: "local.get", index: iLoc });
1293
- this.pushInstruction({ op: "local.get", index: jLoc });
1294
- this.pushInstruction("i32.ge_s");
1295
- this.pushInstruction({ op: "br_if", depth: 1 });
1296
-
1297
- // tmp1 = arr[off + i]
1298
- this.pushInstruction({ op: "local.get", index: arrTmp });
1299
- this.pushInstruction({ op: "local.get", index: offTmp });
1300
- this.pushInstruction({ op: "local.get", index: iLoc });
1301
- this.pushInstruction("i32.add");
1302
- this.pushInstruction({ op: "array.get", typeIndex: arrInfo.typeIndex });
1303
- this.pushInstruction({ op: "local.set", index: tmp1 });
1304
-
1305
- // tmp2 = arr[off + j]
1306
- this.pushInstruction({ op: "local.get", index: arrTmp });
1307
- this.pushInstruction({ op: "local.get", index: offTmp });
1308
- this.pushInstruction({ op: "local.get", index: jLoc });
1309
- this.pushInstruction("i32.add");
1310
- this.pushInstruction({ op: "array.get", typeIndex: arrInfo.typeIndex });
1311
- this.pushInstruction({ op: "local.set", index: tmp2 });
1312
-
1313
- // arr[off + i] = tmp2
1314
- this.pushInstruction({ op: "local.get", index: arrTmp });
1315
- this.pushInstruction({ op: "local.get", index: offTmp });
1316
- this.pushInstruction({ op: "local.get", index: iLoc });
1317
- this.pushInstruction("i32.add");
1318
- this.pushInstruction({ op: "local.get", index: tmp2 });
1319
- this.pushInstruction({ op: "array.set", typeIndex: arrInfo.typeIndex });
1320
-
1321
- // arr[off + j] = tmp1
1322
- this.pushInstruction({ op: "local.get", index: arrTmp });
1323
- this.pushInstruction({ op: "local.get", index: offTmp });
1324
- this.pushInstruction({ op: "local.get", index: jLoc });
1325
- this.pushInstruction("i32.add");
1326
- this.pushInstruction({ op: "local.get", index: tmp1 });
1327
- this.pushInstruction({ op: "array.set", typeIndex: arrInfo.typeIndex });
1328
-
1329
- // i++, j--
1330
- this.pushInstruction({ op: "local.get", index: iLoc });
1331
- this.pushInstruction({ op: "i32.const", value: 1 });
1332
- this.pushInstruction("i32.add");
1333
- this.pushInstruction({ op: "local.set", index: iLoc });
1334
-
1335
- this.pushInstruction({ op: "local.get", index: jLoc });
1336
- this.pushInstruction({ op: "i32.const", value: 1 });
1337
- this.pushInstruction("i32.sub");
1338
- this.pushInstruction({ op: "local.set", index: jLoc });
1339
-
1340
- this.pushInstruction({ op: "br", depth: 0 });
1341
- this.pushInstruction("end");
1342
- this.pushInstruction("end");
1343
-
1344
- this.releaseTemp(tmp2, arrInfo.elemWType);
1345
- this.releaseTemp(tmp1, arrInfo.elemWType);
1346
- this.releaseTemp(jLoc, "i32");
1347
- this.releaseTemp(iLoc, "i32");
1348
- this.releaseTemp(lenTmp, "i32");
1349
- this.releaseTemp(offTmp, "i32");
1350
- this.releaseTemp(arrTmp, {
1351
- kind: "ref",
1352
- nullable: true,
1353
- typeIndex: arrInfo.typeIndex,
1354
- });
1355
-
1356
- this.pushInstruction("end");
1357
- this.releaseTemp(sliceTmp, {
1358
- kind: "ref",
1359
- nullable: true,
1360
- typeIndex: sliceInfo.typeIndex,
1361
- });
1362
- }
1363
-
1364
- emitSlicesContains(args) {
1365
- const sNode = args[0];
1366
- const vNode = args[1];
1367
- const sType = sNode._type ?? this._resolveExprGoType(sNode);
1368
- const elemGoType = this._getSliceElemType(sType);
1369
- const sliceInfo = this.mod.getSliceType(elemGoType);
1370
- const arrInfo = sliceInfo.arrInfo;
1371
-
1372
- const sliceTmp = this.acquireTemp({
1373
- kind: "ref",
1374
- nullable: true,
1375
- typeIndex: sliceInfo.typeIndex,
1376
- });
1377
- const vTmp = this.acquireTemp(arrInfo.elemWType);
1378
- this.emitExpr(sNode, {
1379
- kind: "ref",
1380
- nullable: true,
1381
- typeIndex: sliceInfo.typeIndex,
1382
- });
1383
- this.pushInstruction({ op: "local.set", index: sliceTmp });
1384
- this.emitExpr(vNode, arrInfo.elemWType);
1385
- this.pushInstruction({ op: "local.set", index: vTmp });
1386
-
1387
- const resTmp = this.acquireTemp("i32");
1388
- this.pushInstruction({ op: "i32.const", value: 0 });
1389
- this.pushInstruction({ op: "local.set", index: resTmp });
1390
-
1391
- this.pushInstruction({ op: "block", blockType: "void" });
1392
- this.pushInstruction({ op: "local.get", index: sliceTmp });
1393
- this.pushInstruction("ref.is_null");
1394
- this.pushInstruction({ op: "br_if", depth: 0 });
1395
-
1396
- const arrTmp = this.acquireTemp({
1397
- kind: "ref",
1398
- nullable: true,
1399
- typeIndex: arrInfo.typeIndex,
1400
- });
1401
- const offTmp = this.acquireTemp("i32");
1402
- const lenTmp = this.acquireTemp("i32");
1403
- const iLoc = this.acquireTemp("i32");
1404
- const itemTmp = this.acquireTemp(arrInfo.elemWType);
1405
-
1406
- this._emitSliceUnpack(sliceTmp, sliceInfo, {
1407
- arr: arrTmp,
1408
- off: offTmp,
1409
- len: lenTmp,
1410
- });
1411
-
1412
- this.pushInstruction({ op: "i32.const", value: 0 });
1413
- this.pushInstruction({ op: "local.set", index: iLoc });
1414
-
1415
- this.pushInstruction({ op: "block", blockType: "void" });
1416
- this.pushInstruction({ op: "loop", blockType: "void" });
1417
- this.pushInstruction({ op: "local.get", index: iLoc });
1418
- this.pushInstruction({ op: "local.get", index: lenTmp });
1419
- this.pushInstruction("i32.ge_s");
1420
- this.pushInstruction({ op: "br_if", depth: 1 });
1421
-
1422
- this.pushInstruction({ op: "local.get", index: arrTmp });
1423
- this.pushInstruction({ op: "local.get", index: offTmp });
1424
- this.pushInstruction({ op: "local.get", index: iLoc });
1425
- this.pushInstruction("i32.add");
1426
- this.pushInstruction({ op: "array.get", typeIndex: arrInfo.typeIndex });
1427
- this.pushInstruction({ op: "local.set", index: itemTmp });
1428
-
1429
- this.emitKeyEq(itemTmp, vTmp, elemGoType);
1430
- this.pushInstruction({ op: "if", blockType: "void" });
1431
- this.pushInstruction({ op: "i32.const", value: 1 });
1432
- this.pushInstruction({ op: "local.set", index: resTmp });
1433
- this.pushInstruction({ op: "br", depth: 2 });
1434
- this.pushInstruction("end");
1435
-
1436
- this.pushInstruction({ op: "local.get", index: iLoc });
1437
- this.pushInstruction({ op: "i32.const", value: 1 });
1438
- this.pushInstruction("i32.add");
1439
- this.pushInstruction({ op: "local.set", index: iLoc });
1440
- this.pushInstruction({ op: "br", depth: 0 });
1441
- this.pushInstruction("end");
1442
- this.pushInstruction("end");
1443
-
1444
- this.releaseTemp(itemTmp, arrInfo.elemWType);
1445
- this.releaseTemp(iLoc, "i32");
1446
- this.releaseTemp(lenTmp, "i32");
1447
- this.releaseTemp(offTmp, "i32");
1448
- this.releaseTemp(arrTmp, {
1449
- kind: "ref",
1450
- nullable: true,
1451
- typeIndex: arrInfo.typeIndex,
1452
- });
1453
-
1454
- this.pushInstruction("end"); // end exit block
1455
- this.pushInstruction({ op: "local.get", index: resTmp });
1456
- this.releaseTemp(resTmp, "i32");
1457
- this.releaseTemp(vTmp, arrInfo.elemWType);
1458
- this.releaseTemp(sliceTmp, {
1459
- kind: "ref",
1460
- nullable: true,
1461
- typeIndex: sliceInfo.typeIndex,
1462
- });
1463
- }
1464
-
1465
- emitSlicesIndex(args, targetWasmType) {
1466
- const sNode = args[0];
1467
- const vNode = args[1];
1468
- const sType = sNode._type ?? this._resolveExprGoType(sNode);
1469
- const elemGoType = this._getSliceElemType(sType);
1470
- const sliceInfo = this.mod.getSliceType(elemGoType);
1471
- const arrInfo = sliceInfo.arrInfo;
1472
-
1473
- const sliceTmp = this.acquireTemp({
1474
- kind: "ref",
1475
- nullable: true,
1476
- typeIndex: sliceInfo.typeIndex,
1477
- });
1478
- const vTmp = this.acquireTemp(arrInfo.elemWType);
1479
- this.emitExpr(sNode, {
1480
- kind: "ref",
1481
- nullable: true,
1482
- typeIndex: sliceInfo.typeIndex,
1483
- });
1484
- this.pushInstruction({ op: "local.set", index: sliceTmp });
1485
- this.emitExpr(vNode, arrInfo.elemWType);
1486
- this.pushInstruction({ op: "local.set", index: vTmp });
1487
-
1488
- const resLoc = this.acquireTemp("i32");
1489
- this.pushInstruction({ op: "i32.const", value: -1 });
1490
- this.pushInstruction({ op: "local.set", index: resLoc });
1491
-
1492
- this.pushInstruction({ op: "block", blockType: "void" });
1493
- this.pushInstruction({ op: "local.get", index: sliceTmp });
1494
- this.pushInstruction("ref.is_null");
1495
- this.pushInstruction({ op: "br_if", depth: 0 });
1496
-
1497
- const arrTmp = this.acquireTemp({
1498
- kind: "ref",
1499
- nullable: true,
1500
- typeIndex: arrInfo.typeIndex,
1501
- });
1502
- const offTmp = this.acquireTemp("i32");
1503
- const lenTmp = this.acquireTemp("i32");
1504
- const iLoc = this.acquireTemp("i32");
1505
- const itemTmp = this.acquireTemp(arrInfo.elemWType);
1506
-
1507
- this._emitSliceUnpack(sliceTmp, sliceInfo, {
1508
- arr: arrTmp,
1509
- off: offTmp,
1510
- len: lenTmp,
1511
- });
1512
-
1513
- this.pushInstruction({ op: "i32.const", value: 0 });
1514
- this.pushInstruction({ op: "local.set", index: iLoc });
1515
-
1516
- this.pushInstruction({ op: "block", blockType: "void" });
1517
- this.pushInstruction({ op: "loop", blockType: "void" });
1518
- this.pushInstruction({ op: "local.get", index: iLoc });
1519
- this.pushInstruction({ op: "local.get", index: lenTmp });
1520
- this.pushInstruction("i32.ge_s");
1521
- this.pushInstruction({ op: "br_if", depth: 1 });
1522
-
1523
- this.pushInstruction({ op: "local.get", index: arrTmp });
1524
- this.pushInstruction({ op: "local.get", index: offTmp });
1525
- this.pushInstruction({ op: "local.get", index: iLoc });
1526
- this.pushInstruction("i32.add");
1527
- this.pushInstruction({ op: "array.get", typeIndex: arrInfo.typeIndex });
1528
- this.pushInstruction({ op: "local.set", index: itemTmp });
1529
-
1530
- this.emitKeyEq(itemTmp, vTmp, elemGoType);
1531
- this.pushInstruction({ op: "if", blockType: "void" });
1532
- this.pushInstruction({ op: "local.get", index: iLoc });
1533
- this.pushInstruction({ op: "local.set", index: resLoc });
1534
- this.pushInstruction({ op: "br", depth: 2 });
1535
- this.pushInstruction("end");
1536
-
1537
- this.pushInstruction({ op: "local.get", index: iLoc });
1538
- this.pushInstruction({ op: "i32.const", value: 1 });
1539
- this.pushInstruction("i32.add");
1540
- this.pushInstruction({ op: "local.set", index: iLoc });
1541
- this.pushInstruction({ op: "br", depth: 0 });
1542
- this.pushInstruction("end");
1543
- this.pushInstruction("end");
1544
-
1545
- this.releaseTemp(itemTmp, arrInfo.elemWType);
1546
- this.releaseTemp(iLoc, "i32");
1547
- this.releaseTemp(lenTmp, "i32");
1548
- this.releaseTemp(offTmp, "i32");
1549
- this.releaseTemp(arrTmp, {
1550
- kind: "ref",
1551
- nullable: true,
1552
- typeIndex: arrInfo.typeIndex,
1553
- });
1554
-
1555
- this.pushInstruction("end"); // end exit block
1556
- this.pushInstruction({ op: "local.get", index: resLoc });
1557
- this.releaseTemp(resLoc, "i32");
1558
- this.releaseTemp(vTmp, arrInfo.elemWType);
1559
- this.releaseTemp(sliceTmp, {
1560
- kind: "ref",
1561
- nullable: true,
1562
- typeIndex: sliceInfo.typeIndex,
1563
- });
1564
-
1565
- if ((targetWasmType ?? "i64") === "i64") {
1566
- this.pushInstruction("i64.extend_i32_s");
1567
- }
1568
- }
1569
-
1570
- emitSlicesEqual(args) {
1571
- const s1Node = args[0];
1572
- const s2Node = args[1];
1573
- const sType = s1Node._type ?? this._resolveExprGoType(s1Node);
1574
- const elemGoType = this._getSliceElemType(sType);
1575
- const sliceInfo = this.mod.getSliceType(elemGoType);
1576
- const arrInfo = sliceInfo.arrInfo;
1577
-
1578
- const s1Tmp = this.acquireTemp({
1579
- kind: "ref",
1580
- nullable: true,
1581
- typeIndex: sliceInfo.typeIndex,
1582
- });
1583
- const s2Tmp = this.acquireTemp({
1584
- kind: "ref",
1585
- nullable: true,
1586
- typeIndex: sliceInfo.typeIndex,
1587
- });
1588
- this.emitExpr(s1Node, {
1589
- kind: "ref",
1590
- nullable: true,
1591
- typeIndex: sliceInfo.typeIndex,
1592
- });
1593
- this.pushInstruction({ op: "local.set", index: s1Tmp });
1594
- this.emitExpr(s2Node, {
1595
- kind: "ref",
1596
- nullable: true,
1597
- typeIndex: sliceInfo.typeIndex,
1598
- });
1599
- this.pushInstruction({ op: "local.set", index: s2Tmp });
1600
-
1601
- const resLoc = this.acquireTemp("i32");
1602
- this.pushInstruction({ op: "i32.const", value: 0 });
1603
- this.pushInstruction({ op: "local.set", index: resLoc });
1604
-
1605
- this.pushInstruction({ op: "block", blockType: "void" });
1606
-
1607
- // if s1 == s2 -> 1
1608
- this.pushInstruction({ op: "local.get", index: s1Tmp });
1609
- this.pushInstruction({ op: "local.get", index: s2Tmp });
1610
- this.pushInstruction("ref.eq");
1611
- this.pushInstruction({ op: "if", blockType: "void" });
1612
- this.pushInstruction({ op: "i32.const", value: 1 });
1613
- this.pushInstruction({ op: "local.set", index: resLoc });
1614
- this.pushInstruction({ op: "br", depth: 1 });
1615
- this.pushInstruction("end");
1616
-
1617
- // if s1 == null || s2 == null -> 0 (br 0)
1618
- this.pushInstruction({ op: "local.get", index: s1Tmp });
1619
- this.pushInstruction("ref.is_null");
1620
- this.pushInstruction({ op: "local.get", index: s2Tmp });
1621
- this.pushInstruction("ref.is_null");
1622
- this.pushInstruction("i32.or");
1623
- this.pushInstruction({ op: "br_if", depth: 0 });
1624
-
1625
- // if s1.len != s2.len -> 0 (br 0)
1626
- this.pushInstruction({ op: "local.get", index: s1Tmp });
1627
- this.pushInstruction({
1628
- op: "struct.get",
1629
- typeIndex: sliceInfo.typeIndex,
1630
- fieldIndex: 2,
1631
- });
1632
- this.pushInstruction({ op: "local.get", index: s2Tmp });
1633
- this.pushInstruction({
1634
- op: "struct.get",
1635
- typeIndex: sliceInfo.typeIndex,
1636
- fieldIndex: 2,
1637
- });
1638
- this.pushInstruction("i32.ne");
1639
- this.pushInstruction({ op: "br_if", depth: 0 });
1640
-
1641
- const lenLoc = this.acquireTemp("i32");
1642
- const iLoc = this.acquireTemp("i32");
1643
- const item1 = this.acquireTemp(arrInfo.elemWType);
1644
- const item2 = this.acquireTemp(arrInfo.elemWType);
1645
-
1646
- this.pushInstruction({ op: "local.get", index: s1Tmp });
1647
- this.pushInstruction({
1648
- op: "struct.get",
1649
- typeIndex: sliceInfo.typeIndex,
1650
- fieldIndex: 2,
1651
- });
1652
- this.pushInstruction({ op: "local.set", index: lenLoc });
1653
-
1654
- this.pushInstruction({ op: "i32.const", value: 0 });
1655
- this.pushInstruction({ op: "local.set", index: iLoc });
1656
-
1657
- this.pushInstruction({ op: "block", blockType: "void" });
1658
- this.pushInstruction({ op: "loop", blockType: "void" });
1659
- this.pushInstruction({ op: "local.get", index: iLoc });
1660
- this.pushInstruction({ op: "local.get", index: lenLoc });
1661
- this.pushInstruction("i32.ge_s");
1662
- this.pushInstruction({ op: "br_if", depth: 1 });
1663
-
1664
- // item1 = s1.arr[s1.off + i]
1665
- this.pushInstruction({ op: "local.get", index: s1Tmp });
1666
- this.pushInstruction({
1667
- op: "struct.get",
1668
- typeIndex: sliceInfo.typeIndex,
1669
- fieldIndex: 0,
1670
- });
1671
- this.pushInstruction({ op: "local.get", index: s1Tmp });
1672
- this.pushInstruction({
1673
- op: "struct.get",
1674
- typeIndex: sliceInfo.typeIndex,
1675
- fieldIndex: 1,
1676
- });
1677
- this.pushInstruction({ op: "local.get", index: iLoc });
1678
- this.pushInstruction("i32.add");
1679
- this.pushInstruction({ op: "array.get", typeIndex: arrInfo.typeIndex });
1680
- this.pushInstruction({ op: "local.set", index: item1 });
1681
-
1682
- // item2 = s2.arr[s2.off + i]
1683
- this.pushInstruction({ op: "local.get", index: s2Tmp });
1684
- this.pushInstruction({
1685
- op: "struct.get",
1686
- typeIndex: sliceInfo.typeIndex,
1687
- fieldIndex: 0,
1688
- });
1689
- this.pushInstruction({ op: "local.get", index: s2Tmp });
1690
- this.pushInstruction({
1691
- op: "struct.get",
1692
- typeIndex: sliceInfo.typeIndex,
1693
- fieldIndex: 1,
1694
- });
1695
- this.pushInstruction({ op: "local.get", index: iLoc });
1696
- this.pushInstruction("i32.add");
1697
- this.pushInstruction({ op: "array.get", typeIndex: arrInfo.typeIndex });
1698
- this.pushInstruction({ op: "local.set", index: item2 });
1699
-
1700
- this.emitKeyEq(item1, item2, elemGoType);
1701
- this.pushInstruction("i32.eqz");
1702
- this.pushInstruction({ op: "br_if", depth: 2 }); // mismatch -> res 0
1703
-
1704
- this.pushInstruction({ op: "local.get", index: iLoc });
1705
- this.pushInstruction({ op: "i32.const", value: 1 });
1706
- this.pushInstruction("i32.add");
1707
- this.pushInstruction({ op: "local.set", index: iLoc });
1708
- this.pushInstruction({ op: "br", depth: 0 });
1709
- this.pushInstruction("end");
1710
- this.pushInstruction("end");
1711
-
1712
- this.pushInstruction({ op: "i32.const", value: 1 });
1713
- this.pushInstruction({ op: "local.set", index: resLoc });
1714
-
1715
- this.releaseTemp(item2, arrInfo.elemWType);
1716
- this.releaseTemp(item1, arrInfo.elemWType);
1717
- this.releaseTemp(iLoc, "i32");
1718
- this.releaseTemp(lenLoc, "i32");
1719
-
1720
- this.pushInstruction("end");
1721
-
1722
- this.pushInstruction({ op: "local.get", index: resLoc });
1723
- this.releaseTemp(resLoc, "i32");
1724
- this.releaseTemp(s2Tmp, {
1725
- kind: "ref",
1726
- nullable: true,
1727
- typeIndex: sliceInfo.typeIndex,
1728
- });
1729
- this.releaseTemp(s1Tmp, {
1730
- kind: "ref",
1731
- nullable: true,
1732
- typeIndex: sliceInfo.typeIndex,
1733
- });
1734
- }
1735
-
1736
- emitSlicesClone(args) {
1737
- const sNode = args[0];
1738
- const sType = sNode._type ?? this._resolveExprGoType(sNode);
1739
- const elemGoType = this._getSliceElemType(sType);
1740
- const sliceInfo = this.mod.getSliceType(elemGoType);
1741
- const arrInfo = sliceInfo.arrInfo;
1742
-
1743
- const sliceTmp = this.acquireTemp({
1744
- kind: "ref",
1745
- nullable: true,
1746
- typeIndex: sliceInfo.typeIndex,
1747
- });
1748
- this.emitExpr(sNode, {
1749
- kind: "ref",
1750
- nullable: true,
1751
- typeIndex: sliceInfo.typeIndex,
1752
- });
1753
- this.pushInstruction({ op: "local.set", index: sliceTmp });
1754
-
1755
- this.pushInstruction({ op: "local.get", index: sliceTmp });
1756
- this.pushInstruction("ref.is_null");
1757
- this.pushInstruction({
1758
- op: "if",
1759
- blockType: {
1760
- kind: "ref",
1761
- nullable: true,
1762
- typeIndex: sliceInfo.typeIndex,
1763
- },
1764
- });
1765
- this.pushInstruction({ op: "ref.null", heapType: sliceInfo.typeIndex });
1766
- this.pushInstruction("else");
1767
-
1768
- const lenTmp = this.acquireTemp("i32");
1769
- const newArrTmp = this.acquireTemp({
1770
- kind: "ref",
1771
- nullable: true,
1772
- typeIndex: arrInfo.typeIndex,
1773
- });
1774
-
1775
- this.pushInstruction({ op: "local.get", index: sliceTmp });
1776
- this.pushInstruction({
1777
- op: "struct.get",
1778
- typeIndex: sliceInfo.typeIndex,
1779
- fieldIndex: 2,
1780
- });
1781
- this.pushInstruction({ op: "local.tee", index: lenTmp });
1782
- this.pushInstruction({
1783
- op: "array.new_default",
1784
- typeIndex: arrInfo.typeIndex,
1785
- });
1786
- this.pushInstruction({ op: "local.set", index: newArrTmp });
1787
-
1788
- // array.copy (newArr, 0, srcArr, srcOff, len)
1789
- this.pushInstruction({ op: "local.get", index: newArrTmp });
1790
- this.pushInstruction({ op: "i32.const", value: 0 });
1791
- this.pushInstruction({ op: "local.get", index: sliceTmp });
1792
- this.pushInstruction({
1793
- op: "struct.get",
1794
- typeIndex: sliceInfo.typeIndex,
1795
- fieldIndex: 0,
1796
- });
1797
- this.pushInstruction({ op: "local.get", index: sliceTmp });
1798
- this.pushInstruction({
1799
- op: "struct.get",
1800
- typeIndex: sliceInfo.typeIndex,
1801
- fieldIndex: 1,
1802
- });
1803
- this.pushInstruction({ op: "local.get", index: lenTmp });
1804
- this._emitArrayCopy(arrInfo, elemGoType);
1805
-
1806
- // struct.new slice (newArr, 0, len, len)
1807
- this.pushInstruction({ op: "local.get", index: newArrTmp });
1808
- this.pushInstruction({ op: "i32.const", value: 0 });
1809
- this.pushInstruction({ op: "local.get", index: lenTmp });
1810
- this.pushInstruction({ op: "local.get", index: lenTmp });
1811
- this.pushInstruction({
1812
- op: "struct.new",
1813
- typeIndex: sliceInfo.typeIndex,
1814
- });
1815
-
1816
- this.releaseTemp(newArrTmp, {
1817
- kind: "ref",
1818
- nullable: true,
1819
- typeIndex: arrInfo.typeIndex,
1820
- });
1821
- this.releaseTemp(lenTmp, "i32");
1822
- this.pushInstruction("end"); // end if
1823
-
1824
- this.releaseTemp(sliceTmp, {
1825
- kind: "ref",
1826
- nullable: true,
1827
- typeIndex: sliceInfo.typeIndex,
1828
- });
1829
- }
1830
-
1831
- emitStringsCall(name, args, targetWasmType) {
1832
- switch (name) {
1833
- case "ToUpper": {
1834
- this.emitExpr(args[0], "externref");
1835
- this.pushInstruction({
1836
- op: "call",
1837
- funcIndex: this.mod.getStringToUpperImportIndex(),
1838
- });
1839
- return;
1840
- }
1841
- case "ToLower": {
1842
- this.emitExpr(args[0], "externref");
1843
- this.pushInstruction({
1844
- op: "call",
1845
- funcIndex: this.mod.getStringToLowerImportIndex(),
1846
- });
1847
- return;
1848
- }
1849
- case "TrimSpace": {
1850
- this.emitExpr(args[0], "externref");
1851
- this.pushInstruction({
1852
- op: "call",
1853
- funcIndex: this.mod.getStringTrimSpaceImportIndex(),
1854
- });
1855
- return;
1856
- }
1857
- case "Contains": {
1858
- this.emitExpr(args[0], "externref");
1859
- this.emitExpr(args[1], "externref");
1860
- this.pushInstruction({
1861
- op: "call",
1862
- funcIndex: this.mod.getStringContainsImportIndex(),
1863
- });
1864
- return;
1865
- }
1866
- case "HasPrefix": {
1867
- this.emitExpr(args[0], "externref");
1868
- this.emitExpr(args[1], "externref");
1869
- this.pushInstruction({
1870
- op: "call",
1871
- funcIndex: this.mod.getStringHasPrefixImportIndex(),
1872
- });
1873
- return;
1874
- }
1875
- case "HasSuffix": {
1876
- this.emitExpr(args[0], "externref");
1877
- this.emitExpr(args[1], "externref");
1878
- this.pushInstruction({
1879
- op: "call",
1880
- funcIndex: this.mod.getStringHasSuffixImportIndex(),
1881
- });
1882
- return;
1883
- }
1884
- case "Index": {
1885
- this.emitExpr(args[0], "externref");
1886
- this.emitExpr(args[1], "externref");
1887
- this.pushInstruction({
1888
- op: "call",
1889
- funcIndex: this.mod.getStringIndexImportIndex(),
1890
- });
1891
- if ((targetWasmType ?? "i64") === "i64") {
1892
- this.pushInstruction("i64.extend_i32_s");
1893
- }
1894
- return;
1895
- }
1896
- case "LastIndex": {
1897
- this.emitExpr(args[0], "externref");
1898
- this.emitExpr(args[1], "externref");
1899
- this.pushInstruction({
1900
- op: "call",
1901
- funcIndex: this.mod.getStringLastIndexImportIndex(),
1902
- });
1903
- if ((targetWasmType ?? "i64") === "i64") {
1904
- this.pushInstruction("i64.extend_i32_s");
1905
- }
1906
- return;
1907
- }
1908
- case "Repeat": {
1909
- this.emitExpr(args[0], "externref");
1910
- const cntWType = toWasmType(args[1]?._type, this.mod.checker);
1911
- if (cntWType === "i64") {
1912
- this.emitExpr(args[1], "i64");
1913
- this.pushInstruction("i32.wrap_i64");
1914
- } else {
1915
- this.emitExpr(args[1], "i32");
1916
- }
1917
- this.pushInstruction({
1918
- op: "call",
1919
- funcIndex: this.mod.getStringRepeatImportIndex(),
1920
- });
1921
- return;
1922
- }
1923
- case "ReplaceAll": {
1924
- this.emitExpr(args[0], "externref");
1925
- this.emitExpr(args[1], "externref");
1926
- this.emitExpr(args[2], "externref");
1927
- this.pushInstruction({
1928
- op: "call",
1929
- funcIndex: this.mod.getStringReplaceAllImportIndex(),
1930
- });
1931
- return;
1932
- }
1933
- case "EqualFold": {
1934
- this.emitExpr(args[0], "externref");
1935
- this.emitExpr(args[1], "externref");
1936
- this.pushInstruction({
1937
- op: "call",
1938
- funcIndex: this.mod.getStringEqualFoldImportIndex(),
1939
- });
1940
- return;
1941
- }
1942
- case "Count": {
1943
- this.emitExpr(args[0], "externref");
1944
- this.emitExpr(args[1], "externref");
1945
- this.pushInstruction({
1946
- op: "call",
1947
- funcIndex: this.mod.getStringCountImportIndex(),
1948
- });
1949
- if ((targetWasmType ?? "i64") === "i64") {
1950
- this.pushInstruction("i64.extend_i32_s");
1951
- }
1952
- return;
1953
- }
1954
- case "Trim":
1955
- case "TrimLeft":
1956
- case "TrimRight":
1957
- case "TrimPrefix":
1958
- case "TrimSuffix":
1959
- case "ContainsAny":
1960
- case "IndexAny": {
1961
- const imp = {
1962
- Trim: "str_trim",
1963
- TrimLeft: "str_trim_left",
1964
- TrimRight: "str_trim_right",
1965
- TrimPrefix: "str_trim_prefix",
1966
- TrimSuffix: "str_trim_suffix",
1967
- ContainsAny: "str_contains_any",
1968
- IndexAny: "str_index_any",
1969
- }[name];
1970
- this.emitExpr(args[0], "externref");
1971
- this.emitExpr(args[1], "externref");
1972
- this._callStdlib(imp);
1973
- if (name === "IndexAny" && (targetWasmType ?? "i64") === "i64") {
1974
- this.pushInstruction("i64.extend_i32_s");
1975
- }
1976
- return;
1977
- }
1978
- case "ContainsRune":
1979
- case "IndexByte":
1980
- case "IndexRune":
1981
- case "LastIndexByte": {
1982
- const imp = {
1983
- ContainsRune: "str_contains_rune",
1984
- IndexByte: "str_index_byte",
1985
- IndexRune: "str_index_rune",
1986
- LastIndexByte: "str_last_index_byte",
1987
- }[name];
1988
- this.emitExpr(args[0], "externref");
1989
- this._emitI32Arg(args[1]);
1990
- this._callStdlib(imp);
1991
- if (name !== "ContainsRune" && (targetWasmType ?? "i64") === "i64") {
1992
- this.pushInstruction("i64.extend_i32_s");
1993
- }
1994
- return;
1995
- }
1996
- case "Title":
1997
- case "ToTitle": {
1998
- this.emitExpr(args[0], "externref");
1999
- this._callStdlib(name === "Title" ? "str_title" : "str_to_upper");
2000
- return;
2001
- }
2002
- case "Replace": {
2003
- this.emitExpr(args[0], "externref");
2004
- this.emitExpr(args[1], "externref");
2005
- this.emitExpr(args[2], "externref");
2006
- this._emitI32Arg(args[3]);
2007
- this._callStdlib("str_replace");
2008
- return;
2009
- }
2010
- case "Split":
2011
- case "Fields": {
2012
- // JS splits and parks the parts; wasm pulls them into a []string.
2013
- this.emitExpr(args[0], "externref");
2014
- if (name === "Split") {
2015
- this.emitExpr(args[1], "externref");
2016
- this._callStdlib("str_split");
2017
- } else {
2018
- this._callStdlib("str_fields");
2019
- }
2020
- this._emitCollectStringParts();
2021
- return;
2022
- }
2023
- case "Join":
2024
- this.emitStringsJoin(args);
2025
- return;
2026
- default:
2027
- throw new Error(`Unsupported strings function: strings.${name}`);
2028
- }
2029
- }
2030
-
2031
- _callStdlib(name) {
2032
- this.pushInstruction({
2033
- op: "call",
2034
- funcIndex: this.mod.getStdlibImportIndex(name),
2035
- });
2036
- }
2037
-
2038
- // Pushes an integer-ish argument as i32 (Go ints are i64 in this backend).
2039
- _emitI32Arg(arg) {
2040
- const wType = toWasmType(arg?._type, this.mod.checker);
2041
- if (wType === "i64") {
2042
- this.emitExpr(arg, "i64");
2043
- this.pushInstruction("i32.wrap_i64");
2044
- } else {
2045
- this.emitExpr(arg, "i32");
2046
- }
2047
- }
2048
-
2049
- // Consumes the part count (i32) left by str_split/str_fields and builds a
2050
- // `[]string` by calling `str_part(i)` for each index.
2051
- _emitCollectStringParts() {
2052
- const strGo = { kind: "basic", name: "string" };
2053
- const sliceInfo = this.mod.getSliceType(strGo);
2054
- const arrInfo = sliceInfo.arrInfo;
2055
- const arrW = { kind: "ref", nullable: true, typeIndex: arrInfo.typeIndex };
2056
- const n = this.acquireTemp("i32");
2057
- const i = this.acquireTemp("i32");
2058
- const arr = this.acquireTemp(arrW);
2059
-
2060
- this.pushInstruction({ op: "local.tee", index: n });
2061
- this.pushInstruction({
2062
- op: "array.new_default",
2063
- typeIndex: arrInfo.typeIndex,
2064
- });
2065
- this.pushInstruction({ op: "local.set", index: arr });
2066
- this.pushInstruction({ op: "i32.const", value: 0 });
2067
- this.pushInstruction({ op: "local.set", index: i });
2068
-
2069
- this.pushInstruction({ op: "block", blockType: "void" });
2070
- this.pushInstruction({ op: "loop", blockType: "void" });
2071
- this.pushInstruction({ op: "local.get", index: i });
2072
- this.pushInstruction({ op: "local.get", index: n });
2073
- this.pushInstruction("i32.ge_s");
2074
- this.pushInstruction({ op: "br_if", depth: 1 });
2075
- this.pushInstruction({ op: "local.get", index: arr });
2076
- this.pushInstruction({ op: "local.get", index: i });
2077
- this.pushInstruction({ op: "local.get", index: i });
2078
- this._callStdlib("str_part");
2079
- this.pushInstruction({ op: "array.set", typeIndex: arrInfo.typeIndex });
2080
- this.pushInstruction({ op: "local.get", index: i });
2081
- this.pushInstruction({ op: "i32.const", value: 1 });
2082
- this.pushInstruction("i32.add");
2083
- this.pushInstruction({ op: "local.set", index: i });
2084
- this.pushInstruction({ op: "br", depth: 0 });
2085
- this.pushInstruction("end");
2086
- this.pushInstruction("end");
2087
-
2088
- this.pushInstruction({ op: "local.get", index: arr });
2089
- this.pushInstruction({ op: "i32.const", value: 0 });
2090
- this.pushInstruction({ op: "local.get", index: n });
2091
- this.pushInstruction({ op: "local.get", index: n });
2092
- this.pushInstruction({ op: "struct.new", typeIndex: sliceInfo.typeIndex });
2093
-
2094
- this.releaseTemp(arr, arrW);
2095
- this.releaseTemp(i, "i32");
2096
- this.releaseTemp(n, "i32");
2097
- }
2098
-
2099
- emitStringsJoin(args) {
2100
- const strGo = { kind: "basic", name: "string" };
2101
- const sliceInfo = this.mod.getSliceType(strGo);
2102
- const arrInfo = sliceInfo.arrInfo;
2103
- const sliceW = {
2104
- kind: "ref",
2105
- nullable: true,
2106
- typeIndex: sliceInfo.typeIndex,
2107
- };
2108
- const arrW = { kind: "ref", nullable: true, typeIndex: arrInfo.typeIndex };
2109
- const s = this.acquireTemp(sliceW);
2110
- const arr = this.acquireTemp(arrW);
2111
- const off = this.acquireTemp("i32");
2112
- const len = this.acquireTemp("i32");
2113
- const i = this.acquireTemp("i32");
2114
- const sep = this.acquireTemp("externref");
2115
- const acc = this.acquireTemp("externref");
2116
- const concat = this.mod.getStringConcatImportIndex();
2117
-
2118
- this.emitExpr(args[0], sliceW);
2119
- this.pushInstruction({ op: "local.set", index: s });
2120
- this.emitExpr(args[1], "externref");
2121
- this.pushInstruction({ op: "local.set", index: sep });
2122
- // acc = ""
2123
- this.pushInstruction({ op: "i32.const", value: this.mod.internString("") });
2124
- this.pushInstruction({
2125
- op: "call",
2126
- funcIndex: this.mod.getStringImportIndex(),
2127
- });
2128
- this.pushInstruction({ op: "local.set", index: acc });
2129
-
2130
- this.pushInstruction({ op: "block", blockType: "void" });
2131
- this.pushInstruction({ op: "local.get", index: s });
2132
- this.pushInstruction("ref.is_null");
2133
- this.pushInstruction({ op: "br_if", depth: 0 });
2134
- this._emitSliceUnpack(s, sliceInfo, { arr, off, len });
2135
- this.pushInstruction({ op: "i32.const", value: 0 });
2136
- this.pushInstruction({ op: "local.set", index: i });
2137
- this.pushInstruction({ op: "loop", blockType: "void" });
2138
- this.pushInstruction({ op: "local.get", index: i });
2139
- this.pushInstruction({ op: "local.get", index: len });
2140
- this.pushInstruction("i32.ge_s");
2141
- this.pushInstruction({ op: "br_if", depth: 1 });
2142
- // if i > 0: acc = acc + sep
2143
- this.pushInstruction({ op: "local.get", index: i });
2144
- this.pushInstruction({ op: "if", blockType: "void" });
2145
- this.pushInstruction({ op: "local.get", index: acc });
2146
- this.pushInstruction({ op: "local.get", index: sep });
2147
- this.pushInstruction({ op: "call", funcIndex: concat });
2148
- this.pushInstruction({ op: "local.set", index: acc });
2149
- this.pushInstruction("end");
2150
- // acc = acc + arr[off+i]
2151
- this.pushInstruction({ op: "local.get", index: acc });
2152
- this.pushInstruction({ op: "local.get", index: arr });
2153
- this.pushInstruction({ op: "local.get", index: off });
2154
- this.pushInstruction({ op: "local.get", index: i });
2155
- this.pushInstruction("i32.add");
2156
- this.pushInstruction({ op: "array.get", typeIndex: arrInfo.typeIndex });
2157
- this.pushInstruction({ op: "call", funcIndex: concat });
2158
- this.pushInstruction({ op: "local.set", index: acc });
2159
- this.pushInstruction({ op: "local.get", index: i });
2160
- this.pushInstruction({ op: "i32.const", value: 1 });
2161
- this.pushInstruction("i32.add");
2162
- this.pushInstruction({ op: "local.set", index: i });
2163
- this.pushInstruction({ op: "br", depth: 0 });
2164
- this.pushInstruction("end");
2165
- this.pushInstruction("end");
2166
- this.pushInstruction({ op: "local.get", index: acc });
2167
-
2168
- this.releaseTemp(acc, "externref");
2169
- this.releaseTemp(sep, "externref");
2170
- this.releaseTemp(i, "i32");
2171
- this.releaseTemp(len, "i32");
2172
- this.releaseTemp(off, "i32");
2173
- this.releaseTemp(arr, arrW);
2174
- this.releaseTemp(s, sliceW);
2175
- }
2176
-
2177
- emitStrconvCall(name, args) {
2178
- switch (name) {
2179
- case "Itoa": {
2180
- const argWType = toWasmType(args[0]?._type, this.mod.checker);
2181
- if (argWType === "i64") {
2182
- this.emitExpr(args[0], "i64");
2183
- this.pushInstruction({
2184
- op: "call",
2185
- funcIndex: this.mod.getStrFromI64ImportIndex(),
2186
- });
2187
- } else {
2188
- this.emitExpr(args[0], "i32");
2189
- this.pushInstruction({
2190
- op: "call",
2191
- funcIndex: this.mod.getStrFromI32ImportIndex(),
2192
- });
2193
- }
2194
- return;
2195
- }
2196
- case "FormatInt": {
2197
- this.emitExpr(args[0], "i64");
2198
- this._emitI32Arg(args[1]);
2199
- this._callStdlib("strconv_format_int");
2200
- return;
2201
- }
2202
- case "FormatBool": {
2203
- // bool → "true"/"false" via the string table
2204
- this.emitExpr(args[0], "i32");
2205
- this.pushInstruction({ op: "if", blockType: "externref" });
2206
- this.pushInstruction({
2207
- op: "i32.const",
2208
- value: this.mod.internString("true"),
2209
- });
2210
- this.pushInstruction({
2211
- op: "call",
2212
- funcIndex: this.mod.getStringImportIndex(),
2213
- });
2214
- this.pushInstruction("else");
2215
- this.pushInstruction({
2216
- op: "i32.const",
2217
- value: this.mod.internString("false"),
2218
- });
2219
- this.pushInstruction({
2220
- op: "call",
2221
- funcIndex: this.mod.getStringImportIndex(),
2222
- });
2223
- this.pushInstruction("end");
2224
- return;
2225
- }
2226
- case "FormatFloat": {
2227
- this.emitExpr(args[0], "f64");
2228
- this._emitI32Arg(args[1]);
2229
- this._emitI32Arg(args[2]);
2230
- this._emitI32Arg(args[3]);
2231
- this._callStdlib("strconv_format_float");
2232
- return;
2233
- }
2234
- case "Quote": {
2235
- this.emitExpr(args[0], "externref");
2236
- this._callStdlib("strconv_quote");
2237
- return;
2238
- }
2239
- case "Atoi":
2240
- case "ParseInt":
2241
- case "ParseFloat":
2242
- case "ParseBool": {
2243
- // (value, error): the JS side records a failure message that
2244
- // `strconv_err` returns (null on success) → runtime error value.
2245
- this.emitExpr(args[0], "externref");
2246
- if (name === "Atoi") this._callStdlib("strconv_atoi");
2247
- else if (name === "ParseInt") {
2248
- this._emitI32Arg(args[1]);
2249
- this._emitI32Arg(args[2]);
2250
- this._callStdlib("strconv_parse_int");
2251
- } else if (name === "ParseFloat")
2252
- this._callStdlib("strconv_parse_float");
2253
- else this._callStdlib("strconv_parse_bool");
2254
- this._callStdlib("strconv_err");
2255
- this.pushInstruction({ op: "ref.null", heapType: "any" });
2256
- this.pushInstruction({
2257
- op: "call",
2258
- funcIndex: this.mod.resolveFuncIndex("__mk_error"),
2259
- });
2260
- return;
2261
- }
2262
- default:
2263
- throw new Error(`Unsupported strconv function: strconv.${name}`);
2264
- }
2265
- }
2266
-
2267
- emitFmtCall(name, args) {
2268
- if (name === "Println" || name === "Print") {
2269
- const isPrintln = name === "Println";
2270
- if (args.length === 0 && isPrintln) {
2271
- const logIdx = this.mod.getPrintlnEmptyIndex();
2272
- this.pushInstruction({ op: "call", funcIndex: logIdx });
2273
- return;
2274
- }
2275
- for (let i = 0; i < args.length; i++) {
2276
- const arg = args[i];
2277
- const isLast = i === args.length - 1 && isPrintln;
2278
- const goType = arg._type ?? this._resolveExprGoType(arg);
2279
- const isBool =
2280
- goType?.name === "bool" ||
2281
- (arg.kind === "BasicLit" && arg.litKind === "BOOL");
2282
- if (isErrorType(goType) && this.mod.hasErrorType) {
2283
- this.emitExpr(arg, "anyref");
2284
- this.pushInstruction({
2285
- op: "call",
2286
- funcIndex: this.mod.resolveFuncIndex("__error_str"),
2287
- });
2288
- this.pushInstruction({
2289
- op: "call",
2290
- funcIndex: this.mod.getLogImportIndex("externref", isLast, false),
2291
- });
2292
- continue;
2293
- }
2294
- const wType = toWasmType(goType, this.mod.checker);
2295
- this.emitExpr(arg, wType);
2296
- const logFuncIdx = this.mod.getLogImportIndex(wType, isLast, isBool);
2297
- this.pushInstruction({ op: "call", funcIndex: logFuncIdx });
2298
- }
2299
- return;
2300
- }
2301
- if (name === "Sprintf" || name === "Printf" || name === "Errorf") {
2302
- const [format, ...rest] = args;
2303
- const temps = this._emitArgBuffer(rest);
2304
- this.emitExpr(format, "externref");
2305
- if (name === "Printf") {
2306
- this._callStdlib("fmt_printf");
2307
- this._releaseArgBuffer(temps);
2308
- return;
2309
- }
2310
- this._callStdlib("fmt_sprintf");
2311
- if (name === "Errorf") {
2312
- // `%w` wraps its operand as the cause (first %w only, like Go 1.13).
2313
- const wIdx = _wrapVerbIndex(format);
2314
- const cause = wIdx >= 0 ? temps[wIdx] : null;
2315
- const causeTmp =
2316
- cause?.errTmp ?? (cause?.wType === "anyref" ? cause.tmp : null);
2317
- if (causeTmp !== null && causeTmp !== undefined) {
2318
- this.pushInstruction({ op: "local.get", index: causeTmp });
2319
- } else {
2320
- this.pushInstruction({ op: "ref.null", heapType: "any" });
2321
- }
2322
- this.pushInstruction({
2323
- op: "call",
2324
- funcIndex: this.mod.resolveFuncIndex("__mk_error"),
2325
- });
2326
- }
2327
- this._releaseArgBuffer(temps);
2328
- return;
2329
- }
2330
- throw new Error(`Unsupported fmt function: fmt.${name}`);
2331
- }
2332
-
2333
- emitErrorsCall(name, args) {
2334
- switch (name) {
2335
- case "New":
2336
- this.emitExpr(args[0], "externref");
2337
- this.pushInstruction({ op: "ref.null", heapType: "any" });
2338
- this.pushInstruction({
2339
- op: "call",
2340
- funcIndex: this.mod.resolveFuncIndex("__mk_error"),
2341
- });
2342
- return;
2343
- case "Unwrap":
2344
- this.emitExpr(args[0], "anyref");
2345
- this.pushInstruction({
2346
- op: "call",
2347
- funcIndex: this.mod.resolveFuncIndex("__errors_unwrap"),
2348
- });
2349
- return;
2350
- case "Is":
2351
- this.emitExpr(args[0], "anyref");
2352
- this.emitExpr(args[1], "anyref");
2353
- this.pushInstruction({
2354
- op: "call",
2355
- funcIndex: this.mod.resolveFuncIndex("__errors_is"),
2356
- });
2357
- return;
2358
- default:
2359
- throw new Error(`Unsupported errors function: errors.${name}`);
2360
- }
2361
- }
2362
-
2363
- emitUtf8Call(name, args, targetWasmType) {
2364
- const wide = (targetWasmType ?? "i64") === "i64";
2365
- switch (name) {
2366
- case "RuneCountInString":
2367
- this.emitExpr(args[0], "externref");
2368
- this._callStdlib("utf8_rune_count");
2369
- if (wide) this.pushInstruction("i64.extend_i32_s");
2370
- return;
2371
- case "RuneLen":
2372
- this._emitI32Arg(args[0]);
2373
- this._callStdlib("utf8_rune_len");
2374
- if (wide) this.pushInstruction("i64.extend_i32_s");
2375
- return;
2376
- case "ValidString":
2377
- this.emitExpr(args[0], "externref");
2378
- this._callStdlib("utf8_valid_string");
2379
- return;
2380
- case "FullRuneInString":
2381
- this.emitExpr(args[0], "externref");
2382
- this._callStdlib("utf8_full_rune");
2383
- return;
2384
- case "ValidRune": {
2385
- // 0 <= r <= MaxRune && !(0xD800 <= r <= 0xDFFF) ⇔ RuneLen(r) > 0
2386
- this._emitI32Arg(args[0]);
2387
- this._callStdlib("utf8_rune_len");
2388
- this.pushInstruction({ op: "i32.const", value: 0 });
2389
- this.pushInstruction("i32.gt_s");
2390
- return;
2391
- }
2392
- case "DecodeRuneInString":
2393
- case "DecodeLastRuneInString":
2394
- // (rune, size): decode stores the size for the follow-up import.
2395
- this.emitExpr(args[0], "externref");
2396
- this.pushInstruction({
2397
- op: "i32.const",
2398
- value: name === "DecodeLastRuneInString" ? 1 : 0,
2399
- });
2400
- this._callStdlib("utf8_decode_rune");
2401
- this.pushInstruction("i64.extend_i32_s");
2402
- this._callStdlib("utf8_decode_size");
2403
- this.pushInstruction("i64.extend_i32_s");
2404
- return;
2405
- default:
2406
- throw new Error(`Unsupported utf8 function: utf8.${name}`);
2407
- }
2408
- }
2409
-
2410
- emitSortCall(name, args, targetWasmType) {
2411
- switch (name) {
2412
- case "Ints":
2413
- case "Float64s":
2414
- case "Strings":
2415
- this.emitSlicesSort(args);
2416
- return;
2417
- case "IntsAreSorted":
2418
- case "Float64sAreSorted":
2419
- case "StringsAreSorted":
2420
- this.emitSortIsSorted(args, null);
2421
- return;
2422
- case "Slice":
2423
- case "SliceStable":
2424
- this.emitSortSlice(args);
2425
- return;
2426
- case "SliceIsSorted":
2427
- this.emitSortIsSorted(args, args[1]);
2428
- return;
2429
- case "Search":
2430
- this.emitSortSearch(args, targetWasmType);
2431
- return;
2432
- default:
2433
- throw new Error(`Unsupported sort function: sort.${name}`);
2434
- }
2435
- }
2436
-
2437
- // Calls `less(i, j)` (a `func(int, int) bool` closure held in a temp) with
2438
- // two i32 index temps.
2439
- _emitCallLess(closureTmp, closureInfo, iLoc, jLoc) {
2440
- this.pushInstruction({ op: "local.get", index: closureTmp });
2441
- this.pushInstruction({
2442
- op: "struct.get",
2443
- typeIndex: closureInfo.typeIndex,
2444
- fieldIndex: 1,
2445
- });
2446
- for (const loc of [iLoc, jLoc]) {
2447
- this.pushInstruction({ op: "local.get", index: loc });
2448
- if (closureInfo.paramWTypes[0] === "i64")
2449
- this.pushInstruction("i64.extend_i32_s");
2450
- }
2451
- this.pushInstruction({ op: "local.get", index: closureTmp });
2452
- this.pushInstruction({
2453
- op: "struct.get",
2454
- typeIndex: closureInfo.typeIndex,
2455
- fieldIndex: 0,
2456
- });
2457
- this.pushInstruction({
2458
- op: "call_ref",
2459
- typeIndex: closureInfo.funcTypeIndex,
2460
- });
2461
- }
2462
-
2463
- // Stable insertion sort driven by `less(i, j)` on live indices: swaps
2464
- // adjacent elements while less(j, j-1), so the closure always observes
2465
- // the slice's current layout.
2466
- emitSortSlice(args) {
2467
- const sNode = args[0];
2468
- const sType = sNode._type ?? this._resolveExprGoType(sNode);
2469
- const elemGoType = this._getSliceElemType(sType);
2470
- const sliceInfo = this.mod.getSliceType(elemGoType);
2471
- const arrInfo = sliceInfo.arrInfo;
2472
- const sliceW = {
2473
- kind: "ref",
2474
- nullable: true,
2475
- typeIndex: sliceInfo.typeIndex,
2476
- };
2477
- const arrW = { kind: "ref", nullable: true, typeIndex: arrInfo.typeIndex };
2478
- const closureInfo = this.mod.getClosureType(
2479
- this._resolveExprGoType(args[1]),
2480
- );
2481
- const closureW = {
2482
- kind: "ref",
2483
- nullable: true,
2484
- typeIndex: closureInfo.typeIndex,
2485
- };
2486
-
2487
- const s = this.acquireTemp(sliceW);
2488
- const cl = this.acquireTemp(closureW);
2489
- const arr = this.acquireTemp(arrW);
2490
- const off = this.acquireTemp("i32");
2491
- const len = this.acquireTemp("i32");
2492
- const i = this.acquireTemp("i32");
2493
- const j = this.acquireTemp("i32");
2494
- const jm1 = this.acquireTemp("i32");
2495
- const tmp = this.acquireTemp(arrInfo.elemWType);
2496
-
2497
- this.emitExpr(sNode, sliceW);
2498
- this.pushInstruction({ op: "local.set", index: s });
2499
- this.emitExpr(args[1], closureW);
2500
- this.pushInstruction({ op: "local.set", index: cl });
2501
-
2502
- this.pushInstruction({ op: "block", blockType: "void" });
2503
- this.pushInstruction({ op: "local.get", index: s });
2504
- this.pushInstruction("ref.is_null");
2505
- this.pushInstruction({ op: "br_if", depth: 0 });
2506
- this._emitSliceUnpack(s, sliceInfo, { arr, off, len });
2507
-
2508
- this.pushInstruction({ op: "i32.const", value: 1 });
2509
- this.pushInstruction({ op: "local.set", index: i });
2510
- this.pushInstruction({ op: "loop", blockType: "void" }); // outer
2511
- this.pushInstruction({ op: "local.get", index: i });
2512
- this.pushInstruction({ op: "local.get", index: len });
2513
- this.pushInstruction("i32.ge_s");
2514
- this.pushInstruction({ op: "br_if", depth: 1 });
2515
-
2516
- this.pushInstruction({ op: "local.get", index: i });
2517
- this.pushInstruction({ op: "local.set", index: j });
2518
- this.pushInstruction({ op: "block", blockType: "void" });
2519
- this.pushInstruction({ op: "loop", blockType: "void" }); // inner
2520
- this.pushInstruction({ op: "local.get", index: j });
2521
- this.pushInstruction("i32.eqz");
2522
- this.pushInstruction({ op: "br_if", depth: 1 });
2523
- this.pushInstruction({ op: "local.get", index: j });
2524
- this.pushInstruction({ op: "i32.const", value: 1 });
2525
- this.pushInstruction("i32.sub");
2526
- this.pushInstruction({ op: "local.set", index: jm1 });
2527
- this._emitCallLess(cl, closureInfo, j, jm1);
2528
- this.pushInstruction("i32.eqz");
2529
- this.pushInstruction({ op: "br_if", depth: 1 });
2530
- // swap arr[off+j], arr[off+j-1]
2531
- this.pushInstruction({ op: "local.get", index: arr });
2532
- this.pushInstruction({ op: "local.get", index: off });
2533
- this.pushInstruction({ op: "local.get", index: j });
2534
- this.pushInstruction("i32.add");
2535
- this.pushInstruction({ op: "array.get", typeIndex: arrInfo.typeIndex });
2536
- this.pushInstruction({ op: "local.set", index: tmp });
2537
- this.pushInstruction({ op: "local.get", index: arr });
2538
- this.pushInstruction({ op: "local.get", index: off });
2539
- this.pushInstruction({ op: "local.get", index: j });
2540
- this.pushInstruction("i32.add");
2541
- this.pushInstruction({ op: "local.get", index: arr });
2542
- this.pushInstruction({ op: "local.get", index: off });
2543
- this.pushInstruction({ op: "local.get", index: jm1 });
2544
- this.pushInstruction("i32.add");
2545
- this.pushInstruction({ op: "array.get", typeIndex: arrInfo.typeIndex });
2546
- this.pushInstruction({ op: "array.set", typeIndex: arrInfo.typeIndex });
2547
- this.pushInstruction({ op: "local.get", index: arr });
2548
- this.pushInstruction({ op: "local.get", index: off });
2549
- this.pushInstruction({ op: "local.get", index: jm1 });
2550
- this.pushInstruction("i32.add");
2551
- this.pushInstruction({ op: "local.get", index: tmp });
2552
- this.pushInstruction({ op: "array.set", typeIndex: arrInfo.typeIndex });
2553
- this.pushInstruction({ op: "local.get", index: jm1 });
2554
- this.pushInstruction({ op: "local.set", index: j });
2555
- this.pushInstruction({ op: "br", depth: 0 });
2556
- this.pushInstruction("end");
2557
- this.pushInstruction("end");
2558
-
2559
- this.pushInstruction({ op: "local.get", index: i });
2560
- this.pushInstruction({ op: "i32.const", value: 1 });
2561
- this.pushInstruction("i32.add");
2562
- this.pushInstruction({ op: "local.set", index: i });
2563
- this.pushInstruction({ op: "br", depth: 0 });
2564
- this.pushInstruction("end"); // outer
2565
- this.pushInstruction("end"); // block
2566
-
2567
- this.releaseTemp(tmp, arrInfo.elemWType);
2568
- this.releaseTemp(jm1, "i32");
2569
- this.releaseTemp(j, "i32");
2570
- this.releaseTemp(i, "i32");
2571
- this.releaseTemp(len, "i32");
2572
- this.releaseTemp(off, "i32");
2573
- this.releaseTemp(arr, arrW);
2574
- this.releaseTemp(cl, closureW);
2575
- this.releaseTemp(s, sliceW);
2576
- }
2577
-
2578
- // sort.*AreSorted / sort.SliceIsSorted: true unless some adjacent pair is
2579
- // out of order (`less(i, i-1)` or natural `<`).
2580
- emitSortIsSorted(args, lessNode) {
2581
- const sNode = args[0];
2582
- const sType = sNode._type ?? this._resolveExprGoType(sNode);
2583
- const elemGoType = this._getSliceElemType(sType);
2584
- const sliceInfo = this.mod.getSliceType(elemGoType);
2585
- const arrInfo = sliceInfo.arrInfo;
2586
- const sliceW = {
2587
- kind: "ref",
2588
- nullable: true,
2589
- typeIndex: sliceInfo.typeIndex,
2590
- };
2591
- const arrW = { kind: "ref", nullable: true, typeIndex: arrInfo.typeIndex };
2592
- const closureInfo = lessNode
2593
- ? this.mod.getClosureType(this._resolveExprGoType(lessNode))
2594
- : null;
2595
- const closureW = closureInfo
2596
- ? { kind: "ref", nullable: true, typeIndex: closureInfo.typeIndex }
2597
- : null;
2598
-
2599
- const s = this.acquireTemp(sliceW);
2600
- const cl = closureInfo ? this.acquireTemp(closureW) : null;
2601
- const arr = this.acquireTemp(arrW);
2602
- const off = this.acquireTemp("i32");
2603
- const len = this.acquireTemp("i32");
2604
- const i = this.acquireTemp("i32");
2605
- const im1 = this.acquireTemp("i32");
2606
- const res = this.acquireTemp("i32");
2607
- const a = lessNode ? null : this.acquireTemp(arrInfo.elemWType);
2608
- const b = lessNode ? null : this.acquireTemp(arrInfo.elemWType);
2609
-
2610
- this.emitExpr(sNode, sliceW);
2611
- this.pushInstruction({ op: "local.set", index: s });
2612
- if (lessNode) {
2613
- this.emitExpr(lessNode, closureW);
2614
- this.pushInstruction({ op: "local.set", index: cl });
2615
- }
2616
- this.pushInstruction({ op: "i32.const", value: 1 });
2617
- this.pushInstruction({ op: "local.set", index: res });
2618
-
2619
- this.pushInstruction({ op: "block", blockType: "void" });
2620
- this.pushInstruction({ op: "local.get", index: s });
2621
- this.pushInstruction("ref.is_null");
2622
- this.pushInstruction({ op: "br_if", depth: 0 });
2623
- this._emitSliceUnpack(s, sliceInfo, { arr, off, len });
2624
- this.pushInstruction({ op: "i32.const", value: 1 });
2625
- this.pushInstruction({ op: "local.set", index: i });
2626
- this.pushInstruction({ op: "loop", blockType: "void" });
2627
- this.pushInstruction({ op: "local.get", index: i });
2628
- this.pushInstruction({ op: "local.get", index: len });
2629
- this.pushInstruction("i32.ge_s");
2630
- this.pushInstruction({ op: "br_if", depth: 1 });
2631
- this.pushInstruction({ op: "local.get", index: i });
2632
- this.pushInstruction({ op: "i32.const", value: 1 });
2633
- this.pushInstruction("i32.sub");
2634
- this.pushInstruction({ op: "local.set", index: im1 });
2635
- if (lessNode) {
2636
- this._emitCallLess(cl, closureInfo, i, im1);
2637
- } else {
2638
- for (const [dst, idx] of [
2639
- [a, i],
2640
- [b, im1],
2641
- ]) {
2642
- this.pushInstruction({ op: "local.get", index: arr });
2643
- this.pushInstruction({ op: "local.get", index: off });
2644
- this.pushInstruction({ op: "local.get", index: idx });
2645
- this.pushInstruction("i32.add");
2646
- this.pushInstruction({ op: "array.get", typeIndex: arrInfo.typeIndex });
2647
- this.pushInstruction({ op: "local.set", index: dst });
2648
- }
2649
- this.emitElemLt(a, b, elemGoType);
2650
- }
2651
- // out of order → res = 0, exit
2652
- this.pushInstruction({ op: "if", blockType: "void" });
2653
- this.pushInstruction({ op: "i32.const", value: 0 });
2654
- this.pushInstruction({ op: "local.set", index: res });
2655
- this.pushInstruction({ op: "br", depth: 2 });
2656
- this.pushInstruction("end");
2657
- this.pushInstruction({ op: "local.get", index: i });
2658
- this.pushInstruction({ op: "i32.const", value: 1 });
2659
- this.pushInstruction("i32.add");
2660
- this.pushInstruction({ op: "local.set", index: i });
2661
- this.pushInstruction({ op: "br", depth: 0 });
2662
- this.pushInstruction("end");
2663
- this.pushInstruction("end");
2664
- this.pushInstruction({ op: "local.get", index: res });
2665
-
2666
- if (b !== null) this.releaseTemp(b, arrInfo.elemWType);
2667
- if (a !== null) this.releaseTemp(a, arrInfo.elemWType);
2668
- this.releaseTemp(res, "i32");
2669
- this.releaseTemp(im1, "i32");
2670
- this.releaseTemp(i, "i32");
2671
- this.releaseTemp(len, "i32");
2672
- this.releaseTemp(off, "i32");
2673
- this.releaseTemp(arr, arrW);
2674
- if (cl !== null) this.releaseTemp(cl, closureW);
2675
- this.releaseTemp(s, sliceW);
2676
- }
2677
-
2678
- // sort.Search(n, f): smallest i in [0, n) with f(i) true (binary search).
2679
- emitSortSearch(args, targetWasmType) {
2680
- const closureInfo = this.mod.getClosureType(
2681
- this._resolveExprGoType(args[1]),
2682
- );
2683
- const closureW = {
2684
- kind: "ref",
2685
- nullable: true,
2686
- typeIndex: closureInfo.typeIndex,
2687
- };
2688
- const cl = this.acquireTemp(closureW);
2689
- const lo = this.acquireTemp("i32");
2690
- const hi = this.acquireTemp("i32");
2691
- const mid = this.acquireTemp("i32");
2692
-
2693
- this._emitI32Arg(args[0]);
2694
- this.pushInstruction({ op: "local.set", index: hi });
2695
- this.emitExpr(args[1], closureW);
2696
- this.pushInstruction({ op: "local.set", index: cl });
2697
- this.pushInstruction({ op: "i32.const", value: 0 });
2698
- this.pushInstruction({ op: "local.set", index: lo });
2699
-
2700
- this.pushInstruction({ op: "block", blockType: "void" });
2701
- this.pushInstruction({ op: "loop", blockType: "void" });
2702
- this.pushInstruction({ op: "local.get", index: lo });
2703
- this.pushInstruction({ op: "local.get", index: hi });
2704
- this.pushInstruction("i32.ge_s");
2705
- this.pushInstruction({ op: "br_if", depth: 1 });
2706
- // mid = lo + (hi-lo)/2
2707
- this.pushInstruction({ op: "local.get", index: lo });
2708
- this.pushInstruction({ op: "local.get", index: hi });
2709
- this.pushInstruction({ op: "local.get", index: lo });
2710
- this.pushInstruction("i32.sub");
2711
- this.pushInstruction({ op: "i32.const", value: 1 });
2712
- this.pushInstruction("i32.shr_u");
2713
- this.pushInstruction("i32.add");
2714
- this.pushInstruction({ op: "local.set", index: mid });
2715
- // f(mid)
2716
- this.pushInstruction({ op: "local.get", index: cl });
2717
- this.pushInstruction({
2718
- op: "struct.get",
2719
- typeIndex: closureInfo.typeIndex,
2720
- fieldIndex: 1,
2721
- });
2722
- this.pushInstruction({ op: "local.get", index: mid });
2723
- if (closureInfo.paramWTypes[0] === "i64")
2724
- this.pushInstruction("i64.extend_i32_s");
2725
- this.pushInstruction({ op: "local.get", index: cl });
2726
- this.pushInstruction({
2727
- op: "struct.get",
2728
- typeIndex: closureInfo.typeIndex,
2729
- fieldIndex: 0,
2730
- });
2731
- this.pushInstruction({
2732
- op: "call_ref",
2733
- typeIndex: closureInfo.funcTypeIndex,
2734
- });
2735
- this.pushInstruction({ op: "if", blockType: "void" });
2736
- this.pushInstruction({ op: "local.get", index: mid });
2737
- this.pushInstruction({ op: "local.set", index: hi });
2738
- this.pushInstruction("else");
2739
- this.pushInstruction({ op: "local.get", index: mid });
2740
- this.pushInstruction({ op: "i32.const", value: 1 });
2741
- this.pushInstruction("i32.add");
2742
- this.pushInstruction({ op: "local.set", index: lo });
2743
- this.pushInstruction("end");
2744
- this.pushInstruction({ op: "br", depth: 0 });
2745
- this.pushInstruction("end");
2746
- this.pushInstruction("end");
2747
- this.pushInstruction({ op: "local.get", index: lo });
2748
- if ((targetWasmType ?? "i64") === "i64")
2749
- this.pushInstruction("i64.extend_i32_s");
2750
-
2751
- this.releaseTemp(mid, "i32");
2752
- this.releaseTemp(hi, "i32");
2753
- this.releaseTemp(lo, "i32");
2754
- this.releaseTemp(cl, closureW);
2755
- }
2756
-
2757
- // `a == b` for two anyref operands (interface values). `ref.eq` only
2758
- // accepts eqref, so host strings (internalised externs) are compared via
2759
- // `str_eq` and anything else by identity.
2760
- _emitAnyEq() {
2761
- const idx = this.mod.resolveFuncIndex("__any_eq");
2762
- if (idx === null) {
2763
- throw new Error(
2764
- "internal: anyref equality emitted but __any_eq was not declared (usesAnyEq scan missed a site)",
2765
- );
2766
- }
2767
- this.pushInstruction({ op: "call", funcIndex: idx });
2768
- }
2769
-
2770
- // Bodies of the synthetic runtime helpers declared in compileWasmModule.
2771
- emitRuntimeHelper(fn) {
2772
- const errInfo = this.mod.getStructType(ERROR_STRUCT);
2773
- switch (fn._isRuntimeHelper) {
2774
- case "any_eq": {
2775
- const bothNull = () => {
2776
- this.pushInstruction({ op: "local.get", index: 0 });
2777
- this.pushInstruction("ref.is_null");
2778
- this.pushInstruction({ op: "local.get", index: 1 });
2779
- this.pushInstruction("ref.is_null");
2780
- };
2781
- bothNull();
2782
- this.pushInstruction("i32.and");
2783
- this.pushInstruction({ op: "if", blockType: "void" });
2784
- this.pushInstruction({ op: "i32.const", value: 1 });
2785
- this.pushInstruction("return");
2786
- this.pushInstruction("end");
2787
- bothNull();
2788
- this.pushInstruction("i32.or");
2789
- this.pushInstruction({ op: "if", blockType: "void" });
2790
- this.pushInstruction({ op: "i32.const", value: 0 });
2791
- this.pushInstruction("return");
2792
- this.pushInstruction("end");
2793
- // Boxed scalars compare by value (Go: any(3) == any(3)).
2794
- for (const wType of ["i32", "i64", "f32", "f64"]) {
2795
- const box = this.mod.getBoxType(wType);
2796
- for (const idx of [0, 1]) {
2797
- this.pushInstruction({ op: "local.get", index: idx });
2798
- this.pushInstruction({ op: "ref.test", typeIndex: box.typeIndex });
2799
- }
2800
- this.pushInstruction("i32.and");
2801
- this.pushInstruction({ op: "if", blockType: "void" });
2802
- for (const idx of [0, 1]) {
2803
- this.pushInstruction({ op: "local.get", index: idx });
2804
- this.pushInstruction({ op: "ref.cast", typeIndex: box.typeIndex });
2805
- this.pushInstruction({
2806
- op: "struct.get",
2807
- typeIndex: box.typeIndex,
2808
- fieldIndex: 0,
2809
- });
2810
- }
2811
- this.pushInstruction(`${wType}.eq`);
2812
- this.pushInstruction("return");
2813
- this.pushInstruction("end");
2814
- }
2815
- this.pushInstruction({ op: "local.get", index: 0 });
2816
- this.pushInstruction({ op: "ref.test", heapType: "eq" });
2817
- this.pushInstruction({ op: "local.get", index: 1 });
2818
- this.pushInstruction({ op: "ref.test", heapType: "eq" });
2819
- this.pushInstruction("i32.and");
2820
- this.pushInstruction({ op: "if", blockType: "i32" });
2821
- this.pushInstruction({ op: "local.get", index: 0 });
2822
- this.pushInstruction({ op: "ref.cast", heapType: "eq" });
2823
- this.pushInstruction({ op: "local.get", index: 1 });
2824
- this.pushInstruction({ op: "ref.cast", heapType: "eq" });
2825
- this.pushInstruction("ref.eq");
2826
- this.pushInstruction("else");
2827
- this.pushInstruction({ op: "local.get", index: 0 });
2828
- this.pushInstruction({
2829
- op: "call",
2830
- funcIndex: this.mod.getIsStringImportIndex(),
2831
- });
2832
- this.pushInstruction({ op: "local.get", index: 1 });
2833
- this.pushInstruction({
2834
- op: "call",
2835
- funcIndex: this.mod.getIsStringImportIndex(),
2836
- });
2837
- this.pushInstruction("i32.and");
2838
- this.pushInstruction({ op: "if", blockType: "i32" });
2839
- this.pushInstruction({ op: "local.get", index: 0 });
2840
- this.pushInstruction("extern.convert_any");
2841
- this.pushInstruction({ op: "local.get", index: 1 });
2842
- this.pushInstruction("extern.convert_any");
2843
- this.pushInstruction({
2844
- op: "call",
2845
- funcIndex: this.mod.getStringCmpImportIndex("=="),
2846
- });
2847
- this.pushInstruction("else");
2848
- this.pushInstruction({ op: "i32.const", value: 0 });
2849
- this.pushInstruction("end");
2850
- this.pushInstruction("end");
2851
- this.pushInstruction("return");
2852
- return;
2853
- }
2854
- case "error_msg":
2855
- case "error_cause":
2856
- this.pushInstruction({ op: "local.get", index: 0 });
2857
- this.pushInstruction({
2858
- op: "struct.get",
2859
- typeIndex: errInfo.typeIndex,
2860
- fieldIndex: fn._isRuntimeHelper === "error_msg" ? 0 : 1,
2861
- });
2862
- this.pushInstruction("return");
2863
- return;
2864
- case "mk_error":
2865
- // nil message (strconv success) → nil error
2866
- this.pushInstruction({ op: "local.get", index: 0 });
2867
- this.pushInstruction("any.convert_extern");
2868
- this.pushInstruction("ref.is_null");
2869
- this.pushInstruction({ op: "if", blockType: "void" });
2870
- this.pushInstruction({ op: "ref.null", heapType: "any" });
2871
- this.pushInstruction("return");
2872
- this.pushInstruction("end");
2873
- this.pushInstruction({ op: "local.get", index: 0 });
2874
- this.pushInstruction({ op: "local.get", index: 1 });
2875
- this.pushInstruction({
2876
- op: "struct.new",
2877
- typeIndex: errInfo.typeIndex,
2878
- });
2879
- this.pushInstruction("return");
2880
- return;
2881
- case "error_str":
2882
- this.pushInstruction({ op: "local.get", index: 0 });
2883
- this.pushInstruction("ref.is_null");
2884
- this.pushInstruction({ op: "if", blockType: "void" });
2885
- this.pushInstruction({
2886
- op: "i32.const",
2887
- value: this.mod.internString("<nil>"),
2888
- });
2889
- this.pushInstruction({
2890
- op: "call",
2891
- funcIndex: this.mod.getStringImportIndex(),
2892
- });
2893
- this.pushInstruction("return");
2894
- this.pushInstruction("end");
2895
- this.pushInstruction({ op: "local.get", index: 0 });
2896
- this.pushInstruction({
2897
- op: "call",
2898
- funcIndex: this.mod.resolveFuncIndex("__dispatch_error_Error"),
2899
- });
2900
- this.pushInstruction("return");
2901
- return;
2902
- case "errors_is": {
2903
- // for err != nil { if err == target || sameMsg(err, target) { return true }; err = unwrap(err) }
2904
- this.pushInstruction({ op: "block", blockType: "void" });
2905
- this.pushInstruction({ op: "loop", blockType: "void" });
2906
- this.pushInstruction({ op: "local.get", index: 0 });
2907
- this.pushInstruction("ref.is_null");
2908
- this.pushInstruction({ op: "br_if", depth: 1 });
2909
- this.pushInstruction({ op: "local.get", index: 0 });
2910
- this.pushInstruction({ op: "local.get", index: 1 });
2911
- this._emitAnyEq();
2912
- this.pushInstruction({ op: "if", blockType: "void" });
2913
- this.pushInstruction({ op: "i32.const", value: 1 });
2914
- this.pushInstruction("return");
2915
- this.pushInstruction("end");
2916
- // both runtime errors with equal messages (mirrors the JS backend)
2917
- this.pushInstruction({ op: "local.get", index: 0 });
2918
- this.pushInstruction({ op: "ref.test", typeIndex: errInfo.typeIndex });
2919
- this.pushInstruction({ op: "local.get", index: 1 });
2920
- this.pushInstruction({ op: "ref.test", typeIndex: errInfo.typeIndex });
2921
- this.pushInstruction("i32.and");
2922
- this.pushInstruction({ op: "if", blockType: "void" });
2923
- for (const idx of [0, 1]) {
2924
- this.pushInstruction({ op: "local.get", index: idx });
2925
- this.pushInstruction({
2926
- op: "ref.cast",
2927
- typeIndex: errInfo.typeIndex,
2928
- });
2929
- this.pushInstruction({
2930
- op: "struct.get",
2931
- typeIndex: errInfo.typeIndex,
2932
- fieldIndex: 0,
2933
- });
2934
- }
2935
- this.pushInstruction({
2936
- op: "call",
2937
- funcIndex: this.mod.getStringCmpImportIndex("=="),
2938
- });
2939
- this.pushInstruction({ op: "if", blockType: "void" });
2940
- this.pushInstruction({ op: "i32.const", value: 1 });
2941
- this.pushInstruction("return");
2942
- this.pushInstruction("end");
2943
- this.pushInstruction("end");
2944
- this.pushInstruction({ op: "local.get", index: 0 });
2945
- this.pushInstruction({
2946
- op: "call",
2947
- funcIndex: this.mod.resolveFuncIndex("__errors_unwrap"),
2948
- });
2949
- this.pushInstruction({ op: "local.set", index: 0 });
2950
- this.pushInstruction({ op: "br", depth: 0 });
2951
- this.pushInstruction("end");
2952
- this.pushInstruction("end");
2953
- this.pushInstruction({ op: "i32.const", value: 0 });
2954
- this.pushInstruction("return");
2955
- return;
2956
- }
2957
- case "testing_run_cb": {
2958
- // (fn anyref, t externref): fn is a func(*testing.T) closure
2959
- const closureInfo = this.mod.getTestingRunClosureType();
2960
- this.pushInstruction({ op: "local.get", index: 0 });
2961
- this.pushInstruction({
2962
- op: "ref.cast",
2963
- typeIndex: closureInfo.typeIndex,
2964
- });
2965
- this.pushInstruction({
2966
- op: "struct.get",
2967
- typeIndex: closureInfo.typeIndex,
2968
- fieldIndex: 1,
2969
- });
2970
- this.pushInstruction({ op: "local.get", index: 1 });
2971
- this.pushInstruction({ op: "local.get", index: 0 });
2972
- this.pushInstruction({
2973
- op: "ref.cast",
2974
- typeIndex: closureInfo.typeIndex,
2975
- });
2976
- this.pushInstruction({
2977
- op: "struct.get",
2978
- typeIndex: closureInfo.typeIndex,
2979
- fieldIndex: 0,
2980
- });
2981
- this.pushInstruction({
2982
- op: "call_ref",
2983
- typeIndex: closureInfo.funcTypeIndex,
2984
- });
2985
- this.pushInstruction("return");
2986
- return;
2987
- }
2988
- default:
2989
- throw new Error(`Unknown runtime helper: ${fn._isRuntimeHelper}`);
2990
- }
2991
- }
2992
- }
2993
-
2994
- // Index of the argument consumed by the first `%w` verb in a literal format
2995
- // string, or -1.
2996
- function _wrapVerbIndex(format) {
2997
- if (format?.kind !== "BasicLit" || format.litKind !== "STRING") return -1;
2998
- const re = /%([#+\- 0]*)([0-9]*)\.?([0-9]*)([sdvftxXqobeEgGw%])/g;
2999
- let i = 0;
3000
- for (const m of String(format.value ?? "").matchAll(re)) {
3001
- if (m[4] === "%") continue;
3002
- if (m[4] === "w") return i;
3003
- i++;
3004
- }
3005
- return -1;
3006
- }
2
+ // Backwards compatibility re-export.
3
+ // WASM stdlib implementations have moved to src/backend/wasm/stdlib/.
4
+
5
+ export {
6
+ BINARY_MATH_IMPORTS,
7
+ BytesEmitter,
8
+ IoEmitter,
9
+ PurePackagesEmitter,
10
+ StdlibCoreEmitter,
11
+ StdlibCoreEmitter as StdlibEmitter,
12
+ TimeEmitter,
13
+ } from "./stdlib/index.js";