gofront 1.5.1 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +174 -466
  2. package/package.json +16 -20
  3. package/src/backend/js/expressions.js +50 -19
  4. package/src/backend/js/index.js +100 -55
  5. package/src/backend/js/runtime.js +102 -0
  6. package/src/backend/js/source-map.js +32 -0
  7. package/src/backend/js/statements.js +96 -16
  8. package/src/backend/js/stdlib/errors.js +2 -2
  9. package/src/backend/js/stdlib/fmt.js +26 -8
  10. package/src/backend/js/stdlib/index.js +3 -0
  11. package/src/backend/js/stdlib/maps.js +1 -1
  12. package/src/backend/js/stdlib/path.js +2 -2
  13. package/src/backend/js/stdlib/shared.js +17 -0
  14. package/src/backend/js/stdlib/slices.js +1 -1
  15. package/src/backend/js/stdlib/sort.js +1 -1
  16. package/src/backend/js/stdlib/strconv.js +34 -9
  17. package/src/backend/js/stdlib/strings.js +4 -1
  18. package/src/backend/js/stdlib/testing.js +2 -2
  19. package/src/backend/js/stdlib/time.js +3 -3
  20. package/src/backend/js/templ.js +1 -1
  21. package/src/backend/wasm/boundary.js +486 -33
  22. package/src/backend/wasm/emit-builtins.js +1103 -0
  23. package/src/backend/wasm/emit-exprs.js +1908 -0
  24. package/src/backend/wasm/emit-maps.js +1863 -0
  25. package/src/backend/wasm/emit-shared.js +519 -0
  26. package/src/backend/wasm/emit-stdlib.js +3006 -0
  27. package/src/backend/wasm/emit-stmts.js +2472 -0
  28. package/src/backend/wasm/emit.js +606 -5009
  29. package/src/backend/wasm/encode.js +212 -64
  30. package/src/backend/wasm/glue.js +19 -5
  31. package/src/backend/wasm/index.js +1426 -49
  32. package/src/backend/wasm/monomorph.js +711 -0
  33. package/src/backend/wasm/optimize.js +227 -0
  34. package/src/backend/wasm/types.js +159 -35
  35. package/src/backend/wasm/wat.js +49 -2
  36. package/src/cli-core.js +275 -72
  37. package/src/compiler.js +144 -21
  38. package/src/index.js +86 -115
  39. package/src/lower/boxing.js +3 -4
  40. package/src/lower/captures.js +10 -17
  41. package/src/lower/escape.js +4 -9
  42. package/src/lower/functions.js +277 -5
  43. package/src/lower/globals.js +7 -15
  44. package/src/lower/index.js +5 -2
  45. package/src/lower/ownership.js +5 -10
  46. package/src/lower/range.js +2 -5
  47. package/src/lower/walk.js +25 -0
  48. package/src/resolver.js +2 -1
  49. package/src/test-runner.js +20 -0
  50. package/src/typechecker/assignability.js +42 -1
  51. package/src/typechecker/expressions.js +142 -2
  52. package/src/typechecker/index.js +27 -1
  53. package/src/typechecker/resolve.js +35 -6
  54. package/src/typechecker/statements.js +0 -7
  55. package/src/typechecker/stdlib/core.js +17 -19
  56. package/src/typechecker/stdlib/extended.js +18 -3
  57. package/src/typechecker/stdlib/shared.js +53 -0
  58. package/src/typechecker/stdlib.js +2 -0
  59. package/src/typechecker/types.js +206 -7
  60. package/src/vendor.js +2 -2
