gofront 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -13
- package/package.json +8 -37
- package/src/backend/js/expressions.js +1341 -148
- package/src/backend/js/fmt-runtime.js +573 -0
- package/src/backend/js/index.js +375 -57
- package/src/backend/js/runtime.js +1460 -133
- package/src/backend/js/statements.js +900 -86
- package/src/backend/js/stdlib/bits.js +87 -14
- package/src/backend/js/stdlib/builder.js +41 -6
- package/src/backend/js/stdlib/bytes.js +253 -7
- package/src/backend/js/stdlib/errors.js +2 -1
- package/src/backend/js/stdlib/fmt.js +349 -10
- package/src/backend/js/stdlib/index.js +8 -3
- package/src/backend/js/stdlib/json.js +247 -52
- package/src/backend/js/stdlib/maps.js +0 -8
- package/src/backend/js/stdlib/math.js +17 -1
- package/src/backend/js/stdlib/simd.js +10 -0
- package/src/backend/js/stdlib/slices.js +36 -6
- package/src/backend/js/stdlib/strconv.js +10 -5
- package/src/backend/js/stdlib/strings.js +83 -4
- package/src/backend/js/stdlib/testing.js +7 -0
- package/src/backend/js/stdlib/time.js +79 -55
- package/src/backend/js/stdlib/unicode.js +1 -1
- package/src/backend/js/stdlib/utf8.js +1 -1
- package/src/backend/js/templ.js +38 -76
- package/src/backend/js/type-info.js +272 -0
- package/src/backend/shared/fmt-types.js +188 -0
- package/src/backend/wasm/boundary.js +436 -80
- package/src/backend/wasm/emit-builtins.js +303 -13
- package/src/backend/wasm/emit-exprs.js +1397 -203
- package/src/backend/wasm/emit-maps.js +61 -13
- package/src/backend/wasm/emit-stmts.js +1359 -164
- package/src/backend/wasm/emit.js +512 -77
- package/src/backend/wasm/encode.js +43 -13
- package/src/backend/wasm/globals.js +452 -0
- package/src/backend/wasm/index.js +194 -2489
- package/src/backend/wasm/method-expr.js +381 -0
- package/src/backend/wasm/module-emitter.js +2240 -0
- package/src/backend/wasm/monomorph.js +62 -4
- package/src/backend/wasm/prescan.js +397 -0
- package/src/backend/wasm/stdlib/bytes.js +1804 -127
- package/src/backend/wasm/stdlib/core.js +1574 -199
- package/src/backend/wasm/stdlib/index.js +0 -5
- package/src/backend/wasm/stdlib/io.js +1567 -150
- package/src/backend/wasm/stdlib/packages.js +2 -0
- package/src/backend/wasm/stdlib/simd.js +293 -28
- package/src/backend/wasm/stdlib/time.js +1041 -12
- package/src/backend/wasm/types.js +200 -15
- package/src/backend/wasm/variadic.js +93 -0
- package/src/backend/wasm/wat.js +47 -20
- package/src/{cli-core.js → cli/cli-core.js} +236 -10
- package/src/{test-runner.js → cli/test-runner.js} +332 -39
- package/src/{vendor.js → cli/vendor.js} +3 -4
- package/src/compiler.js +107 -269
- package/src/index.js +15 -169
- package/src/{lexer.js → lexer/lexer.js} +102 -55
- package/src/{tokens.js → lexer/tokens.js} +1 -0
- package/src/link/sourcemap.js +97 -0
- package/src/link/stdlib.js +515 -0
- package/src/link/wasm.js +294 -0
- package/src/lower/bounds.js +394 -0
- package/src/lower/boxing.js +1 -1
- package/src/lower/captures.js +176 -32
- package/src/lower/index.js +26 -18
- package/src/lower/json-codecs.js +1398 -0
- package/src/lower/ownership.js +22 -1
- package/src/lower/range.js +86 -1
- package/src/lower/reachability.js +132 -0
- package/src/parser/declarations.js +58 -13
- package/src/parser/expressions.js +29 -7
- package/src/parser/index.js +2 -1
- package/src/parser/statements.js +37 -22
- package/src/{templ-parser.js → parser/templ.js} +138 -15
- package/src/parser/types.js +69 -9
- package/src/resolver.js +22 -1
- package/src/typechecker/assignability.js +176 -17
- package/src/typechecker/const-fold.js +356 -0
- package/src/typechecker/constants.js +256 -0
- package/src/typechecker/expressions.js +931 -122
- package/src/typechecker/index.js +459 -67
- package/src/typechecker/init-order.js +330 -0
- package/src/typechecker/resolve.js +451 -56
- package/src/typechecker/statements.js +90 -9
- package/src/typechecker/stdlib/core.js +333 -55
- package/src/typechecker/stdlib/excluded.js +898 -0
- package/src/typechecker/stdlib/extended.js +322 -26
- package/src/typechecker/{stdlib.js → stdlib/index.js} +5 -5
- package/src/typechecker/stdlib/simd.js +65 -15
- package/src/typechecker/termination.js +1 -1
- package/src/typechecker/types.js +149 -25
- package/stdlib/bytes/iter.go +192 -0
- package/stdlib/bytes/search.go +240 -0
- package/stdlib/bytes/split.go +218 -0
- package/stdlib/cmp/cmp.go +65 -0
- package/stdlib/encoding/json/errors.go +31 -0
- package/stdlib/encoding/json/indent.go +71 -0
- package/stdlib/encoding/json/number.go +12 -0
- package/stdlib/encoding/json/stream.go +173 -0
- package/stdlib/errors/overlay.go +12 -0
- package/stdlib/hash/hash.go +53 -0
- package/stdlib/internal/jsonrt/dec.go +738 -0
- package/stdlib/internal/jsonrt/enc.go +366 -0
- package/stdlib/internal/jsonrt/indent.go +135 -0
- package/stdlib/internal/jsonrt/number.go +27 -0
- package/stdlib/internal/jsonrt/reader.go +73 -0
- package/stdlib/internal/jsonrt/scanner.go +649 -0
- package/stdlib/io/io.go +346 -0
- package/stdlib/iter/iter.go +18 -0
- package/stdlib/maps/iter.go +65 -0
- package/stdlib/math/extra.go +270 -0
- package/stdlib/slices/extra.go +73 -0
- package/stdlib/slices/iter.go +113 -0
- package/stdlib/sort/sort.go +561 -0
- package/stdlib/strconv/quote.go +341 -0
- package/stdlib/strings/extra.go +35 -0
- package/stdlib/strings/iter.go +122 -0
- package/stdlib/sync/extra.go +105 -0
- package/stdlib/unicode/unicode.go +112 -0
- package/stdlib/unicode/utf8/bytes.go +239 -0
- package/src/backend/wasm/const-fold.js +0 -205
- package/src/backend/wasm/emit-stdlib.js +0 -13
- /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
- /package/src/{colors.js → cli/colors.js} +0 -0
- /package/src/{dev-server.js → cli/dev-server.js} +0 -0
- /package/src/{minifier.js → cli/minifier.js} +0 -0
- /package/src/{project-config.js → cli/project-config.js} +0 -0
- /package/src/{pwa.js → cli/pwa.js} +0 -0
- /package/src/{templ-lexer.js → lexer/templ-lexer.js} +0 -0
|
@@ -0,0 +1,2240 @@
|
|
|
1
|
+
// src/backend/wasm/module-emitter.js
|
|
2
|
+
// ModuleEmitter: Module IR builder that registers types, imports, globals and functions.
|
|
3
|
+
|
|
4
|
+
import { wTypeKey } from "./boundary.js";
|
|
5
|
+
import { hasErrorUsage, importsAnyOf } from "./prescan.js";
|
|
6
|
+
import { BINARY_MATH_IMPORTS } from "./stdlib/index.js";
|
|
7
|
+
import {
|
|
8
|
+
ERROR_STRUCT,
|
|
9
|
+
getFuncSignature,
|
|
10
|
+
getSliceElemType,
|
|
11
|
+
goTypeKey,
|
|
12
|
+
isScalarType,
|
|
13
|
+
isSliceType,
|
|
14
|
+
toWasmType,
|
|
15
|
+
} from "./types.js";
|
|
16
|
+
|
|
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
|
+
export class ModuleEmitter {
|
|
227
|
+
constructor(checker, lowerResult = null, programs = [], options = {}) {
|
|
228
|
+
this.checker = checker;
|
|
229
|
+
this.lowerResult = lowerResult;
|
|
230
|
+
// Local package names whose code is linked into this module; selectors
|
|
231
|
+
// like `mathx.NewVec3` are de-qualified to `NewVec3` by the emitter.
|
|
232
|
+
this.bundledPackages = options.bundledPackages ?? new Set();
|
|
233
|
+
this.constCache = new Map(); // name -> BasicLit (package-level literal consts)
|
|
234
|
+
this.nonLiteralConsts = new Set(); // package-level consts the backend cannot evaluate yet
|
|
235
|
+
|
|
236
|
+
this.types = []; // type entries
|
|
237
|
+
this.typeCache = new Map(); // signature string -> index
|
|
238
|
+
this._openRec = null; // rec group receiving new types while structs are resolved
|
|
239
|
+
|
|
240
|
+
this.structTypes = new Map(); // name -> struct info
|
|
241
|
+
this.complexTypes = new Map(); // key -> complex struct info
|
|
242
|
+
this.ptrTypes = new Map(); // key -> ptr supertype info
|
|
243
|
+
this.boxTypes = new Map(); // wType key -> box info
|
|
244
|
+
this.ifaceBoxTypes = new Map(); // key -> iface box info
|
|
245
|
+
this.nilBoxTypes = new Map(); // key -> typed-nil marker info
|
|
246
|
+
this.arrayTypes = new Map(); // wType key -> array info
|
|
247
|
+
this.sliceTypes = new Map(); // wType key -> slice info
|
|
248
|
+
this.sliceTypesByIndex = new Map(); // typeIndex -> slice info
|
|
249
|
+
this.mapTypes = new Map(); // wType key -> map info
|
|
250
|
+
this.mapTypesByIndex = new Map(); // typeIndex -> map info
|
|
251
|
+
this.mapBucketsType = null; // shared (array (mut i32))
|
|
252
|
+
this.closureTypes = new Map(); // key -> closure info
|
|
253
|
+
this.envTypes = new Map(); // key -> env info
|
|
254
|
+
|
|
255
|
+
this.elements = []; // declarative elements for ref.func
|
|
256
|
+
this.elementSet = new Set();
|
|
257
|
+
|
|
258
|
+
this.imports = []; // import entries
|
|
259
|
+
this.importCache = new Map(); // key -> func index
|
|
260
|
+
|
|
261
|
+
this.tags = []; // exception tags
|
|
262
|
+
this.globals = []; // global entries
|
|
263
|
+
this.globalCache = new Map(); // name -> index
|
|
264
|
+
|
|
265
|
+
this.funcs = []; // internal func definitions
|
|
266
|
+
this.funcMap = new Map(); // name -> global func index
|
|
267
|
+
this.funcParamTypes = new Map(); // name -> param types array
|
|
268
|
+
this.funcParamGoTypes = new Map(); // name -> param Go types array
|
|
269
|
+
this.funcReturnTypesByIdx = new Map(); // global func index -> result wasm types
|
|
270
|
+
|
|
271
|
+
this.exports = []; // export entries
|
|
272
|
+
this.stringTable = []; // string literals
|
|
273
|
+
this.stringCache = new Map();
|
|
274
|
+
|
|
275
|
+
// `errors.New` / `fmt.Errorf` / strconv parse failures allocate the
|
|
276
|
+
// runtime's own error struct; it joins the struct rec group so the
|
|
277
|
+
// `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"]);
|
|
282
|
+
|
|
283
|
+
if (programs && programs.length > 0) {
|
|
284
|
+
this.initTypes(programs);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
this._importsLocked = false;
|
|
288
|
+
this._initRuntimeImports();
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
initTypes(programs) {
|
|
292
|
+
const progs = Array.isArray(programs) ? programs : [programs];
|
|
293
|
+
const rawStructs = [];
|
|
294
|
+
const seen = new Set();
|
|
295
|
+
const aliases = []; // qualified key (pkg.T) -> unqualified struct name
|
|
296
|
+
|
|
297
|
+
if (this.hasErrorType) {
|
|
298
|
+
seen.add(ERROR_STRUCT);
|
|
299
|
+
rawStructs.push({
|
|
300
|
+
name: ERROR_STRUCT,
|
|
301
|
+
structType: {
|
|
302
|
+
kind: "struct",
|
|
303
|
+
fields: new Map([
|
|
304
|
+
["msg", { kind: "basic", name: "string" }],
|
|
305
|
+
["cause", { kind: "basic", name: "any" }],
|
|
306
|
+
]),
|
|
307
|
+
},
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
for (const p of progs) {
|
|
312
|
+
for (const d of p.decls ?? []) {
|
|
313
|
+
if (
|
|
314
|
+
d.kind === "TypeDecl" &&
|
|
315
|
+
!d.isAlias &&
|
|
316
|
+
d.type?.kind === "StructType"
|
|
317
|
+
) {
|
|
318
|
+
if (!seen.has(d.name)) {
|
|
319
|
+
seen.add(d.name);
|
|
320
|
+
rawStructs.push({
|
|
321
|
+
name: d.name,
|
|
322
|
+
astFields: d.type.fields ?? [],
|
|
323
|
+
pkgName: p.pkg?.name ?? "main",
|
|
324
|
+
pkgTarget: p.target ?? "wasm",
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (this.checker?.types) {
|
|
332
|
+
for (const [key, t] of this.checker.types.entries()) {
|
|
333
|
+
if (t.kind === "named" && t.underlying?.kind === "struct") {
|
|
334
|
+
const name = t.name ?? key;
|
|
335
|
+
if (
|
|
336
|
+
name.startsWith("testing.") &&
|
|
337
|
+
(!this.importsTesting || name !== "testing.BenchmarkResult")
|
|
338
|
+
)
|
|
339
|
+
continue;
|
|
340
|
+
if (!seen.has(name)) {
|
|
341
|
+
seen.add(name);
|
|
342
|
+
rawStructs.push({ name, structType: t.underlying });
|
|
343
|
+
}
|
|
344
|
+
if (key !== name) aliases.push([key, name]);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (rawStructs.length === 0) return;
|
|
350
|
+
|
|
351
|
+
// Field resolution may create array/slice/box types; they join this rec
|
|
352
|
+
// group so struct indices (base + i) stay valid and may be referenced
|
|
353
|
+
// from the auxiliary types in any order.
|
|
354
|
+
const base = this._getTotalTypeCount();
|
|
355
|
+
const structEntries = rawStructs.map(() => ({
|
|
356
|
+
form: "struct",
|
|
357
|
+
fields: [],
|
|
358
|
+
}));
|
|
359
|
+
this.types.push({ form: "rec", types: structEntries });
|
|
360
|
+
this._openRec = structEntries;
|
|
361
|
+
|
|
362
|
+
for (let i = 0; i < rawStructs.length; i++) {
|
|
363
|
+
const s = rawStructs[i];
|
|
364
|
+
this.structTypes.set(s.name, {
|
|
365
|
+
name: s.name,
|
|
366
|
+
typeIndex: base + i,
|
|
367
|
+
fields: [],
|
|
368
|
+
fieldIndexMap: new Map(),
|
|
369
|
+
embeds: [],
|
|
370
|
+
pkgName: s.pkgName ?? null,
|
|
371
|
+
pkgTarget: s.pkgTarget ?? "wasm",
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
// `mathx.Vec3` resolves to the same struct info as `Vec3` once linked.
|
|
375
|
+
for (const [key, name] of aliases) {
|
|
376
|
+
if (!this.structTypes.has(key))
|
|
377
|
+
this.structTypes.set(key, this.structTypes.get(name));
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
for (let i = 0; i < rawStructs.length; i++) {
|
|
381
|
+
const s = rawStructs[i];
|
|
382
|
+
const info = this.structTypes.get(s.name);
|
|
383
|
+
const resolvedStruct =
|
|
384
|
+
s.structType ?? this.checker?.types?.get(s.name)?.underlying;
|
|
385
|
+
|
|
386
|
+
const fields = [];
|
|
387
|
+
const fieldIndexMap = new Map();
|
|
388
|
+
const embeds = [];
|
|
389
|
+
|
|
390
|
+
if (s.astFields && s.astFields.length > 0) {
|
|
391
|
+
let fIdx = 0;
|
|
392
|
+
for (const f of s.astFields) {
|
|
393
|
+
const names = f.embedded
|
|
394
|
+
? [
|
|
395
|
+
(
|
|
396
|
+
f.embeddedName ??
|
|
397
|
+
(f.type?.kind === "PointerType"
|
|
398
|
+
? f.type.base?.name
|
|
399
|
+
: f.type?.name)
|
|
400
|
+
)
|
|
401
|
+
?.split(".")
|
|
402
|
+
.pop(),
|
|
403
|
+
]
|
|
404
|
+
: f.names?.length
|
|
405
|
+
? f.names
|
|
406
|
+
: f.name
|
|
407
|
+
? [f.name]
|
|
408
|
+
: [];
|
|
409
|
+
for (const n of names) {
|
|
410
|
+
const wType = toWasmType(f.type, this.checker, this);
|
|
411
|
+
fields.push({
|
|
412
|
+
name: n,
|
|
413
|
+
goType: f.type,
|
|
414
|
+
wType,
|
|
415
|
+
embedded: Boolean(f.embedded),
|
|
416
|
+
});
|
|
417
|
+
fieldIndexMap.set(n, fIdx);
|
|
418
|
+
if (f.embedded) {
|
|
419
|
+
embeds.push({
|
|
420
|
+
name: n,
|
|
421
|
+
fieldIndex: fIdx,
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
fIdx++;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
} else if (resolvedStruct?.fields) {
|
|
428
|
+
let fIdx = 0;
|
|
429
|
+
// `fields` also holds promoted fields; the declared fields
|
|
430
|
+
// (`_structFields`) give the real layout of a defined struct.
|
|
431
|
+
const declared = resolvedStruct._structFields
|
|
432
|
+
? resolvedStruct._structFields.flatMap((sf) => {
|
|
433
|
+
if (!sf.embedded) return sf.names.map((n) => [n, sf.type]);
|
|
434
|
+
const inner = sf.type.kind === "pointer" ? sf.type.base : sf.type;
|
|
435
|
+
const n = (inner?.name ?? "").split(".").pop();
|
|
436
|
+
return n ? [[n, sf.type]] : [];
|
|
437
|
+
})
|
|
438
|
+
: [...resolvedStruct.fields.entries()];
|
|
439
|
+
for (const [fName, fType] of declared) {
|
|
440
|
+
const wType = toWasmType(fType, this.checker, this);
|
|
441
|
+
fields.push({ name: fName, goType: fType, wType });
|
|
442
|
+
fieldIndexMap.set(fName, fIdx);
|
|
443
|
+
fIdx++;
|
|
444
|
+
}
|
|
445
|
+
if (resolvedStruct?._embeds) {
|
|
446
|
+
for (const embed of resolvedStruct._embeds) {
|
|
447
|
+
const inner = embed.kind === "pointer" ? embed.base : embed;
|
|
448
|
+
const embedName = inner?.kind === "named" ? inner.name : null;
|
|
449
|
+
if (embedName && fieldIndexMap.has(embedName)) {
|
|
450
|
+
embeds.push({
|
|
451
|
+
name: embedName,
|
|
452
|
+
fieldIndex: fieldIndexMap.get(embedName),
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
info.fields = fields;
|
|
460
|
+
info.fieldIndexMap = fieldIndexMap;
|
|
461
|
+
info.embeds = embeds;
|
|
462
|
+
|
|
463
|
+
const typeEntry = structEntries[i];
|
|
464
|
+
typeEntry.fields = fields.map((f) => ({ type: f.wType, mutable: true }));
|
|
465
|
+
info.typeEntry = typeEntry;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
this._openRec = null;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
_pushType(typeEntry) {
|
|
472
|
+
(this._openRec ?? this.types).push(typeEntry);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
getStructType(name) {
|
|
476
|
+
return this.structTypes.get(name) ?? null;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
getAnonStructType(fields) {
|
|
480
|
+
const key = fields.map((f) => `${f.name}:${goTypeKey(f.goType)}`).join(";");
|
|
481
|
+
const name = `__anon_${key.replace(/[^a-zA-Z0-9_]/g, "_")}`;
|
|
482
|
+
if (this.structTypes.has(name)) {
|
|
483
|
+
return this.structTypes.get(name);
|
|
484
|
+
}
|
|
485
|
+
const fieldWTypes = fields.map((f) =>
|
|
486
|
+
toWasmType(f.goType, this.checker, this),
|
|
487
|
+
);
|
|
488
|
+
const typeIndex = this._getTotalTypeCount();
|
|
489
|
+
const typeEntry = {
|
|
490
|
+
form: "struct",
|
|
491
|
+
fields: fieldWTypes.map((t) => ({ type: t, mutable: true })),
|
|
492
|
+
};
|
|
493
|
+
this._pushType(typeEntry);
|
|
494
|
+
const info = {
|
|
495
|
+
name,
|
|
496
|
+
typeIndex,
|
|
497
|
+
typeEntry,
|
|
498
|
+
fields: fields.map((f, i) => ({
|
|
499
|
+
name: f.name,
|
|
500
|
+
wType: fieldWTypes[i],
|
|
501
|
+
goType: f.goType,
|
|
502
|
+
fieldIndex: i,
|
|
503
|
+
})),
|
|
504
|
+
fieldIndexMap: new Map(fields.map((f, i) => [f.name, i])),
|
|
505
|
+
embeds: [],
|
|
506
|
+
pkgName: null,
|
|
507
|
+
pkgTarget: "wasm",
|
|
508
|
+
isAnon: true,
|
|
509
|
+
};
|
|
510
|
+
this.structTypes.set(name, info);
|
|
511
|
+
return info;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
getComplexType(name = "complex128") {
|
|
515
|
+
const key = name === "complex64" ? "complex64" : "complex128";
|
|
516
|
+
if (this.complexTypes.has(key)) {
|
|
517
|
+
return this.complexTypes.get(key);
|
|
518
|
+
}
|
|
519
|
+
const isF32 = key === "complex64";
|
|
520
|
+
const elemWType = isF32 ? "f32" : "f64";
|
|
521
|
+
const elemGoType = { kind: "basic", name: isF32 ? "float32" : "float64" };
|
|
522
|
+
const typeName = `$${key}`;
|
|
523
|
+
const typeIndex = this._getTotalTypeCount();
|
|
524
|
+
const typeEntry = {
|
|
525
|
+
form: "struct",
|
|
526
|
+
name: typeName,
|
|
527
|
+
fields: [
|
|
528
|
+
{ type: elemWType, mutable: false },
|
|
529
|
+
{ type: elemWType, mutable: false },
|
|
530
|
+
],
|
|
531
|
+
};
|
|
532
|
+
this._pushType(typeEntry);
|
|
533
|
+
const fields = [
|
|
534
|
+
{ name: "re", fieldIndex: 0, wType: elemWType, goType: elemGoType },
|
|
535
|
+
{ name: "im", fieldIndex: 1, wType: elemWType, goType: elemGoType },
|
|
536
|
+
];
|
|
537
|
+
const info = {
|
|
538
|
+
name: typeName,
|
|
539
|
+
typeIndex,
|
|
540
|
+
typeEntry,
|
|
541
|
+
fields,
|
|
542
|
+
fieldIndexMap: new Map([
|
|
543
|
+
["re", 0],
|
|
544
|
+
["im", 1],
|
|
545
|
+
]),
|
|
546
|
+
embeds: [],
|
|
547
|
+
pkgName: null,
|
|
548
|
+
pkgTarget: "wasm",
|
|
549
|
+
isComplex: true,
|
|
550
|
+
elemWType,
|
|
551
|
+
elemGoType,
|
|
552
|
+
};
|
|
553
|
+
this.complexTypes.set(key, info);
|
|
554
|
+
this.structTypes.set(key, info);
|
|
555
|
+
this.structTypes.set(typeName, info);
|
|
556
|
+
return info;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
getPtrType(goType) {
|
|
560
|
+
const key = goTypeKey(goType);
|
|
561
|
+
if (this.ptrTypes.has(key)) {
|
|
562
|
+
return this.ptrTypes.get(key);
|
|
563
|
+
}
|
|
564
|
+
const typeIndex = this._getTotalTypeCount();
|
|
565
|
+
const name = `$ptr_${key}`;
|
|
566
|
+
const typeEntry = {
|
|
567
|
+
form: "struct",
|
|
568
|
+
name,
|
|
569
|
+
sub: true,
|
|
570
|
+
fields: [],
|
|
571
|
+
};
|
|
572
|
+
this._pushType(typeEntry);
|
|
573
|
+
const ptrInfo = { key, name, typeIndex, typeEntry, goType };
|
|
574
|
+
this.ptrTypes.set(key, ptrInfo);
|
|
575
|
+
return ptrInfo;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
getBoxType(goType) {
|
|
579
|
+
const key = goTypeKey(goType);
|
|
580
|
+
if (this.boxTypes.has(key)) {
|
|
581
|
+
return this.boxTypes.get(key);
|
|
582
|
+
}
|
|
583
|
+
const ptrInfo = this.getPtrType(goType);
|
|
584
|
+
const wType = toWasmType(goType, this.checker, this);
|
|
585
|
+
const typeIndex = this._getTotalTypeCount();
|
|
586
|
+
const name = `$box_${key}`;
|
|
587
|
+
const typeEntry = {
|
|
588
|
+
form: "struct",
|
|
589
|
+
name,
|
|
590
|
+
sub: true,
|
|
591
|
+
superType: ptrInfo.name,
|
|
592
|
+
superTypeIndex: ptrInfo.typeIndex,
|
|
593
|
+
fields: [{ type: wType, mutable: true }],
|
|
594
|
+
};
|
|
595
|
+
this._pushType(typeEntry);
|
|
596
|
+
const boxInfo = {
|
|
597
|
+
key,
|
|
598
|
+
name,
|
|
599
|
+
typeIndex,
|
|
600
|
+
typeEntry,
|
|
601
|
+
ptrType: ptrInfo,
|
|
602
|
+
ptrTypeIndex: ptrInfo.typeIndex,
|
|
603
|
+
wType,
|
|
604
|
+
goType,
|
|
605
|
+
};
|
|
606
|
+
this.boxTypes.set(key, boxInfo);
|
|
607
|
+
return boxInfo;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
getIfaceBoxType(goType) {
|
|
611
|
+
const key = goTypeKey(goType);
|
|
612
|
+
if (this.ifaceBoxTypes.has(key)) {
|
|
613
|
+
return this.ifaceBoxTypes.get(key);
|
|
614
|
+
}
|
|
615
|
+
const wType = toWasmType(goType, this.checker, this);
|
|
616
|
+
const tagIndex = this.ifaceBoxTypes.size;
|
|
617
|
+
const tagTypeIndex = this._getTotalTypeCount();
|
|
618
|
+
const tagFields = [];
|
|
619
|
+
for (let i = 0; i < tagIndex; i++) {
|
|
620
|
+
tagFields.push({ type: "i32", mutable: false });
|
|
621
|
+
}
|
|
622
|
+
this._pushType({
|
|
623
|
+
form: "struct",
|
|
624
|
+
fields: tagFields,
|
|
625
|
+
});
|
|
626
|
+
const typeIndex = this._getTotalTypeCount();
|
|
627
|
+
const typeEntry = {
|
|
628
|
+
form: "struct",
|
|
629
|
+
fields: [
|
|
630
|
+
{ type: wType, mutable: true },
|
|
631
|
+
{
|
|
632
|
+
type: {
|
|
633
|
+
kind: "ref",
|
|
634
|
+
nullable: true,
|
|
635
|
+
typeIndex: tagTypeIndex,
|
|
636
|
+
},
|
|
637
|
+
mutable: false,
|
|
638
|
+
},
|
|
639
|
+
],
|
|
640
|
+
};
|
|
641
|
+
this._pushType(typeEntry);
|
|
642
|
+
const boxInfo = { key, typeIndex, typeEntry, wType, goType, tagTypeIndex };
|
|
643
|
+
this.ifaceBoxTypes.set(key, boxInfo);
|
|
644
|
+
return boxInfo;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// The value a nil map, func or `*T` takes inside an interface: a marker
|
|
648
|
+
// object per Go type, so `any(nilMap) != nil` and a type switch still sees
|
|
649
|
+
// the dynamic type. Struct types are structural, so each marker gets a
|
|
650
|
+
// distinct field count to keep them apart.
|
|
651
|
+
getNilBoxType(goType) {
|
|
652
|
+
const key = goTypeKey(goType);
|
|
653
|
+
if (this.nilBoxTypes.has(key)) return this.nilBoxTypes.get(key);
|
|
654
|
+
const n = this.nilBoxTypes.size + 1;
|
|
655
|
+
const typeIndex = this._getTotalTypeCount();
|
|
656
|
+
this._pushType({
|
|
657
|
+
form: "struct",
|
|
658
|
+
fields: Array.from({ length: n }, () => ({
|
|
659
|
+
type: "i32",
|
|
660
|
+
mutable: true,
|
|
661
|
+
})),
|
|
662
|
+
});
|
|
663
|
+
const globalIndex = this.globals.length;
|
|
664
|
+
this.globals.push({
|
|
665
|
+
type: { kind: "ref", nullable: true, typeIndex },
|
|
666
|
+
mutable: false,
|
|
667
|
+
init: [
|
|
668
|
+
...Array.from({ length: n }, () => ({ op: "i32.const", value: 0 })),
|
|
669
|
+
{ op: "struct.new", typeIndex },
|
|
670
|
+
],
|
|
671
|
+
});
|
|
672
|
+
const info = { key, goType, typeIndex, globalIndex };
|
|
673
|
+
this.nilBoxTypes.set(key, info);
|
|
674
|
+
return info;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
getArrayType(elemGoType) {
|
|
678
|
+
const elemWType = toWasmType(elemGoType, this.checker, this);
|
|
679
|
+
const key =
|
|
680
|
+
typeof elemWType === "string" ? elemWType : JSON.stringify(elemWType);
|
|
681
|
+
if (this.arrayTypes.has(key)) {
|
|
682
|
+
return this.arrayTypes.get(key);
|
|
683
|
+
}
|
|
684
|
+
const typeIndex = this._getTotalTypeCount();
|
|
685
|
+
const typeEntry = {
|
|
686
|
+
form: "array",
|
|
687
|
+
elemType: elemWType,
|
|
688
|
+
mutable: true,
|
|
689
|
+
};
|
|
690
|
+
this._pushType(typeEntry);
|
|
691
|
+
const arrInfo = { typeIndex, typeEntry, elemWType, elemGoType };
|
|
692
|
+
this.arrayTypes.set(key, arrInfo);
|
|
693
|
+
return arrInfo;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
getSliceType(elemGoType) {
|
|
697
|
+
const elemWType = toWasmType(elemGoType, this.checker, this);
|
|
698
|
+
const key =
|
|
699
|
+
typeof elemWType === "string" ? elemWType : JSON.stringify(elemWType);
|
|
700
|
+
if (this.sliceTypes.has(key)) {
|
|
701
|
+
return this.sliceTypes.get(key);
|
|
702
|
+
}
|
|
703
|
+
const arrInfo = this.getArrayType(elemGoType);
|
|
704
|
+
const typeIndex = this._getTotalTypeCount();
|
|
705
|
+
const typeEntry = {
|
|
706
|
+
form: "struct",
|
|
707
|
+
fields: [
|
|
708
|
+
{
|
|
709
|
+
type: {
|
|
710
|
+
kind: "ref",
|
|
711
|
+
nullable: true,
|
|
712
|
+
typeIndex: arrInfo.typeIndex,
|
|
713
|
+
},
|
|
714
|
+
mutable: false,
|
|
715
|
+
},
|
|
716
|
+
{ type: "i32", mutable: false }, // offset
|
|
717
|
+
{ type: "i32", mutable: false }, // len
|
|
718
|
+
{ type: "i32", mutable: false }, // cap
|
|
719
|
+
],
|
|
720
|
+
};
|
|
721
|
+
this._pushType(typeEntry);
|
|
722
|
+
|
|
723
|
+
const emptyGlobalIndex = this.globals.length;
|
|
724
|
+
this.globals.push({
|
|
725
|
+
type: {
|
|
726
|
+
kind: "ref",
|
|
727
|
+
nullable: true,
|
|
728
|
+
typeIndex,
|
|
729
|
+
},
|
|
730
|
+
mutable: false,
|
|
731
|
+
init: [
|
|
732
|
+
{ op: "ref.null", heapType: arrInfo.typeIndex },
|
|
733
|
+
{ op: "i32.const", value: 0 },
|
|
734
|
+
{ op: "i32.const", value: 0 },
|
|
735
|
+
{ op: "i32.const", value: 0 },
|
|
736
|
+
{ op: "struct.new", typeIndex },
|
|
737
|
+
],
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
const sliceInfo = {
|
|
741
|
+
typeIndex,
|
|
742
|
+
typeEntry,
|
|
743
|
+
arrInfo,
|
|
744
|
+
elemWType,
|
|
745
|
+
elemGoType,
|
|
746
|
+
emptyGlobalIndex,
|
|
747
|
+
};
|
|
748
|
+
this.sliceTypes.set(key, sliceInfo);
|
|
749
|
+
this.sliceTypesByIndex.set(typeIndex, sliceInfo);
|
|
750
|
+
return sliceInfo;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
getSliceTypeByIndex(typeIndex) {
|
|
754
|
+
return this.sliceTypesByIndex?.get(typeIndex) ?? null;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
getMapType(keyGoType, valGoType) {
|
|
758
|
+
const keyWType = toWasmType(keyGoType, this.checker, this);
|
|
759
|
+
const valWType = toWasmType(valGoType, this.checker, this);
|
|
760
|
+
// Vector keys share one wasm type but compare differently (floats vs
|
|
761
|
+
// mask words), so the Go type is part of the key.
|
|
762
|
+
const keyPart =
|
|
763
|
+
keyWType === "v128" ? `v128_${goTypeKey(keyGoType)}` : wTypeKey(keyWType);
|
|
764
|
+
const key = `${keyPart}:${wTypeKey(valWType)}`;
|
|
765
|
+
if (this.mapTypes.has(key)) {
|
|
766
|
+
return this.mapTypes.get(key);
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
if (!this.mapBucketsType) {
|
|
770
|
+
const typeIndex = this._getTotalTypeCount();
|
|
771
|
+
this._pushType({
|
|
772
|
+
form: "array",
|
|
773
|
+
elemType: "i32",
|
|
774
|
+
mutable: true,
|
|
775
|
+
});
|
|
776
|
+
this.mapBucketsType = { typeIndex };
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
const entryTypeIndex = this._getTotalTypeCount();
|
|
780
|
+
this._pushType({
|
|
781
|
+
form: "struct",
|
|
782
|
+
fields: [
|
|
783
|
+
{ type: keyWType, mutable: true }, // 0: key
|
|
784
|
+
{ type: valWType, mutable: true }, // 1: val
|
|
785
|
+
{ type: "i32", mutable: true }, // 2: next (bucket chain)
|
|
786
|
+
{ type: "i32", mutable: true }, // 3: order_prev (insertion order)
|
|
787
|
+
{ type: "i32", mutable: true }, // 4: order_next (insertion order)
|
|
788
|
+
{ type: "i32", mutable: true }, // 5: active (1 = active, 0 = deleted)
|
|
789
|
+
],
|
|
790
|
+
});
|
|
791
|
+
|
|
792
|
+
const entriesTypeIndex = this._getTotalTypeCount();
|
|
793
|
+
this._pushType({
|
|
794
|
+
form: "array",
|
|
795
|
+
elemType: { kind: "ref", nullable: true, typeIndex: entryTypeIndex },
|
|
796
|
+
mutable: true,
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
const mapTypeIndex = this._getTotalTypeCount();
|
|
800
|
+
this._pushType({
|
|
801
|
+
form: "struct",
|
|
802
|
+
fields: [
|
|
803
|
+
{
|
|
804
|
+
type: {
|
|
805
|
+
kind: "ref",
|
|
806
|
+
nullable: true,
|
|
807
|
+
typeIndex: this.mapBucketsType.typeIndex,
|
|
808
|
+
},
|
|
809
|
+
mutable: true,
|
|
810
|
+
}, // 0: buckets
|
|
811
|
+
{
|
|
812
|
+
type: {
|
|
813
|
+
kind: "ref",
|
|
814
|
+
nullable: true,
|
|
815
|
+
typeIndex: entriesTypeIndex,
|
|
816
|
+
},
|
|
817
|
+
mutable: true,
|
|
818
|
+
}, // 1: entries
|
|
819
|
+
{ type: "i32", mutable: true }, // 2: len
|
|
820
|
+
{ type: "i32", mutable: true }, // 3: cap
|
|
821
|
+
{ type: "i32", mutable: true }, // 4: count
|
|
822
|
+
{ type: "i32", mutable: true }, // 5: head
|
|
823
|
+
{ type: "i32", mutable: true }, // 6: tail
|
|
824
|
+
{ type: "i32", mutable: true }, // 7: free_head
|
|
825
|
+
{ type: "i32", mutable: true }, // 8: num_buckets
|
|
826
|
+
],
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
const suffix = key.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
830
|
+
const mapInfo = {
|
|
831
|
+
key,
|
|
832
|
+
suffix,
|
|
833
|
+
typeIndex: mapTypeIndex,
|
|
834
|
+
entryTypeIndex,
|
|
835
|
+
entriesTypeIndex,
|
|
836
|
+
bucketsTypeIndex: this.mapBucketsType.typeIndex,
|
|
837
|
+
keyWType,
|
|
838
|
+
valWType,
|
|
839
|
+
keyGoType,
|
|
840
|
+
valGoType,
|
|
841
|
+
wType: { kind: "ref", nullable: true, typeIndex: mapTypeIndex },
|
|
842
|
+
makeFuncName: `__map_make_${suffix}`,
|
|
843
|
+
getFuncName: `__map_get_${suffix}`,
|
|
844
|
+
getOkFuncName: `__map_get_ok_${suffix}`,
|
|
845
|
+
setFuncName: `__map_set_${suffix}`,
|
|
846
|
+
deleteFuncName: `__map_delete_${suffix}`,
|
|
847
|
+
lenFuncName: `__map_len_${suffix}`,
|
|
848
|
+
clearFuncName: `__map_clear_${suffix}`,
|
|
849
|
+
keysFuncName: `__map_keys_${suffix}`,
|
|
850
|
+
valuesFuncName: `__map_values_${suffix}`,
|
|
851
|
+
cloneFuncName: `__map_clone_${suffix}`,
|
|
852
|
+
};
|
|
853
|
+
this.mapTypes.set(key, mapInfo);
|
|
854
|
+
this.mapTypesByIndex.set(mapTypeIndex, mapInfo);
|
|
855
|
+
return mapInfo;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
getMapTypeByIndex(typeIndex) {
|
|
859
|
+
return this.mapTypesByIndex?.get(typeIndex) ?? null;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
getClosureType(goType) {
|
|
863
|
+
const sig = getFuncSignature(goType, this.checker);
|
|
864
|
+
const paramWTypes = sig.params
|
|
865
|
+
.map((p) => toWasmType(p, this.checker, this))
|
|
866
|
+
.filter(Boolean);
|
|
867
|
+
const returnWTypes = sig.returns
|
|
868
|
+
.map((r) => toWasmType(r, this.checker, this))
|
|
869
|
+
.filter(Boolean);
|
|
870
|
+
|
|
871
|
+
const key = `${paramWTypes.map((p) => (typeof p === "object" ? JSON.stringify(p) : p)).join(",")}=>${returnWTypes.map((r) => (typeof r === "object" ? JSON.stringify(r) : r)).join(",")}`;
|
|
872
|
+
if (this.closureTypes.has(key)) {
|
|
873
|
+
return this.closureTypes.get(key);
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
// 1. Function signature type index: (param anyref, ...paramWTypes) -> (...returnWTypes)
|
|
877
|
+
const funcTypeIndex = this.getTypeIndex(
|
|
878
|
+
["anyref", ...paramWTypes],
|
|
879
|
+
returnWTypes,
|
|
880
|
+
);
|
|
881
|
+
|
|
882
|
+
// 2. Closure struct type index: struct { fn: (ref funcTypeIndex), env: anyref }
|
|
883
|
+
const typeIndex = this._getTotalTypeCount();
|
|
884
|
+
const typeEntry = {
|
|
885
|
+
form: "struct",
|
|
886
|
+
fields: [
|
|
887
|
+
{
|
|
888
|
+
type: {
|
|
889
|
+
kind: "ref",
|
|
890
|
+
nullable: false,
|
|
891
|
+
typeIndex: funcTypeIndex,
|
|
892
|
+
},
|
|
893
|
+
mutable: false,
|
|
894
|
+
},
|
|
895
|
+
{
|
|
896
|
+
type: "anyref",
|
|
897
|
+
mutable: true,
|
|
898
|
+
},
|
|
899
|
+
],
|
|
900
|
+
};
|
|
901
|
+
this._pushType(typeEntry);
|
|
902
|
+
|
|
903
|
+
const closureInfo = {
|
|
904
|
+
typeIndex,
|
|
905
|
+
typeEntry,
|
|
906
|
+
funcTypeIndex,
|
|
907
|
+
paramWTypes,
|
|
908
|
+
returnWTypes,
|
|
909
|
+
sig,
|
|
910
|
+
};
|
|
911
|
+
this.closureTypes.set(key, closureInfo);
|
|
912
|
+
return closureInfo;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
getEnvType(fieldTypes) {
|
|
916
|
+
const key = fieldTypes
|
|
917
|
+
.map((f) =>
|
|
918
|
+
typeof f.wType === "object" ? JSON.stringify(f.wType) : f.wType,
|
|
919
|
+
)
|
|
920
|
+
.join(",");
|
|
921
|
+
if (this.envTypes.has(key)) {
|
|
922
|
+
return this.envTypes.get(key);
|
|
923
|
+
}
|
|
924
|
+
const typeIndex = this._getTotalTypeCount();
|
|
925
|
+
const typeEntry = {
|
|
926
|
+
form: "struct",
|
|
927
|
+
fields: fieldTypes.map((f) => ({ type: f.wType, mutable: true })),
|
|
928
|
+
};
|
|
929
|
+
this._pushType(typeEntry);
|
|
930
|
+
const envInfo = { typeIndex, typeEntry, fieldTypes };
|
|
931
|
+
this.envTypes.set(key, envInfo);
|
|
932
|
+
return envInfo;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
addRefFuncElement(funcIdx) {
|
|
936
|
+
if (!this.elementSet.has(funcIdx)) {
|
|
937
|
+
this.elementSet.add(funcIdx);
|
|
938
|
+
this.elements.push(funcIdx);
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
_getTotalTypeCount() {
|
|
943
|
+
let count = 0;
|
|
944
|
+
for (const entry of this.types) {
|
|
945
|
+
if (entry.form === "rec") count += entry.types.length;
|
|
946
|
+
else count += 1;
|
|
947
|
+
}
|
|
948
|
+
return count;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
_initRuntimeImports() {
|
|
952
|
+
this._initPanicTag();
|
|
953
|
+
this._initPanicGlobal();
|
|
954
|
+
this.getPanicImportIndex(); // env.panic
|
|
955
|
+
this.internString("runtime error: index out of range");
|
|
956
|
+
this.internString("runtime error: slice bounds out of range");
|
|
957
|
+
this.internString("assignment to entry in nil map");
|
|
958
|
+
|
|
959
|
+
// String built-ins
|
|
960
|
+
this.getStringImportIndex(); // env.str
|
|
961
|
+
this.getStringLenImportIndex(); // env.str_len
|
|
962
|
+
this.getStringConcatImportIndex(); // env.str_concat
|
|
963
|
+
this.getStringCmpImportIndex("=="); // env.str_eq
|
|
964
|
+
this.getStringCmpImportIndex("!="); // env.str_ne
|
|
965
|
+
this.getStringCmpImportIndex("<"); // env.str_lt
|
|
966
|
+
this.getStringCmpImportIndex("<="); // env.str_le
|
|
967
|
+
this.getStringCmpImportIndex(">"); // env.str_gt
|
|
968
|
+
this.getStringCmpImportIndex(">="); // env.str_ge
|
|
969
|
+
this.getStringGetImportIndex(); // env.str_get
|
|
970
|
+
this.getStringSliceImportIndex(); // env.str_slice
|
|
971
|
+
this.getStringFromCodePointImportIndex(); // env.str_from_code_point
|
|
972
|
+
this.getStringCodePointAtImportIndex(); // env.str_code_point_at
|
|
973
|
+
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
|
+
this.getStrFromI64ImportIndex();
|
|
988
|
+
this.getStrFromI32ImportIndex();
|
|
989
|
+
this.getStrFromF64ImportIndex();
|
|
990
|
+
this.getIsStringImportIndex(); // env.is_string
|
|
991
|
+
this.getStrToBytesImportIndex();
|
|
992
|
+
this.getStrByteAtImportIndex();
|
|
993
|
+
this.getBytesToStrInitImportIndex();
|
|
994
|
+
this.getBytesToStrPushImportIndex();
|
|
995
|
+
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
|
+
|
|
1026
|
+
// Logging built-ins
|
|
1027
|
+
this.getPrintlnEmptyIndex(); // env.println_empty
|
|
1028
|
+
const logTypes = ["i32", "i64", "f32", "f64", "externref", "anyref"];
|
|
1029
|
+
for (const t of logTypes) {
|
|
1030
|
+
this.getLogImportIndex(t, false, false); // env.print_*
|
|
1031
|
+
this.getLogImportIndex(t, true, false); // env.println_*
|
|
1032
|
+
}
|
|
1033
|
+
this.getLogImportIndex("i32", false, true); // env.print_bool
|
|
1034
|
+
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
|
+
}
|
|
1060
|
+
|
|
1061
|
+
_initPanicTag() {
|
|
1062
|
+
// Type: (externref) -> ()
|
|
1063
|
+
const typeIdx = this.getTypeIndex(["externref"], []);
|
|
1064
|
+
// Tag 0
|
|
1065
|
+
this.tags.push({ typeIndex: typeIdx });
|
|
1066
|
+
// Export tag 0 as "panicTag"
|
|
1067
|
+
this.exports.push({ name: "panicTag", kind: "tag", index: 0 });
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
getPanicNodeTypeIndex() {
|
|
1071
|
+
if (this.panicNodeTypeIndex !== undefined) {
|
|
1072
|
+
return this.panicNodeTypeIndex;
|
|
1073
|
+
}
|
|
1074
|
+
const typeIndex = this._getTotalTypeCount();
|
|
1075
|
+
const typeEntry = {
|
|
1076
|
+
form: "rec",
|
|
1077
|
+
types: [
|
|
1078
|
+
{
|
|
1079
|
+
form: "struct",
|
|
1080
|
+
fields: [
|
|
1081
|
+
{
|
|
1082
|
+
type: "anyref",
|
|
1083
|
+
mutable: true,
|
|
1084
|
+
},
|
|
1085
|
+
{
|
|
1086
|
+
type: "i32",
|
|
1087
|
+
mutable: true,
|
|
1088
|
+
},
|
|
1089
|
+
{
|
|
1090
|
+
type: {
|
|
1091
|
+
kind: "ref",
|
|
1092
|
+
nullable: true,
|
|
1093
|
+
typeIndex,
|
|
1094
|
+
},
|
|
1095
|
+
mutable: true,
|
|
1096
|
+
},
|
|
1097
|
+
],
|
|
1098
|
+
},
|
|
1099
|
+
],
|
|
1100
|
+
};
|
|
1101
|
+
this._pushType(typeEntry);
|
|
1102
|
+
this.panicNodeTypeIndex = typeIndex;
|
|
1103
|
+
return typeIndex;
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
_initPanicGlobal() {
|
|
1107
|
+
const pType = this.getPanicNodeTypeIndex();
|
|
1108
|
+
const gIdx = this.globals.length;
|
|
1109
|
+
this.globals.push({
|
|
1110
|
+
type: { kind: "ref", nullable: true, typeIndex: pType },
|
|
1111
|
+
mutable: true,
|
|
1112
|
+
init: [{ op: "ref.null", typeIndex: pType }],
|
|
1113
|
+
});
|
|
1114
|
+
this.panicGlobalIndex = gIdx;
|
|
1115
|
+
this.exports.push({ name: "__panic", kind: "global", index: gIdx });
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
getPanicGlobalIndex() {
|
|
1119
|
+
return this.panicGlobalIndex;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
// `__deferArmed` is set to 1 immediately before a deferred closure is
|
|
1123
|
+
// invoked and cleared by that closure's prologue. recover() only takes
|
|
1124
|
+
// effect when the current frame observed the flag, which gives Go's
|
|
1125
|
+
// "recover must be called directly by the deferred function" rule.
|
|
1126
|
+
getDeferArmedGlobalIndex() {
|
|
1127
|
+
if (this.deferArmedGlobalIndex !== undefined) {
|
|
1128
|
+
return this.deferArmedGlobalIndex;
|
|
1129
|
+
}
|
|
1130
|
+
const gIdx = this.globals.length;
|
|
1131
|
+
this.globals.push({
|
|
1132
|
+
type: "i32",
|
|
1133
|
+
mutable: true,
|
|
1134
|
+
init: [{ op: "i32.const", value: 0 }],
|
|
1135
|
+
});
|
|
1136
|
+
this.deferArmedGlobalIndex = gIdx;
|
|
1137
|
+
return gIdx;
|
|
1138
|
+
}
|
|
1139
|
+
|
|
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
|
+
getDeferNodeTypeIndex() {
|
|
1228
|
+
if (this.deferNodeTypeIndex !== undefined) {
|
|
1229
|
+
return this.deferNodeTypeIndex;
|
|
1230
|
+
}
|
|
1231
|
+
const deferClosureSig = { kind: "Signature", params: [], results: [] };
|
|
1232
|
+
const deferClosureInfo = this.getClosureType(deferClosureSig);
|
|
1233
|
+
const typeIndex = this._getTotalTypeCount();
|
|
1234
|
+
const typeEntry = {
|
|
1235
|
+
form: "rec",
|
|
1236
|
+
types: [
|
|
1237
|
+
{
|
|
1238
|
+
form: "struct",
|
|
1239
|
+
fields: [
|
|
1240
|
+
{
|
|
1241
|
+
type: {
|
|
1242
|
+
kind: "ref",
|
|
1243
|
+
nullable: true,
|
|
1244
|
+
typeIndex: deferClosureInfo.typeIndex,
|
|
1245
|
+
},
|
|
1246
|
+
mutable: false,
|
|
1247
|
+
},
|
|
1248
|
+
{
|
|
1249
|
+
type: {
|
|
1250
|
+
kind: "ref",
|
|
1251
|
+
nullable: true,
|
|
1252
|
+
typeIndex,
|
|
1253
|
+
},
|
|
1254
|
+
mutable: true,
|
|
1255
|
+
},
|
|
1256
|
+
],
|
|
1257
|
+
},
|
|
1258
|
+
],
|
|
1259
|
+
};
|
|
1260
|
+
this._pushType(typeEntry);
|
|
1261
|
+
this.deferNodeTypeIndex = typeIndex;
|
|
1262
|
+
return typeIndex;
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
getTypeIndex(params, results) {
|
|
1266
|
+
const key = `${params.map((p) => (typeof p === "object" ? JSON.stringify(p) : p)).join(",")}=>${results.map((r) => (typeof r === "object" ? JSON.stringify(r) : r)).join(",")}`;
|
|
1267
|
+
if (this.typeCache.has(key)) {
|
|
1268
|
+
return this.typeCache.get(key);
|
|
1269
|
+
}
|
|
1270
|
+
const idx = this._getTotalTypeCount();
|
|
1271
|
+
this._pushType({ form: "func", params, results });
|
|
1272
|
+
this.typeCache.set(key, idx);
|
|
1273
|
+
return idx;
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
internString(str) {
|
|
1277
|
+
if (this.stringCache.has(str)) {
|
|
1278
|
+
return this.stringCache.get(str);
|
|
1279
|
+
}
|
|
1280
|
+
const idx = this.stringTable.length;
|
|
1281
|
+
this.stringTable.push(str);
|
|
1282
|
+
this.stringCache.set(str, idx);
|
|
1283
|
+
return idx;
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
getOrAddFuncImport(module, name, params, results) {
|
|
1287
|
+
const key = `${module}.${name}`;
|
|
1288
|
+
if (this.importCache.has(key)) {
|
|
1289
|
+
return this.importCache.get(key);
|
|
1290
|
+
}
|
|
1291
|
+
if (this._importsLocked) {
|
|
1292
|
+
throw new Error(`Unexpected import added after lock: ${key}`);
|
|
1293
|
+
}
|
|
1294
|
+
const typeIndex = this.getTypeIndex(params, results);
|
|
1295
|
+
const globalFuncIdx = this.imports.filter((i) => i.kind === "func").length;
|
|
1296
|
+
this.imports.push({
|
|
1297
|
+
module,
|
|
1298
|
+
name,
|
|
1299
|
+
kind: "func",
|
|
1300
|
+
typeIndex,
|
|
1301
|
+
});
|
|
1302
|
+
this.importCache.set(key, globalFuncIdx);
|
|
1303
|
+
return globalFuncIdx;
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
getStringImportIndex() {
|
|
1307
|
+
return this.getOrAddFuncImport("env", "str", ["i32"], ["externref"]);
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
// Panics are raised by a JS import that throws a plain `Error`, so wasm
|
|
1311
|
+
// frames unwind without a wasm `throw` and the JS caller needs no
|
|
1312
|
+
// try/catch guard around exports. (V8 cannot inline JS→wasm calls made
|
|
1313
|
+
// inside a try block, and a non-inlined call boxes every float argument.)
|
|
1314
|
+
getPanicImportIndex() {
|
|
1315
|
+
return this.getOrAddFuncImport("env", "panic", ["externref"], []);
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
getBoundsPanicFuncIndex() {
|
|
1319
|
+
return this.resolveFuncIndex("__bounds_panic");
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
getIndexPanicFuncIndex() {
|
|
1323
|
+
return this.resolveFuncIndex("__index_panic");
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
getNilPanicFuncIndex() {
|
|
1327
|
+
return this.resolveFuncIndex("__nil_panic");
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
// `gofront/shared` buffers are `(base i32, len i32)` views over the single
|
|
1331
|
+
// exported linear memory, bump-allocated by `__shared_new` during start.
|
|
1332
|
+
getSharedType() {
|
|
1333
|
+
if (this.sharedType) return this.sharedType;
|
|
1334
|
+
const typeIndex = this._getTotalTypeCount();
|
|
1335
|
+
const typeEntry = {
|
|
1336
|
+
form: "struct",
|
|
1337
|
+
fields: [
|
|
1338
|
+
{ type: "i32", mutable: false }, // byte base
|
|
1339
|
+
{ type: "i32", mutable: false }, // element count
|
|
1340
|
+
],
|
|
1341
|
+
};
|
|
1342
|
+
this._pushType(typeEntry);
|
|
1343
|
+
this.memory = { min: 1 };
|
|
1344
|
+
this.exports.push({ name: "memory", kind: "memory", index: 0 });
|
|
1345
|
+
const topGlobalIndex = this.globals.length;
|
|
1346
|
+
this.globals.push({
|
|
1347
|
+
type: "i32",
|
|
1348
|
+
mutable: true,
|
|
1349
|
+
init: [{ op: "i32.const", value: 0 }],
|
|
1350
|
+
});
|
|
1351
|
+
const wType = { kind: "ref", nullable: true, typeIndex };
|
|
1352
|
+
this.sharedType = { typeIndex, typeEntry, wType, topGlobalIndex };
|
|
1353
|
+
return this.sharedType;
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
getSliceBoundsPanicFuncIndex() {
|
|
1357
|
+
return this.resolveFuncIndex("__slice_bounds_panic");
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
getPrintlnEmptyIndex() {
|
|
1361
|
+
return this.getOrAddFuncImport("env", "println_empty", [], []);
|
|
1362
|
+
}
|
|
1363
|
+
|
|
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
|
+
);
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
getLogImportIndex(wType, isLast = false, isBool = false) {
|
|
1551
|
+
const prefix = isLast ? "println" : "print";
|
|
1552
|
+
if (isBool) {
|
|
1553
|
+
return this.getOrAddFuncImport("env", `${prefix}_bool`, ["i32"], []);
|
|
1554
|
+
}
|
|
1555
|
+
switch (wType) {
|
|
1556
|
+
case "i32":
|
|
1557
|
+
return this.getOrAddFuncImport("env", `${prefix}_i32`, ["i32"], []);
|
|
1558
|
+
case "i64":
|
|
1559
|
+
return this.getOrAddFuncImport("env", `${prefix}_i64`, ["i64"], []);
|
|
1560
|
+
case "f32":
|
|
1561
|
+
return this.getOrAddFuncImport("env", `${prefix}_f32`, ["f32"], []);
|
|
1562
|
+
case "f64":
|
|
1563
|
+
return this.getOrAddFuncImport("env", `${prefix}_f64`, ["f64"], []);
|
|
1564
|
+
case "externref":
|
|
1565
|
+
return this.getOrAddFuncImport(
|
|
1566
|
+
"env",
|
|
1567
|
+
`${prefix}_str`,
|
|
1568
|
+
["externref"],
|
|
1569
|
+
[],
|
|
1570
|
+
);
|
|
1571
|
+
default:
|
|
1572
|
+
if (typeof wType === "object") {
|
|
1573
|
+
return this.getOrAddFuncImport(
|
|
1574
|
+
"env",
|
|
1575
|
+
`${prefix}_any`,
|
|
1576
|
+
["anyref"],
|
|
1577
|
+
[],
|
|
1578
|
+
);
|
|
1579
|
+
}
|
|
1580
|
+
return this.getOrAddFuncImport("env", `${prefix}_i32`, ["i32"], []);
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
getStringLenImportIndex() {
|
|
1585
|
+
return this.getOrAddFuncImport("env", "str_len", ["externref"], ["i32"]);
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
getStringConcatImportIndex() {
|
|
1589
|
+
return this.getOrAddFuncImport(
|
|
1590
|
+
"env",
|
|
1591
|
+
"str_concat",
|
|
1592
|
+
["externref", "externref"],
|
|
1593
|
+
["externref"],
|
|
1594
|
+
);
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
getStringCmpImportIndex(op) {
|
|
1598
|
+
const opMap = {
|
|
1599
|
+
"==": "str_eq",
|
|
1600
|
+
"!=": "str_ne",
|
|
1601
|
+
"<": "str_lt",
|
|
1602
|
+
"<=": "str_le",
|
|
1603
|
+
">": "str_gt",
|
|
1604
|
+
">=": "str_ge",
|
|
1605
|
+
};
|
|
1606
|
+
const fn = opMap[op] ?? "str_eq";
|
|
1607
|
+
return this.getOrAddFuncImport(
|
|
1608
|
+
"env",
|
|
1609
|
+
fn,
|
|
1610
|
+
["externref", "externref"],
|
|
1611
|
+
["i32"],
|
|
1612
|
+
);
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
getStringGetImportIndex() {
|
|
1616
|
+
return this.getOrAddFuncImport(
|
|
1617
|
+
"env",
|
|
1618
|
+
"str_get",
|
|
1619
|
+
["externref", "i32"],
|
|
1620
|
+
["i32"],
|
|
1621
|
+
);
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
getStringSliceImportIndex() {
|
|
1625
|
+
return this.getOrAddFuncImport(
|
|
1626
|
+
"env",
|
|
1627
|
+
"str_slice",
|
|
1628
|
+
["externref", "i32", "i32"],
|
|
1629
|
+
["externref"],
|
|
1630
|
+
);
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
getStringFromCodePointImportIndex() {
|
|
1634
|
+
return this.getOrAddFuncImport(
|
|
1635
|
+
"env",
|
|
1636
|
+
"str_from_code_point",
|
|
1637
|
+
["i32"],
|
|
1638
|
+
["externref"],
|
|
1639
|
+
);
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
getStringCodePointAtImportIndex() {
|
|
1643
|
+
return this.getOrAddFuncImport(
|
|
1644
|
+
"env",
|
|
1645
|
+
"str_code_point_at",
|
|
1646
|
+
["externref", "i32"],
|
|
1647
|
+
["i32"],
|
|
1648
|
+
);
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
getIsStringImportIndex() {
|
|
1652
|
+
return this.getOrAddFuncImport("env", "is_string", ["anyref"], ["i32"]);
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
getStringHashImportIndex() {
|
|
1656
|
+
return this.getOrAddFuncImport("env", "str_hash", ["externref"], ["i32"]);
|
|
1657
|
+
}
|
|
1658
|
+
|
|
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
|
+
getStrFromI64ImportIndex() {
|
|
1777
|
+
return this.getOrAddFuncImport(
|
|
1778
|
+
"env",
|
|
1779
|
+
"str_from_i64",
|
|
1780
|
+
["i64"],
|
|
1781
|
+
["externref"],
|
|
1782
|
+
);
|
|
1783
|
+
}
|
|
1784
|
+
|
|
1785
|
+
getStrFromI32ImportIndex() {
|
|
1786
|
+
return this.getOrAddFuncImport(
|
|
1787
|
+
"env",
|
|
1788
|
+
"str_from_i32",
|
|
1789
|
+
["i32"],
|
|
1790
|
+
["externref"],
|
|
1791
|
+
);
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
getStrFromF64ImportIndex() {
|
|
1795
|
+
return this.getOrAddFuncImport(
|
|
1796
|
+
"env",
|
|
1797
|
+
"str_from_f64",
|
|
1798
|
+
["f64"],
|
|
1799
|
+
["externref"],
|
|
1800
|
+
);
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1803
|
+
getStrToBytesImportIndex() {
|
|
1804
|
+
return this.getOrAddFuncImport(
|
|
1805
|
+
"env",
|
|
1806
|
+
"str_to_bytes",
|
|
1807
|
+
["externref"],
|
|
1808
|
+
["i32"],
|
|
1809
|
+
);
|
|
1810
|
+
}
|
|
1811
|
+
|
|
1812
|
+
getStrByteAtImportIndex() {
|
|
1813
|
+
return this.getOrAddFuncImport("env", "str_byte_at", ["i32"], ["i32"]);
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
getBytesToStrInitImportIndex() {
|
|
1817
|
+
return this.getOrAddFuncImport("env", "bytes_to_str_init", ["i32"], []);
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
getBytesToStrPushImportIndex() {
|
|
1821
|
+
return this.getOrAddFuncImport("env", "bytes_to_str_push", ["i32"], []);
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
getBytesToStrFinishImportIndex() {
|
|
1825
|
+
return this.getOrAddFuncImport(
|
|
1826
|
+
"env",
|
|
1827
|
+
"bytes_to_str_finish",
|
|
1828
|
+
[],
|
|
1829
|
+
["externref"],
|
|
1830
|
+
);
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
getMathImportIndex(jsName, isBinary = false) {
|
|
1834
|
+
const params = isBinary ? ["f64", "f64"] : ["f64"];
|
|
1835
|
+
return this.getOrAddFuncImport("Math", jsName, params, ["f64"]);
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
getMathRandomImportIndex() {
|
|
1839
|
+
return this.getOrAddFuncImport("Math", "random", [], ["f64"]);
|
|
1840
|
+
}
|
|
1841
|
+
|
|
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
|
+
resolveFuncIndex(name) {
|
|
1872
|
+
return this.funcMap.get(name) ?? null;
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1875
|
+
getFuncParamTypes(name) {
|
|
1876
|
+
return this.funcParamTypes.get(name) ?? [];
|
|
1877
|
+
}
|
|
1878
|
+
|
|
1879
|
+
getFuncParamGoTypes(name) {
|
|
1880
|
+
return this.funcParamGoTypes.get(name) ?? [];
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
resolveGlobal(name) {
|
|
1884
|
+
return this.globalCache.get(name) ?? null;
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1887
|
+
resolveConst(name) {
|
|
1888
|
+
return this.constCache.get(name) ?? null;
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
isNonLiteralConst(name) {
|
|
1892
|
+
return this.nonLiteralConsts.has(name);
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1895
|
+
// Interface method dispatch is a `ref.test` chain over every concrete type
|
|
1896
|
+
// that declares `methodName` (no itabs); the static interface type is not
|
|
1897
|
+
// needed to build the candidate list.
|
|
1898
|
+
findInterfaceCandidates(_ifaceType, methodName) {
|
|
1899
|
+
const candidates = [];
|
|
1900
|
+
const seenKeys = new Set();
|
|
1901
|
+
|
|
1902
|
+
// 1. Struct types
|
|
1903
|
+
for (const [sName, sInfo] of this.structTypes.entries()) {
|
|
1904
|
+
const directMethodName = `${sName}.${methodName}`;
|
|
1905
|
+
if (this.funcMap.has(directMethodName)) {
|
|
1906
|
+
const fIdx = this.resolveFuncIndex(directMethodName);
|
|
1907
|
+
const paramGoTypes = this.getFuncParamGoTypes(directMethodName);
|
|
1908
|
+
const paramWTypes = this.getFuncParamTypes(directMethodName);
|
|
1909
|
+
const recvGoType = paramGoTypes[0];
|
|
1910
|
+
const isPtrRecv = recvGoType?.kind === "pointer";
|
|
1911
|
+
|
|
1912
|
+
if (isPtrRecv) {
|
|
1913
|
+
const key = `ptr:${sName}`;
|
|
1914
|
+
if (!seenKeys.has(key)) {
|
|
1915
|
+
seenKeys.add(key);
|
|
1916
|
+
candidates.push({
|
|
1917
|
+
recvTypeName: sName,
|
|
1918
|
+
testTypeIndex: sInfo.typeIndex,
|
|
1919
|
+
isBoxedValue: false,
|
|
1920
|
+
needsValueDeref: false,
|
|
1921
|
+
embedPath: null,
|
|
1922
|
+
funcIndex: fIdx,
|
|
1923
|
+
targetParamTypes: paramWTypes,
|
|
1924
|
+
structInfo: sInfo,
|
|
1925
|
+
});
|
|
1926
|
+
}
|
|
1927
|
+
} else {
|
|
1928
|
+
// Value receiver: both *sName and sName match
|
|
1929
|
+
const ptrKey = `ptr:${sName}`;
|
|
1930
|
+
if (!seenKeys.has(ptrKey)) {
|
|
1931
|
+
seenKeys.add(ptrKey);
|
|
1932
|
+
candidates.push({
|
|
1933
|
+
recvTypeName: sName,
|
|
1934
|
+
testTypeIndex: sInfo.typeIndex,
|
|
1935
|
+
isBoxedValue: false,
|
|
1936
|
+
needsValueDeref: true,
|
|
1937
|
+
embedPath: null,
|
|
1938
|
+
funcIndex: fIdx,
|
|
1939
|
+
targetParamTypes: paramWTypes,
|
|
1940
|
+
targetRecvWType: paramWTypes[0],
|
|
1941
|
+
structInfo: sInfo,
|
|
1942
|
+
});
|
|
1943
|
+
}
|
|
1944
|
+
const box = this.getIfaceBoxType({ kind: "named", name: sName });
|
|
1945
|
+
const valKey = `val:${sName}`;
|
|
1946
|
+
if (box && !seenKeys.has(valKey)) {
|
|
1947
|
+
seenKeys.add(valKey);
|
|
1948
|
+
candidates.push({
|
|
1949
|
+
recvTypeName: sName,
|
|
1950
|
+
testTypeIndex: box.typeIndex,
|
|
1951
|
+
isBoxedValue: true,
|
|
1952
|
+
needsValueDeref: false,
|
|
1953
|
+
embedPath: null,
|
|
1954
|
+
funcIndex: fIdx,
|
|
1955
|
+
targetParamTypes: paramWTypes,
|
|
1956
|
+
targetRecvWType: paramWTypes[0],
|
|
1957
|
+
structInfo: sInfo,
|
|
1958
|
+
});
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
|
|
1963
|
+
// 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
|
+
}
|
|
1994
|
+
|
|
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
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
|
|
2023
|
+
// 2. Named non-struct types
|
|
2024
|
+
for (const [fnName, fIdx] of this.funcMap.entries()) {
|
|
2025
|
+
if (fnName.endsWith(`.${methodName}`)) {
|
|
2026
|
+
const rName = fnName.slice(0, fnName.length - methodName.length - 1);
|
|
2027
|
+
if (!this.structTypes.has(rName)) {
|
|
2028
|
+
const rType = this.checker?.types?.get(rName) ?? {
|
|
2029
|
+
kind: "named",
|
|
2030
|
+
name: rName,
|
|
2031
|
+
};
|
|
2032
|
+
const paramGoTypes = this.getFuncParamGoTypes(fnName);
|
|
2033
|
+
const paramWTypes = this.getFuncParamTypes(fnName);
|
|
2034
|
+
const isPtrRecv = paramGoTypes[0]?.kind === "pointer";
|
|
2035
|
+
const isSlice = isSliceType(rType, this.checker);
|
|
2036
|
+
|
|
2037
|
+
if (isSlice) {
|
|
2038
|
+
const elemType = getSliceElemType(rType, this.checker);
|
|
2039
|
+
const sliceInfo = this.getSliceType(elemType);
|
|
2040
|
+
const ptrIfaceBox = this.getIfaceBoxType({
|
|
2041
|
+
kind: "pointer",
|
|
2042
|
+
base: rType,
|
|
2043
|
+
});
|
|
2044
|
+
const box = this.getBoxType(rType);
|
|
2045
|
+
if (isPtrRecv) {
|
|
2046
|
+
const ptrKey = `ptr:${rName}`;
|
|
2047
|
+
if (ptrIfaceBox && !seenKeys.has(ptrKey)) {
|
|
2048
|
+
seenKeys.add(ptrKey);
|
|
2049
|
+
candidates.push({
|
|
2050
|
+
recvTypeName: rName,
|
|
2051
|
+
testTypeIndex: ptrIfaceBox.typeIndex,
|
|
2052
|
+
isBoxedValue: true,
|
|
2053
|
+
needsValueDeref: false,
|
|
2054
|
+
embedPath: null,
|
|
2055
|
+
funcIndex: fIdx,
|
|
2056
|
+
targetParamTypes: paramWTypes,
|
|
2057
|
+
});
|
|
2058
|
+
}
|
|
2059
|
+
} else {
|
|
2060
|
+
const ptrKey = `ptr:${rName}`;
|
|
2061
|
+
if (ptrIfaceBox && box && !seenKeys.has(ptrKey)) {
|
|
2062
|
+
seenKeys.add(ptrKey);
|
|
2063
|
+
candidates.push({
|
|
2064
|
+
recvTypeName: rName,
|
|
2065
|
+
testTypeIndex: ptrIfaceBox.typeIndex,
|
|
2066
|
+
isBoxedValue: true,
|
|
2067
|
+
nilPtrPanic: true,
|
|
2068
|
+
needsValueDeref: false,
|
|
2069
|
+
embedPath: [
|
|
2070
|
+
{
|
|
2071
|
+
castTypeIndex: box.typeIndex,
|
|
2072
|
+
parentTypeIndex: box.typeIndex,
|
|
2073
|
+
fieldIndex: 0,
|
|
2074
|
+
},
|
|
2075
|
+
],
|
|
2076
|
+
funcIndex: fIdx,
|
|
2077
|
+
targetParamTypes: paramWTypes,
|
|
2078
|
+
});
|
|
2079
|
+
}
|
|
2080
|
+
const key = `slice:${rName}`;
|
|
2081
|
+
if (sliceInfo && !seenKeys.has(key)) {
|
|
2082
|
+
seenKeys.add(key);
|
|
2083
|
+
candidates.push({
|
|
2084
|
+
recvTypeName: rName,
|
|
2085
|
+
testTypeIndex: sliceInfo.typeIndex,
|
|
2086
|
+
isBoxedValue: false,
|
|
2087
|
+
needsValueDeref: false,
|
|
2088
|
+
embedPath: null,
|
|
2089
|
+
funcIndex: fIdx,
|
|
2090
|
+
targetParamTypes: paramWTypes,
|
|
2091
|
+
});
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
} else {
|
|
2095
|
+
const box = isPtrRecv
|
|
2096
|
+
? this.getIfaceBoxType({ kind: "pointer", base: rType })
|
|
2097
|
+
: this.getIfaceBoxType(rType);
|
|
2098
|
+
const key = `${isPtrRecv ? "ptr" : "named"}:${rName}`;
|
|
2099
|
+
if (box && !seenKeys.has(key)) {
|
|
2100
|
+
seenKeys.add(key);
|
|
2101
|
+
candidates.push({
|
|
2102
|
+
recvTypeName: rName,
|
|
2103
|
+
testTypeIndex: box.typeIndex,
|
|
2104
|
+
isBoxedValue: true,
|
|
2105
|
+
needsValueDeref: false,
|
|
2106
|
+
embedPath: null,
|
|
2107
|
+
funcIndex: fIdx,
|
|
2108
|
+
targetParamTypes: paramWTypes,
|
|
2109
|
+
});
|
|
2110
|
+
}
|
|
2111
|
+
if (!isPtrRecv)
|
|
2112
|
+
this._addPtrScalarCandidate(
|
|
2113
|
+
candidates,
|
|
2114
|
+
seenKeys,
|
|
2115
|
+
rName,
|
|
2116
|
+
rType,
|
|
2117
|
+
fIdx,
|
|
2118
|
+
paramWTypes,
|
|
2119
|
+
);
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
|
|
2125
|
+
return candidates;
|
|
2126
|
+
}
|
|
2127
|
+
|
|
2128
|
+
// A value-receiver method of a named scalar is also in the method set of
|
|
2129
|
+
// the pointer: `var g Getter = &x` holds a pointer box whose target is read
|
|
2130
|
+
// before the call.
|
|
2131
|
+
_addPtrScalarCandidate(
|
|
2132
|
+
candidates,
|
|
2133
|
+
seenKeys,
|
|
2134
|
+
rName,
|
|
2135
|
+
rType,
|
|
2136
|
+
fIdx,
|
|
2137
|
+
paramWTypes,
|
|
2138
|
+
) {
|
|
2139
|
+
if (!isScalarType(rType, this.checker, this)) return;
|
|
2140
|
+
const ptrBox = this.getIfaceBoxType({ kind: "pointer", base: rType });
|
|
2141
|
+
const target = this.getBoxType(rType);
|
|
2142
|
+
const ptrKey = `ptr:${rName}`;
|
|
2143
|
+
if (!ptrBox || seenKeys.has(ptrKey)) return;
|
|
2144
|
+
seenKeys.add(ptrKey);
|
|
2145
|
+
candidates.push({
|
|
2146
|
+
recvTypeName: rName,
|
|
2147
|
+
testTypeIndex: ptrBox.typeIndex,
|
|
2148
|
+
isBoxedValue: true,
|
|
2149
|
+
nilPtrPanic: true,
|
|
2150
|
+
needsValueDeref: false,
|
|
2151
|
+
embedPath: [
|
|
2152
|
+
{
|
|
2153
|
+
castTypeIndex: target.typeIndex,
|
|
2154
|
+
parentTypeIndex: target.typeIndex,
|
|
2155
|
+
fieldIndex: 0,
|
|
2156
|
+
},
|
|
2157
|
+
],
|
|
2158
|
+
funcIndex: fIdx,
|
|
2159
|
+
targetParamTypes: paramWTypes,
|
|
2160
|
+
});
|
|
2161
|
+
}
|
|
2162
|
+
|
|
2163
|
+
getTypesImplementingInterface(ifaceType) {
|
|
2164
|
+
const candidates = [];
|
|
2165
|
+
const seen = new Set();
|
|
2166
|
+
const ifaceResolved =
|
|
2167
|
+
this.checker?.resolveType?.(
|
|
2168
|
+
ifaceType?.kind === "named" ? ifaceType.underlying : ifaceType,
|
|
2169
|
+
) ?? ifaceType;
|
|
2170
|
+
|
|
2171
|
+
// 1. Struct types
|
|
2172
|
+
for (const [sName, sInfo] of this.structTypes.entries()) {
|
|
2173
|
+
const namedType = this.checker?.types?.get(sName) ?? {
|
|
2174
|
+
kind: "named",
|
|
2175
|
+
name: sName,
|
|
2176
|
+
};
|
|
2177
|
+
const ptrType = { kind: "pointer", base: namedType };
|
|
2178
|
+
|
|
2179
|
+
// Check pointer to struct
|
|
2180
|
+
if (this.checker?.implements?.(ptrType, ifaceResolved)) {
|
|
2181
|
+
if (!seen.has(sInfo.typeIndex)) {
|
|
2182
|
+
seen.add(sInfo.typeIndex);
|
|
2183
|
+
candidates.push({ name: `*${sName}`, typeIndex: sInfo.typeIndex });
|
|
2184
|
+
}
|
|
2185
|
+
}
|
|
2186
|
+
|
|
2187
|
+
// Check struct value (boxed)
|
|
2188
|
+
if (this.checker?.implements?.(namedType, ifaceResolved)) {
|
|
2189
|
+
const box = this.getIfaceBoxType(namedType);
|
|
2190
|
+
if (box && !seen.has(box.typeIndex)) {
|
|
2191
|
+
seen.add(box.typeIndex);
|
|
2192
|
+
candidates.push({ name: sName, typeIndex: box.typeIndex });
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
|
|
2197
|
+
// 2. Named non-struct types that have methods
|
|
2198
|
+
if (this.checker?.types) {
|
|
2199
|
+
for (const [tName, tVal] of this.checker.types.entries()) {
|
|
2200
|
+
if (
|
|
2201
|
+
tVal?.kind === "named" &&
|
|
2202
|
+
tVal.underlying?.kind !== "struct" &&
|
|
2203
|
+
tVal.underlying?.kind !== "interface"
|
|
2204
|
+
) {
|
|
2205
|
+
if (this.checker?.implements?.(tVal, ifaceResolved)) {
|
|
2206
|
+
const box = this.getIfaceBoxType(tVal);
|
|
2207
|
+
if (box && !seen.has(box.typeIndex)) {
|
|
2208
|
+
seen.add(box.typeIndex);
|
|
2209
|
+
candidates.push({ name: tName, typeIndex: box.typeIndex });
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
this._addPtrNamedImplementor(
|
|
2213
|
+
tVal,
|
|
2214
|
+
tName,
|
|
2215
|
+
ifaceResolved,
|
|
2216
|
+
seen,
|
|
2217
|
+
candidates,
|
|
2218
|
+
);
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
|
|
2223
|
+
return candidates;
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2226
|
+
// `*T` of a named scalar or slice is boxed apart from `T`.
|
|
2227
|
+
_addPtrNamedImplementor(tVal, tName, ifaceResolved, seen, candidates) {
|
|
2228
|
+
const ptrType = { kind: "pointer", base: tVal };
|
|
2229
|
+
if (
|
|
2230
|
+
!isScalarType(tVal, this.checker, this) &&
|
|
2231
|
+
!isSliceType(tVal, this.checker)
|
|
2232
|
+
)
|
|
2233
|
+
return;
|
|
2234
|
+
if (!this.checker?.implements?.(ptrType, ifaceResolved)) return;
|
|
2235
|
+
const box = this.getIfaceBoxType(ptrType);
|
|
2236
|
+
if (!box || seen.has(box.typeIndex)) return;
|
|
2237
|
+
seen.add(box.typeIndex);
|
|
2238
|
+
candidates.push({ name: `*${tName}`, typeIndex: box.typeIndex });
|
|
2239
|
+
}
|
|
2240
|
+
}
|