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
@@ -1,6 +1,11 @@
1
1
  // src/backend/wasm/types.js
2
2
  // Maps GoFront types to WasmGC types and manages type registry.
3
3
 
4
+ import { SHARED_KINDS } from "../../typechecker/stdlib/shared.js";
5
+
6
+ // Name of the runtime's error struct (`errors.New`, `fmt.Errorf`, strconv).
7
+ export const ERROR_STRUCT = "__errorString";
8
+
4
9
  // `*testing.T` is a JS-side object (the harness's __GoFront_T) passed through
5
10
  // the boundary as an opaque externref.
6
11
  export function isTestingT(goType) {
@@ -16,6 +21,22 @@ export function isTestingT(goType) {
16
21
 
17
22
  export function toWasmType(goType, checker = null, mod = null) {
18
23
  if (!goType) return "i32";
24
+ if (typeof goType === "string") {
25
+ if (
26
+ goType === "i32" ||
27
+ goType === "i64" ||
28
+ goType === "f32" ||
29
+ goType === "f64" ||
30
+ goType === "anyref" ||
31
+ goType === "externref" ||
32
+ goType === "funcref"
33
+ ) {
34
+ return goType;
35
+ }
36
+ }
37
+ if (typeof goType === "object" && goType.kind === "ref") {
38
+ return goType;
39
+ }
19
40
  if (isTestingT(goType)) return "externref";
20
41
 
21
42
  // Pointer AST nodes
@@ -30,13 +51,20 @@ export function toWasmType(goType, checker = null, mod = null) {
30
51
  );
31
52
  }
32
53
 
33
- // Slice and Array AST nodes
54
+ // Slice, Array, and Map AST nodes
34
55
  if (goType.kind === "SliceType") {
35
56
  return toWasmType({ kind: "slice", elem: goType.elem }, checker, mod);
36
57
  }
37
58
  if (goType.kind === "ArrayType") {
38
59
  return toWasmType({ kind: "array", elem: goType.elem }, checker, mod);
39
60
  }
61
+ if (goType.kind === "MapType") {
62
+ return toWasmType(
63
+ { kind: "map", key: goType.key, value: goType.value ?? goType.elem },
64
+ checker,
65
+ mod,
66
+ );
67
+ }
40
68
 
41
69
  // TypeName AST node
42
70
  if (goType.kind === "TypeName" || goType.kind === "Ident") {
@@ -114,6 +142,7 @@ export function toWasmType(goType, checker = null, mod = null) {
114
142
  return "externref";
115
143
 
116
144
  case "any":
145
+ case "error":
117
146
  return "anyref";
118
147
 
119
148
  case "void":
@@ -144,6 +173,16 @@ export function toWasmType(goType, checker = null, mod = null) {
144
173
  return { kind: "ref", nullable: true, heapType: "any" };
145
174
  }
146
175
 
176
+ if (goType.kind === "shared") {
177
+ if (mod?.getSharedType)
178
+ return {
179
+ kind: "ref",
180
+ nullable: true,
181
+ typeIndex: mod.getSharedType().typeIndex,
182
+ };
183
+ return { kind: "ref", nullable: true, heapType: "struct" };
184
+ }
185
+
147
186
  if (goType.kind === "slice") {
148
187
  if (mod?.getSliceType && goType.elem) {
149
188
  const sliceInfo = mod.getSliceType(goType.elem);
@@ -168,7 +207,21 @@ export function toWasmType(goType, checker = null, mod = null) {
168
207
  return { kind: "ref", nullable: true, heapType: "array" };
169
208
  }
170
209
 
171
- if (goType.kind === "struct") {
210
+ if (goType.kind === "map") {
211
+ const key = goType.key;
212
+ const val = goType.value ?? goType.elem;
213
+ if (mod?.getMapType && key && val) {
214
+ const mapInfo = mod.getMapType(key, val);
215
+ return {
216
+ kind: "ref",
217
+ nullable: true,
218
+ typeIndex: mapInfo.typeIndex,
219
+ };
220
+ }
221
+ return { kind: "ref", nullable: true, heapType: "struct" };
222
+ }
223
+
224
+ if (goType.kind === "struct" || goType.kind === "StructType") {
172
225
  if (goType.name && mod?.getStructType(goType.name)) {
173
226
  return {
174
227
  kind: "ref",
@@ -176,6 +229,23 @@ export function toWasmType(goType, checker = null, mod = null) {
176
229
  typeIndex: mod.getStructType(goType.name).typeIndex,
177
230
  };
178
231
  }
232
+ if (mod?.getAnonStructType && goType.fields) {
233
+ const rawFields = [];
234
+ if (goType.fields instanceof Map) {
235
+ for (const [k, v] of goType.fields.entries()) {
236
+ rawFields.push({ name: k, goType: v });
237
+ }
238
+ } else if (Array.isArray(goType.fields)) {
239
+ for (const f of goType.fields) {
240
+ const names = f.names ?? (f.name ? [f.name] : []);
241
+ for (const n of names) rawFields.push({ name: n, goType: f.type });
242
+ }
243
+ }
244
+ if (rawFields.length > 0) {
245
+ const info = mod.getAnonStructType(rawFields);
246
+ return { kind: "ref", nullable: true, typeIndex: info.typeIndex };
247
+ }
248
+ }
179
249
  return { kind: "ref", nullable: true, heapType: "struct" };
180
250
  }
181
251
 
@@ -198,6 +268,96 @@ export function toWasmType(goType, checker = null, mod = null) {
198
268
  return "i32";
199
269
  }
200
270
 
271
+ export function getInterfaceMethodList(iface) {
272
+ if (!iface) return [];
273
+ if (iface.methods instanceof Map) {
274
+ return Array.from(iface.methods.entries()).map(([name, sig]) => ({
275
+ name,
276
+ params: (sig.params ?? []).map((p) => p.type ?? p),
277
+ returns: (sig.returns ?? []).map((r) => r.type ?? r),
278
+ }));
279
+ }
280
+ if (Array.isArray(iface.methods)) {
281
+ return iface.methods.map((m) => ({
282
+ name: m.name?.value ?? m.name ?? m,
283
+ params: (m.params ?? []).map((p) => p.type ?? p),
284
+ returns: m.returnType
285
+ ? m.returnType.kind === "TupleType" || m.returnType.kind === "tuple"
286
+ ? m.returnType.types
287
+ : [m.returnType]
288
+ : [],
289
+ }));
290
+ }
291
+ return [];
292
+ }
293
+
294
+ export function goTypeKey(t) {
295
+ if (!t) return "any";
296
+ if (typeof t === "string") {
297
+ if (t === "byte") return "uint8";
298
+ if (t === "rune") return "int32";
299
+ return t;
300
+ }
301
+ if (t.kind === "basic" || t.kind === "TypeName" || t.kind === "Ident") {
302
+ const name = t.name ?? "any";
303
+ if (name === "byte") return "uint8";
304
+ if (name === "rune") return "int32";
305
+ return name;
306
+ }
307
+ if (t.kind === "untyped") {
308
+ return t.base ?? "int";
309
+ }
310
+ if (t.kind === "pointer" || t.kind === "PointerType") {
311
+ return `ptr_${goTypeKey(t.base)}`;
312
+ }
313
+ if (t.kind === "StarExpr") {
314
+ return `ptr_${goTypeKey(t.expr ?? t.operand)}`;
315
+ }
316
+ if (t.kind === "slice" || t.kind === "SliceType") {
317
+ return `sl_${goTypeKey(t.elem)}`;
318
+ }
319
+ if (t.kind === "array" || t.kind === "ArrayType") {
320
+ const sz =
321
+ typeof t.size === "number" ? t.size : (t.size?.value ?? t.len ?? "");
322
+ return `arr_${sz}_${goTypeKey(t.elem)}`;
323
+ }
324
+ if (t.kind === "map" || t.kind === "MapType") {
325
+ return `map_${goTypeKey(t.key ?? t.keyType)}_${goTypeKey(t.value ?? t.val ?? t.elem ?? t.valueType ?? t.valType)}`;
326
+ }
327
+ if (t.kind === "struct" || t.kind === "StructType") {
328
+ if (t.name) return t.name;
329
+ const rawFields = [];
330
+ if (t.fields instanceof Map) {
331
+ for (const [k, v] of t.fields.entries())
332
+ rawFields.push(`${k}:${goTypeKey(v)}`);
333
+ } else if (Array.isArray(t.fields)) {
334
+ for (const f of t.fields) {
335
+ const names = f.names ?? (f.name ? [f.name] : []);
336
+ for (const n of names)
337
+ rawFields.push(`${n}:${goTypeKey(f.type ?? f.goType)}`);
338
+ }
339
+ }
340
+ return `struct_${rawFields.join("_")}`;
341
+ }
342
+ if (t.kind === "named") {
343
+ return t.name ?? (t.underlying ? goTypeKey(t.underlying) : "any");
344
+ }
345
+ if (t.name) return t.name;
346
+ return t.kind ?? "any";
347
+ }
348
+
349
+ export function anonIfaceName(iface, _checker = null, _mod = null) {
350
+ const methods = getInterfaceMethodList(iface);
351
+ methods.sort((a, b) => a.name.localeCompare(b.name));
352
+ const parts = methods.map((m) => {
353
+ const ps = m.params.map((p) => goTypeKey(p)).join("_");
354
+ const rs = m.returns.map((r) => goTypeKey(r)).join("_");
355
+ return `${m.name}_${ps}_${rs}`;
356
+ });
357
+ const key = parts.join("__").replace(/[^a-zA-Z0-9_]/g, "_");
358
+ return `anon_iface_${key}`;
359
+ }
360
+
201
361
  export function isStructType(goType, checker = null, mod = null) {
202
362
  if (!goType) return false;
203
363
  if (goType.kind === "TypeName" || goType.kind === "Ident") {
@@ -235,6 +395,20 @@ export function isSliceType(goType, checker = null) {
235
395
  return goType.kind === "slice" || goType.kind === "SliceType";
236
396
  }
237
397
 
398
+ // `gofront/shared` linear-memory buffer: returns the SHARED_KINDS entry
399
+ // (elem, ctor, bytes, key) or null.
400
+ export function getSharedInfo(goType, checker = null) {
401
+ if (!goType) return null;
402
+ if (goType.kind === "TypeName" || goType.kind === "Ident") {
403
+ const resolved = checker?.types?.get(goType.name);
404
+ return resolved ? getSharedInfo(resolved, checker) : null;
405
+ }
406
+ if (goType.kind === "named" && goType.underlying)
407
+ return getSharedInfo(goType.underlying, checker);
408
+ if (goType.kind !== "shared") return null;
409
+ return { name: goType.shared, ...SHARED_KINDS[goType.shared] };
410
+ }
411
+
238
412
  export function isArrayType(goType, checker = null) {
239
413
  if (!goType) return false;
240
414
  if (goType.kind === "TypeName" || goType.kind === "Ident") {
@@ -247,6 +421,37 @@ export function isArrayType(goType, checker = null) {
247
421
  return goType.kind === "array" || goType.kind === "ArrayType";
248
422
  }
249
423
 
424
+ export function isMapType(goType, checker = null) {
425
+ if (!goType) return false;
426
+ if (goType.kind === "TypeName" || goType.kind === "Ident") {
427
+ const resolved = checker?.types?.get(goType.name);
428
+ if (resolved) return isMapType(resolved, checker);
429
+ }
430
+ if (goType.kind === "named" && goType.underlying) {
431
+ return isMapType(goType.underlying, checker);
432
+ }
433
+ return goType.kind === "map" || goType.kind === "MapType";
434
+ }
435
+
436
+ // Returns `{ keyType, valType }` (Go types) for a map type, or nulls.
437
+ export function getMapKeyValTypes(goType, checker = null) {
438
+ if (!goType) return { keyType: null, valType: null };
439
+ if (goType.kind === "TypeName" || goType.kind === "Ident") {
440
+ const resolved = checker?.types?.get(goType.name);
441
+ if (resolved) return getMapKeyValTypes(resolved, checker);
442
+ }
443
+ if (goType.kind === "named" && goType.underlying) {
444
+ return getMapKeyValTypes(goType.underlying, checker);
445
+ }
446
+ if (goType.kind === "map" || goType.kind === "MapType") {
447
+ const key = goType.key ?? goType.keyType;
448
+ const val =
449
+ goType.value ?? goType.elem ?? goType.valueType ?? goType.valType;
450
+ return { keyType: key, valType: val };
451
+ }
452
+ return { keyType: null, valType: null };
453
+ }
454
+
250
455
  export function isStringType(goType, checker = null) {
251
456
  if (!goType) return false;
252
457
  if (goType.kind === "TypeName" || goType.kind === "Ident") {
@@ -279,27 +484,80 @@ export function isAnyType(goType, checker = null) {
279
484
  return false;
280
485
  }
281
486
 
282
- export function isNarrowInt(goType) {
487
+ export function isInterfaceType(goType, checker = null) {
283
488
  if (!goType) return false;
489
+ if (
490
+ goType.kind === "basic" &&
491
+ (goType.name === "any" || goType.name === "interface{}")
492
+ )
493
+ return true;
494
+ if (goType.kind === "interface" || goType.kind === "InterfaceType")
495
+ return true;
284
496
  if (goType.kind === "TypeName" || goType.kind === "Ident") {
285
- return isNarrowInt({ kind: "basic", name: goType.name });
497
+ if (goType.name === "any" || goType.name === "interface{}") return true;
498
+ const resolved = checker?.types?.get(goType.name);
499
+ if (resolved) return isInterfaceType(resolved, checker);
286
500
  }
287
- if (goType.kind === "named" && goType.underlying) {
288
- return isNarrowInt(goType.underlying);
501
+ if (goType.kind === "named") {
502
+ if (goType.name === "any" || goType.name === "interface{}") return true;
503
+ if (goType.underlying) return isInterfaceType(goType.underlying, checker);
504
+ const resolved = checker?.types?.get(goType.name);
505
+ if (resolved) return isInterfaceType(resolved, checker);
289
506
  }
290
- if (goType.kind === "basic") {
291
- switch (goType.name) {
292
- case "int8":
293
- case "int16":
294
- case "uint8":
295
- case "uint16":
296
- case "byte":
297
- return true;
298
- }
507
+ return false;
508
+ }
509
+
510
+ // The built-in `error` interface (checker ERROR type or its name).
511
+ export function isErrorType(goType) {
512
+ if (!goType) return false;
513
+ if (goType.kind === "interface") return goType.name === "error";
514
+ if (
515
+ (goType.kind === "named" ||
516
+ goType.kind === "TypeName" ||
517
+ goType.kind === "Ident") &&
518
+ goType.name === "error"
519
+ ) {
520
+ return true;
521
+ }
522
+ if (goType.kind === "named" && goType.underlying)
523
+ return isErrorType(goType.underlying);
524
+ return false;
525
+ }
526
+
527
+ export function isNonEmptyInterface(goType, checker = null) {
528
+ if (!goType) return false;
529
+ if (goType.name === "any" || goType.name === "interface{}") return false;
530
+ if (goType.kind === "TypeName" || goType.kind === "Ident") {
531
+ const resolved = checker?.types?.get(goType.name);
532
+ if (resolved) return isNonEmptyInterface(resolved, checker);
533
+ }
534
+ if (goType.kind === "named") {
535
+ if (goType.name === "any" || goType.name === "interface{}") return false;
536
+ if (goType.underlying)
537
+ return isNonEmptyInterface(goType.underlying, checker);
538
+ const resolved = checker?.types?.get(goType.name);
539
+ if (resolved) return isNonEmptyInterface(resolved, checker);
540
+ }
541
+ if (goType.kind === "interface" || goType.kind === "InterfaceType") {
542
+ if (goType.methods instanceof Map) return goType.methods.size > 0;
543
+ if (Array.isArray(goType.methods)) return goType.methods.length > 0;
544
+ if (goType.methods instanceof Set) return goType.methods.size > 0;
545
+ return false;
299
546
  }
300
547
  return false;
301
548
  }
302
549
 
550
+ export function getReceiverTypeName(recvType, _node = null) {
551
+ if (recvType) {
552
+ let t = recvType;
553
+ while (t.kind === "pointer") t = t.base;
554
+ if (t.name) return t.name;
555
+ if (t.kind === "named") return t.name;
556
+ if (t.kind === "TypeName" || t.kind === "Ident") return t.name;
557
+ }
558
+ return null;
559
+ }
560
+
303
561
  export function isSigned(goType) {
304
562
  if (!goType) return true;
305
563
  if (goType.kind === "TypeName" || goType.kind === "Ident") {
@@ -329,27 +587,6 @@ export function isFloat(goType) {
329
587
  );
330
588
  }
331
589
 
332
- export function isInt(goType) {
333
- if (!goType) return false;
334
- if (goType.kind === "TypeName" || goType.kind === "Ident") {
335
- return isInt({ kind: "basic", name: goType.name });
336
- }
337
- if (goType.kind === "named" && goType.underlying) {
338
- return isInt(goType.underlying);
339
- }
340
- if (goType.kind === "untyped") return goType.base === "int";
341
- if (goType.kind === "basic") {
342
- return (
343
- goType.name.startsWith("int") ||
344
- goType.name.startsWith("uint") ||
345
- goType.name === "byte" ||
346
- goType.name === "rune" ||
347
- goType.name === "uintptr"
348
- );
349
- }
350
- return false;
351
- }
352
-
353
590
  export function isFuncType(goType, checker = null) {
354
591
  if (!goType) return false;
355
592
  if (goType.kind === "TypeName" || goType.kind === "Ident") {
@@ -418,3 +655,42 @@ export function getFuncSignature(goType, checker = null) {
418
655
  }
419
656
  return { params: [], returns: [] };
420
657
  }
658
+
659
+ export function isUnhashableGoType(goType, checker = null, seen = new Set()) {
660
+ if (!goType) return false;
661
+ if (seen.has(goType)) return false;
662
+ seen.add(goType);
663
+ if (
664
+ isSliceType(goType, checker) ||
665
+ isMapType(goType, checker) ||
666
+ isFuncType(goType, checker)
667
+ ) {
668
+ return true;
669
+ }
670
+ if (isArrayType(goType, checker)) {
671
+ const elem = goType.elem ?? goType.underlying?.elem;
672
+ return isUnhashableGoType(elem, checker, seen);
673
+ }
674
+ if (goType.kind === "struct" || goType.kind === "StructType") {
675
+ if (goType.fields instanceof Map) {
676
+ for (const f of goType.fields.values()) {
677
+ if (isUnhashableGoType(f, checker, seen)) return true;
678
+ }
679
+ } else if (Array.isArray(goType.fields)) {
680
+ for (const f of goType.fields) {
681
+ const fType = f.type ?? f.goType;
682
+ if (isUnhashableGoType(fType, checker, seen)) return true;
683
+ }
684
+ }
685
+ }
686
+ if (goType.kind === "TypeName" || goType.kind === "Ident") {
687
+ const resolved = checker?.types?.get(goType.name);
688
+ if (resolved && resolved !== goType) {
689
+ return isUnhashableGoType(resolved, checker, seen);
690
+ }
691
+ }
692
+ if (goType.kind === "named" && goType.underlying) {
693
+ return isUnhashableGoType(goType.underlying, checker, seen);
694
+ }
695
+ return false;
696
+ }
@@ -83,6 +83,21 @@ function formatInstruction(inst) {
83
83
  case "global.get":
84
84
  case "global.set":
85
85
  return `${op} ${inst.globalIndex ?? inst.index ?? 0}`;
86
+ case "i32.load":
87
+ case "i64.load":
88
+ case "f32.load":
89
+ case "f64.load":
90
+ case "i32.load8_s":
91
+ case "i32.load8_u":
92
+ case "i32.load16_s":
93
+ case "i32.load16_u":
94
+ case "i32.store":
95
+ case "i64.store":
96
+ case "f32.store":
97
+ case "f64.store":
98
+ case "i32.store8":
99
+ case "i32.store16":
100
+ return inst.offset ? `${op} offset=${inst.offset}` : op;
86
101
  case "i32.const":
87
102
  case "i64.const":
88
103
  return `${op} ${inst.value ?? 0}`;
@@ -96,6 +111,26 @@ function formatInstruction(inst) {
96
111
  }
97
112
  case "throw":
98
113
  return `${op} ${inst.tagIndex ?? inst.index ?? 0}`;
114
+ case "throw_ref":
115
+ return "throw_ref";
116
+ case "try_table": {
117
+ const bt = inst.blockType ?? inst.resultType;
118
+ const res = bt && bt !== "void" ? ` (result ${formatValType(bt)})` : "";
119
+ const catches = (inst.catches ?? [])
120
+ .map((c) => {
121
+ const k = c.kind ?? c.op;
122
+ if (k === "catch")
123
+ return `(catch ${c.tagIndex ?? 0} ${c.label ?? 0})`;
124
+ if (k === "catch_ref")
125
+ return `(catch_ref ${c.tagIndex ?? 0} ${c.label ?? 0})`;
126
+ if (k === "catch_all") return `(catch_all ${c.label ?? 0})`;
127
+ if (k === "catch_all_ref") return `(catch_all_ref ${c.label ?? 0})`;
128
+ return "";
129
+ })
130
+ .filter(Boolean)
131
+ .join(" ");
132
+ return `${op}${res}${catches ? ` ${catches}` : ""}`;
133
+ }
99
134
  case "struct.new":
100
135
  return `struct.new ${inst.typeIndex !== undefined ? `$t${inst.typeIndex}` : 0}`;
101
136
  case "struct.get":
@@ -119,8 +154,15 @@ function formatInstruction(inst) {
119
154
  case "ref.test":
120
155
  case "ref.test_null":
121
156
  case "ref.cast":
122
- case "ref.cast_null":
123
- return `${op} ${inst.typeIndex !== undefined ? `$t${inst.typeIndex}` : 0}`;
157
+ case "ref.cast_null": {
158
+ const ht =
159
+ inst.typeIndex !== undefined
160
+ ? `$t${inst.typeIndex}`
161
+ : inst.heapType !== undefined
162
+ ? String(inst.heapType)
163
+ : 0;
164
+ return `${op} ${ht}`;
165
+ }
124
166
  case "any.convert_extern":
125
167
  case "extern.convert_any":
126
168
  return op;
@@ -166,6 +208,11 @@ export function emitWat(mod) {
166
208
  }
167
209
  }
168
210
 
211
+ if (mod.memory) {
212
+ const max = mod.memory.max != null ? ` ${mod.memory.max}` : "";
213
+ lines.push(` (memory $mem ${mod.memory.min}${max})`);
214
+ }
215
+
169
216
  // 4. Globals
170
217
  if (mod.globals && mod.globals.length > 0) {
171
218
  for (let i = 0; i < mod.globals.length; i++) {