gofront 1.8.0 → 1.9.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 (178) hide show
  1. package/README.md +3 -3
  2. package/package.json +6 -5
  3. package/src/backend/js/expressions.js +1157 -408
  4. package/src/backend/js/index.js +327 -153
  5. package/src/backend/js/natives.js +383 -0
  6. package/src/backend/js/runtime.js +559 -2552
  7. package/src/backend/js/statements.js +670 -158
  8. package/src/backend/js/stdlib/index.js +45 -93
  9. package/src/backend/js/templ.js +32 -45
  10. package/src/backend/js/type-info.js +298 -19
  11. package/src/backend/shared/fixed-float.js +20 -0
  12. package/src/backend/shared/fmt-types.js +91 -170
  13. package/src/backend/shared/host-unicode.js +36 -0
  14. package/src/backend/wasm/boundary.js +490 -965
  15. package/src/backend/wasm/emit-builtins.js +884 -256
  16. package/src/backend/wasm/emit-exprs.js +185 -77
  17. package/src/backend/wasm/emit-maps.js +17 -367
  18. package/src/backend/wasm/emit-shared.js +4 -1
  19. package/src/backend/wasm/emit-stmts.js +524 -240
  20. package/src/backend/wasm/emit.js +176 -38
  21. package/src/backend/wasm/globals.js +48 -8
  22. package/src/backend/wasm/glue.js +2 -4
  23. package/src/backend/wasm/index.js +44 -530
  24. package/src/backend/wasm/method-expr.js +12 -1
  25. package/src/backend/wasm/module-emitter.js +299 -770
  26. package/src/backend/wasm/monomorph.js +139 -8
  27. package/src/backend/wasm/natives.js +600 -0
  28. package/src/backend/wasm/prescan.js +5 -153
  29. package/src/backend/wasm/prune-funcs.js +89 -0
  30. package/src/backend/wasm/stdlib/index.js +2 -32
  31. package/src/backend/wasm/types.js +97 -20
  32. package/src/backend/wasm/variadic.js +9 -4
  33. package/src/cli/cli-core.js +5 -1
  34. package/src/cli/test-runner.js +115 -342
  35. package/src/compiler.js +113 -130
  36. package/src/lexer/lexer.js +106 -11
  37. package/src/lexer/templ-lexer.js +61 -18
  38. package/src/lexer/tokens.js +1 -0
  39. package/src/link/stdlib.js +355 -132
  40. package/src/link/type-names.js +204 -0
  41. package/src/link/wasm.js +30 -6
  42. package/src/lower/bounds.js +153 -0
  43. package/src/lower/captures.js +92 -33
  44. package/src/lower/embedding.js +62 -5
  45. package/src/lower/errors-as.js +196 -0
  46. package/src/lower/fmt-formatters.js +929 -0
  47. package/src/lower/functions.js +225 -7
  48. package/src/lower/gen-types.js +293 -0
  49. package/src/lower/index.js +14 -2
  50. package/src/lower/json-codecs.js +334 -245
  51. package/src/lower/ownership.js +25 -7
  52. package/src/lower/reachability.js +69 -51
  53. package/src/lower/walk.js +12 -0
  54. package/src/parser/declarations.js +30 -5
  55. package/src/parser/expressions.js +111 -57
  56. package/src/parser/statements.js +108 -0
  57. package/src/parser/templ.js +44 -5
  58. package/src/parser/types.js +12 -0
  59. package/src/resolver.js +1 -2
  60. package/src/typechecker/assignability.js +214 -19
  61. package/src/typechecker/const-fold.js +70 -27
  62. package/src/typechecker/constants.js +156 -8
  63. package/src/typechecker/expressions.js +490 -108
  64. package/src/typechecker/index.js +346 -42
  65. package/src/typechecker/init-order.js +136 -40
  66. package/src/typechecker/operand-text.js +41 -0
  67. package/src/typechecker/resolve.js +490 -65
  68. package/src/typechecker/statements.js +288 -27
  69. package/src/typechecker/stdlib/excluded.js +9 -5
  70. package/src/typechecker/stdlib/index.js +25 -9
  71. package/src/typechecker/termination.js +10 -1
  72. package/src/typechecker/types.js +287 -575
  73. package/stdlib/bytes/buffer.go +479 -0
  74. package/stdlib/bytes/bytes.go +1010 -0
  75. package/stdlib/bytes/reader.go +161 -0
  76. package/stdlib/context/context.go +115 -0
  77. package/stdlib/crypto/rand/rand.go +34 -0
  78. package/stdlib/crypto/sha256/sha256.go +249 -0
  79. package/stdlib/encoding/base64/base64.go +331 -0
  80. package/stdlib/encoding/binary/binary.go +332 -0
  81. package/stdlib/encoding/hex/hex.go +140 -0
  82. package/stdlib/encoding/json/json.go +62 -0
  83. package/stdlib/errors/errors.go +174 -0
  84. package/stdlib/fmt/errors.go +110 -0
  85. package/stdlib/fmt/format.go +732 -0
  86. package/stdlib/fmt/print.go +1232 -0
  87. package/stdlib/gom/gom.go +293 -0
  88. package/stdlib/html/html.go +251 -0
  89. package/stdlib/internal/fmtrt/fmtrt.go +163 -0
  90. package/stdlib/internal/jsonrt/dec.go +171 -38
  91. package/stdlib/internal/jsonrt/enc.go +186 -36
  92. package/stdlib/internal/jsonrt/scanner.go +17 -8
  93. package/stdlib/io/io.go +157 -1
  94. package/stdlib/maps/maps.go +131 -0
  95. package/stdlib/math/bits/bits.go +507 -0
  96. package/stdlib/math/extra.go +16 -14
  97. package/stdlib/math/math.go +439 -0
  98. package/stdlib/math/rand/rand.go +297 -0
  99. package/stdlib/os/os.go +93 -0
  100. package/stdlib/path/filepath/filepath.go +47 -0
  101. package/stdlib/path/path.go +394 -0
  102. package/stdlib/regexp/regexp.go +231 -0
  103. package/stdlib/slices/slices.go +526 -0
  104. package/stdlib/slices/sort.go +209 -0
  105. package/stdlib/slices/zsort_cmp.go +480 -0
  106. package/stdlib/slices/zsort_ordered.go +482 -0
  107. package/stdlib/sort/sort.go +391 -452
  108. package/stdlib/sort/zsort_func.go +480 -0
  109. package/stdlib/sort/zsort_iface.go +480 -0
  110. package/stdlib/strconv/float.go +1699 -0
  111. package/stdlib/strconv/isprint.go +752 -0
  112. package/stdlib/strconv/quote.go +235 -50
  113. package/stdlib/strconv/strconv.go +616 -0
  114. package/stdlib/strings/builder.go +95 -0
  115. package/stdlib/strings/reader.go +163 -0
  116. package/stdlib/strings/replacer.go +102 -0
  117. package/stdlib/strings/strings.go +807 -0
  118. package/stdlib/sync/sync.go +268 -0
  119. package/stdlib/testing/async_js.go +61 -0
  120. package/stdlib/testing/benchmark.go +297 -0
  121. package/stdlib/testing/testing.go +523 -0
  122. package/stdlib/time/duration.go +233 -0
  123. package/stdlib/time/format.go +745 -0
  124. package/stdlib/time/parse.go +790 -0
  125. package/stdlib/time/sleep_js.go +63 -0
  126. package/stdlib/time/time.go +834 -0
  127. package/stdlib/unicode/unicode.go +141 -3
  128. package/stdlib/unicode/utf8/utf8.go +464 -0
  129. package/src/backend/js/fmt-runtime.js +0 -573
  130. package/src/backend/js/stdlib/base64.js +0 -12
  131. package/src/backend/js/stdlib/binary.js +0 -102
  132. package/src/backend/js/stdlib/bits.js +0 -119
  133. package/src/backend/js/stdlib/builder.js +0 -98
  134. package/src/backend/js/stdlib/bytes.js +0 -368
  135. package/src/backend/js/stdlib/errors.js +0 -45
  136. package/src/backend/js/stdlib/fmt.js +0 -517
  137. package/src/backend/js/stdlib/gom.js +0 -155
  138. package/src/backend/js/stdlib/hex.js +0 -17
  139. package/src/backend/js/stdlib/html.js +0 -20
  140. package/src/backend/js/stdlib/io.js +0 -137
  141. package/src/backend/js/stdlib/json.js +0 -1167
  142. package/src/backend/js/stdlib/maps.js +0 -37
  143. package/src/backend/js/stdlib/math.js +0 -85
  144. package/src/backend/js/stdlib/os.js +0 -13
  145. package/src/backend/js/stdlib/path.js +0 -48
  146. package/src/backend/js/stdlib/rand.js +0 -32
  147. package/src/backend/js/stdlib/regexp.js +0 -54
  148. package/src/backend/js/stdlib/sha256.js +0 -12
  149. package/src/backend/js/stdlib/shared.js +0 -17
  150. package/src/backend/js/stdlib/simd.js +0 -34
  151. package/src/backend/js/stdlib/slices.js +0 -107
  152. package/src/backend/js/stdlib/sort.js +0 -30
  153. package/src/backend/js/stdlib/strconv.js +0 -79
  154. package/src/backend/js/stdlib/strings.js +0 -301
  155. package/src/backend/js/stdlib/sync.js +0 -13
  156. package/src/backend/js/stdlib/testing.js +0 -41
  157. package/src/backend/js/stdlib/time.js +0 -152
  158. package/src/backend/js/stdlib/unicode.js +0 -25
  159. package/src/backend/js/stdlib/utf8.js +0 -26
  160. package/src/backend/wasm/stdlib/bytes.js +0 -6716
  161. package/src/backend/wasm/stdlib/core.js +0 -10278
  162. package/src/backend/wasm/stdlib/io.js +0 -3578
  163. package/src/backend/wasm/stdlib/packages.js +0 -2402
  164. package/src/backend/wasm/stdlib/pure.js +0 -720
  165. package/src/backend/wasm/stdlib/time.js +0 -2229
  166. package/src/typechecker/stdlib/core.js +0 -1079
  167. package/src/typechecker/stdlib/extended.js +0 -1210
  168. package/stdlib/bytes/iter.go +0 -192
  169. package/stdlib/bytes/search.go +0 -240
  170. package/stdlib/bytes/split.go +0 -218
  171. package/stdlib/errors/overlay.go +0 -12
  172. package/stdlib/maps/iter.go +0 -65
  173. package/stdlib/slices/extra.go +0 -73
  174. package/stdlib/slices/iter.go +0 -113
  175. package/stdlib/strings/extra.go +0 -35
  176. package/stdlib/strings/iter.go +0 -122
  177. package/stdlib/sync/extra.go +0 -105
  178. package/stdlib/unicode/utf8/bytes.go +0 -239
