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
@@ -12,16 +12,21 @@ import {
12
12
  scanBoundaryImports,
13
13
  wTypeKey,
14
14
  } from "./boundary.js";
15
+ import { foldConstExpr } from "./const-fold.js";
15
16
  import { FunctionEmitter } from "./emit.js";
16
- import { BINARY_MATH_IMPORTS } from "./emit-stdlib.js";
17
17
  import { encodeModule, isGoFrontWasm } from "./encode.js";
18
18
  import { hasGenerics, monomorphise } from "./monomorph.js";
19
19
  import { hasNativeWasmOpt, optimizeWasm } from "./optimize.js";
20
+ import { BINARY_MATH_IMPORTS } from "./stdlib/index.js";
20
21
  import {
22
+ anonIfaceName,
21
23
  ERROR_STRUCT,
22
24
  getFuncSignature,
25
+ getInterfaceMethodList,
23
26
  getMapKeyValTypes,
24
27
  getReceiverTypeName,
28
+ goTypeKey,
29
+ isInterfaceType,
25
30
  isMapType,
26
31
  isNonEmptyInterface,
27
32
  isTestingT,
@@ -31,7 +36,28 @@ import { emitWat } from "./wat.js";
31
36
 
32
37
  export { hasNativeWasmOpt, isGoFrontWasm, optimizeWasm };
33
38
 
34
- const ERROR_PRODUCING_PKGS = new Set(["errors", "fmt", "strconv"]);
39
+ const ERROR_PRODUCING_PKGS = new Set([
40
+ "errors",
41
+ "fmt",
42
+ "strconv",
43
+ "path",
44
+ "path/filepath",
45
+ "io",
46
+ "strings",
47
+ "bytes",
48
+ "time",
49
+ "sync",
50
+ "crypto/rand",
51
+ "rand",
52
+ "crypto/sha256",
53
+ "sha256",
54
+ "encoding/binary",
55
+ "binary",
56
+ "encoding/hex",
57
+ "hex",
58
+ "encoding/base64",
59
+ "base64",
60
+ ]);
35
61
  // Packages whose calls lower to the `env.*` imports in STDLIB_IMPORTS (the
36
62
  // matching JS lives in WASM_STDLIB_JS). Only modules importing one of them
37
63
  // declare the imports and ship that JS block.
@@ -41,16 +67,64 @@ const STDLIB_ENV_PKGS = new Set([
41
67
  "unicode/utf8",
42
68
  "fmt",
43
69
  "testing",
70
+ "unicode",
71
+ "math/rand",
72
+ "path",
73
+ "path/filepath",
74
+ "html",
75
+ "bytes",
76
+ "io",
77
+ "time",
78
+ "sync",
79
+ "crypto/rand",
80
+ "rand",
81
+ "crypto/sha256",
82
+ "sha256",
83
+ "encoding/binary",
84
+ "binary",
85
+ "encoding/hex",
86
+ "hex",
87
+ "encoding/base64",
88
+ "base64",
44
89
  ]);
45
90
 
46
91
  function importsAnyOf(programs, pkgs) {
92
+ const has = (p) => (pkgs instanceof Set ? pkgs.has(p) : pkgs.includes(p));
47
93
  return (programs ?? []).some((p) =>
48
94
  (p.imports ?? []).some((decl) =>
49
- (decl.imports ?? [decl]).some((imp) => pkgs.has(imp.path)),
95
+ (decl.imports ?? [decl]).some((imp) => has(imp.path)),
50
96
  ),
51
97
  );
52
98
  }
53
99
 
100
+ function hasErrorUsage(programs) {
101
+ return (programs ?? []).some((p) => {
102
+ let found = false;
103
+ const scan = (n) => {
104
+ if (!n || found || typeof n !== "object") return;
105
+ if (Array.isArray(n)) {
106
+ for (const x of n) scan(x);
107
+ return;
108
+ }
109
+ if (
110
+ (n.kind === "Ident" ||
111
+ n.kind === "TypeName" ||
112
+ n.kind === "basic" ||
113
+ n.kind === "interface") &&
114
+ n.name === "error"
115
+ ) {
116
+ found = true;
117
+ return;
118
+ }
119
+ for (const k of Object.keys(n)) {
120
+ if (!k.startsWith("_")) scan(n[k]);
121
+ }
122
+ };
123
+ scan(p);
124
+ return found;
125
+ });
126
+ }
127
+
54
128
  // [name, params, results] for `env.*` imports backing stdlib calls; the JS
55
129
  // side lives in WASM_STDLIB_JS (boundary.js). All are registered eagerly
56
130
  // (when any STDLIB_ENV_PKGS package is imported) because the import table is
@@ -63,6 +137,11 @@ const STDLIB_IMPORTS = [
63
137
  ["str_trim_right", [S, S], [S]],
64
138
  ["str_trim_prefix", [S, S], [S]],
65
139
  ["str_trim_suffix", [S, S], [S]],
140
+ ["str_cut", [S, S], ["i32"]],
141
+ ["str_cut_before", [], [S]],
142
+ ["str_cut_after", [], [S]],
143
+ ["str_cut_prefix", [S, S], ["i32"]],
144
+ ["str_cut_suffix", [S, S], ["i32"]],
66
145
  ["str_replace", [S, S, S, "i32"], [S]],
67
146
  ["str_contains_rune", [S, "i32"], ["i32"]],
68
147
  ["str_contains_any", [S, S], ["i32"]],
@@ -70,19 +149,30 @@ const STDLIB_IMPORTS = [
70
149
  ["str_index_rune", [S, "i32"], ["i32"]],
71
150
  ["str_index_any", [S, S], ["i32"]],
72
151
  ["str_last_index_byte", [S, "i32"], ["i32"]],
152
+ ["str_last_index_any", [S, S], ["i32"]],
73
153
  ["str_title", [S], [S]],
74
154
  ["str_split", [S, S], ["i32"]],
155
+ ["str_split_n", [S, S, "i32"], ["i32"]],
156
+ ["str_split_after", [S, S], ["i32"]],
157
+ ["str_split_after_n", [S, S, "i32"], ["i32"]],
75
158
  ["str_fields", [S], ["i32"]],
76
159
  ["str_part", ["i32"], [S]],
160
+ ["str_replacer_init", [S], []],
161
+ ["str_replacer_add", [S, S], []],
162
+ ["str_replacer_run", [], [S]],
77
163
  // strconv
78
164
  ["strconv_atoi", [S], ["i64"]],
79
165
  ["strconv_parse_int", [S, "i32", "i32"], ["i64"]],
80
166
  ["strconv_parse_float", [S], ["f64"]],
81
167
  ["strconv_parse_bool", [S], ["i32"]],
82
168
  ["strconv_err", [], [S]],
169
+ ["strconv_err_kind", [], ["i32"]],
83
170
  ["strconv_format_float", ["f64", "i32", "i32", "i32"], [S]],
84
171
  ["strconv_format_int", ["i64", "i32"], [S]],
172
+ ["strconv_format_uint", ["i64", "i32"], [S]],
173
+ ["strconv_parse_uint", [S, "i32", "i32"], ["i64"]],
85
174
  ["strconv_quote", [S], [S]],
175
+ ["strconv_unquote", [S], [S]],
86
176
  // unicode/utf8
87
177
  ["utf8_rune_count", [S], ["i32"]],
88
178
  ["utf8_rune_len", ["i32"], ["i32"]],
@@ -93,8 +183,78 @@ const STDLIB_IMPORTS = [
93
183
  // fmt (arguments travel through the testing_arg_* buffer)
94
184
  ["fmt_sprintf", [S], [S]],
95
185
  ["fmt_printf", [S], []],
186
+ ["fmt_format_v", [S], [S]],
187
+ ["fmt_sscan_init", [S, "i32"], ["i32"]],
188
+ ["fmt_sscanf_init", [S, S], ["i32"]],
189
+ ["fmt_sscan_has_err", [], ["i32"]],
190
+ ["fmt_sscan_err_msg", [], [S]],
191
+ ["fmt_sscan_token_str", ["i32"], [S]],
192
+ ["fmt_sscan_token_i64", ["i32"], ["i64"]],
193
+ ["fmt_sscan_token_f64", ["i32"], ["f64"]],
194
+ ["fmt_sscan_token_bool", ["i32"], ["i32"]],
96
195
  // testing.T.Run: the harness calls back into the exported __testing_run_cb
97
196
  ["testing_run", [S, S, "anyref"], ["i32"]],
197
+ // unicode
198
+ ["unicode_is_letter", ["i32"], ["i32"]],
199
+ ["unicode_is_digit", ["i32"], ["i32"]],
200
+ ["unicode_is_space", ["i32"], ["i32"]],
201
+ ["unicode_is_upper", ["i32"], ["i32"]],
202
+ ["unicode_is_lower", ["i32"], ["i32"]],
203
+ ["unicode_is_punct", ["i32"], ["i32"]],
204
+ ["unicode_is_control", ["i32"], ["i32"]],
205
+ ["unicode_is_print", ["i32"], ["i32"]],
206
+ ["unicode_is_graphic", ["i32"], ["i32"]],
207
+ ["unicode_to_upper", ["i32"], ["i32"]],
208
+ ["unicode_to_lower", ["i32"], ["i32"]],
209
+ // html
210
+ ["html_escape", [S], [S]],
211
+ ["html_unescape", [S], [S]],
212
+ // path
213
+ ["path_base", [S], [S]],
214
+ ["path_dir", [S], [S]],
215
+ ["path_ext", [S], [S]],
216
+ ["path_is_abs", [S], ["i32"]],
217
+ ["path_clean", [S], [S]],
218
+ ["path_split", [S], []],
219
+ ["path_split_dir", [], [S]],
220
+ ["path_split_file", [], [S]],
221
+ ["path_join_init", [], []],
222
+ ["path_join_push", [S], []],
223
+ ["path_join_run", [], [S]],
224
+ ["path_match", [S, S], ["i32"]],
225
+ ["path_match_has_err", [], ["i32"]],
226
+ ["bytes_trim", [S, S, "i32", "i32"], []],
227
+ ["bytes_trim_start", [], ["i32"]],
228
+ ["bytes_trim_end", [], ["i32"]],
229
+ // time
230
+ ["time_now_ms", [], ["f64"]],
231
+ ["time_timezone_offset", ["f64"], ["i32"]],
232
+ ["time_parse", [S, S], ["f64"]],
233
+ ["time_parse_err", [], [S]],
234
+ ["time_parse_offset", [], ["i32"]],
235
+ [
236
+ "time_format",
237
+ ["i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", S],
238
+ [S],
239
+ ],
240
+ ["time_duration_string", ["i64"], [S]],
241
+ // raw binary buffer for crypto/rand, crypto/sha256, hex, base64
242
+ ["raw_bytes_init", ["i32"], []],
243
+ ["raw_bytes_push", ["i32"], []],
244
+ ["raw_bytes_len", [], ["i32"]],
245
+ ["raw_bytes_get", ["i32"], ["i32"]],
246
+ // crypto/rand
247
+ ["crypto_rand_fill", ["i32"], ["i32"]],
248
+ // crypto/sha256
249
+ ["crypto_sha256_sum", [], ["i32"]],
250
+ // encoding/hex
251
+ ["hex_encode_to_string", [], [S]],
252
+ ["hex_decode_string", [S], ["i32"]],
253
+ ["hex_get_err", [], [S]],
254
+ // encoding/base64
255
+ ["base64_encode_to_string", ["i32"], [S]],
256
+ ["base64_decode_string", ["i32", S], ["i32"]],
257
+ ["base64_get_err", [], [S]],
98
258
  ];
99
259
 
100
260
  class ModuleEmitter {
@@ -113,6 +273,7 @@ class ModuleEmitter {
113
273
 
114
274
  this.structTypes = new Map(); // name -> struct info
115
275
  this.boxTypes = new Map(); // wType key -> box info
276
+ this.ifaceBoxTypes = new Map(); // key -> iface box info
116
277
  this.arrayTypes = new Map(); // wType key -> array info
117
278
  this.sliceTypes = new Map(); // wType key -> slice info
118
279
  this.sliceTypesByIndex = new Map(); // typeIndex -> slice info
@@ -145,7 +306,8 @@ class ModuleEmitter {
145
306
  // `errors.New` / `fmt.Errorf` / strconv parse failures allocate the
146
307
  // runtime's own error struct; it joins the struct rec group so the
147
308
  // `error.Error` dispatcher can `ref.test` it like any user type.
148
- this.hasErrorType = importsAnyOf(programs, ERROR_PRODUCING_PKGS);
309
+ this.hasErrorType =
310
+ importsAnyOf(programs, ERROR_PRODUCING_PKGS) || hasErrorUsage(programs);
149
311
  this.usesStdlibEnv = importsAnyOf(programs, STDLIB_ENV_PKGS);
150
312
 
151
313
  if (programs && programs.length > 0) {
@@ -315,23 +477,95 @@ class ModuleEmitter {
315
477
  return this.structTypes.get(name) ?? null;
316
478
  }
317
479
 
480
+ getAnonStructType(fields) {
481
+ const key = fields.map((f) => `${f.name}:${goTypeKey(f.goType)}`).join(";");
482
+ const name = `__anon_${key.replace(/[^a-zA-Z0-9_]/g, "_")}`;
483
+ if (this.structTypes.has(name)) {
484
+ return this.structTypes.get(name);
485
+ }
486
+ const fieldWTypes = fields.map((f) =>
487
+ toWasmType(f.goType, this.checker, this),
488
+ );
489
+ const typeIndex = this._getTotalTypeCount();
490
+ const typeEntry = {
491
+ form: "struct",
492
+ fields: fieldWTypes.map((t) => ({ type: t, mutable: true })),
493
+ };
494
+ this._pushType(typeEntry);
495
+ const info = {
496
+ name,
497
+ typeIndex,
498
+ typeEntry,
499
+ fields: fields.map((f, i) => ({
500
+ name: f.name,
501
+ wType: fieldWTypes[i],
502
+ goType: f.goType,
503
+ fieldIndex: i,
504
+ })),
505
+ fieldIndexMap: new Map(fields.map((f, i) => [f.name, i])),
506
+ embeds: [],
507
+ pkgName: null,
508
+ pkgTarget: "wasm",
509
+ isAnon: true,
510
+ };
511
+ this.structTypes.set(name, info);
512
+ return info;
513
+ }
514
+
318
515
  getBoxType(goType) {
319
- const wType = toWasmType(goType, this.checker, this);
320
- const key = typeof wType === "string" ? wType : JSON.stringify(wType);
516
+ const key = goTypeKey(goType);
321
517
  if (this.boxTypes.has(key)) {
322
518
  return this.boxTypes.get(key);
323
519
  }
520
+ const wType = toWasmType(goType, this.checker, this);
324
521
  const typeIndex = this._getTotalTypeCount();
325
522
  const typeEntry = {
326
523
  form: "struct",
327
524
  fields: [{ type: wType, mutable: true }],
328
525
  };
329
526
  this._pushType(typeEntry);
330
- const boxInfo = { typeIndex, typeEntry, wType };
527
+ const boxInfo = { key, typeIndex, typeEntry, wType, goType };
331
528
  this.boxTypes.set(key, boxInfo);
332
529
  return boxInfo;
333
530
  }
334
531
 
532
+ getIfaceBoxType(goType) {
533
+ const key = goTypeKey(goType);
534
+ if (this.ifaceBoxTypes.has(key)) {
535
+ return this.ifaceBoxTypes.get(key);
536
+ }
537
+ const wType = toWasmType(goType, this.checker, this);
538
+ const tagIndex = this.ifaceBoxTypes.size;
539
+ const tagTypeIndex = this._getTotalTypeCount();
540
+ const tagFields = [];
541
+ for (let i = 0; i < tagIndex; i++) {
542
+ tagFields.push({ type: "i32", mutable: false });
543
+ }
544
+ this._pushType({
545
+ form: "struct",
546
+ fields: tagFields,
547
+ });
548
+ const typeIndex = this._getTotalTypeCount();
549
+ const typeEntry = {
550
+ form: "struct",
551
+ fields: [
552
+ { type: wType, mutable: true },
553
+ {
554
+ type: {
555
+ kind: "ref",
556
+ nullable: true,
557
+ typeIndex: tagTypeIndex,
558
+ },
559
+ mutable: false,
560
+ },
561
+ ],
562
+ };
563
+ this._pushType(typeEntry);
564
+ const boxInfo = { key, typeIndex, typeEntry, wType, goType, tagTypeIndex };
565
+ this.ifaceBoxTypes.set(key, boxInfo);
566
+ return boxInfo;
567
+ }
568
+
335
569
  getArrayType(elemGoType) {
336
570
  const elemWType = toWasmType(elemGoType, this.checker, this);
337
571
  const key =
@@ -641,6 +875,11 @@ class ModuleEmitter {
641
875
  this.getStrFromI32ImportIndex();
642
876
  this.getStrFromF64ImportIndex();
643
877
  this.getIsStringImportIndex(); // env.is_string
878
+ this.getStrToBytesImportIndex();
879
+ this.getStrByteAtImportIndex();
880
+ this.getBytesToStrInitImportIndex();
881
+ this.getBytesToStrPushImportIndex();
882
+ this.getBytesToStrFinishImportIndex();
644
883
  this.getTestingCallImportIndex();
645
884
  this.getTestingNameImportIndex();
646
885
  this.getTestingFlagImportIndex();
@@ -648,6 +887,17 @@ class ModuleEmitter {
648
887
  this.getTestingArgImportIndex(t, false);
649
888
  }
650
889
  this.getTestingArgImportIndex("i32", true);
890
+ this.getFmtNewArrayIndex();
891
+ this.getFmtArrayPushIndex();
892
+ this.getFmtNewMapIndex();
893
+ this.getFmtMapSetIndex();
894
+ this.getFmtNewStructIndex();
895
+ this.getFmtStructSetIndex();
896
+ this.getFmtBoxI32Index();
897
+ this.getFmtBoxI64Index();
898
+ this.getFmtBoxF64Index();
899
+ this.getFmtBoxBoolIndex();
900
+ this.getFmtNewBoxIndex();
651
901
  if (this.usesStdlibEnv) {
652
902
  for (const [name, params, results] of STDLIB_IMPORTS) {
653
903
  this.getOrAddFuncImport("env", name, params, results);
@@ -766,6 +1016,80 @@ class ModuleEmitter {
766
1016
  return gIdx;
767
1017
  }
768
1018
 
1019
+ getRandStateGlobalIndex() {
1020
+ if (this.randStateGlobalIndex !== undefined) {
1021
+ return this.randStateGlobalIndex;
1022
+ }
1023
+ const gIdx = this.globals.length;
1024
+ this.globals.push({
1025
+ type: "i64",
1026
+ mutable: true,
1027
+ init: [{ op: "i64.const", value: 1n }],
1028
+ });
1029
+ this.randStateGlobalIndex = gIdx;
1030
+ return gIdx;
1031
+ }
1032
+
1033
+ getStrconvErrGlobalIndex(field) {
1034
+ if (!this.strconvErrGlobals) this.strconvErrGlobals = new Map();
1035
+ if (this.strconvErrGlobals.has(field)) {
1036
+ return this.strconvErrGlobals.get(field);
1037
+ }
1038
+ const gIdx = this.globals.length;
1039
+ this.globals.push({
1040
+ type: "anyref",
1041
+ mutable: true,
1042
+ init: [{ op: "ref.null", heapType: "any" }],
1043
+ });
1044
+ this.strconvErrGlobals.set(field, gIdx);
1045
+ return gIdx;
1046
+ }
1047
+
1048
+ getPathErrGlobalIndex(field) {
1049
+ if (!this.pathErrGlobals) this.pathErrGlobals = new Map();
1050
+ if (this.pathErrGlobals.has(field)) {
1051
+ return this.pathErrGlobals.get(field);
1052
+ }
1053
+ const gIdx = this.globals.length;
1054
+ this.globals.push({
1055
+ type: "anyref",
1056
+ mutable: true,
1057
+ init: [{ op: "ref.null", heapType: "any" }],
1058
+ });
1059
+ this.pathErrGlobals.set(field, gIdx);
1060
+ return gIdx;
1061
+ }
1062
+
1063
+ getIoErrGlobalIndex(field) {
1064
+ if (!this.ioErrGlobals) this.ioErrGlobals = new Map();
1065
+ if (this.ioErrGlobals.has(field)) {
1066
+ return this.ioErrGlobals.get(field);
1067
+ }
1068
+ const gIdx = this.globals.length;
1069
+ this.globals.push({
1070
+ type: "anyref",
1071
+ mutable: true,
1072
+ init: [{ op: "ref.null", heapType: "any" }],
1073
+ });
1074
+ this.ioErrGlobals.set(field, gIdx);
1075
+ return gIdx;
1076
+ }
1077
+
1078
+ getIoDiscardGlobalIndex() {
1079
+ if (this.ioDiscardGlobalIndex !== undefined) {
1080
+ return this.ioDiscardGlobalIndex;
1081
+ }
1082
+ this.getStructType("io.discard");
1083
+ const gIdx = this.globals.length;
1084
+ this.globals.push({
1085
+ type: "anyref",
1086
+ mutable: true,
1087
+ init: [{ op: "ref.null", heapType: "any" }],
1088
+ });
1089
+ this.ioDiscardGlobalIndex = gIdx;
1090
+ return gIdx;
1091
+ }
1092
+
769
1093
  getDeferNodeTypeIndex() {
770
1094
  if (this.deferNodeTypeIndex !== undefined) {
771
1095
  return this.deferNodeTypeIndex;
@@ -895,6 +1219,90 @@ class ModuleEmitter {
895
1219
  return this.getOrAddFuncImport("env", "println_empty", [], []);
896
1220
  }
897
1221
 
1222
+ getFmtNewArrayIndex() {
1223
+ return this.getOrAddFuncImport("env", "fmt_new_array", [], ["externref"]);
1224
+ }
1225
+
1226
+ getFmtArrayPushIndex() {
1227
+ return this.getOrAddFuncImport(
1228
+ "env",
1229
+ "fmt_array_push",
1230
+ ["externref", "externref"],
1231
+ [],
1232
+ );
1233
+ }
1234
+
1235
+ getFmtNewMapIndex() {
1236
+ return this.getOrAddFuncImport("env", "fmt_new_map", [], ["externref"]);
1237
+ }
1238
+
1239
+ getFmtMapSetIndex() {
1240
+ return this.getOrAddFuncImport(
1241
+ "env",
1242
+ "fmt_map_set",
1243
+ ["externref", "externref", "externref"],
1244
+ [],
1245
+ );
1246
+ }
1247
+
1248
+ getFmtNewStructIndex() {
1249
+ return this.getOrAddFuncImport(
1250
+ "env",
1251
+ "fmt_new_struct",
1252
+ ["anyref", "i32"],
1253
+ ["externref"],
1254
+ );
1255
+ }
1256
+
1257
+ getFmtStructSetIndex() {
1258
+ return this.getOrAddFuncImport(
1259
+ "env",
1260
+ "fmt_struct_set",
1261
+ ["externref", "i32", "externref"],
1262
+ [],
1263
+ );
1264
+ }
1265
+
1266
+ getFmtBoxI32Index() {
1267
+ return this.getOrAddFuncImport(
1268
+ "env",
1269
+ "fmt_box_i32",
1270
+ ["i32"],
1271
+ ["externref"],
1272
+ );
1273
+ }
1274
+
1275
+ getFmtBoxI64Index() {
1276
+ return this.getOrAddFuncImport(
1277
+ "env",
1278
+ "fmt_box_i64",
1279
+ ["i64"],
1280
+ ["externref"],
1281
+ );
1282
+ }
1283
+
1284
+ getFmtBoxF64Index() {
1285
+ return this.getOrAddFuncImport(
1286
+ "env",
1287
+ "fmt_box_f64",
1288
+ ["f64"],
1289
+ ["externref"],
1290
+ );
1291
+ }
1292
+
1293
+ getFmtBoxBoolIndex() {
1294
+ return this.getOrAddFuncImport(
1295
+ "env",
1296
+ "fmt_box_bool",
1297
+ ["i32"],
1298
+ ["externref"],
1299
+ );
1300
+ }
1301
+
1302
+ getFmtNewBoxIndex() {
1303
+ return this.getOrAddFuncImport("env", "fmt_new_box", [], ["externref"]);
1304
+ }
1305
+
898
1306
  // `*testing.T` method calls: args are pushed one by one to a JS-side
899
1307
  // buffer, then `testing_call(t, methodNameStrIdx)` invokes the method.
900
1308
  getTestingArgImportIndex(wType, isBool = false) {
@@ -1186,11 +1594,45 @@ class ModuleEmitter {
1186
1594
  );
1187
1595
  }
1188
1596
 
1597
+ getStrToBytesImportIndex() {
1598
+ return this.getOrAddFuncImport(
1599
+ "env",
1600
+ "str_to_bytes",
1601
+ ["externref"],
1602
+ ["i32"],
1603
+ );
1604
+ }
1605
+
1606
+ getStrByteAtImportIndex() {
1607
+ return this.getOrAddFuncImport("env", "str_byte_at", ["i32"], ["i32"]);
1608
+ }
1609
+
1610
+ getBytesToStrInitImportIndex() {
1611
+ return this.getOrAddFuncImport("env", "bytes_to_str_init", ["i32"], []);
1612
+ }
1613
+
1614
+ getBytesToStrPushImportIndex() {
1615
+ return this.getOrAddFuncImport("env", "bytes_to_str_push", ["i32"], []);
1616
+ }
1617
+
1618
+ getBytesToStrFinishImportIndex() {
1619
+ return this.getOrAddFuncImport(
1620
+ "env",
1621
+ "bytes_to_str_finish",
1622
+ [],
1623
+ ["externref"],
1624
+ );
1625
+ }
1626
+
1189
1627
  getMathImportIndex(jsName, isBinary = false) {
1190
1628
  const params = isBinary ? ["f64", "f64"] : ["f64"];
1191
1629
  return this.getOrAddFuncImport("Math", jsName, params, ["f64"]);
1192
1630
  }
1193
1631
 
1632
+ getMathRandomImportIndex() {
1633
+ return this.getOrAddFuncImport("Math", "random", [], ["f64"]);
1634
+ }
1635
+
1194
1636
  // Imports from the STDLIB_IMPORTS table (strings/strconv/utf8/fmt/testing.Run).
1195
1637
  getStdlibImportIndex(name) {
1196
1638
  const idx = this.importCache.get(`env.${name}`);
@@ -1198,6 +1640,10 @@ class ModuleEmitter {
1198
1640
  return idx;
1199
1641
  }
1200
1642
 
1643
+ getTimezoneOffsetImportIndex() {
1644
+ return this.getStdlibImportIndex("time_timezone_offset");
1645
+ }
1646
+
1201
1647
  // Closure type of `func(*testing.T)` (the t.Run body).
1202
1648
  getTestingRunClosureType() {
1203
1649
  return this.getClosureType({
@@ -1280,7 +1726,7 @@ class ModuleEmitter {
1280
1726
  structInfo: sInfo,
1281
1727
  });
1282
1728
  }
1283
- const box = this.getBoxType({ kind: "named", name: sName });
1729
+ const box = this.getIfaceBoxType({ kind: "named", name: sName });
1284
1730
  const valKey = `val:${sName}`;
1285
1731
  if (box && !seenKeys.has(valKey)) {
1286
1732
  seenKeys.add(valKey);
@@ -1332,7 +1778,7 @@ class ModuleEmitter {
1332
1778
  }
1333
1779
 
1334
1780
  if (!isPtrRecv) {
1335
- const box = this.getBoxType({ kind: "named", name: sName });
1781
+ const box = this.getIfaceBoxType({ kind: "named", name: sName });
1336
1782
  const valKey = `val:${sName}`;
1337
1783
  if (box && !seenKeys.has(valKey)) {
1338
1784
  seenKeys.add(valKey);
@@ -1364,7 +1810,7 @@ class ModuleEmitter {
1364
1810
  if (fnName.endsWith(`.${methodName}`)) {
1365
1811
  const rName = fnName.slice(0, fnName.length - methodName.length - 1);
1366
1812
  if (!this.structTypes.has(rName)) {
1367
- const box = this.getBoxType({ kind: "named", name: rName });
1813
+ const box = this.getIfaceBoxType({ kind: "named", name: rName });
1368
1814
  const key = `named:${rName}`;
1369
1815
  if (box && !seenKeys.has(key)) {
1370
1816
  seenKeys.add(key);
@@ -1412,7 +1858,7 @@ class ModuleEmitter {
1412
1858
 
1413
1859
  // Check struct value (boxed)
1414
1860
  if (this.checker?.implements?.(namedType, ifaceResolved)) {
1415
- const box = this.getBoxType(namedType);
1861
+ const box = this.getIfaceBoxType(namedType);
1416
1862
  if (box && !seen.has(box.typeIndex)) {
1417
1863
  seen.add(box.typeIndex);
1418
1864
  candidates.push({ name: sName, typeIndex: box.typeIndex });
@@ -1429,7 +1875,7 @@ class ModuleEmitter {
1429
1875
  tVal.underlying?.kind !== "interface"
1430
1876
  ) {
1431
1877
  if (this.checker.implements(tVal, ifaceResolved)) {
1432
- const box = this.getBoxType(tVal);
1878
+ const box = this.getIfaceBoxType(tVal);
1433
1879
  if (box && !seen.has(box.typeIndex)) {
1434
1880
  seen.add(box.typeIndex);
1435
1881
  candidates.push({ name: tName, typeIndex: box.typeIndex });
@@ -1685,8 +2131,19 @@ export function compileWasmModule(
1685
2131
  }
1686
2132
  funcDecls.push(...trampolines);
1687
2133
 
2134
+ _collectPackageConsts(progs, mod, checker);
2135
+ if (checker?.errors?.length > 0) {
2136
+ if (options.boundary) {
2137
+ throw new Error(checker.errors.map((e) => e.message).join("\n"));
2138
+ }
2139
+ return {
2140
+ wasm: null,
2141
+ facade: null,
2142
+ stringTable: null,
2143
+ errors: checker.errors,
2144
+ };
2145
+ }
1688
2146
  const pendingInits = _collectPackageGlobals(progs, mod, checker);
1689
- _collectPackageConsts(progs, mod);
1690
2147
  const globalInitFn =
1691
2148
  pendingInits.length > 0 || initNames.length > 0
1692
2149
  ? _makeGlobalInitFunc(pendingInits, initNames)
@@ -1975,6 +2432,23 @@ export function compileWasmModule(
1975
2432
  ),
1976
2433
  );
1977
2434
  }
2435
+ mod.usesAnyHash = false;
2436
+ for (const info of mod.mapTypes.values()) {
2437
+ if (isInterfaceType(info.keyGoType, checker)) {
2438
+ mod.usesAnyHash = true;
2439
+ break;
2440
+ }
2441
+ }
2442
+ if (mod.usesAnyHash) {
2443
+ funcDecls.push(
2444
+ helper(
2445
+ "__any_hash",
2446
+ [{ name: "a", type: ANY_T }],
2447
+ { kind: "basic", name: "int32" },
2448
+ "any_hash",
2449
+ ),
2450
+ );
2451
+ }
1978
2452
  if (mod.hasErrorType) {
1979
2453
  const errPtr = {
1980
2454
  kind: "pointer",
@@ -2057,6 +2531,7 @@ export function compileWasmModule(
2057
2531
  ),
2058
2532
  );
2059
2533
  }
2534
+ _registerIoSyntheticMethods(funcDecls, mod, STR_T, progs);
2060
2535
 
2061
2536
  // 1f. Boundary metadata (exported surface of wasm packages)
2062
2537
  const boundaryMeta = options.boundary
@@ -2118,6 +2593,8 @@ export function compileWasmModule(
2118
2593
  fn._forceExport ||
2119
2594
  (!fn._isGlobalInit &&
2120
2595
  !fn._isRuntimeHelper &&
2596
+ !fn._isIoHelper &&
2597
+ !fn._isPhase7Helper &&
2121
2598
  (options.exportAll ||
2122
2599
  fn.name === "main" ||
2123
2600
  (fn.name[0] >= "A" && fn.name[0] <= "Z") ||
@@ -2177,6 +2654,8 @@ export function compileWasmModule(
2177
2654
  emitter.emitSharedHelper(fn);
2178
2655
  } else if (fn._isRuntimeHelper) {
2179
2656
  emitter.emitRuntimeHelper(fn);
2657
+ } else if (fn._isIoHelper || fn._isPhase7Helper) {
2658
+ emitter.emitIoHelper(fn);
2180
2659
  } else if (fn.body) {
2181
2660
  emitter.emitFunctionBody(fn.body);
2182
2661
  }
@@ -2264,29 +2743,65 @@ export function compileWasmModule(
2264
2743
  };
2265
2744
  }
2266
2745
 
2267
- // Package-level `const` declarations with literal values (iota is already
2268
- // substituted by the parser). Non-literal constant expressions are not
2269
- // supported by the wasm backend yet.
2270
- function _collectPackageConsts(progs, mod) {
2746
+ // Package-level `const` declarations (iota is already substituted by the
2747
+ // parser). Literals and expressions the folder can reduce become BasicLits in
2748
+ // `constCache`; whatever is left is recorded in `nonLiteralConsts` and later
2749
+ // becomes a global initialised by the start function.
2750
+ function _collectPackageConsts(progs, mod, checker = null) {
2751
+ const entries = [];
2271
2752
  for (const p of progs) {
2272
2753
  for (const d of p.decls ?? []) {
2273
2754
  if (d.kind !== "ConstDecl") continue;
2274
2755
  for (const spec of d.decls ?? []) {
2275
2756
  for (let i = 0; i < spec.names.length; i++) {
2276
- let lit = spec.value?.[i];
2277
- if (
2278
- lit?.kind === "UnaryExpr" &&
2279
- lit.op === "-" &&
2280
- lit.operand?.kind === "BasicLit"
2281
- ) {
2282
- lit = { ...lit.operand, value: `-${lit.operand.value}` };
2283
- }
2284
- if (lit?.kind === "BasicLit") mod.constCache.set(spec.names[i], lit);
2285
- else if (lit) mod.nonLiteralConsts.add(spec.names[i]);
2757
+ if (spec.value?.[i])
2758
+ entries.push({
2759
+ name: spec.names[i],
2760
+ expr: spec.value[i],
2761
+ filename: p._filename,
2762
+ source: p._source,
2763
+ });
2286
2764
  }
2287
2765
  }
2288
2766
  }
2289
2767
  }
2768
+ const resolve = (name) => mod.constCache.get(name) ?? null;
2769
+ // Iterate to a fixpoint so a constant may refer to one declared later.
2770
+ let pending = entries;
2771
+ let progress = true;
2772
+ while (progress && pending.length > 0) {
2773
+ progress = false;
2774
+ const rest = [];
2775
+ for (const entry of pending) {
2776
+ let lit = null;
2777
+ try {
2778
+ lit = foldConstExpr(entry.expr, resolve);
2779
+ } catch (err) {
2780
+ if (checker?.err) {
2781
+ const savedFile = checker._currentFile;
2782
+ const savedSource = checker._currentSource;
2783
+ if (entry.filename) checker._currentFile = entry.filename;
2784
+ if (entry.source) checker._currentSource = entry.source;
2785
+ checker.err(err.message, entry.expr);
2786
+ checker._currentFile = savedFile;
2787
+ checker._currentSource = savedSource;
2788
+ } else {
2789
+ throw new Error(
2790
+ `Constant expression error for '${entry.name}': ${err.message}`,
2791
+ );
2792
+ }
2793
+ continue;
2794
+ }
2795
+ if (lit) {
2796
+ mod.constCache.set(entry.name, lit);
2797
+ progress = true;
2798
+ } else {
2799
+ rest.push(entry);
2800
+ }
2801
+ }
2802
+ pending = rest;
2803
+ }
2804
+ for (const entry of pending) mod.nonLiteralConsts.add(entry.name);
2290
2805
  }
2291
2806
 
2292
2807
  // Depth-first search over AST nodes (skipping `_`-prefixed annotations).
@@ -2318,6 +2833,9 @@ function _usesTestingRun(node) {
2318
2833
  // site, so a gap surfaces as a compile error rather than a bad module.
2319
2834
  function _usesAnyEq(funcDecls, mod) {
2320
2835
  if (mod.hasErrorType) return true;
2836
+ for (const info of mod.mapTypes.values()) {
2837
+ if (isInterfaceType(info.keyGoType, mod.checker)) return true;
2838
+ }
2321
2839
  for (const info of mod.structTypes.values()) {
2322
2840
  if (info.fields?.some((f) => f.wType === "anyref")) return true;
2323
2841
  }
@@ -2377,6 +2895,9 @@ function _scanImportsInBody(node, mod) {
2377
2895
  Log2: "log2",
2378
2896
  Log10: "log10",
2379
2897
  Round: "round",
2898
+ Sinh: "sinh",
2899
+ Cosh: "cosh",
2900
+ Tanh: "tanh",
2380
2901
  };
2381
2902
  if (jsMathMap[func.field]) {
2382
2903
  const isBinary = BINARY_MATH_IMPORTS.has(jsMathMap[func.field]);
@@ -2445,6 +2966,10 @@ export function compileWasm(source, options = {}) {
2445
2966
  exportAll: options.exportAll ?? true,
2446
2967
  });
2447
2968
 
2969
+ if (checker.errors?.length > 0) {
2970
+ return { wasm: null, errors: checker.errors };
2971
+ }
2972
+
2448
2973
  return { ...res, errors: [] };
2449
2974
  }
2450
2975
 
@@ -2490,14 +3015,18 @@ function _zeroFieldConst(f, mod) {
2490
3015
 
2491
3016
  function _collectPackageGlobals(progs, mod, checker) {
2492
3017
  const pending = []; // non-constant initializers, run by the start function
3018
+ const pendingConsts = []; // unfoldable consts; they run before any var
2493
3019
  for (const p of progs) {
2494
3020
  for (const d of p.decls ?? []) {
2495
- if (d.kind !== "VarDecl") continue;
3021
+ const isConst = d.kind === "ConstDecl";
3022
+ if (d.kind !== "VarDecl" && !isConst) continue;
2496
3023
  for (const spec of d.decls ?? d.specs ?? [d]) {
2497
3024
  const names = spec.names ?? (spec.name ? [spec.name] : []);
2498
3025
  const values = spec.value ?? (spec.init ? [spec.init] : []);
2499
3026
  for (let i = 0; i < names.length; i++) {
2500
3027
  const name = names[i];
3028
+ // Foldable consts live in `constCache`, not in a global.
3029
+ if (isConst && !mod.isNonLiteralConst(name)) continue;
2501
3030
  const rawType = spec.type ?? values[i]?._type;
2502
3031
  const wType = toWasmType(rawType, checker, mod);
2503
3032
  const gIdx = mod.globals.length;
@@ -2562,15 +3091,17 @@ function _collectPackageGlobals(progs, mod, checker) {
2562
3091
  if (litNode && litNode.kind === "BasicLit") {
2563
3092
  if (litNode.litKind === "INT") {
2564
3093
  isConstInit = true;
3094
+ const s = String(litNode.value);
3095
+ const clean = /^0[0-7]+$/.test(s) ? `0o${s.slice(1)}` : s;
2565
3096
  initInsts = [
2566
3097
  wType === "i64"
2567
3098
  ? {
2568
3099
  op: "i64.const",
2569
- value: BigInt(sign) * BigInt(litNode.value),
3100
+ value: BigInt(sign) * BigInt(clean),
2570
3101
  }
2571
3102
  : {
2572
3103
  op: "i32.const",
2573
- value: (sign * Number(litNode.value)) | 0,
3104
+ value: (sign * Number(clean)) | 0,
2574
3105
  },
2575
3106
  ];
2576
3107
  } else if (litNode.litKind === "FLOAT") {
@@ -2592,7 +3123,10 @@ function _collectPackageGlobals(progs, mod, checker) {
2592
3123
  }
2593
3124
  }
2594
3125
  if (values[i] && !isConstInit && names.length === values.length) {
2595
- pending.push({ name, expr: values[i] });
3126
+ (isConst ? pendingConsts : pending).push({
3127
+ name,
3128
+ expr: values[i],
3129
+ });
2596
3130
  }
2597
3131
  mod.globals.push({
2598
3132
  type: wType,
@@ -2608,7 +3142,26 @@ function _collectPackageGlobals(progs, mod, checker) {
2608
3142
  }
2609
3143
  }
2610
3144
  }
2611
- return pending;
3145
+ return [..._orderConstInits(pendingConsts), ...pending];
3146
+ }
3147
+
3148
+ // Orders runtime-initialised consts so each runs after the consts it
3149
+ // references (Go allows a constant to use one declared later).
3150
+ function _orderConstInits(entries) {
3151
+ const byName = new Map(entries.map((e) => [e.name, e]));
3152
+ const ordered = [];
3153
+ const state = new Map();
3154
+ const visit = (entry) => {
3155
+ if (state.has(entry.name)) return;
3156
+ state.set(entry.name, true);
3157
+ _someNode(entry.expr, (n) => {
3158
+ if (n.kind === "Ident" && byName.has(n.name)) visit(byName.get(n.name));
3159
+ return false;
3160
+ });
3161
+ ordered.push(entry);
3162
+ };
3163
+ for (const e of entries) visit(e);
3164
+ return ordered;
2612
3165
  }
2613
3166
 
2614
3167
  // Synthesises `__init_globals()` assigning every non-constant package-level
@@ -2759,12 +3312,57 @@ function _makeInterfaceDispatcher(info, checker) {
2759
3312
  };
2760
3313
  }
2761
3314
 
3315
+ function _scanFuncSigInterfaces(
3316
+ progs,
3317
+ funcDecls,
3318
+ interfaceCalls,
3319
+ checker,
3320
+ mod,
3321
+ ) {
3322
+ const scanType = (t) => {
3323
+ if (!t) return;
3324
+ if (t.kind === "InterfaceType" || (t.kind === "interface" && !t.name)) {
3325
+ const mList = getInterfaceMethodList(t);
3326
+ if (mList.length === 0) return;
3327
+ const ifaceName = anonIfaceName(t, checker, mod);
3328
+ for (const m of mList) {
3329
+ const key = `${ifaceName}.${m.name}`;
3330
+ if (!interfaceCalls.has(key)) {
3331
+ interfaceCalls.set(key, {
3332
+ ifaceName,
3333
+ ifaceType: t,
3334
+ methodName: m.name,
3335
+ methodSig: {
3336
+ kind: "func",
3337
+ params: m.params,
3338
+ returns: m.returns,
3339
+ },
3340
+ });
3341
+ }
3342
+ }
3343
+ }
3344
+ };
3345
+ for (const fn of funcDecls) {
3346
+ for (const p of fn.params ?? []) scanType(p.type);
3347
+ scanType(fn.returnType);
3348
+ }
3349
+ for (const p of progs) {
3350
+ for (const d of p.decls ?? []) {
3351
+ if (d.kind === "FuncDecl") {
3352
+ for (const param of d.params ?? []) scanType(param.type);
3353
+ scanType(d.returnType);
3354
+ }
3355
+ }
3356
+ }
3357
+ }
3358
+
2762
3359
  function _collectInterfaceDispatchers(progs, funcDecls, mod, checker) {
2763
3360
  const interfaceCalls = new Map();
2764
3361
  for (const fn of funcDecls) {
2765
3362
  if (fn.body) _scanInterfaceCallNodes(fn.body, interfaceCalls, checker);
2766
3363
  }
2767
3364
  _scanInterfaceTypeDecls(progs, interfaceCalls, checker);
3365
+ _scanFuncSigInterfaces(progs, funcDecls, interfaceCalls, checker, mod);
2768
3366
 
2769
3367
  // `error.Error()` is reached implicitly by fmt verbs and errors.Is, so
2770
3368
  // the dispatcher must exist even when user code never calls it.
@@ -2784,7 +3382,378 @@ function _collectInterfaceDispatchers(progs, funcDecls, mod, checker) {
2784
3382
  });
2785
3383
  }
2786
3384
 
3385
+ const hasIo = importsAnyOf(progs, [
3386
+ "io",
3387
+ "fmt",
3388
+ "strings",
3389
+ "bytes",
3390
+ "crypto/sha256",
3391
+ "sha256",
3392
+ "encoding/binary",
3393
+ "binary",
3394
+ "encoding/hex",
3395
+ "hex",
3396
+ "encoding/base64",
3397
+ "base64",
3398
+ ]);
3399
+ if (hasIo) {
3400
+ const byteSliceType = {
3401
+ kind: "slice",
3402
+ elem: { kind: "basic", name: "byte" },
3403
+ };
3404
+ const intErrReturns = [
3405
+ { kind: "basic", name: "int" },
3406
+ { kind: "interface", name: "error" },
3407
+ ];
3408
+ if (!interfaceCalls.has("io.Reader.Read")) {
3409
+ interfaceCalls.set("io.Reader.Read", {
3410
+ ifaceName: "io.Reader",
3411
+ ifaceType: checker?.types?.get("io.Reader"),
3412
+ methodName: "Read",
3413
+ methodSig: {
3414
+ kind: "func",
3415
+ params: [byteSliceType],
3416
+ returns: intErrReturns,
3417
+ },
3418
+ });
3419
+ }
3420
+ if (!interfaceCalls.has("io.Writer.Write")) {
3421
+ interfaceCalls.set("io.Writer.Write", {
3422
+ ifaceName: "io.Writer",
3423
+ ifaceType: checker?.types?.get("io.Writer"),
3424
+ methodName: "Write",
3425
+ methodSig: {
3426
+ kind: "func",
3427
+ params: [byteSliceType],
3428
+ returns: intErrReturns,
3429
+ },
3430
+ });
3431
+ }
3432
+ }
3433
+
2787
3434
  for (const info of interfaceCalls.values()) {
2788
3435
  funcDecls.push(_makeInterfaceDispatcher(info, checker));
2789
3436
  }
2790
3437
  }
3438
+
3439
+ function _registerIoSyntheticMethods(funcDecls, mod, strType, programs) {
3440
+ const progs = Array.isArray(programs) ? programs : [programs];
3441
+ const hasStrings = importsAnyOf(progs, ["strings"]);
3442
+ const hasBytes = importsAnyOf(progs, ["bytes"]);
3443
+ const hasIo = importsAnyOf(progs, ["io"]);
3444
+
3445
+ const BYTE_SLICE_T = {
3446
+ kind: "slice",
3447
+ elem: { kind: "basic", name: "byte" },
3448
+ };
3449
+ const INT_T = { kind: "basic", name: "int" };
3450
+ const ERR_T = { kind: "interface", name: "error" };
3451
+ const INT_ERR_RET = { kind: "tuple", types: [INT_T, ERR_T] };
3452
+ const ioHelper = (name, params, returnType, kind, extra = {}) => ({
3453
+ kind: "FuncDecl",
3454
+ name,
3455
+ params,
3456
+ returnType,
3457
+ body: { kind: "Block", stmts: [], list: [] },
3458
+ _isIoHelper: kind,
3459
+ _rootFuncDecl: null,
3460
+ ...extra,
3461
+ });
3462
+
3463
+ if (hasStrings && mod.structTypes.has("strings.Builder")) {
3464
+ const bPtr = {
3465
+ kind: "pointer",
3466
+ base: { kind: "named", name: "strings.Builder" },
3467
+ };
3468
+ funcDecls.push(
3469
+ ioHelper(
3470
+ "strings.Builder.Write",
3471
+ [
3472
+ { name: "b", type: bPtr },
3473
+ { name: "p", type: BYTE_SLICE_T },
3474
+ ],
3475
+ INT_ERR_RET,
3476
+ "builder_write",
3477
+ {
3478
+ _isMethod: true,
3479
+ _recvTypeName: "strings.Builder",
3480
+ _methodName: "Write",
3481
+ },
3482
+ ),
3483
+ ioHelper(
3484
+ "strings.Builder.WriteString",
3485
+ [
3486
+ { name: "b", type: bPtr },
3487
+ { name: "s", type: strType },
3488
+ ],
3489
+ INT_ERR_RET,
3490
+ "builder_write_string",
3491
+ {
3492
+ _isMethod: true,
3493
+ _recvTypeName: "strings.Builder",
3494
+ _methodName: "WriteString",
3495
+ },
3496
+ ),
3497
+ );
3498
+ }
3499
+ if (hasBytes && mod.structTypes.has("bytes.Buffer")) {
3500
+ const bPtr = {
3501
+ kind: "pointer",
3502
+ base: { kind: "named", name: "bytes.Buffer" },
3503
+ };
3504
+ funcDecls.push(
3505
+ ioHelper(
3506
+ "bytes.Buffer.Write",
3507
+ [
3508
+ { name: "b", type: bPtr },
3509
+ { name: "p", type: BYTE_SLICE_T },
3510
+ ],
3511
+ INT_ERR_RET,
3512
+ "buffer_write",
3513
+ {
3514
+ _isMethod: true,
3515
+ _recvTypeName: "bytes.Buffer",
3516
+ _methodName: "Write",
3517
+ },
3518
+ ),
3519
+ ioHelper(
3520
+ "bytes.Buffer.WriteString",
3521
+ [
3522
+ { name: "b", type: bPtr },
3523
+ { name: "s", type: strType },
3524
+ ],
3525
+ INT_ERR_RET,
3526
+ "buffer_write_string",
3527
+ {
3528
+ _isMethod: true,
3529
+ _recvTypeName: "bytes.Buffer",
3530
+ _methodName: "WriteString",
3531
+ },
3532
+ ),
3533
+ ioHelper(
3534
+ "bytes.Buffer.Read",
3535
+ [
3536
+ { name: "b", type: bPtr },
3537
+ { name: "p", type: BYTE_SLICE_T },
3538
+ ],
3539
+ INT_ERR_RET,
3540
+ "buffer_read",
3541
+ {
3542
+ _isMethod: true,
3543
+ _recvTypeName: "bytes.Buffer",
3544
+ _methodName: "Read",
3545
+ },
3546
+ ),
3547
+ );
3548
+ }
3549
+ if (hasStrings && mod.structTypes.has("strings.Reader")) {
3550
+ const rPtr = {
3551
+ kind: "pointer",
3552
+ base: { kind: "named", name: "strings.Reader" },
3553
+ };
3554
+ funcDecls.push(
3555
+ ioHelper(
3556
+ "strings.Reader.Read",
3557
+ [
3558
+ { name: "r", type: rPtr },
3559
+ { name: "p", type: BYTE_SLICE_T },
3560
+ ],
3561
+ INT_ERR_RET,
3562
+ "strings_reader_read",
3563
+ {
3564
+ _isMethod: true,
3565
+ _recvTypeName: "strings.Reader",
3566
+ _methodName: "Read",
3567
+ },
3568
+ ),
3569
+ ioHelper(
3570
+ "strings.Reader.Len",
3571
+ [{ name: "r", type: rPtr }],
3572
+ INT_T,
3573
+ "strings_reader_len",
3574
+ {
3575
+ _isMethod: true,
3576
+ _recvTypeName: "strings.Reader",
3577
+ _methodName: "Len",
3578
+ },
3579
+ ),
3580
+ ioHelper(
3581
+ "strings.Reader.Reset",
3582
+ [
3583
+ { name: "r", type: rPtr },
3584
+ { name: "s", type: strType },
3585
+ ],
3586
+ null,
3587
+ "strings_reader_reset",
3588
+ {
3589
+ _isMethod: true,
3590
+ _recvTypeName: "strings.Reader",
3591
+ _methodName: "Reset",
3592
+ },
3593
+ ),
3594
+ );
3595
+ }
3596
+ if (hasBytes && mod.structTypes.has("bytes.Reader")) {
3597
+ const rPtr = {
3598
+ kind: "pointer",
3599
+ base: { kind: "named", name: "bytes.Reader" },
3600
+ };
3601
+ funcDecls.push(
3602
+ ioHelper(
3603
+ "bytes.Reader.Read",
3604
+ [
3605
+ { name: "r", type: rPtr },
3606
+ { name: "p", type: BYTE_SLICE_T },
3607
+ ],
3608
+ INT_ERR_RET,
3609
+ "bytes_reader_read",
3610
+ { _isMethod: true, _recvTypeName: "bytes.Reader", _methodName: "Read" },
3611
+ ),
3612
+ ioHelper(
3613
+ "bytes.Reader.Len",
3614
+ [{ name: "r", type: rPtr }],
3615
+ INT_T,
3616
+ "bytes_reader_len",
3617
+ { _isMethod: true, _recvTypeName: "bytes.Reader", _methodName: "Len" },
3618
+ ),
3619
+ ioHelper(
3620
+ "bytes.Reader.Reset",
3621
+ [
3622
+ { name: "r", type: rPtr },
3623
+ { name: "b", type: BYTE_SLICE_T },
3624
+ ],
3625
+ null,
3626
+ "bytes_reader_reset",
3627
+ {
3628
+ _isMethod: true,
3629
+ _recvTypeName: "bytes.Reader",
3630
+ _methodName: "Reset",
3631
+ },
3632
+ ),
3633
+ );
3634
+ }
3635
+ if (hasIo && mod.structTypes.has("io.discard")) {
3636
+ const dPtr = {
3637
+ kind: "pointer",
3638
+ base: { kind: "named", name: "io.discard" },
3639
+ };
3640
+ funcDecls.push(
3641
+ ioHelper(
3642
+ "io.discard.Write",
3643
+ [
3644
+ { name: "d", type: dPtr },
3645
+ { name: "p", type: BYTE_SLICE_T },
3646
+ ],
3647
+ INT_ERR_RET,
3648
+ "discard_write",
3649
+ { _isMethod: true, _recvTypeName: "io.discard", _methodName: "Write" },
3650
+ ),
3651
+ ioHelper(
3652
+ "io.discard.WriteString",
3653
+ [
3654
+ { name: "d", type: dPtr },
3655
+ { name: "s", type: strType },
3656
+ ],
3657
+ INT_ERR_RET,
3658
+ "discard_write_string",
3659
+ {
3660
+ _isMethod: true,
3661
+ _recvTypeName: "io.discard",
3662
+ _methodName: "WriteString",
3663
+ },
3664
+ ),
3665
+ );
3666
+ }
3667
+ if (hasIo && mod.structTypes.has("io.LimitedReader")) {
3668
+ const lrPtr = {
3669
+ kind: "pointer",
3670
+ base: { kind: "named", name: "io.LimitedReader" },
3671
+ };
3672
+ funcDecls.push(
3673
+ ioHelper(
3674
+ "io.LimitedReader.Read",
3675
+ [
3676
+ { name: "l", type: lrPtr },
3677
+ { name: "p", type: BYTE_SLICE_T },
3678
+ ],
3679
+ INT_ERR_RET,
3680
+ "limited_reader_read",
3681
+ {
3682
+ _isMethod: true,
3683
+ _recvTypeName: "io.LimitedReader",
3684
+ _methodName: "Read",
3685
+ },
3686
+ ),
3687
+ );
3688
+ }
3689
+ const hasSha256 = importsAnyOf(progs, ["crypto/sha256", "sha256"]);
3690
+ if (
3691
+ hasSha256 &&
3692
+ (mod.structTypes.has("sha256.digest") ||
3693
+ mod.structTypes.has("crypto/sha256.digest"))
3694
+ ) {
3695
+ const dPtr = {
3696
+ kind: "pointer",
3697
+ base: { kind: "named", name: "sha256.digest" },
3698
+ };
3699
+ funcDecls.push(
3700
+ ioHelper(
3701
+ "sha256.digest.Write",
3702
+ [
3703
+ { name: "d", type: dPtr },
3704
+ { name: "p", type: BYTE_SLICE_T },
3705
+ ],
3706
+ INT_ERR_RET,
3707
+ "sha256_write",
3708
+ {
3709
+ _isMethod: true,
3710
+ _recvTypeName: "sha256.digest",
3711
+ _methodName: "Write",
3712
+ },
3713
+ ),
3714
+ ioHelper(
3715
+ "sha256.digest.Sum",
3716
+ [
3717
+ { name: "d", type: dPtr },
3718
+ { name: "b", type: BYTE_SLICE_T },
3719
+ ],
3720
+ BYTE_SLICE_T,
3721
+ "sha256_sum",
3722
+ { _isMethod: true, _recvTypeName: "sha256.digest", _methodName: "Sum" },
3723
+ ),
3724
+ ioHelper(
3725
+ "sha256.digest.Reset",
3726
+ [{ name: "d", type: dPtr }],
3727
+ null,
3728
+ "sha256_reset",
3729
+ {
3730
+ _isMethod: true,
3731
+ _recvTypeName: "sha256.digest",
3732
+ _methodName: "Reset",
3733
+ },
3734
+ ),
3735
+ ioHelper(
3736
+ "sha256.digest.Size",
3737
+ [{ name: "d", type: dPtr }],
3738
+ INT_T,
3739
+ "sha256_size",
3740
+ {
3741
+ _isMethod: true,
3742
+ _recvTypeName: "sha256.digest",
3743
+ _methodName: "Size",
3744
+ },
3745
+ ),
3746
+ ioHelper(
3747
+ "sha256.digest.BlockSize",
3748
+ [{ name: "d", type: dPtr }],
3749
+ INT_T,
3750
+ "sha256_block_size",
3751
+ {
3752
+ _isMethod: true,
3753
+ _recvTypeName: "sha256.digest",
3754
+ _methodName: "BlockSize",
3755
+ },
3756
+ ),
3757
+ );
3758
+ }
3759
+ }