@@ -0,0 +1,519 @@
1
+ // src/backend/wasm/emit-shared.js
2
+ // `gofront/shared` linear-memory buffers: a `(base i32, len i32)` struct that
3
+ // views the module's single exported memory. Elements are read/written with
4
+ // plain loads/stores so JS can alias the same bytes through a TypedArray.
5
+
6
+ import { getSharedInfo } from "./types.js";
7
+
8
+ const LOAD_OP = {
9
+ f32: "f32.load",
10
+ f64: "f64.load",
11
+ i8: "i32.load8_s",
12
+ u8: "i32.load8_u",
13
+ i16: "i32.load16_s",
14
+ u16: "i32.load16_u",
15
+ i32: "i32.load",
16
+ u32: "i32.load",
17
+ };
18
+
19
+ const STORE_OP = {
20
+ f32: "f32.store",
21
+ f64: "f64.store",
22
+ i8: "i32.store8",
23
+ u8: "i32.store8",
24
+ i16: "i32.store16",
25
+ u16: "i32.store16",
26
+ i32: "i32.store",
27
+ u32: "i32.store",
28
+ };
29
+
30
+ const SHIFT = { 1: 0, 2: 1, 4: 2, 8: 3 };
31
+
32
+ export class SharedEmitter {
33
+ _sharedInfo(goType) {
34
+ return getSharedInfo(goType, this.mod.checker);
35
+ }
36
+
37
+ _sharedElemWType(info) {
38
+ return info.key === "f32" ? "f32" : info.key === "f64" ? "f64" : "i32";
39
+ }
40
+
41
+ // Pushes the i32 byte address of `base[idx]` with a bounds check.
42
+ _emitSharedElemAddr(baseNode, indexNode, info) {
43
+ const { wType, typeIndex } = this.mod.getSharedType();
44
+ const bufTmp = this.acquireTemp(wType);
45
+ this.emitExpr(baseNode, wType);
46
+ this.pushInstruction({ op: "local.tee", index: bufTmp });
47
+ this.pushInstruction({ op: "struct.get", typeIndex, fieldIndex: 0 });
48
+ this._emitCheckedIndex(indexNode, () => {
49
+ this.pushInstruction({ op: "local.get", index: bufTmp });
50
+ this.pushInstruction({ op: "struct.get", typeIndex, fieldIndex: 1 });
51
+ });
52
+ this._emitSharedScale(info);
53
+ this.pushInstruction("i32.add");
54
+ this.releaseTemp(bufTmp, wType);
55
+ }
56
+
57
+ _emitSharedScale(info) {
58
+ const shift = SHIFT[info.bytes];
59
+ if (shift) {
60
+ this.pushInstruction({ op: "i32.const", value: shift });
61
+ this.pushInstruction("i32.shl");
62
+ }
63
+ }
64
+
65
+ // Loads the element at the address on the stack, widened to `target`.
66
+ _emitSharedLoad(info, targetWasmType) {
67
+ this.pushInstruction(LOAD_OP[info.key]);
68
+ this._emitSharedWiden(info, targetWasmType);
69
+ }
70
+
71
+ _emitSharedWiden(info, targetWasmType) {
72
+ const elemW = this._sharedElemWType(info);
73
+ if (targetWasmType === "i64" && elemW === "i32") {
74
+ this.pushInstruction(
75
+ info.key.startsWith("u") ? "i64.extend_i32_u" : "i64.extend_i32_s",
76
+ );
77
+ } else if (targetWasmType === "f64" && elemW === "f32") {
78
+ this.pushInstruction("f64.promote_f32");
79
+ }
80
+ }
81
+
82
+ _emitSharedStore(info) {
83
+ this.pushInstruction(STORE_OP[info.key]);
84
+ }
85
+
86
+ // `buf[i]` as an rvalue.
87
+ emitSharedIndexExpr(expr, info, targetWasmType) {
88
+ this._emitSharedElemAddr(expr.expr, expr.index, info);
89
+ this._emitSharedLoad(info, targetWasmType);
90
+ }
91
+
92
+ // `buf[i] = v`
93
+ emitSharedIndexAssign(lhs, rhsNode, info) {
94
+ this._emitSharedElemAddr(lhs.expr, lhs.index, info);
95
+ this.emitExpr(rhsNode, this._sharedElemWType(info));
96
+ this._emitSharedStore(info);
97
+ }
98
+
99
+ // `buf[i] op= v` / `buf[i]++`: `emitRhs` must leave the new element value
100
+ // (as the element's wasm type) given the old value already on the stack.
101
+ emitSharedIndexUpdate(lhs, info, emitRhs) {
102
+ const addrTmp = this.acquireTemp("i32");
103
+ this._emitSharedElemAddr(lhs.expr, lhs.index, info);
104
+ this.pushInstruction({ op: "local.tee", index: addrTmp });
105
+ this.pushInstruction({ op: "local.get", index: addrTmp });
106
+ this.pushInstruction(LOAD_OP[info.key]);
107
+ emitRhs(this._sharedElemWType(info), info.elem);
108
+ this._emitSharedStore(info);
109
+ this.releaseTemp(addrTmp, "i32");
110
+ }
111
+
112
+ // `len(buf)`
113
+ emitSharedLen(arg, targetWasmType) {
114
+ const { wType, typeIndex } = this.mod.getSharedType();
115
+ this.emitExpr(arg, wType);
116
+ this.pushInstruction({ op: "struct.get", typeIndex, fieldIndex: 1 });
117
+ if ((targetWasmType ?? "i64") === "i64") {
118
+ this.pushInstruction("i64.extend_i32_u");
119
+ }
120
+ }
121
+
122
+ // `shared.NewFloat32(n)` → `__shared_new(n, bytes)`
123
+ emitSharedNew(field, args) {
124
+ const info = getSharedInfo(
125
+ { kind: "shared", shared: field.slice(3) },
126
+ this.mod.checker,
127
+ );
128
+ if (!info?.ctor) throw new Error(`Unsupported shared.${field}`);
129
+ this._emitIndexArgI32(args[0]);
130
+ this.pushInstruction({ op: "i32.const", value: info.bytes });
131
+ this.pushInstruction({
132
+ op: "call",
133
+ funcIndex: this.mod.resolveFuncIndex("__shared_new"),
134
+ });
135
+ }
136
+
137
+ // `buf.Subarray(lo, hi)` → `__shared_sub(buf, lo, hi, bytes)`
138
+ emitSharedSubarray(recvNode, args, info) {
139
+ const { wType } = this.mod.getSharedType();
140
+ this.emitExpr(recvNode, wType);
141
+ this._emitIndexArgI32(args[0]);
142
+ this._emitIndexArgI32(args[1]);
143
+ this.pushInstruction({ op: "i32.const", value: info.bytes });
144
+ this.pushInstruction({
145
+ op: "call",
146
+ funcIndex: this.mod.resolveFuncIndex("__shared_sub"),
147
+ });
148
+ }
149
+
150
+ _emitIndexArgI32(node) {
151
+ if (this._isI32IndexExpr(node)) {
152
+ this.emitExpr(node, "i32");
153
+ return;
154
+ }
155
+ this.emitExpr(node, "i64");
156
+ this.pushInstruction("i32.wrap_i64");
157
+ }
158
+
159
+ // `copy(dst, src)` where at least one side is a shared buffer. Returns the
160
+ // number of elements copied as i64 (or i32 when `targetWasmType` is i32).
161
+ emitSharedCopy(dstNode, srcNode, targetWasmType) {
162
+ const dstInfo = this._sharedInfo(dstNode._type);
163
+ const srcInfo = this._sharedInfo(srcNode._type);
164
+ const info = dstInfo ?? srcInfo;
165
+ const elemW = this._sharedElemWType(info);
166
+
167
+ const nTmp = this.acquireTemp("i32");
168
+ const iTmp = this.acquireTemp("i32");
169
+ const dst = this._emitSharedCopyOperand(dstNode, dstInfo, info.elem);
170
+ const src = this._emitSharedCopyOperand(srcNode, srcInfo, info.elem);
171
+
172
+ // n = min(len(dst), len(src))
173
+ dst.emitLen();
174
+ src.emitLen();
175
+ const tmpA = this.acquireTemp("i32");
176
+ const tmpB = this.acquireTemp("i32");
177
+ this.pushInstruction({ op: "local.set", index: tmpB });
178
+ this.pushInstruction({ op: "local.tee", index: tmpA });
179
+ this.pushInstruction({ op: "local.get", index: tmpB });
180
+ this.pushInstruction({ op: "local.get", index: tmpA });
181
+ this.pushInstruction({ op: "local.get", index: tmpB });
182
+ this.pushInstruction("i32.lt_s");
183
+ this.pushInstruction("select");
184
+ this.pushInstruction({ op: "local.set", index: nTmp });
185
+ this.releaseTemp(tmpB, "i32");
186
+ this.releaseTemp(tmpA, "i32");
187
+
188
+ this.pushInstruction({ op: "i32.const", value: 0 });
189
+ this.pushInstruction({ op: "local.set", index: iTmp });
190
+ this.pushInstruction({ op: "block", blockType: "void" });
191
+ this.pushInstruction({ op: "loop", blockType: "void" });
192
+ this.pushInstruction({ op: "local.get", index: iTmp });
193
+ this.pushInstruction({ op: "local.get", index: nTmp });
194
+ this.pushInstruction("i32.ge_s");
195
+ this.pushInstruction({ op: "br_if", depth: 1 });
196
+
197
+ dst.emitStore(iTmp, () => src.emitLoad(iTmp, elemW));
198
+
199
+ this.pushInstruction({ op: "local.get", index: iTmp });
200
+ this.pushInstruction({ op: "i32.const", value: 1 });
201
+ this.pushInstruction("i32.add");
202
+ this.pushInstruction({ op: "local.set", index: iTmp });
203
+ this.pushInstruction({ op: "br", depth: 0 });
204
+ this.pushInstruction("end");
205
+ this.pushInstruction("end");
206
+
207
+ src.release();
208
+ dst.release();
209
+ this.pushInstruction({ op: "local.get", index: nTmp });
210
+ if ((targetWasmType ?? "i64") === "i64") {
211
+ this.pushInstruction("i64.extend_i32_s");
212
+ }
213
+ this.releaseTemp(iTmp, "i32");
214
+ this.releaseTemp(nTmp, "i32");
215
+ }
216
+
217
+ // Evaluates one `copy` operand into temps and returns element accessors.
218
+ _emitSharedCopyOperand(node, info, elemGoType) {
219
+ if (info) {
220
+ const { wType, typeIndex } = this.mod.getSharedType();
221
+ const bufTmp = this.acquireTemp(wType);
222
+ this.emitExpr(node, wType);
223
+ this.pushInstruction({ op: "local.set", index: bufTmp });
224
+ const emitAddr = (iTmp) => {
225
+ this.pushInstruction({ op: "local.get", index: bufTmp });
226
+ this.pushInstruction({ op: "struct.get", typeIndex, fieldIndex: 0 });
227
+ this.pushInstruction({ op: "local.get", index: iTmp });
228
+ this._emitSharedScale(info);
229
+ this.pushInstruction("i32.add");
230
+ };
231
+ return {
232
+ emitLen: () => {
233
+ this.pushInstruction({ op: "local.get", index: bufTmp });
234
+ this.pushInstruction({ op: "struct.get", typeIndex, fieldIndex: 1 });
235
+ },
236
+ emitLoad: (iTmp) => {
237
+ emitAddr(iTmp);
238
+ this.pushInstruction(LOAD_OP[info.key]);
239
+ },
240
+ emitStore: (iTmp, emitVal) => {
241
+ emitAddr(iTmp);
242
+ emitVal();
243
+ this._emitSharedStore(info);
244
+ },
245
+ release: () => this.releaseTemp(bufTmp, wType),
246
+ };
247
+ }
248
+
249
+ // GC slice side.
250
+ const sliceInfo = this.mod.getSliceType(elemGoType);
251
+ const sliceWType = {
252
+ kind: "ref",
253
+ nullable: true,
254
+ typeIndex: sliceInfo.typeIndex,
255
+ };
256
+ const arrWType = {
257
+ kind: "ref",
258
+ nullable: true,
259
+ typeIndex: sliceInfo.arrInfo.typeIndex,
260
+ };
261
+ const sliceTmp = this.acquireTemp(sliceWType);
262
+ const arrTmp = this.acquireTemp(arrWType);
263
+ const offTmp = this.acquireTemp("i32");
264
+ const lenTmp = this.acquireTemp("i32");
265
+ this.emitExpr(node, sliceWType);
266
+ this.pushInstruction({ op: "local.set", index: sliceTmp });
267
+ this._emitSliceUnpack(sliceTmp, sliceInfo, {
268
+ arr: arrTmp,
269
+ off: offTmp,
270
+ len: lenTmp,
271
+ });
272
+ const emitIdx = (iTmp) => {
273
+ this.pushInstruction({ op: "local.get", index: arrTmp });
274
+ this.pushInstruction({ op: "local.get", index: offTmp });
275
+ this.pushInstruction({ op: "local.get", index: iTmp });
276
+ this.pushInstruction("i32.add");
277
+ };
278
+ return {
279
+ emitLen: () => this.pushInstruction({ op: "local.get", index: lenTmp }),
280
+ emitLoad: (iTmp) => {
281
+ emitIdx(iTmp);
282
+ this.pushInstruction({
283
+ op: "array.get",
284
+ typeIndex: sliceInfo.arrInfo.typeIndex,
285
+ });
286
+ },
287
+ emitStore: (iTmp, emitVal) => {
288
+ emitIdx(iTmp);
289
+ emitVal();
290
+ this.pushInstruction({
291
+ op: "array.set",
292
+ typeIndex: sliceInfo.arrInfo.typeIndex,
293
+ });
294
+ },
295
+ release: () => {
296
+ this.releaseTemp(lenTmp, "i32");
297
+ this.releaseTemp(offTmp, "i32");
298
+ this.releaseTemp(arrTmp, arrWType);
299
+ this.releaseTemp(sliceTmp, sliceWType);
300
+ },
301
+ };
302
+ }
303
+
304
+ // `for i, v := range buf`
305
+ _emitSharedRange(stmt, iterExpr, info, lhs, isAssign) {
306
+ const { wType, typeIndex } = this.mod.getSharedType();
307
+ const elemW = this._sharedElemWType(info);
308
+ const bufTmp = this.acquireTemp(wType);
309
+ const baseTmp = this.acquireTemp("i32");
310
+ const lenTmp = this.acquireTemp("i32");
311
+ const idxTmp = this.acquireTemp("i32");
312
+
313
+ this.emitExpr(iterExpr, wType);
314
+ this.pushInstruction({ op: "local.tee", index: bufTmp });
315
+ this.pushInstruction({ op: "struct.get", typeIndex, fieldIndex: 0 });
316
+ this.pushInstruction({ op: "local.set", index: baseTmp });
317
+ this.pushInstruction({ op: "local.get", index: bufTmp });
318
+ this.pushInstruction({ op: "struct.get", typeIndex, fieldIndex: 1 });
319
+ this.pushInstruction({ op: "local.set", index: lenTmp });
320
+ this.pushInstruction({ op: "i32.const", value: 0 });
321
+ this.pushInstruction({ op: "local.set", index: idxTmp });
322
+
323
+ const bodyBlock = stmt.body ?? stmt.block;
324
+ const bindVar = (node, wTypeNew, goTypeNew) => {
325
+ if (!node || node.name === "_") return null;
326
+ if (!isAssign) {
327
+ const lIdx = this.allocLocal(node.name, wTypeNew, goTypeNew);
328
+ return { index: lIdx, type: wTypeNew };
329
+ }
330
+ return this.resolveLocal(node.name) ?? this.mod.resolveGlobal(node.name);
331
+ };
332
+ let idxWType = "i64";
333
+ if (lhs[0] && lhs[0].name !== "_" && !isAssign) {
334
+ const isNonEscaping =
335
+ !this.mutatedCaptures?.has(lhs[0].name) &&
336
+ !this.capturedNames?.includes(lhs[0].name) &&
337
+ !this._varMutatesOrEscapes(bodyBlock, lhs[0].name);
338
+ idxWType = isNonEscaping ? "i32" : "i64";
339
+ }
340
+ const idxLocalInfo = bindVar(lhs[0], idxWType, {
341
+ kind: "basic",
342
+ name: "int",
343
+ });
344
+ const valLocalInfo = bindVar(lhs[1], elemW, info.elem);
345
+ const store = (node, localInfo) => {
346
+ if (this.resolveLocal(node.name)) {
347
+ this.pushInstruction({ op: "local.set", index: localInfo.index });
348
+ } else {
349
+ this.pushInstruction({ op: "global.set", index: localInfo.index });
350
+ }
351
+ };
352
+
353
+ this.pushInstruction({ op: "block", blockType: "void" });
354
+ this.pushControl("break", stmt.label);
355
+ this.pushInstruction({ op: "loop", blockType: "void" });
356
+ this.pushControl("loop", stmt.label);
357
+
358
+ this.pushInstruction({ op: "local.get", index: idxTmp });
359
+ this.pushInstruction({ op: "local.get", index: lenTmp });
360
+ this.pushInstruction("i32.ge_s");
361
+ this.pushInstruction({
362
+ op: "br_if",
363
+ depth: this.resolveBranchDepth(stmt.label, false),
364
+ });
365
+
366
+ this.pushInstruction({ op: "block", blockType: "void" });
367
+ this.pushControl("continue", stmt.label);
368
+
369
+ if (idxLocalInfo) {
370
+ this.pushInstruction({ op: "local.get", index: idxTmp });
371
+ if (idxLocalInfo.type === "i64") this.pushInstruction("i64.extend_i32_s");
372
+ store(lhs[0], idxLocalInfo);
373
+ }
374
+ if (valLocalInfo) {
375
+ this.pushInstruction({ op: "local.get", index: baseTmp });
376
+ this.pushInstruction({ op: "local.get", index: idxTmp });
377
+ this._emitSharedScale(info);
378
+ this.pushInstruction("i32.add");
379
+ this.pushInstruction(LOAD_OP[info.key]);
380
+ this._emitSharedWiden(info, valLocalInfo.type);
381
+ store(lhs[1], valLocalInfo);
382
+ }
383
+
384
+ if (bodyBlock) this.emitBlock(bodyBlock);
385
+
386
+ this.pushInstruction("end");
387
+ this.pushInstruction({ op: "local.get", index: idxTmp });
388
+ this.pushInstruction({ op: "i32.const", value: 1 });
389
+ this.pushInstruction("i32.add");
390
+ this.pushInstruction({ op: "local.set", index: idxTmp });
391
+ this.pushInstruction({ op: "br", depth: 0 });
392
+ this.pushInstruction("end");
393
+ this.pushInstruction("end");
394
+
395
+ this.releaseTemp(idxTmp, "i32");
396
+ this.releaseTemp(lenTmp, "i32");
397
+ this.releaseTemp(baseTmp, "i32");
398
+ this.releaseTemp(bufTmp, wType);
399
+ }
400
+
401
+ // Bodies of the synthetic `__shared_new` / `__shared_sub` helpers.
402
+ emitSharedHelper(fn) {
403
+ const { typeIndex, topGlobalIndex } = this.mod.getSharedType();
404
+ if (fn._isSharedHelper === "new") {
405
+ // params: len=0, bytes=1
406
+ const sizeTmp = this.acquireTemp("i32");
407
+ const baseTmp = this.acquireTemp("i32");
408
+ const pagesTmp = this.acquireTemp("i32");
409
+
410
+ // len < 0 or len*bytes beyond the i32 address space → panic
411
+ // (the byte size must stay representable after 8-byte rounding).
412
+ this.pushInstruction({ op: "local.get", index: 0 });
413
+ this.pushInstruction({ op: "i32.const", value: 0 });
414
+ this.pushInstruction("i32.lt_s");
415
+ this.pushInstruction({ op: "local.get", index: 0 });
416
+ this.pushInstruction("i64.extend_i32_s");
417
+ this.pushInstruction({ op: "local.get", index: 1 });
418
+ this.pushInstruction("i64.extend_i32_s");
419
+ this.pushInstruction("i64.mul");
420
+ this.pushInstruction({ op: "i64.const", value: 0x7ffffff0 });
421
+ this.pushInstruction("i64.gt_s");
422
+ this.pushInstruction("i32.or");
423
+ this.pushInstruction({ op: "if", blockType: "void" });
424
+ this.pushInstruction({
425
+ op: "call",
426
+ funcIndex: this.mod.resolveFuncIndex("__shared_len_panic"),
427
+ });
428
+ this.pushInstruction("unreachable");
429
+ this.pushInstruction("end");
430
+
431
+ // size = (len*bytes + 7) & ~7
432
+ this.pushInstruction({ op: "local.get", index: 0 });
433
+ this.pushInstruction({ op: "local.get", index: 1 });
434
+ this.pushInstruction("i32.mul");
435
+ this.pushInstruction({ op: "i32.const", value: 7 });
436
+ this.pushInstruction("i32.add");
437
+ this.pushInstruction({ op: "i32.const", value: -8 });
438
+ this.pushInstruction("i32.and");
439
+ this.pushInstruction({ op: "local.set", index: sizeTmp });
440
+
441
+ // base = top
442
+ this.pushInstruction({ op: "global.get", index: topGlobalIndex });
443
+ this.pushInstruction({ op: "local.set", index: baseTmp });
444
+
445
+ // needed pages = ((base + size) - memory.size*65536 + 65535) >> 16
446
+ this.pushInstruction({ op: "local.get", index: baseTmp });
447
+ this.pushInstruction({ op: "local.get", index: sizeTmp });
448
+ this.pushInstruction("i32.add");
449
+ this.pushInstruction("memory.size");
450
+ this.pushInstruction({ op: "i32.const", value: 16 });
451
+ this.pushInstruction("i32.shl");
452
+ this.pushInstruction("i32.sub");
453
+ this.pushInstruction({ op: "i32.const", value: 65535 });
454
+ this.pushInstruction("i32.add");
455
+ this.pushInstruction({ op: "i32.const", value: 16 });
456
+ this.pushInstruction("i32.shr_s");
457
+ this.pushInstruction({ op: "local.tee", index: pagesTmp });
458
+ this.pushInstruction({ op: "i32.const", value: 0 });
459
+ this.pushInstruction("i32.gt_s");
460
+ this.pushInstruction({ op: "if", blockType: "void" });
461
+ this.pushInstruction({ op: "local.get", index: pagesTmp });
462
+ this.pushInstruction("memory.grow");
463
+ this.pushInstruction({ op: "i32.const", value: -1 });
464
+ this.pushInstruction("i32.eq");
465
+ this.pushInstruction({ op: "if", blockType: "void" });
466
+ this.pushInstruction("unreachable");
467
+ this.pushInstruction("end");
468
+ this.pushInstruction("end");
469
+
470
+ // top = base + size
471
+ this.pushInstruction({ op: "local.get", index: baseTmp });
472
+ this.pushInstruction({ op: "local.get", index: sizeTmp });
473
+ this.pushInstruction("i32.add");
474
+ this.pushInstruction({ op: "global.set", index: topGlobalIndex });
475
+
476
+ this.pushInstruction({ op: "local.get", index: baseTmp });
477
+ this.pushInstruction({ op: "local.get", index: 0 });
478
+ this.pushInstruction({ op: "struct.new", typeIndex });
479
+ this.pushInstruction("return");
480
+
481
+ this.releaseTemp(pagesTmp, "i32");
482
+ this.releaseTemp(baseTmp, "i32");
483
+ this.releaseTemp(sizeTmp, "i32");
484
+ return;
485
+ }
486
+
487
+ // sub: params buf=0, lo=1, hi=2, bytes=3
488
+ // 0 <= lo <= hi <= len, else slice-bounds panic
489
+ this.pushInstruction({ op: "local.get", index: 1 });
490
+ this.pushInstruction({ op: "local.get", index: 2 });
491
+ this.pushInstruction("i32.gt_u");
492
+ this.pushInstruction({ op: "local.get", index: 2 });
493
+ this.pushInstruction({ op: "local.get", index: 0 });
494
+ this.pushInstruction({ op: "struct.get", typeIndex, fieldIndex: 1 });
495
+ this.pushInstruction("i32.gt_u");
496
+ this.pushInstruction("i32.or");
497
+ this.pushInstruction({ op: "if", blockType: "void" });
498
+ this.pushInstruction({
499
+ op: "call",
500
+ funcIndex: this.mod.getSliceBoundsPanicFuncIndex(),
501
+ });
502
+ this.pushInstruction("unreachable");
503
+ this.pushInstruction("end");
504
+
505
+ // base + lo*bytes
506
+ this.pushInstruction({ op: "local.get", index: 0 });
507
+ this.pushInstruction({ op: "struct.get", typeIndex, fieldIndex: 0 });
508
+ this.pushInstruction({ op: "local.get", index: 1 });
509
+ this.pushInstruction({ op: "local.get", index: 3 });
510
+ this.pushInstruction("i32.mul");
511
+ this.pushInstruction("i32.add");
512
+ // hi - lo
513
+ this.pushInstruction({ op: "local.get", index: 2 });
514
+ this.pushInstruction({ op: "local.get", index: 1 });
515
+ this.pushInstruction("i32.sub");
516
+ this.pushInstruction({ op: "struct.new", typeIndex });
517
+ this.pushInstruction("return");
518
+ }
519
+ }