@@ -2,8 +2,7 @@
2
2
  // ModuleEmitter: Module IR builder that registers types, imports, globals and functions.
3
3
 
4
4
  import { wTypeKey } from "./boundary.js";
5
- import { hasErrorUsage, importsAnyOf } from "./prescan.js";
6
- import { BINARY_MATH_IMPORTS } from "./stdlib/index.js";
5
+ import { hasErrorUsage } from "./prescan.js";
7
6
  import {
8
7
  ERROR_STRUCT,
9
8
  getFuncSignature,
@@ -14,215 +13,6 @@ import {
14
13
  toWasmType,
15
14
  } from "./types.js";
16
15
 
17
- const ERROR_PRODUCING_PKGS = new Set([
18
- "errors",
19
- "fmt",
20
- "strconv",
21
- "path",
22
- "path/filepath",
23
- "io",
24
- "os",
25
- "strings",
26
- "bytes",
27
- "time",
28
- "sync",
29
- "crypto/rand",
30
- "rand",
31
- "crypto/sha256",
32
- "sha256",
33
- "encoding/binary",
34
- "binary",
35
- "encoding/hex",
36
- "hex",
37
- "encoding/base64",
38
- "base64",
39
- "math/bits",
40
- "bits",
41
- ]);
42
- // Packages whose calls lower to the `env.*` imports in STDLIB_IMPORTS (the
43
- // matching JS lives in WASM_STDLIB_JS). Only modules importing one of them
44
- // declare the imports and ship that JS block.
45
- const STDLIB_ENV_PKGS = new Set([
46
- "strings",
47
- "strconv",
48
- "unicode/utf8",
49
- "fmt",
50
- "testing",
51
- "unicode",
52
- "math/rand",
53
- "path",
54
- "path/filepath",
55
- "html",
56
- "bytes",
57
- "io",
58
- "os",
59
- "time",
60
- "sync",
61
- "crypto/rand",
62
- "rand",
63
- "crypto/sha256",
64
- "sha256",
65
- "encoding/binary",
66
- "binary",
67
- "encoding/hex",
68
- "hex",
69
- "encoding/base64",
70
- "base64",
71
- "math/bits",
72
- "bits",
73
- ]);
74
-
75
- // [name, params, results] for `env.*` imports backing stdlib calls; the JS
76
- // side lives in WASM_STDLIB_JS (boundary.js). All are registered eagerly
77
- // (when any STDLIB_ENV_PKGS package is imported) because the import table is
78
- // locked before function bodies are emitted.
79
- const S = "externref";
80
- const STDLIB_IMPORTS = [
81
- // strings
82
- ["str_trim", [S, S], [S]],
83
- ["str_trim_left", [S, S], [S]],
84
- ["str_trim_right", [S, S], [S]],
85
- ["str_trim_prefix", [S, S], [S]],
86
- ["str_trim_suffix", [S, S], [S]],
87
- ["str_cut", [S, S], ["i32"]],
88
- ["str_cut_before", [], [S]],
89
- ["str_cut_after", [], [S]],
90
- ["str_cut_prefix", [S, S], ["i32"]],
91
- ["str_cut_suffix", [S, S], ["i32"]],
92
- ["str_replace", [S, S, S, "i32"], [S]],
93
- ["str_contains_rune", [S, "i32"], ["i32"]],
94
- ["str_contains_any", [S, S], ["i32"]],
95
- ["str_index_byte", [S, "i32"], ["i32"]],
96
- ["str_index_rune", [S, "i32"], ["i32"]],
97
- ["str_index_any", [S, S], ["i32"]],
98
- ["str_last_index_byte", [S, "i32"], ["i32"]],
99
- ["str_last_index_any", [S, S], ["i32"]],
100
- ["str_title", [S], [S]],
101
- ["str_split", [S, S], ["i32"]],
102
- ["str_split_n", [S, S, "i32"], ["i32"]],
103
- ["str_split_after", [S, S], ["i32"]],
104
- ["str_split_after_n", [S, S, "i32"], ["i32"]],
105
- ["str_fields", [S], ["i32"]],
106
- ["str_part", ["i32"], [S]],
107
- ["str_replacer_init", [S], []],
108
- ["str_replacer_add", [S, S], []],
109
- ["str_replacer_run", [], [S]],
110
- // strconv
111
- ["strconv_atoi", [S], ["i64"]],
112
- ["strconv_parse_int", [S, "i32", "i32"], ["i64"]],
113
- ["strconv_parse_float", [S], ["f64"]],
114
- ["strconv_parse_bool", [S], ["i32"]],
115
- ["strconv_err", [], [S]],
116
- ["strconv_err_kind", [], ["i32"]],
117
- ["strconv_format_float", ["f64", "i32", "i32", "i32"], [S]],
118
- ["strconv_format_int", ["i64", "i32"], [S]],
119
- ["strconv_format_uint", ["i64", "i32"], [S]],
120
- ["strconv_parse_uint", [S, "i32", "i32"], ["i64"]],
121
- ["strconv_quote", [S], [S]],
122
- ["strconv_unquote", [S], [S]],
123
- // unicode/utf8
124
- ["utf8_rune_count", [S], ["i32"]],
125
- ["utf8_rune_len", ["i32"], ["i32"]],
126
- ["utf8_valid_string", [S], ["i32"]],
127
- ["utf8_decode_rune", [S, "i32"], ["i32"]],
128
- ["utf8_decode_size", [], ["i32"]],
129
- ["utf8_full_rune", [S], ["i32"]],
130
- // fmt (arguments travel through the testing_arg_* buffer)
131
- ["fmt_sprintf", [S], [S]],
132
- ["fmt_printf", [S], []],
133
- ["fmt_sprint", ["i32"], [S]],
134
- ["fmt_print", ["i32"], []],
135
- ["fmt_print_raw", [S], []],
136
- ["fmt_format_v", [S], [S]],
137
- ["fmt_sscan_init", [S, "i32"], ["i32"]],
138
- ["fmt_sscanf_init", [S, S], ["i32"]],
139
- ["fmt_sscan_has_err", [], ["i32"]],
140
- ["fmt_sscan_err_msg", [], [S]],
141
- ["fmt_sscan_token_str", ["i32"], [S]],
142
- ["fmt_sscan_token_i64", ["i32"], ["i64"]],
143
- ["fmt_sscan_token_f64", ["i32"], ["f64"]],
144
- ["fmt_sscan_token_bool", ["i32"], ["i32"]],
145
- // testing.T.Run: the harness calls back into the exported __testing_run_cb
146
- ["testing_run", [S, S, "anyref"], ["i32"]],
147
- ["testing_cleanup", [S, "anyref"], []],
148
- ["testing_n", [S], ["i64"]],
149
- ["testing_elapsed", [S], ["i64"]],
150
- ["testing_context", [S], [S]],
151
- ["testing_output", [S], [S]],
152
- // unicode
153
- ["unicode_is_letter", ["i32"], ["i32"]],
154
- ["unicode_is_digit", ["i32"], ["i32"]],
155
- ["unicode_is_space", ["i32"], ["i32"]],
156
- ["unicode_is_upper", ["i32"], ["i32"]],
157
- ["unicode_is_lower", ["i32"], ["i32"]],
158
- ["unicode_is_punct", ["i32"], ["i32"]],
159
- ["unicode_is_control", ["i32"], ["i32"]],
160
- ["unicode_is_print", ["i32"], ["i32"]],
161
- ["unicode_is_graphic", ["i32"], ["i32"]],
162
- ["unicode_to_upper", ["i32"], ["i32"]],
163
- ["unicode_to_lower", ["i32"], ["i32"]],
164
- // html
165
- ["html_escape", [S], [S]],
166
- ["html_unescape", [S], [S]],
167
- // path
168
- ["path_base", [S], [S]],
169
- ["path_dir", [S], [S]],
170
- ["path_ext", [S], [S]],
171
- ["path_is_abs", [S], ["i32"]],
172
- ["path_clean", [S], [S]],
173
- ["path_split", [S], []],
174
- ["path_split_dir", [], [S]],
175
- ["path_split_file", [], [S]],
176
- ["path_join_init", [], []],
177
- ["path_join_push", [S], []],
178
- ["path_join_run", [], [S]],
179
- ["path_match", [S, S], ["i32"]],
180
- ["path_match_has_err", [], ["i32"]],
181
- ["bytes_trim", [S, S, "i32", "i32"], []],
182
- ["bytes_trim_start", [], ["i32"]],
183
- ["bytes_trim_end", [], ["i32"]],
184
- // time
185
- ["time_now_ms", [], ["f64"]],
186
- ["time_timezone_offset", ["f64"], ["i32"]],
187
- ["time_parse", [S, S], ["f64"]],
188
- ["time_parse_err", [], [S]],
189
- ["time_parse_offset", [], ["i32"]],
190
- ["time_parse_sub_ns", [], ["i32"]],
191
- [
192
- "time_format",
193
- ["i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", S],
194
- [S],
195
- ],
196
- ["time_duration_string", ["i64"], [S]],
197
- ["time_parse_duration", [S], ["i64"]],
198
- ["time_parse_duration_err", [], [S]],
199
- ["time_month_string", ["i32"], [S]],
200
- ["time_weekday_string", ["i32"], [S]],
201
- // raw binary buffer for crypto/rand, crypto/sha256, hex, base64
202
- ["raw_bytes_init", ["i32"], []],
203
- ["raw_bytes_push", ["i32"], []],
204
- ["raw_bytes_len", [], ["i32"]],
205
- ["raw_bytes_get", ["i32"], ["i32"]],
206
- // crypto/rand
207
- ["crypto_rand_fill", ["i32"], ["i32"]],
208
- // crypto/sha256
209
- ["crypto_sha256_sum", [], ["i32"]],
210
- // encoding/hex
211
- ["hex_encode_to_string", [], [S]],
212
- ["hex_decode_string", [S], ["i32"]],
213
- ["hex_get_err", [], [S]],
214
- // encoding/base64
215
- ["base64_encode_to_string", ["i32"], [S]],
216
- ["base64_decode_string", ["i32", S], ["i32"]],
217
- ["base64_get_err", [], [S]],
218
- ["bits_div32", ["i32", "i32", "i32"], ["i32"]],
219
- ["bits_div32_rem", [], ["i32"]],
220
- ["bits_rem32", ["i32", "i32", "i32"], ["i32"]],
221
- ["bits_div64", ["i64", "i64", "i64"], ["i64"]],
222
- ["bits_div64_rem", [], ["i64"]],
223
- ["bits_rem64", ["i64", "i64", "i64"], ["i64"]],
224
- ];
225
-
226
16
  export class ModuleEmitter {
227
17
  constructor(checker, lowerResult = null, programs = [], options = {}) {
228
18
  this.checker = checker;
@@ -230,6 +20,7 @@ export class ModuleEmitter {
230
20
  // Local package names whose code is linked into this module; selectors
231
21
  // like `mathx.NewVec3` are de-qualified to `NewVec3` by the emitter.
232
22
  this.bundledPackages = options.bundledPackages ?? new Set();
23
+ this.natives = options.natives !== false; // replace native calls with host code
233
24
  this.constCache = new Map(); // name -> BasicLit (package-level literal consts)
234
25
  this.nonLiteralConsts = new Set(); // package-level consts the backend cannot evaluate yet
235
26
 
@@ -246,6 +37,7 @@ export class ModuleEmitter {
246
37
  this.arrayTypes = new Map(); // wType key -> array info
247
38
  this.sliceTypes = new Map(); // wType key -> slice info
248
39
  this.sliceTypesByIndex = new Map(); // typeIndex -> slice info
40
+ this.taggedSliceTypes = new Map(); // Go slice type key -> tagged info
249
41
  this.mapTypes = new Map(); // wType key -> map info
250
42
  this.mapTypesByIndex = new Map(); // typeIndex -> map info
251
43
  this.mapBucketsType = null; // shared (array (mut i32))
@@ -275,10 +67,7 @@ export class ModuleEmitter {
275
67
  // `errors.New` / `fmt.Errorf` / strconv parse failures allocate the
276
68
  // runtime's own error struct; it joins the struct rec group so the
277
69
  // `error.Error` dispatcher can `ref.test` it like any user type.
278
- this.hasErrorType =
279
- importsAnyOf(programs, ERROR_PRODUCING_PKGS) || hasErrorUsage(programs);
280
- this.usesStdlibEnv = importsAnyOf(programs, STDLIB_ENV_PKGS);
281
- this.importsTesting = importsAnyOf(programs, ["testing"]);
70
+ this.hasErrorType = hasErrorUsage(programs);
282
71
 
283
72
  if (programs && programs.length > 0) {
284
73
  this.initTypes(programs);
@@ -288,6 +77,26 @@ export class ModuleEmitter {
288
77
  this._initRuntimeImports();
289
78
  }
290
79
 
80
+ // The runtime error struct is not declared in Go source: give the checker a
81
+ // named type with its method set (`Error() string`) so type assertions to
82
+ // `interface{ Error() string }` find it as a candidate.
83
+ _registerRuntimeErrorType() {
84
+ const types = this.checker?.types;
85
+ if (!types || types.has(ERROR_STRUCT)) return;
86
+ const STRING = { kind: "basic", name: "string" };
87
+ types.set(ERROR_STRUCT, {
88
+ kind: "named",
89
+ name: ERROR_STRUCT,
90
+ underlying: {
91
+ kind: "struct",
92
+ fields: new Map(),
93
+ methods: new Map([
94
+ ["Error", { kind: "func", params: [], returns: [STRING] }],
95
+ ]),
96
+ },
97
+ });
98
+ }
99
+
291
100
  initTypes(programs) {
292
101
  const progs = Array.isArray(programs) ? programs : [programs];
293
102
  const rawStructs = [];
@@ -295,15 +104,13 @@ export class ModuleEmitter {
295
104
  const aliases = []; // qualified key (pkg.T) -> unqualified struct name
296
105
 
297
106
  if (this.hasErrorType) {
107
+ this._registerRuntimeErrorType();
298
108
  seen.add(ERROR_STRUCT);
299
109
  rawStructs.push({
300
110
  name: ERROR_STRUCT,
301
111
  structType: {
302
112
  kind: "struct",
303
- fields: new Map([
304
- ["msg", { kind: "basic", name: "string" }],
305
- ["cause", { kind: "basic", name: "any" }],
306
- ]),
113
+ fields: new Map([["msg", { kind: "basic", name: "string" }]]),
307
114
  },
308
115
  });
309
116
  }
@@ -332,11 +139,6 @@ export class ModuleEmitter {
332
139
  for (const [key, t] of this.checker.types.entries()) {
333
140
  if (t.kind === "named" && t.underlying?.kind === "struct") {
334
141
  const name = t.name ?? key;
335
- if (
336
- name.startsWith("testing.") &&
337
- (!this.importsTesting || name !== "testing.BenchmarkResult")
338
- )
339
- continue;
340
142
  if (!seen.has(name)) {
341
143
  seen.add(name);
342
144
  rawStructs.push({ name, structType: t.underlying });
@@ -416,8 +218,16 @@ export class ModuleEmitter {
416
218
  });
417
219
  fieldIndexMap.set(n, fIdx);
418
220
  if (f.embedded) {
221
+ // The embedded type, when it differs from the field
222
+ // name (`Mutex$$sync` for the field `Mutex`).
223
+ const ref =
224
+ f.type?.kind === "PointerType"
225
+ ? f.type.base?.name
226
+ : f.type?.name;
227
+ const typeName = this.checker?.types?.get(ref)?.name;
419
228
  embeds.push({
420
229
  name: n,
230
+ ...(typeName && typeName !== n && { typeName }),
421
231
  fieldIndex: fIdx,
422
232
  });
423
233
  }
@@ -524,9 +334,11 @@ export class ModuleEmitter {
524
334
  const typeEntry = {
525
335
  form: "struct",
526
336
  name: typeName,
337
+ // Mutable so the facade's per-field setters for an exported struct
338
+ // that holds a complex value validate.
527
339
  fields: [
528
- { type: elemWType, mutable: false },
529
- { type: elemWType, mutable: false },
340
+ { type: elemWType, mutable: true },
341
+ { type: elemWType, mutable: true },
530
342
  ],
531
343
  };
532
344
  this._pushType(typeEntry);
@@ -704,6 +516,10 @@ export class ModuleEmitter {
704
516
  const typeIndex = this._getTotalTypeCount();
705
517
  const typeEntry = {
706
518
  form: "struct",
519
+ // Slice types are open: each defined slice type, and each Go type among
520
+ // the 32-bit-or-smaller elements, gets a tagged subtype when it
521
+ // reaches an interface.
522
+ sub: true,
707
523
  fields: [
708
524
  {
709
525
  type: {
@@ -750,6 +566,61 @@ export class ModuleEmitter {
750
566
  return sliceInfo;
751
567
  }
752
568
 
569
+ // The subtype of the slice struct a value of Go slice type `sliceGoType`
570
+ // takes inside an interface. Slices of booleans and small integers share
571
+ // one WebAssembly type, so the extra tag field (a nullable ref to a struct
572
+ // with one immutable i32 field per tag index) is what tells `[]byte` from
573
+ // `[]bool` and `json.RawMessage` from `[]byte` in a type test.
574
+ getTaggedSliceType(sliceGoType, elemGoType) {
575
+ const key = goTypeKey(sliceGoType);
576
+ const known = this.taggedSliceTypes.get(key);
577
+ if (known) return known;
578
+ const base = this.getSliceType(elemGoType);
579
+ const root = this.getTaggedSliceRoot(base);
580
+ const tagIndex = this.taggedSliceTypes.size + 1;
581
+ const tagTypeIndex = this._getTotalTypeCount();
582
+ this._pushType({
583
+ form: "struct",
584
+ fields: Array.from({ length: tagIndex }, () => ({
585
+ type: "i32",
586
+ mutable: false,
587
+ })),
588
+ });
589
+ const typeIndex = this._getTotalTypeCount();
590
+ this._pushType({
591
+ form: "struct",
592
+ sub: true,
593
+ superTypeIndex: root.typeIndex,
594
+ fields: [
595
+ ...base.typeEntry.fields,
596
+ {
597
+ type: { kind: "ref", nullable: true, typeIndex: tagTypeIndex },
598
+ mutable: false,
599
+ },
600
+ ],
601
+ });
602
+ const info = { key, typeIndex, tagTypeIndex, base, goType: sliceGoType };
603
+ this.taggedSliceTypes.set(key, info);
604
+ return info;
605
+ }
606
+
607
+ // The common supertype of every tagged subtype of the slice type `base`. An
608
+ // unnamed `[]int` in an interface is the bare slice type, which every
609
+ // tagged subtype (`type S []int`) also passes `ref.test` for; testing
610
+ // "base and not root" is what keeps `any(S{}).([]int)` from matching.
611
+ getTaggedSliceRoot(base) {
612
+ if (base.tagRoot) return base.tagRoot;
613
+ const typeIndex = this._getTotalTypeCount();
614
+ this._pushType({
615
+ form: "struct",
616
+ sub: true,
617
+ superTypeIndex: base.typeIndex,
618
+ fields: [...base.typeEntry.fields],
619
+ });
620
+ base.tagRoot = { typeIndex };
621
+ return base.tagRoot;
622
+ }
623
+
753
624
  getSliceTypeByIndex(typeIndex) {
754
625
  return this.sliceTypesByIndex?.get(typeIndex) ?? null;
755
626
  }
@@ -848,17 +719,12 @@ export class ModuleEmitter {
848
719
  clearFuncName: `__map_clear_${suffix}`,
849
720
  keysFuncName: `__map_keys_${suffix}`,
850
721
  valuesFuncName: `__map_values_${suffix}`,
851
- cloneFuncName: `__map_clone_${suffix}`,
852
722
  };
853
723
  this.mapTypes.set(key, mapInfo);
854
724
  this.mapTypesByIndex.set(mapTypeIndex, mapInfo);
855
725
  return mapInfo;
856
726
  }
857
727
 
858
- getMapTypeByIndex(typeIndex) {
859
- return this.mapTypesByIndex?.get(typeIndex) ?? null;
860
- }
861
-
862
728
  getClosureType(goType) {
863
729
  const sig = getFuncSignature(goType, this.checker);
864
730
  const paramWTypes = sig.params
@@ -971,19 +837,6 @@ export class ModuleEmitter {
971
837
  this.getStringFromCodePointImportIndex(); // env.str_from_code_point
972
838
  this.getStringCodePointAtImportIndex(); // env.str_code_point_at
973
839
  this.getStringHashImportIndex(); // env.str_hash
974
- this.getStringToUpperImportIndex();
975
- this.getStringToLowerImportIndex();
976
- this.getStringTrimSpaceImportIndex();
977
- this.getStringContainsImportIndex();
978
- this.getStringHasPrefixImportIndex();
979
- this.getStringHasSuffixImportIndex();
980
- this.getStringIndexImportIndex();
981
- this.getStringLastIndexImportIndex();
982
- this.getStringRepeatImportIndex();
983
- this.getStringReplaceAllImportIndex();
984
- this.getStringEqualFoldImportIndex();
985
- this.getStringCountImportIndex();
986
- this.getStringToValidUTF8ImportIndex();
987
840
  this.getStrFromI64ImportIndex();
988
841
  this.getStrFromI32ImportIndex();
989
842
  this.getStrFromF64ImportIndex();
@@ -993,35 +846,6 @@ export class ModuleEmitter {
993
846
  this.getBytesToStrInitImportIndex();
994
847
  this.getBytesToStrPushImportIndex();
995
848
  this.getBytesToStrFinishImportIndex();
996
- this.getTestingCallImportIndex();
997
- this.getTestingNameImportIndex();
998
- this.getTestingFlagImportIndex();
999
- for (const t of ["i32", "i64", "f32", "f64", "externref", "anyref"]) {
1000
- this.getTestingArgImportIndex(t, false);
1001
- }
1002
- this.getTestingArgImportIndex("i32", true);
1003
- this.getTestingArgErrImportIndex();
1004
- this.getTestingArgSpreadImportIndex();
1005
- this.getTestingArgU64ImportIndex();
1006
- this.getFmtArgTypeImportIndex();
1007
- this.getFmtStructTypeImportIndex();
1008
- this.getFmtNewArrayIndex();
1009
- this.getFmtArrayPushIndex();
1010
- this.getFmtNewMapIndex();
1011
- this.getFmtMapSetIndex();
1012
- this.getFmtNewStructIndex();
1013
- this.getFmtStructSetIndex();
1014
- this.getFmtBoxI32Index();
1015
- this.getFmtBoxI64Index();
1016
- this.getFmtBoxF64Index();
1017
- this.getFmtBoxBoolIndex();
1018
- this.getFmtNewBoxIndex();
1019
- this.getFmtNewStringerIndex();
1020
- if (this.usesStdlibEnv) {
1021
- for (const [name, params, results] of STDLIB_IMPORTS) {
1022
- this.getOrAddFuncImport("env", name, params, results);
1023
- }
1024
- }
1025
849
 
1026
850
  // Logging built-ins
1027
851
  this.getPrintlnEmptyIndex(); // env.println_empty
@@ -1032,30 +856,6 @@ export class ModuleEmitter {
1032
856
  }
1033
857
  this.getLogImportIndex("i32", false, true); // env.print_bool
1034
858
  this.getLogImportIndex("i32", true, true); // env.println_bool
1035
-
1036
- // Math built-ins
1037
- const unaryMath = [
1038
- "sin",
1039
- "cos",
1040
- "tan",
1041
- "asin",
1042
- "acos",
1043
- "atan",
1044
- "exp",
1045
- "log",
1046
- "log2",
1047
- "log10",
1048
- "round",
1049
- "cbrt",
1050
- "log1p",
1051
- "expm1",
1052
- ];
1053
- for (const m of unaryMath) {
1054
- this.getMathImportIndex(m, false);
1055
- }
1056
- for (const m of BINARY_MATH_IMPORTS) {
1057
- this.getMathImportIndex(m, true);
1058
- }
1059
859
  }
1060
860
 
1061
861
  _initPanicTag() {
@@ -1137,93 +937,6 @@ export class ModuleEmitter {
1137
937
  return gIdx;
1138
938
  }
1139
939
 
1140
- getRandStateGlobalIndex() {
1141
- if (this.randStateGlobalIndex !== undefined) {
1142
- return this.randStateGlobalIndex;
1143
- }
1144
- const gIdx = this.globals.length;
1145
- this.globals.push({
1146
- type: "i64",
1147
- mutable: true,
1148
- init: [{ op: "i64.const", value: 1n }],
1149
- });
1150
- this.randStateGlobalIndex = gIdx;
1151
- return gIdx;
1152
- }
1153
-
1154
- getStrconvErrGlobalIndex(field) {
1155
- if (!this.strconvErrGlobals) this.strconvErrGlobals = new Map();
1156
- if (this.strconvErrGlobals.has(field)) {
1157
- return this.strconvErrGlobals.get(field);
1158
- }
1159
- const gIdx = this.globals.length;
1160
- this.globals.push({
1161
- type: "anyref",
1162
- mutable: true,
1163
- init: [{ op: "ref.null", heapType: "any" }],
1164
- });
1165
- this.strconvErrGlobals.set(field, gIdx);
1166
- return gIdx;
1167
- }
1168
-
1169
- getPathErrGlobalIndex(field) {
1170
- if (!this.pathErrGlobals) this.pathErrGlobals = new Map();
1171
- if (this.pathErrGlobals.has(field)) {
1172
- return this.pathErrGlobals.get(field);
1173
- }
1174
- const gIdx = this.globals.length;
1175
- this.globals.push({
1176
- type: "anyref",
1177
- mutable: true,
1178
- init: [{ op: "ref.null", heapType: "any" }],
1179
- });
1180
- this.pathErrGlobals.set(field, gIdx);
1181
- return gIdx;
1182
- }
1183
-
1184
- getIoErrGlobalIndex(field) {
1185
- if (!this.ioErrGlobals) this.ioErrGlobals = new Map();
1186
- if (this.ioErrGlobals.has(field)) {
1187
- return this.ioErrGlobals.get(field);
1188
- }
1189
- const gIdx = this.globals.length;
1190
- this.globals.push({
1191
- type: "anyref",
1192
- mutable: true,
1193
- init: [{ op: "ref.null", heapType: "any" }],
1194
- });
1195
- this.ioErrGlobals.set(field, gIdx);
1196
- return gIdx;
1197
- }
1198
-
1199
- getIoDiscardGlobalIndex() {
1200
- if (this.ioDiscardGlobalIndex !== undefined) {
1201
- return this.ioDiscardGlobalIndex;
1202
- }
1203
- this.getStructType("io.discard");
1204
- const gIdx = this.globals.length;
1205
- this.globals.push({
1206
- type: "anyref",
1207
- mutable: true,
1208
- init: [{ op: "ref.null", heapType: "any" }],
1209
- });
1210
- this.ioDiscardGlobalIndex = gIdx;
1211
- return gIdx;
1212
- }
1213
-
1214
- getOsGlobalIndex(field) {
1215
- this.osGlobals ??= new Map();
1216
- if (this.osGlobals.has(field)) return this.osGlobals.get(field);
1217
- const gIdx = this.globals.length;
1218
- this.globals.push({
1219
- type: "anyref",
1220
- mutable: true,
1221
- init: [{ op: "ref.null", heapType: "any" }],
1222
- });
1223
- this.osGlobals.set(field, gIdx);
1224
- return gIdx;
1225
- }
1226
-
1227
940
  getDeferNodeTypeIndex() {
1228
941
  if (this.deferNodeTypeIndex !== undefined) {
1229
942
  return this.deferNodeTypeIndex;
@@ -1361,190 +1074,8 @@ export class ModuleEmitter {
1361
1074
  return this.getOrAddFuncImport("env", "println_empty", [], []);
1362
1075
  }
1363
1076
 
1364
- getFmtNewArrayIndex() {
1365
- return this.getOrAddFuncImport("env", "fmt_new_array", [], ["externref"]);
1366
- }
1367
-
1368
- getFmtArrayPushIndex() {
1369
- return this.getOrAddFuncImport(
1370
- "env",
1371
- "fmt_array_push",
1372
- ["externref", "externref"],
1373
- [],
1374
- );
1375
- }
1376
-
1377
- getFmtNewMapIndex() {
1378
- return this.getOrAddFuncImport("env", "fmt_new_map", [], ["externref"]);
1379
- }
1380
-
1381
- getFmtMapSetIndex() {
1382
- return this.getOrAddFuncImport(
1383
- "env",
1384
- "fmt_map_set",
1385
- ["externref", "externref", "externref"],
1386
- [],
1387
- );
1388
- }
1389
-
1390
- getFmtNewStringerIndex() {
1391
- return this.getOrAddFuncImport(
1392
- "env",
1393
- "fmt_new_stringer",
1394
- ["externref", "externref", "i32"],
1395
- ["externref"],
1396
- );
1397
- }
1398
-
1399
- getFmtNewStructIndex() {
1400
- return this.getOrAddFuncImport(
1401
- "env",
1402
- "fmt_new_struct",
1403
- ["anyref", "i32"],
1404
- ["externref"],
1405
- );
1406
- }
1407
-
1408
- // Names the Go type of a struct built for fmt (`%T`, `%#v` of an any).
1409
- getFmtStructTypeImportIndex() {
1410
- return this.getOrAddFuncImport(
1411
- "env",
1412
- "fmt_struct_type",
1413
- ["externref", "i32"],
1414
- [],
1415
- );
1416
- }
1417
-
1418
- getFmtStructSetIndex() {
1419
- return this.getOrAddFuncImport(
1420
- "env",
1421
- "fmt_struct_set",
1422
- ["externref", "i32", "externref"],
1423
- [],
1424
- );
1425
- }
1426
-
1427
- getFmtBoxI32Index() {
1428
- return this.getOrAddFuncImport(
1429
- "env",
1430
- "fmt_box_i32",
1431
- ["i32"],
1432
- ["externref"],
1433
- );
1434
- }
1435
-
1436
- getFmtBoxI64Index() {
1437
- return this.getOrAddFuncImport(
1438
- "env",
1439
- "fmt_box_i64",
1440
- ["i64"],
1441
- ["externref"],
1442
- );
1443
- }
1444
-
1445
- // Registered only for programs whose fmt operands may hold a uint64
1446
- // (see scanFmtU64), so other modules keep their import table.
1447
- getFmtBoxU64Index() {
1448
- return this.getOrAddFuncImport(
1449
- "env",
1450
- "fmt_box_u64",
1451
- ["i64"],
1452
- ["externref"],
1453
- );
1454
- }
1455
-
1456
- getFmtBoxF64Index() {
1457
- return this.getOrAddFuncImport(
1458
- "env",
1459
- "fmt_box_f64",
1460
- ["f64"],
1461
- ["externref"],
1462
- );
1463
- }
1464
-
1465
- getFmtBoxBoolIndex() {
1466
- return this.getOrAddFuncImport(
1467
- "env",
1468
- "fmt_box_bool",
1469
- ["i32"],
1470
- ["externref"],
1471
- );
1472
- }
1473
-
1474
- getFmtNewBoxIndex() {
1475
- return this.getOrAddFuncImport("env", "fmt_new_box", [], ["externref"]);
1476
- }
1477
-
1478
- // `*testing.T` method calls: args are pushed one by one to a JS-side
1479
- // buffer, then `testing_call(t, methodNameStrIdx)` invokes the method.
1480
- getTestingArgImportIndex(wType, isBool = false) {
1481
- if (isBool)
1482
- return this.getOrAddFuncImport("env", "testing_arg_bool", ["i32"], []);
1483
- const suffix =
1484
- wType === "externref" ? "str" : wType === "anyref" ? "any" : wType;
1485
- if (!["i32", "i64", "f32", "f64", "str", "any"].includes(suffix)) {
1486
- throw new Error(
1487
- "only primitive, string, error and any arguments are supported for fmt and testing.T calls in wasm packages (format slices/structs field by field)",
1488
- );
1489
- }
1490
- return this.getOrAddFuncImport(
1491
- "env",
1492
- `testing_arg_${suffix}`,
1493
- [suffix === "str" ? "externref" : suffix === "any" ? "anyref" : wType],
1494
- [],
1495
- );
1496
- }
1497
-
1498
- // Error operands of fmt.Sprint/Print/Append: a non-string for the host.
1499
- getTestingArgSpreadImportIndex() {
1500
- return this.getOrAddFuncImport(
1501
- "env",
1502
- "testing_arg_spread",
1503
- ["externref"],
1504
- [],
1505
- );
1506
- }
1507
-
1508
- // An unsigned 64-bit operand: the host reinterprets the i64 as unsigned.
1509
- getTestingArgU64ImportIndex() {
1510
- return this.getOrAddFuncImport("env", "testing_arg_u64", ["i64"], []);
1511
- }
1512
-
1513
- // Tags the last operand in the argument buffer with the static type
1514
- // descriptor (JSON) held in string table entry `i32`, for `%T` and `%#v`.
1515
- getFmtArgTypeImportIndex() {
1516
- return this.getOrAddFuncImport("env", "fmt_arg_type", ["i32"], []);
1517
- }
1518
-
1519
- getTestingArgErrImportIndex() {
1520
- return this.getOrAddFuncImport("env", "testing_arg_err", ["externref"], []);
1521
- }
1522
-
1523
- getTestingCallImportIndex() {
1524
- return this.getOrAddFuncImport(
1525
- "env",
1526
- "testing_call",
1527
- ["externref", "i32"],
1528
- [],
1529
- );
1530
- }
1531
-
1532
- getTestingNameImportIndex() {
1533
- return this.getOrAddFuncImport(
1534
- "env",
1535
- "testing_name",
1536
- ["externref"],
1537
- ["externref"],
1538
- );
1539
- }
1540
-
1541
- getTestingFlagImportIndex() {
1542
- return this.getOrAddFuncImport(
1543
- "env",
1544
- "testing_flag",
1545
- ["externref", "i32"],
1546
- ["i32"],
1547
- );
1077
+ getPrintResetIndex() {
1078
+ return this.getOrAddFuncImport("env", "print_reset", [], []);
1548
1079
  }
1549
1080
 
1550
1081
  getLogImportIndex(wType, isLast = false, isBool = false) {
@@ -1557,6 +1088,8 @@ export class ModuleEmitter {
1557
1088
  return this.getOrAddFuncImport("env", `${prefix}_i32`, ["i32"], []);
1558
1089
  case "i64":
1559
1090
  return this.getOrAddFuncImport("env", `${prefix}_i64`, ["i64"], []);
1091
+ case "u64":
1092
+ return this.getOrAddFuncImport("env", `${prefix}_u64`, ["i64"], []);
1560
1093
  case "f32":
1561
1094
  return this.getOrAddFuncImport("env", `${prefix}_f32`, ["f32"], []);
1562
1095
  case "f64":
@@ -1656,123 +1189,6 @@ export class ModuleEmitter {
1656
1189
  return this.getOrAddFuncImport("env", "str_hash", ["externref"], ["i32"]);
1657
1190
  }
1658
1191
 
1659
- getStringToUpperImportIndex() {
1660
- return this.getOrAddFuncImport(
1661
- "env",
1662
- "str_to_upper",
1663
- ["externref"],
1664
- ["externref"],
1665
- );
1666
- }
1667
-
1668
- getStringToLowerImportIndex() {
1669
- return this.getOrAddFuncImport(
1670
- "env",
1671
- "str_to_lower",
1672
- ["externref"],
1673
- ["externref"],
1674
- );
1675
- }
1676
-
1677
- getStringTrimSpaceImportIndex() {
1678
- return this.getOrAddFuncImport(
1679
- "env",
1680
- "str_trim_space",
1681
- ["externref"],
1682
- ["externref"],
1683
- );
1684
- }
1685
-
1686
- getStringContainsImportIndex() {
1687
- return this.getOrAddFuncImport(
1688
- "env",
1689
- "str_contains",
1690
- ["externref", "externref"],
1691
- ["i32"],
1692
- );
1693
- }
1694
-
1695
- getStringHasPrefixImportIndex() {
1696
- return this.getOrAddFuncImport(
1697
- "env",
1698
- "str_has_prefix",
1699
- ["externref", "externref"],
1700
- ["i32"],
1701
- );
1702
- }
1703
-
1704
- getStringHasSuffixImportIndex() {
1705
- return this.getOrAddFuncImport(
1706
- "env",
1707
- "str_has_suffix",
1708
- ["externref", "externref"],
1709
- ["i32"],
1710
- );
1711
- }
1712
-
1713
- getStringIndexImportIndex() {
1714
- return this.getOrAddFuncImport(
1715
- "env",
1716
- "str_index",
1717
- ["externref", "externref"],
1718
- ["i32"],
1719
- );
1720
- }
1721
-
1722
- getStringLastIndexImportIndex() {
1723
- return this.getOrAddFuncImport(
1724
- "env",
1725
- "str_last_index",
1726
- ["externref", "externref"],
1727
- ["i32"],
1728
- );
1729
- }
1730
-
1731
- getStringRepeatImportIndex() {
1732
- return this.getOrAddFuncImport(
1733
- "env",
1734
- "str_repeat",
1735
- ["externref", "i32"],
1736
- ["externref"],
1737
- );
1738
- }
1739
-
1740
- getStringReplaceAllImportIndex() {
1741
- return this.getOrAddFuncImport(
1742
- "env",
1743
- "str_replace_all",
1744
- ["externref", "externref", "externref"],
1745
- ["externref"],
1746
- );
1747
- }
1748
-
1749
- getStringEqualFoldImportIndex() {
1750
- return this.getOrAddFuncImport(
1751
- "env",
1752
- "str_equal_fold",
1753
- ["externref", "externref"],
1754
- ["i32"],
1755
- );
1756
- }
1757
-
1758
- getStringCountImportIndex() {
1759
- return this.getOrAddFuncImport(
1760
- "env",
1761
- "str_count",
1762
- ["externref", "externref"],
1763
- ["i32"],
1764
- );
1765
- }
1766
-
1767
- getStringToValidUTF8ImportIndex() {
1768
- return this.getOrAddFuncImport(
1769
- "env",
1770
- "str_to_valid_utf8",
1771
- ["externref", "externref"],
1772
- ["externref"],
1773
- );
1774
- }
1775
-
1776
1192
  getStrFromI64ImportIndex() {
1777
1193
  return this.getOrAddFuncImport(
1778
1194
  "env",
@@ -1839,35 +1255,6 @@ export class ModuleEmitter {
1839
1255
  return this.getOrAddFuncImport("Math", "random", [], ["f64"]);
1840
1256
  }
1841
1257
 
1842
- // Imports from the STDLIB_IMPORTS table (strings/strconv/utf8/fmt/testing.Run).
1843
- getStdlibImportIndex(name) {
1844
- const idx = this.importCache.get(`env.${name}`);
1845
- if (idx === undefined) throw new Error(`Unknown runtime import: ${name}`);
1846
- return idx;
1847
- }
1848
-
1849
- getTimezoneOffsetImportIndex() {
1850
- return this.getStdlibImportIndex("time_timezone_offset");
1851
- }
1852
-
1853
- // Closure type of `func(*testing.T)` (the t.Run body).
1854
- getTestingRunClosureType() {
1855
- return this.getClosureType({
1856
- kind: "func",
1857
- params: [{ kind: "pointer", base: { kind: "named", name: "testing.T" } }],
1858
- returns: [],
1859
- });
1860
- }
1861
-
1862
- // Closure type of `func()` (the t.Cleanup body).
1863
- getTestingCleanupClosureType() {
1864
- return this.getClosureType({
1865
- kind: "func",
1866
- params: [],
1867
- returns: [],
1868
- });
1869
- }
1870
-
1871
1258
  resolveFuncIndex(name) {
1872
1259
  return this.funcMap.get(name) ?? null;
1873
1260
  }
@@ -1961,60 +1348,60 @@ export class ModuleEmitter {
1961
1348
  }
1962
1349
 
1963
1350
  // Promoted methods on sName
1964
- if (sInfo.embeds && sInfo.embeds.length > 0) {
1965
- for (const embed of sInfo.embeds) {
1966
- const embedMethodName = `${embed.name}.${methodName}`;
1967
- if (this.funcMap.has(embedMethodName)) {
1968
- const fIdx = this.resolveFuncIndex(embedMethodName);
1969
- const paramGoTypes = this.getFuncParamGoTypes(embedMethodName);
1970
- const paramWTypes = this.getFuncParamTypes(embedMethodName);
1971
- const isPtrRecv = paramGoTypes[0]?.kind === "pointer";
1972
- const embedSInfo = this.getStructType(embed.name);
1973
-
1974
- const ptrKey = `ptr:${sName}`;
1975
- if (!seenKeys.has(ptrKey)) {
1976
- seenKeys.add(ptrKey);
1977
- candidates.push({
1978
- recvTypeName: sName,
1979
- testTypeIndex: sInfo.typeIndex,
1980
- isBoxedValue: false,
1981
- needsValueDeref: !isPtrRecv,
1982
- embedPath: [
1983
- {
1984
- parentTypeIndex: sInfo.typeIndex,
1985
- fieldIndex: embed.fieldIndex,
1986
- },
1987
- ],
1988
- funcIndex: fIdx,
1989
- targetParamTypes: paramWTypes,
1990
- targetRecvWType: paramWTypes[0],
1991
- structInfo: embedSInfo,
1992
- });
1993
- }
1351
+ const promoted = this._findPromotedMethod(sInfo, methodName);
1352
+ if (promoted?.iface) {
1353
+ this._addIfaceEmbedCandidates(
1354
+ candidates,
1355
+ seenKeys,
1356
+ sName,
1357
+ sInfo,
1358
+ promoted.embedPath,
1359
+ );
1360
+ } else if (promoted) {
1361
+ const { embedType, embedPath, viaPointer } = promoted;
1362
+ const embedMethodName = `${embedType}.${methodName}`;
1363
+ const fIdx = this.resolveFuncIndex(embedMethodName);
1364
+ const paramGoTypes = this.getFuncParamGoTypes(embedMethodName);
1365
+ const paramWTypes = this.getFuncParamTypes(embedMethodName);
1366
+ const isPtrRecv = paramGoTypes[0]?.kind === "pointer";
1367
+ const embedSInfo = this.getStructType(embedType);
1368
+
1369
+ const ptrKey = `ptr:${sName}`;
1370
+ if (!seenKeys.has(ptrKey)) {
1371
+ seenKeys.add(ptrKey);
1372
+ candidates.push({
1373
+ recvTypeName: sName,
1374
+ testTypeIndex: sInfo.typeIndex,
1375
+ isBoxedValue: false,
1376
+ needsValueDeref: !isPtrRecv,
1377
+ embedPath,
1378
+ nilCheckLast: !isPtrRecv,
1379
+ funcIndex: fIdx,
1380
+ targetParamTypes: paramWTypes,
1381
+ targetRecvWType: paramWTypes[0],
1382
+ structInfo: embedSInfo,
1383
+ });
1384
+ }
1994
1385
 
1995
- if (!isPtrRecv) {
1996
- const box = this.getIfaceBoxType({ kind: "named", name: sName });
1997
- const valKey = `val:${sName}`;
1998
- if (box && !seenKeys.has(valKey)) {
1999
- seenKeys.add(valKey);
2000
- candidates.push({
2001
- recvTypeName: sName,
2002
- testTypeIndex: box.typeIndex,
2003
- isBoxedValue: true,
2004
- needsValueDeref: false,
2005
- embedPath: [
2006
- {
2007
- parentTypeIndex: sInfo.typeIndex,
2008
- fieldIndex: embed.fieldIndex,
2009
- },
2010
- ],
2011
- funcIndex: fIdx,
2012
- targetParamTypes: paramWTypes,
2013
- targetRecvWType: paramWTypes[0],
2014
- structInfo: embedSInfo,
2015
- });
2016
- }
2017
- }
1386
+ // A pointer-receiver method is in the value's method set only
1387
+ // when it is reached through an embedded pointer.
1388
+ if (!isPtrRecv || viaPointer) {
1389
+ const box = this.getIfaceBoxType({ kind: "named", name: sName });
1390
+ const valKey = `val:${sName}`;
1391
+ if (box && !seenKeys.has(valKey)) {
1392
+ seenKeys.add(valKey);
1393
+ candidates.push({
1394
+ recvTypeName: sName,
1395
+ testTypeIndex: box.typeIndex,
1396
+ isBoxedValue: true,
1397
+ needsValueDeref: false,
1398
+ embedPath,
1399
+ nilCheckLast: !isPtrRecv,
1400
+ funcIndex: fIdx,
1401
+ targetParamTypes: paramWTypes,
1402
+ targetRecvWType: paramWTypes[0],
1403
+ structInfo: embedSInfo,
1404
+ });
2018
1405
  }
2019
1406
  }
2020
1407
  }
@@ -2125,6 +1512,116 @@ export class ModuleEmitter {
2125
1512
  return candidates;
2126
1513
  }
2127
1514
 
1515
+ // Whether `goType`, the type of an embedded field, is an interface whose
1516
+ // method set has `methodName`.
1517
+ embeddedInterfaceHas(goType, methodName) {
1518
+ let t = goType;
1519
+ for (let i = 0; i < 8 && t; i++) {
1520
+ if (t.kind === "TypeName" || t.kind === "Ident") {
1521
+ const named = this.checker?.types?.get(t.name);
1522
+ if (!named) return t.name === "error" && methodName === "Error";
1523
+ t = named;
1524
+ } else if (t.kind === "named") {
1525
+ t = t.underlying ?? this.checker?.types?.get(t.name)?.underlying;
1526
+ } else if (t.kind === "interface") {
1527
+ return t.methods?.has?.(methodName) ?? false;
1528
+ } else if (t.kind === "InterfaceType") {
1529
+ return (t.methods ?? []).some((m) => (m.name ?? m) === methodName);
1530
+ } else {
1531
+ return false;
1532
+ }
1533
+ }
1534
+ return false;
1535
+ }
1536
+
1537
+ // Dispatch candidates for a method `sName` promotes from an embedded
1538
+ // interface: the embedded value is loaded and dispatched again through the
1539
+ // same dispatcher (`selfDispatch`).
1540
+ _addIfaceEmbedCandidates(candidates, seenKeys, sName, sInfo, embedPath) {
1541
+ const base = {
1542
+ recvTypeName: sName,
1543
+ needsValueDeref: false,
1544
+ embedPath,
1545
+ selfDispatch: true,
1546
+ structInfo: sInfo,
1547
+ };
1548
+ const ptrKey = `ptr:${sName}`;
1549
+ if (!seenKeys.has(ptrKey)) {
1550
+ seenKeys.add(ptrKey);
1551
+ candidates.push({
1552
+ ...base,
1553
+ testTypeIndex: sInfo.typeIndex,
1554
+ isBoxedValue: false,
1555
+ });
1556
+ }
1557
+ const box = this.getIfaceBoxType({ kind: "named", name: sName });
1558
+ const valKey = `val:${sName}`;
1559
+ if (box && !seenKeys.has(valKey)) {
1560
+ seenKeys.add(valKey);
1561
+ candidates.push({
1562
+ ...base,
1563
+ testTypeIndex: box.typeIndex,
1564
+ isBoxedValue: true,
1565
+ });
1566
+ }
1567
+ }
1568
+
1569
+ // The shallowest embedded type of `sInfo` (any depth) that declares
1570
+ // `methodName`: its name, the field path to reach it from an `sInfo`
1571
+ // struct, and whether that path goes through an embedded pointer. Null
1572
+ // when two embeds at that depth declare it (ambiguous, as in Go).
1573
+ _findPromotedMethod(sInfo, methodName) {
1574
+ let level = [{ info: sInfo, path: [], viaPointer: false }];
1575
+ const seen = new Set([sInfo]);
1576
+ while (level.length > 0) {
1577
+ const next = [];
1578
+ const hits = [];
1579
+ for (const { info, path, viaPointer } of level) {
1580
+ for (const embed of info.embeds ?? []) {
1581
+ const embedType = embed.typeName ?? embed.name;
1582
+ const goType = info.fields?.[embed.fieldIndex]?.goType;
1583
+ const isPtr =
1584
+ goType?.kind === "PointerType" || goType?.kind === "pointer";
1585
+ const step = {
1586
+ path: [
1587
+ ...path,
1588
+ {
1589
+ parentTypeIndex: info.typeIndex,
1590
+ fieldIndex: embed.fieldIndex,
1591
+ // An embedded pointer may be nil: reading through it
1592
+ // is a recoverable nil dereference.
1593
+ ...(isPtr && {
1594
+ ptrWType: info.fields[embed.fieldIndex].wType,
1595
+ }),
1596
+ },
1597
+ ],
1598
+ viaPointer: viaPointer || isPtr,
1599
+ };
1600
+ if (this.embeddedInterfaceHas(goType, methodName)) {
1601
+ hits.push({ iface: true, embedPath: step.path, viaPointer: false });
1602
+ continue;
1603
+ }
1604
+ if (this.funcMap.has(`${embedType}.${methodName}`)) {
1605
+ hits.push({
1606
+ embedType,
1607
+ embedPath: step.path,
1608
+ viaPointer: step.viaPointer,
1609
+ });
1610
+ continue;
1611
+ }
1612
+ // A type reached twice at one depth stays twice in the next
1613
+ // level, so a diamond's method is ambiguous there too.
1614
+ const inner = this.structTypes.get(embedType);
1615
+ if (inner && !seen.has(inner)) next.push({ info: inner, ...step });
1616
+ }
1617
+ }
1618
+ if (hits.length > 0) return hits.length === 1 ? hits[0] : null;
1619
+ for (const n of next) seen.add(n.info);
1620
+ level = next;
1621
+ }
1622
+ return null;
1623
+ }
1624
+
2128
1625
  // A value-receiver method of a named scalar is also in the method set of
2129
1626
  // the pointer: `var g Getter = &x` holds a pointer box whose target is read
2130
1627
  // before the call.
@@ -2180,12 +1677,17 @@ export class ModuleEmitter {
2180
1677
  if (this.checker?.implements?.(ptrType, ifaceResolved)) {
2181
1678
  if (!seen.has(sInfo.typeIndex)) {
2182
1679
  seen.add(sInfo.typeIndex);
2183
- candidates.push({ name: `*${sName}`, typeIndex: sInfo.typeIndex });
1680
+ // A typed nil `*S` is a marker object, which also satisfies it.
1681
+ candidates.push({
1682
+ name: `*${sName}`,
1683
+ typeIndex: sInfo.typeIndex,
1684
+ nilTypeIndex: this.getNilBoxType(ptrType).typeIndex,
1685
+ });
2184
1686
  }
2185
1687
  }
2186
1688
 
2187
1689
  // Check struct value (boxed)
2188
- if (this.checker?.implements?.(namedType, ifaceResolved)) {
1690
+ if (this._valueImplements(namedType, ifaceResolved)) {
2189
1691
  const box = this.getIfaceBoxType(namedType);
2190
1692
  if (box && !seen.has(box.typeIndex)) {
2191
1693
  seen.add(box.typeIndex);
@@ -2203,7 +1705,7 @@ export class ModuleEmitter {
2203
1705
  tVal.underlying?.kind !== "interface"
2204
1706
  ) {
2205
1707
  if (this.checker?.implements?.(tVal, ifaceResolved)) {
2206
- const box = this.getIfaceBoxType(tVal);
1708
+ const box = this._namedValueBox(tVal);
2207
1709
  if (box && !seen.has(box.typeIndex)) {
2208
1710
  seen.add(box.typeIndex);
2209
1711
  candidates.push({ name: tName, typeIndex: box.typeIndex });
@@ -2223,6 +1725,33 @@ export class ModuleEmitter {
2223
1725
  return candidates;
2224
1726
  }
2225
1727
 
1728
+ // The WebAssembly type a value of the named non-struct type `tVal` takes
1729
+ // inside an interface: a defined slice type is a tagged slice (see
1730
+ // getTaggedSliceType), anything else an interface box.
1731
+ _namedValueBox(tVal) {
1732
+ if (!isSliceType(tVal, this.checker)) return this.getIfaceBoxType(tVal);
1733
+ const elem = getSliceElemType(tVal, this.checker);
1734
+ return elem ? this.getTaggedSliceType(tVal, elem) : null;
1735
+ }
1736
+
1737
+ // Whether the struct value `namedType` implements `iface`, including when its
1738
+ // pointer-receiver methods are promoted through an embedded pointer, which
1739
+ // puts them in the value's method set (`struct{ *Inner }`).
1740
+ _valueImplements(namedType, iface) {
1741
+ const checker = this.checker;
1742
+ if (checker?.implements?.(namedType, iface)) return true;
1743
+ const ptr = { kind: "pointer", base: namedType };
1744
+ if (!checker?.implements?.(ptr, iface)) return false;
1745
+ const st = checker.resolveType(namedType.underlying);
1746
+ const methods = st?.methods?.size ? st.methods : namedType.methods;
1747
+ const names = [...(iface.methods?.keys() ?? [])];
1748
+ return names.every(
1749
+ (n) =>
1750
+ !methods?.get(n)?._ptrRecv ||
1751
+ checker._promotedThroughPointer(namedType, n),
1752
+ );
1753
+ }
1754
+
2226
1755
  // `*T` of a named scalar or slice is boxed apart from `T`.
2227
1756
  _addPtrNamedImplementor(tVal, tName, ifaceResolved, seen, candidates) {
2228
1757
  const ptrType = { kind: "pointer", base: tVal };