gofront 1.5.1 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/README.md +174 -466
  2. package/package.json +16 -20
  3. package/src/backend/js/expressions.js +288 -128
  4. package/src/backend/js/index.js +138 -56
  5. package/src/backend/js/runtime.js +1055 -53
  6. package/src/backend/js/source-map.js +32 -0
  7. package/src/backend/js/statements.js +151 -83
  8. package/src/backend/js/stdlib/base64.js +12 -0
  9. package/src/backend/js/stdlib/binary.js +102 -0
  10. package/src/backend/js/stdlib/bits.js +15 -1
  11. package/src/backend/js/stdlib/builder.js +28 -9
  12. package/src/backend/js/stdlib/bytes.js +75 -18
  13. package/src/backend/js/stdlib/errors.js +22 -3
  14. package/src/backend/js/stdlib/fmt.js +95 -20
  15. package/src/backend/js/stdlib/hex.js +17 -0
  16. package/src/backend/js/stdlib/html.js +1 -1
  17. package/src/backend/js/stdlib/index.js +19 -0
  18. package/src/backend/js/stdlib/io.js +105 -3
  19. package/src/backend/js/stdlib/maps.js +1 -1
  20. package/src/backend/js/stdlib/math.js +13 -1
  21. package/src/backend/js/stdlib/path.js +20 -6
  22. package/src/backend/js/stdlib/rand.js +10 -5
  23. package/src/backend/js/stdlib/sha256.js +12 -0
  24. package/src/backend/js/stdlib/shared.js +17 -0
  25. package/src/backend/js/stdlib/slices.js +53 -27
  26. package/src/backend/js/stdlib/sort.js +1 -1
  27. package/src/backend/js/stdlib/strconv.js +52 -11
  28. package/src/backend/js/stdlib/strings.js +152 -16
  29. package/src/backend/js/stdlib/sync.js +13 -0
  30. package/src/backend/js/stdlib/testing.js +2 -2
  31. package/src/backend/js/stdlib/time.js +93 -22
  32. package/src/backend/js/stdlib/unicode.js +2 -2
  33. package/src/backend/js/templ.js +1 -1
  34. package/src/backend/wasm/boundary.js +1677 -58
  35. package/src/backend/wasm/const-fold.js +205 -0
  36. package/src/backend/wasm/emit-builtins.js +1230 -0
  37. package/src/backend/wasm/emit-exprs.js +2096 -0
  38. package/src/backend/wasm/emit-maps.js +1999 -0
  39. package/src/backend/wasm/emit-shared.js +519 -0
  40. package/src/backend/wasm/emit-stdlib.js +13 -0
  41. package/src/backend/wasm/emit-stmts.js +2466 -0
  42. package/src/backend/wasm/emit.js +656 -5031
  43. package/src/backend/wasm/encode.js +259 -85
  44. package/src/backend/wasm/glue.js +19 -5
  45. package/src/backend/wasm/index.js +2432 -86
  46. package/src/backend/wasm/monomorph.js +711 -0
  47. package/src/backend/wasm/optimize.js +236 -0
  48. package/src/backend/wasm/stdlib/bytes.js +5039 -0
  49. package/src/backend/wasm/stdlib/core.js +8903 -0
  50. package/src/backend/wasm/stdlib/index.js +40 -0
  51. package/src/backend/wasm/stdlib/io.js +2161 -0
  52. package/src/backend/wasm/stdlib/packages.js +2400 -0
  53. package/src/backend/wasm/stdlib/pure.js +720 -0
  54. package/src/backend/wasm/stdlib/time.js +1200 -0
  55. package/src/backend/wasm/types.js +312 -36
  56. package/src/backend/wasm/wat.js +49 -2
  57. package/src/cli-core.js +276 -73
  58. package/src/compiler.js +162 -25
  59. package/src/dts-parser.js +79 -126
  60. package/src/index.js +88 -121
  61. package/src/lexer.js +80 -29
  62. package/src/lower/boxing.js +4 -6
  63. package/src/lower/captures.js +12 -23
  64. package/src/lower/escape.js +4 -9
  65. package/src/lower/functions.js +277 -5
  66. package/src/lower/globals.js +7 -15
  67. package/src/lower/index.js +5 -2
  68. package/src/lower/ownership.js +35 -19
  69. package/src/lower/range.js +2 -5
  70. package/src/lower/walk.js +25 -0
  71. package/src/minifier.js +20 -68
  72. package/src/parser/declarations.js +3 -6
  73. package/src/parser/expressions.js +3 -8
  74. package/src/parser/index.js +18 -3
  75. package/src/parser/statements.js +25 -38
  76. package/src/parser/types.js +1 -2
  77. package/src/resolver.js +3 -27
  78. package/src/templ-lexer.js +29 -63
  79. package/src/templ-parser.js +22 -45
  80. package/src/test-runner.js +20 -0
  81. package/src/typechecker/assignability.js +123 -14
  82. package/src/typechecker/expressions.js +244 -4
  83. package/src/typechecker/index.js +27 -1
  84. package/src/typechecker/resolve.js +71 -12
  85. package/src/typechecker/statements.js +0 -7
  86. package/src/typechecker/stdlib/core.js +292 -103
  87. package/src/typechecker/stdlib/extended.js +471 -4
  88. package/src/typechecker/stdlib/shared.js +53 -0
  89. package/src/typechecker/stdlib.js +2 -0
  90. package/src/typechecker/types.js +545 -8
  91. package/src/vendor.js +2 -2
@@ -0,0 +1,720 @@
1
+ // src/backend/wasm/stdlib/pure.js
2
+ // FunctionEmitter mixin: pure utility stdlib packages:
3
+ // unicode, math/rand, html, path/filepath.
4
+
5
+ export class PurePackagesEmitter {
6
+ emitUnicodeCall(name, args, targetWasmType) {
7
+ const rTmp = this.acquireTemp("i32");
8
+ this._emitI32Arg(args[0]);
9
+ this.pushInstruction({ op: "local.set", index: rTmp });
10
+
11
+ // Inline ASCII check: 0 <= r < 128
12
+ this.pushInstruction({ op: "local.get", index: rTmp });
13
+ this.pushInstruction({ op: "i32.const", value: 128 });
14
+ this.pushInstruction("i32.lt_u");
15
+ this.pushInstruction({ op: "if", blockType: "i32" });
16
+
17
+ // Inline ASCII fast path
18
+ switch (name) {
19
+ case "IsLetter": {
20
+ // (r - 'A' <= 25) || (r - 'a' <= 25)
21
+ this.pushInstruction({ op: "local.get", index: rTmp });
22
+ this.pushInstruction({ op: "i32.const", value: 65 });
23
+ this.pushInstruction("i32.sub");
24
+ this.pushInstruction({ op: "i32.const", value: 25 });
25
+ this.pushInstruction("i32.le_u");
26
+ this.pushInstruction({ op: "local.get", index: rTmp });
27
+ this.pushInstruction({ op: "i32.const", value: 97 });
28
+ this.pushInstruction("i32.sub");
29
+ this.pushInstruction({ op: "i32.const", value: 25 });
30
+ this.pushInstruction("i32.le_u");
31
+ this.pushInstruction("i32.or");
32
+ break;
33
+ }
34
+ case "IsDigit": {
35
+ // r - '0' <= 9
36
+ this.pushInstruction({ op: "local.get", index: rTmp });
37
+ this.pushInstruction({ op: "i32.const", value: 48 });
38
+ this.pushInstruction("i32.sub");
39
+ this.pushInstruction({ op: "i32.const", value: 9 });
40
+ this.pushInstruction("i32.le_u");
41
+ break;
42
+ }
43
+ case "IsSpace": {
44
+ // r == 32 || (r >= 9 && r <= 13)
45
+ this.pushInstruction({ op: "local.get", index: rTmp });
46
+ this.pushInstruction({ op: "i32.const", value: 32 });
47
+ this.pushInstruction("i32.eq");
48
+ this.pushInstruction({ op: "local.get", index: rTmp });
49
+ this.pushInstruction({ op: "i32.const", value: 9 });
50
+ this.pushInstruction("i32.sub");
51
+ this.pushInstruction({ op: "i32.const", value: 4 });
52
+ this.pushInstruction("i32.le_u");
53
+ this.pushInstruction("i32.or");
54
+ break;
55
+ }
56
+ case "IsUpper": {
57
+ // r - 'A' <= 25
58
+ this.pushInstruction({ op: "local.get", index: rTmp });
59
+ this.pushInstruction({ op: "i32.const", value: 65 });
60
+ this.pushInstruction("i32.sub");
61
+ this.pushInstruction({ op: "i32.const", value: 25 });
62
+ this.pushInstruction("i32.le_u");
63
+ break;
64
+ }
65
+ case "IsLower": {
66
+ // r - 'a' <= 25
67
+ this.pushInstruction({ op: "local.get", index: rTmp });
68
+ this.pushInstruction({ op: "i32.const", value: 97 });
69
+ this.pushInstruction("i32.sub");
70
+ this.pushInstruction({ op: "i32.const", value: 25 });
71
+ this.pushInstruction("i32.le_u");
72
+ break;
73
+ }
74
+ case "IsControl": {
75
+ // r <= 31 || r == 127
76
+ this.pushInstruction({ op: "local.get", index: rTmp });
77
+ this.pushInstruction({ op: "i32.const", value: 31 });
78
+ this.pushInstruction("i32.le_u");
79
+ this.pushInstruction({ op: "local.get", index: rTmp });
80
+ this.pushInstruction({ op: "i32.const", value: 127 });
81
+ this.pushInstruction("i32.eq");
82
+ this.pushInstruction("i32.or");
83
+ break;
84
+ }
85
+ case "IsPrint":
86
+ case "IsGraphic": {
87
+ // 32 <= r <= 126
88
+ this.pushInstruction({ op: "local.get", index: rTmp });
89
+ this.pushInstruction({ op: "i32.const", value: 32 });
90
+ this.pushInstruction("i32.sub");
91
+ this.pushInstruction({ op: "i32.const", value: 94 });
92
+ this.pushInstruction("i32.le_u");
93
+ break;
94
+ }
95
+ case "ToUpper": {
96
+ // if (r >= 'a' && r <= 'z') r - 32 else r
97
+ this.pushInstruction({ op: "local.get", index: rTmp });
98
+ this.pushInstruction({ op: "i32.const", value: 97 });
99
+ this.pushInstruction("i32.sub");
100
+ this.pushInstruction({ op: "i32.const", value: 25 });
101
+ this.pushInstruction("i32.le_u");
102
+ this.pushInstruction({ op: "if", blockType: "i32" });
103
+ this.pushInstruction({ op: "local.get", index: rTmp });
104
+ this.pushInstruction({ op: "i32.const", value: 32 });
105
+ this.pushInstruction("i32.sub");
106
+ this.pushInstruction("else");
107
+ this.pushInstruction({ op: "local.get", index: rTmp });
108
+ this.pushInstruction("end");
109
+ break;
110
+ }
111
+ case "ToLower": {
112
+ // if (r >= 'A' && r <= 'Z') r + 32 else r
113
+ this.pushInstruction({ op: "local.get", index: rTmp });
114
+ this.pushInstruction({ op: "i32.const", value: 65 });
115
+ this.pushInstruction("i32.sub");
116
+ this.pushInstruction({ op: "i32.const", value: 25 });
117
+ this.pushInstruction("i32.le_u");
118
+ this.pushInstruction({ op: "if", blockType: "i32" });
119
+ this.pushInstruction({ op: "local.get", index: rTmp });
120
+ this.pushInstruction({ op: "i32.const", value: 32 });
121
+ this.pushInstruction("i32.add");
122
+ this.pushInstruction("else");
123
+ this.pushInstruction({ op: "local.get", index: rTmp });
124
+ this.pushInstruction("end");
125
+ break;
126
+ }
127
+ case "IsPunct": {
128
+ this.pushInstruction({ op: "local.get", index: rTmp });
129
+ this._callStdlib("unicode_is_punct");
130
+ break;
131
+ }
132
+ default:
133
+ throw new Error(`Unsupported unicode function: unicode.${name}`);
134
+ }
135
+
136
+ this.pushInstruction("else");
137
+
138
+ // Non-ASCII fallback via host JS import
139
+ const hostName = {
140
+ IsLetter: "unicode_is_letter",
141
+ IsDigit: "unicode_is_digit",
142
+ IsSpace: "unicode_is_space",
143
+ IsUpper: "unicode_is_upper",
144
+ IsLower: "unicode_is_lower",
145
+ IsPunct: "unicode_is_punct",
146
+ IsControl: "unicode_is_control",
147
+ IsPrint: "unicode_is_print",
148
+ IsGraphic: "unicode_is_graphic",
149
+ ToUpper: "unicode_to_upper",
150
+ ToLower: "unicode_to_lower",
151
+ }[name];
152
+ this.pushInstruction({ op: "local.get", index: rTmp });
153
+ this._callStdlib(hostName);
154
+ this.pushInstruction("end");
155
+
156
+ if (
157
+ (name === "ToUpper" || name === "ToLower") &&
158
+ (targetWasmType ?? "i32") === "i64"
159
+ ) {
160
+ this.pushInstruction("i64.extend_i32_s");
161
+ }
162
+ this.releaseTemp(rTmp, "i32");
163
+ }
164
+
165
+ _emitRandNextU64() {
166
+ const gIdx = this.mod.getRandStateGlobalIndex();
167
+ const sTmp = this.acquireTemp("i64");
168
+ const zTmp = this.acquireTemp("i64");
169
+
170
+ // state = randState + 0x9e3779b97f4a7c15n
171
+ this.pushInstruction({ op: "global.get", index: gIdx });
172
+ this.pushInstruction({ op: "i64.const", value: 0x9e3779b97f4a7c15n });
173
+ this.pushInstruction("i64.add");
174
+ this.pushInstruction({ op: "local.tee", index: sTmp });
175
+ this.pushInstruction({ op: "global.set", index: gIdx });
176
+
177
+ // z = (state ^ (state >> 30)) * 0xbf58476d1ce4e5b9n
178
+ this.pushInstruction({ op: "local.get", index: sTmp });
179
+ this.pushInstruction({ op: "local.get", index: sTmp });
180
+ this.pushInstruction({ op: "i64.const", value: 30n });
181
+ this.pushInstruction("i64.shr_u");
182
+ this.pushInstruction("i64.xor");
183
+ this.pushInstruction({ op: "i64.const", value: 0xbf58476d1ce4e5b9n });
184
+ this.pushInstruction("i64.mul");
185
+ this.pushInstruction({ op: "local.set", index: zTmp });
186
+
187
+ // z = (z ^ (z >> 27)) * 0x94d049bb133111ebn
188
+ this.pushInstruction({ op: "local.get", index: zTmp });
189
+ this.pushInstruction({ op: "local.get", index: zTmp });
190
+ this.pushInstruction({ op: "i64.const", value: 27n });
191
+ this.pushInstruction("i64.shr_u");
192
+ this.pushInstruction("i64.xor");
193
+ this.pushInstruction({ op: "i64.const", value: 0x94d049bb133111ebn });
194
+ this.pushInstruction("i64.mul");
195
+ this.pushInstruction({ op: "local.set", index: zTmp });
196
+
197
+ // return z ^ (z >> 31)
198
+ this.pushInstruction({ op: "local.get", index: zTmp });
199
+ this.pushInstruction({ op: "local.get", index: zTmp });
200
+ this.pushInstruction({ op: "i64.const", value: 31n });
201
+ this.pushInstruction("i64.shr_u");
202
+ this.pushInstruction("i64.xor");
203
+
204
+ this.releaseTemp(zTmp, "i64");
205
+ this.releaseTemp(sTmp, "i64");
206
+ }
207
+
208
+ emitRandCall(name, args, targetWasmType) {
209
+ switch (name) {
210
+ case "Float64":
211
+ this._emitRandNextU64();
212
+ this.pushInstruction({ op: "i64.const", value: 11n });
213
+ this.pushInstruction("i64.shr_u");
214
+ this.pushInstruction("f64.convert_i64_u");
215
+ this.pushInstruction({
216
+ op: "f64.const",
217
+ value: 1.1102230246251565e-16,
218
+ });
219
+ this.pushInstruction("f64.mul");
220
+ return;
221
+ case "Float32":
222
+ this._emitRandNextU64();
223
+ this.pushInstruction({ op: "i64.const", value: 40n });
224
+ this.pushInstruction("i64.shr_u");
225
+ this.pushInstruction("f32.convert_i64_u");
226
+ this.pushInstruction({ op: "f32.const", value: 5.9604644775390625e-8 });
227
+ this.pushInstruction("f32.mul");
228
+ return;
229
+ case "Int":
230
+ case "Int31": {
231
+ this._emitRandNextU64();
232
+ this.pushInstruction({ op: "i64.const", value: 0x7fffffffn });
233
+ this.pushInstruction("i64.and");
234
+ if (targetWasmType === "i32") {
235
+ this.pushInstruction("i32.wrap_i64");
236
+ }
237
+ return;
238
+ }
239
+ case "Int63": {
240
+ this._emitRandNextU64();
241
+ this.pushInstruction({ op: "i64.const", value: 0x7fffffffffffffffn });
242
+ this.pushInstruction("i64.and");
243
+ if (targetWasmType === "i32") {
244
+ this.pushInstruction("i32.wrap_i64");
245
+ }
246
+ return;
247
+ }
248
+ case "Intn":
249
+ case "Int31n":
250
+ case "Int63n": {
251
+ const nTmp = this.acquireTemp("i64");
252
+ this.emitExpr(args[0], "i64");
253
+ this.pushInstruction({ op: "local.set", index: nTmp });
254
+
255
+ this.pushInstruction({ op: "local.get", index: nTmp });
256
+ this.pushInstruction({ op: "i64.const", value: 0n });
257
+ this.pushInstruction("i64.le_s");
258
+ this.pushInstruction({ op: "if", blockType: "void" });
259
+ this.emitPanic(`invalid argument to ${name}`);
260
+ this.pushInstruction("end");
261
+
262
+ this._emitRandNextU64();
263
+ this.pushInstruction({ op: "i64.const", value: 0x7fffffffffffffffn });
264
+ this.pushInstruction("i64.and");
265
+ this.pushInstruction({ op: "local.get", index: nTmp });
266
+ this.pushInstruction("i64.rem_u");
267
+ if (targetWasmType === "i32") {
268
+ this.pushInstruction("i32.wrap_i64");
269
+ }
270
+ this.releaseTemp(nTmp, "i64");
271
+ return;
272
+ }
273
+ case "Seed": {
274
+ const gIdx = this.mod.getRandStateGlobalIndex();
275
+ this.emitExpr(args[0], "i64");
276
+ this.pushInstruction({ op: "global.set", index: gIdx });
277
+ return;
278
+ }
279
+ case "Shuffle": {
280
+ const closureInfo = this.mod.getClosureType(
281
+ this._resolveExprGoType(args[1]),
282
+ );
283
+ const closureW = {
284
+ kind: "ref",
285
+ nullable: true,
286
+ typeIndex: closureInfo.typeIndex,
287
+ };
288
+ const nTmp = this.acquireTemp("i32");
289
+ const clTmp = this.acquireTemp(closureW);
290
+ const iTmp = this.acquireTemp("i32");
291
+ const jTmp = this.acquireTemp("i32");
292
+
293
+ this._emitI32Arg(args[0]);
294
+ this.pushInstruction({ op: "local.set", index: nTmp });
295
+
296
+ this.pushInstruction({ op: "local.get", index: nTmp });
297
+ this.pushInstruction({ op: "i32.const", value: 0 });
298
+ this.pushInstruction("i32.lt_s");
299
+ this.pushInstruction({ op: "if", blockType: "void" });
300
+ this.emitPanic("invalid argument to Shuffle");
301
+ this.pushInstruction("end");
302
+
303
+ this.emitExpr(args[1], closureW);
304
+ this.pushInstruction({ op: "local.set", index: clTmp });
305
+
306
+ // i = n - 1
307
+ this.pushInstruction({ op: "local.get", index: nTmp });
308
+ this.pushInstruction({ op: "i32.const", value: 1 });
309
+ this.pushInstruction("i32.sub");
310
+ this.pushInstruction({ op: "local.set", index: iTmp });
311
+
312
+ this.pushInstruction({ op: "block", blockType: "void" });
313
+ this.pushInstruction({ op: "loop", blockType: "void" });
314
+ this.pushInstruction({ op: "local.get", index: iTmp });
315
+ this.pushInstruction({ op: "i32.const", value: 0 });
316
+ this.pushInstruction("i32.le_s");
317
+ this.pushInstruction({ op: "br_if", depth: 1 });
318
+
319
+ // j = rand() % (i + 1)
320
+ this._emitRandNextU64();
321
+ this.pushInstruction({ op: "i64.const", value: 0x7fffffffn });
322
+ this.pushInstruction("i64.and");
323
+ this.pushInstruction("i32.wrap_i64");
324
+ this.pushInstruction({ op: "local.get", index: iTmp });
325
+ this.pushInstruction({ op: "i32.const", value: 1 });
326
+ this.pushInstruction("i32.add");
327
+ this.pushInstruction("i32.rem_u");
328
+ this.pushInstruction({ op: "local.set", index: jTmp });
329
+
330
+ // swap(i, j)
331
+ this.pushInstruction({ op: "local.get", index: clTmp });
332
+ this.pushInstruction({
333
+ op: "struct.get",
334
+ typeIndex: closureInfo.typeIndex,
335
+ fieldIndex: 1,
336
+ });
337
+ for (const loc of [iTmp, jTmp]) {
338
+ this.pushInstruction({ op: "local.get", index: loc });
339
+ if (closureInfo.paramWTypes[0] === "i64")
340
+ this.pushInstruction("i64.extend_i32_s");
341
+ }
342
+ this.pushInstruction({ op: "local.get", index: clTmp });
343
+ this.pushInstruction({
344
+ op: "struct.get",
345
+ typeIndex: closureInfo.typeIndex,
346
+ fieldIndex: 0,
347
+ });
348
+ this.pushInstruction({
349
+ op: "call_ref",
350
+ typeIndex: closureInfo.funcTypeIndex,
351
+ });
352
+
353
+ // i--
354
+ this.pushInstruction({ op: "local.get", index: iTmp });
355
+ this.pushInstruction({ op: "i32.const", value: 1 });
356
+ this.pushInstruction("i32.sub");
357
+ this.pushInstruction({ op: "local.set", index: iTmp });
358
+ this.pushInstruction({ op: "br", depth: 0 });
359
+ this.pushInstruction("end");
360
+ this.pushInstruction("end");
361
+
362
+ this.releaseTemp(jTmp, "i32");
363
+ this.releaseTemp(iTmp, "i32");
364
+ this.releaseTemp(clTmp, closureW);
365
+ this.releaseTemp(nTmp, "i32");
366
+ return;
367
+ }
368
+ case "Perm": {
369
+ const elemGo = { kind: "basic", name: "int" };
370
+ const sliceInfo = this.mod.getSliceType(elemGo);
371
+ const arrInfo = sliceInfo.arrInfo;
372
+ const arrW = {
373
+ kind: "ref",
374
+ nullable: true,
375
+ typeIndex: arrInfo.typeIndex,
376
+ };
377
+
378
+ const nTmp = this.acquireTemp("i32");
379
+ const arrTmp = this.acquireTemp(arrW);
380
+ const iTmp = this.acquireTemp("i32");
381
+ const jTmp = this.acquireTemp("i32");
382
+ const valITmp = this.acquireTemp("i64");
383
+ const valJTmp = this.acquireTemp("i64");
384
+
385
+ this._emitI32Arg(args[0]);
386
+ this.pushInstruction({ op: "local.set", index: nTmp });
387
+
388
+ this.pushInstruction({ op: "local.get", index: nTmp });
389
+ this.pushInstruction({ op: "i32.const", value: 0 });
390
+ this.pushInstruction("i32.lt_s");
391
+ this.pushInstruction({ op: "if", blockType: "void" });
392
+ this.emitPanic("invalid argument to Perm");
393
+ this.pushInstruction("end");
394
+
395
+ this.pushInstruction({ op: "local.get", index: nTmp });
396
+ this.pushInstruction({
397
+ op: "array.new_default",
398
+ typeIndex: arrInfo.typeIndex,
399
+ });
400
+ this.pushInstruction({ op: "local.set", index: arrTmp });
401
+
402
+ // initialize arr[i] = i
403
+ this.pushInstruction({ op: "i32.const", value: 0 });
404
+ this.pushInstruction({ op: "local.set", index: iTmp });
405
+ this.pushInstruction({ op: "block", blockType: "void" });
406
+ this.pushInstruction({ op: "loop", blockType: "void" });
407
+ this.pushInstruction({ op: "local.get", index: iTmp });
408
+ this.pushInstruction({ op: "local.get", index: nTmp });
409
+ this.pushInstruction("i32.ge_s");
410
+ this.pushInstruction({ op: "br_if", depth: 1 });
411
+ this.pushInstruction({ op: "local.get", index: arrTmp });
412
+ this.pushInstruction({ op: "local.get", index: iTmp });
413
+ this.pushInstruction({ op: "local.get", index: iTmp });
414
+ this.pushInstruction("i64.extend_i32_s");
415
+ this.pushInstruction({
416
+ op: "array.set",
417
+ typeIndex: arrInfo.typeIndex,
418
+ });
419
+ this.pushInstruction({ op: "local.get", index: iTmp });
420
+ this.pushInstruction({ op: "i32.const", value: 1 });
421
+ this.pushInstruction("i32.add");
422
+ this.pushInstruction({ op: "local.set", index: iTmp });
423
+ this.pushInstruction({ op: "br", depth: 0 });
424
+ this.pushInstruction("end");
425
+ this.pushInstruction("end");
426
+
427
+ // shuffle: for i = n - 1 down to 1
428
+ this.pushInstruction({ op: "local.get", index: nTmp });
429
+ this.pushInstruction({ op: "i32.const", value: 1 });
430
+ this.pushInstruction("i32.sub");
431
+ this.pushInstruction({ op: "local.set", index: iTmp });
432
+ this.pushInstruction({ op: "block", blockType: "void" });
433
+ this.pushInstruction({ op: "loop", blockType: "void" });
434
+ this.pushInstruction({ op: "local.get", index: iTmp });
435
+ this.pushInstruction({ op: "i32.const", value: 0 });
436
+ this.pushInstruction("i32.le_s");
437
+ this.pushInstruction({ op: "br_if", depth: 1 });
438
+
439
+ // j = rand() % (i + 1)
440
+ this._emitRandNextU64();
441
+ this.pushInstruction({ op: "i64.const", value: 0x7fffffffn });
442
+ this.pushInstruction("i64.and");
443
+ this.pushInstruction("i32.wrap_i64");
444
+ this.pushInstruction({ op: "local.get", index: iTmp });
445
+ this.pushInstruction({ op: "i32.const", value: 1 });
446
+ this.pushInstruction("i32.add");
447
+ this.pushInstruction("i32.rem_u");
448
+ this.pushInstruction({ op: "local.set", index: jTmp });
449
+
450
+ // swap arr[i], arr[j]
451
+ this.pushInstruction({ op: "local.get", index: arrTmp });
452
+ this.pushInstruction({ op: "local.get", index: iTmp });
453
+ this.pushInstruction({ op: "array.get", typeIndex: arrInfo.typeIndex });
454
+ this.pushInstruction({ op: "local.set", index: valITmp });
455
+ this.pushInstruction({ op: "local.get", index: arrTmp });
456
+ this.pushInstruction({ op: "local.get", index: jTmp });
457
+ this.pushInstruction({ op: "array.get", typeIndex: arrInfo.typeIndex });
458
+ this.pushInstruction({ op: "local.set", index: valJTmp });
459
+
460
+ this.pushInstruction({ op: "local.get", index: arrTmp });
461
+ this.pushInstruction({ op: "local.get", index: iTmp });
462
+ this.pushInstruction({ op: "local.get", index: valJTmp });
463
+ this.pushInstruction({ op: "array.set", typeIndex: arrInfo.typeIndex });
464
+ this.pushInstruction({ op: "local.get", index: arrTmp });
465
+ this.pushInstruction({ op: "local.get", index: jTmp });
466
+ this.pushInstruction({ op: "local.get", index: valITmp });
467
+ this.pushInstruction({ op: "array.set", typeIndex: arrInfo.typeIndex });
468
+
469
+ this.pushInstruction({ op: "local.get", index: iTmp });
470
+ this.pushInstruction({ op: "i32.const", value: 1 });
471
+ this.pushInstruction("i32.sub");
472
+ this.pushInstruction({ op: "local.set", index: iTmp });
473
+ this.pushInstruction({ op: "br", depth: 0 });
474
+ this.pushInstruction("end");
475
+ this.pushInstruction("end");
476
+
477
+ // wrap in slice struct
478
+ this.pushInstruction({ op: "local.get", index: arrTmp });
479
+ this.pushInstruction({ op: "i32.const", value: 0 });
480
+ this.pushInstruction({ op: "local.get", index: nTmp });
481
+ this.pushInstruction({ op: "local.get", index: nTmp });
482
+ this.pushInstruction({
483
+ op: "struct.new",
484
+ typeIndex: sliceInfo.typeIndex,
485
+ });
486
+
487
+ this.releaseTemp(valJTmp, "i64");
488
+ this.releaseTemp(valITmp, "i64");
489
+ this.releaseTemp(jTmp, "i32");
490
+ this.releaseTemp(iTmp, "i32");
491
+ this.releaseTemp(arrTmp, arrW);
492
+ this.releaseTemp(nTmp, "i32");
493
+ return;
494
+ }
495
+ case "Read": {
496
+ const sNode = args[0];
497
+ const byteGo = { kind: "basic", name: "byte" };
498
+ const sliceInfo = this.mod.getSliceType(byteGo);
499
+ const arrInfo = sliceInfo.arrInfo;
500
+ const sliceW = {
501
+ kind: "ref",
502
+ nullable: true,
503
+ typeIndex: sliceInfo.typeIndex,
504
+ };
505
+ const arrW = {
506
+ kind: "ref",
507
+ nullable: true,
508
+ typeIndex: arrInfo.typeIndex,
509
+ };
510
+
511
+ const sTmp = this.acquireTemp(sliceW);
512
+ this.emitExpr(sNode, sliceW);
513
+ this.pushInstruction({ op: "local.set", index: sTmp });
514
+
515
+ const arrTmp = this.acquireTemp(arrW);
516
+ const offTmp = this.acquireTemp("i32");
517
+ const lenTmp = this.acquireTemp("i32");
518
+ this._emitSliceUnpack(sTmp, sliceInfo, {
519
+ arr: arrTmp,
520
+ off: offTmp,
521
+ len: lenTmp,
522
+ });
523
+
524
+ // crypto_rand_fill(len)
525
+ this.pushInstruction({ op: "local.get", index: lenTmp });
526
+ this._callStdlib("crypto_rand_fill");
527
+ this.pushInstruction("drop");
528
+
529
+ const iLoc = this.acquireTemp("i32");
530
+ this.pushInstruction({ op: "i32.const", value: 0 });
531
+ this.pushInstruction({ op: "local.set", index: iLoc });
532
+
533
+ this.pushInstruction({ op: "block", blockType: "void" });
534
+ this.pushInstruction({ op: "loop", blockType: "void" });
535
+ this.pushInstruction({ op: "local.get", index: iLoc });
536
+ this.pushInstruction({ op: "local.get", index: lenTmp });
537
+ this.pushInstruction("i32.ge_s");
538
+ this.pushInstruction({ op: "br_if", depth: 1 });
539
+
540
+ // arr[off + i] = raw_bytes_get(i)
541
+ this.pushInstruction({ op: "local.get", index: arrTmp });
542
+ this.pushInstruction({ op: "local.get", index: offTmp });
543
+ this.pushInstruction({ op: "local.get", index: iLoc });
544
+ this.pushInstruction("i32.add");
545
+
546
+ this.pushInstruction({ op: "local.get", index: iLoc });
547
+ this._callStdlib("raw_bytes_get");
548
+
549
+ this.pushInstruction({
550
+ op: "array.set",
551
+ typeIndex: arrInfo.typeIndex,
552
+ });
553
+
554
+ // i++
555
+ this.pushInstruction({ op: "local.get", index: iLoc });
556
+ this.pushInstruction({ op: "i32.const", value: 1 });
557
+ this.pushInstruction("i32.add");
558
+ this.pushInstruction({ op: "local.set", index: iLoc });
559
+ this.pushInstruction({ op: "br", depth: 0 });
560
+ this.pushInstruction("end");
561
+ this.pushInstruction("end");
562
+
563
+ // return (len, nil)
564
+ this.pushInstruction({ op: "local.get", index: lenTmp });
565
+ this.pushInstruction("i64.extend_i32_u");
566
+ this.pushInstruction({ op: "ref.null", heapType: "any" });
567
+
568
+ this.releaseTemp(iLoc, "i32");
569
+ this.releaseTemp(lenTmp, "i32");
570
+ this.releaseTemp(offTmp, "i32");
571
+ this.releaseTemp(arrTmp, arrW);
572
+ this.releaseTemp(sTmp, sliceW);
573
+ return;
574
+ }
575
+ default:
576
+ throw new Error(`Unsupported rand function: rand.${name}`);
577
+ }
578
+ }
579
+
580
+ emitHtmlCall(name, args) {
581
+ this.emitExpr(args[0], "externref");
582
+ if (name === "EscapeString") {
583
+ this._callStdlib("html_escape");
584
+ } else if (name === "UnescapeString") {
585
+ this._callStdlib("html_unescape");
586
+ } else {
587
+ throw new Error(`Unsupported html function: html.${name}`);
588
+ }
589
+ }
590
+
591
+ emitPathCall(name, args) {
592
+ switch (name) {
593
+ case "Base":
594
+ this.emitExpr(args[0], "externref");
595
+ this._callStdlib("path_base");
596
+ return;
597
+ case "Dir":
598
+ this.emitExpr(args[0], "externref");
599
+ this._callStdlib("path_dir");
600
+ return;
601
+ case "Ext":
602
+ this.emitExpr(args[0], "externref");
603
+ this._callStdlib("path_ext");
604
+ return;
605
+ case "Clean":
606
+ this.emitExpr(args[0], "externref");
607
+ this._callStdlib("path_clean");
608
+ return;
609
+ case "IsAbs":
610
+ this.emitExpr(args[0], "externref");
611
+ this._callStdlib("path_is_abs");
612
+ return;
613
+ case "Join": {
614
+ this._callStdlib("path_join_init");
615
+ for (const a of args) {
616
+ const isSpread =
617
+ a._spread ||
618
+ (args.length === 1 && this._resolveExprGoType(a)?.kind === "slice");
619
+ if (isSpread) {
620
+ const elemGo = { kind: "basic", name: "string" };
621
+ const sliceInfo = this.mod.getSliceType(elemGo);
622
+ const arrInfo = sliceInfo.arrInfo;
623
+ const sliceW = {
624
+ kind: "ref",
625
+ nullable: true,
626
+ typeIndex: sliceInfo.typeIndex,
627
+ };
628
+ const arrW = {
629
+ kind: "ref",
630
+ nullable: true,
631
+ typeIndex: arrInfo.typeIndex,
632
+ };
633
+ const sTmp = this.acquireTemp(sliceW);
634
+ this.emitExpr(a, sliceW);
635
+ this.pushInstruction({ op: "local.set", index: sTmp });
636
+
637
+ const arrTmp = this.acquireTemp(arrW);
638
+ const offTmp = this.acquireTemp("i32");
639
+ const lenTmp = this.acquireTemp("i32");
640
+ this._emitSliceUnpack(sTmp, sliceInfo, {
641
+ arr: arrTmp,
642
+ off: offTmp,
643
+ len: lenTmp,
644
+ });
645
+ this.releaseTemp(sTmp, sliceW);
646
+
647
+ const iTmp = this.acquireTemp("i32");
648
+ this.pushInstruction({ op: "i32.const", value: 0 });
649
+ this.pushInstruction({ op: "local.set", index: iTmp });
650
+
651
+ this.pushInstruction({ op: "block", blockType: "void" });
652
+ this.pushInstruction({ op: "loop", blockType: "void" });
653
+ this.pushInstruction({ op: "local.get", index: iTmp });
654
+ this.pushInstruction({ op: "local.get", index: lenTmp });
655
+ this.pushInstruction("i32.ge_s");
656
+ this.pushInstruction({ op: "br_if", depth: 1 });
657
+
658
+ this.pushInstruction({ op: "local.get", index: arrTmp });
659
+ this.pushInstruction({ op: "local.get", index: offTmp });
660
+ this.pushInstruction({ op: "local.get", index: iTmp });
661
+ this.pushInstruction("i32.add");
662
+ this.pushInstruction({
663
+ op: "array.get",
664
+ typeIndex: arrInfo.typeIndex,
665
+ });
666
+ this._callStdlib("path_join_push");
667
+
668
+ this.pushInstruction({ op: "local.get", index: iTmp });
669
+ this.pushInstruction({ op: "i32.const", value: 1 });
670
+ this.pushInstruction("i32.add");
671
+ this.pushInstruction({ op: "local.set", index: iTmp });
672
+ this.pushInstruction({ op: "br", depth: 0 });
673
+ this.pushInstruction("end");
674
+ this.pushInstruction("end");
675
+
676
+ this.releaseTemp(iTmp, "i32");
677
+ this.releaseTemp(lenTmp, "i32");
678
+ this.releaseTemp(offTmp, "i32");
679
+ this.releaseTemp(arrTmp, arrW);
680
+ } else {
681
+ this.emitExpr(a, "externref");
682
+ this._callStdlib("path_join_push");
683
+ }
684
+ }
685
+ this._callStdlib("path_join_run");
686
+ return;
687
+ }
688
+ case "Split":
689
+ this.emitExpr(args[0], "externref");
690
+ this._callStdlib("path_split");
691
+ this._callStdlib("path_split_dir");
692
+ this._callStdlib("path_split_file");
693
+ return;
694
+ case "Match": {
695
+ this.emitExpr(args[0], "externref");
696
+ this.emitExpr(args[1], "externref");
697
+ const matchedTmp = this.acquireTemp("i32");
698
+ this._callStdlib("path_match");
699
+ this.pushInstruction({ op: "local.set", index: matchedTmp });
700
+
701
+ this._callStdlib("path_match_has_err");
702
+ this.pushInstruction({ op: "if", blockType: "anyref" });
703
+ this.emitPathConst("ErrBadPattern");
704
+ this.pushInstruction("else");
705
+ this.pushInstruction({ op: "ref.null", heapType: "any" });
706
+ this.pushInstruction("end");
707
+ const errTmp = this.acquireTemp("anyref");
708
+ this.pushInstruction({ op: "local.set", index: errTmp });
709
+
710
+ this.pushInstruction({ op: "local.get", index: matchedTmp });
711
+ this.pushInstruction({ op: "local.get", index: errTmp });
712
+ this.releaseTemp(errTmp, "anyref");
713
+ this.releaseTemp(matchedTmp, "i32");
714
+ return;
715
+ }
716
+ default:
717
+ throw new Error(`Unsupported path function: path.${name}`);
718
+ }
719
+ }
720
+ }