gofront 1.5.1 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +174 -466
- package/package.json +16 -20
- package/src/backend/js/expressions.js +288 -128
- package/src/backend/js/index.js +138 -56
- package/src/backend/js/runtime.js +1055 -53
- package/src/backend/js/source-map.js +32 -0
- package/src/backend/js/statements.js +151 -83
- package/src/backend/js/stdlib/base64.js +12 -0
- package/src/backend/js/stdlib/binary.js +102 -0
- package/src/backend/js/stdlib/bits.js +15 -1
- package/src/backend/js/stdlib/builder.js +28 -9
- package/src/backend/js/stdlib/bytes.js +75 -18
- package/src/backend/js/stdlib/errors.js +22 -3
- package/src/backend/js/stdlib/fmt.js +95 -20
- package/src/backend/js/stdlib/hex.js +17 -0
- package/src/backend/js/stdlib/html.js +1 -1
- package/src/backend/js/stdlib/index.js +19 -0
- package/src/backend/js/stdlib/io.js +105 -3
- package/src/backend/js/stdlib/maps.js +1 -1
- package/src/backend/js/stdlib/math.js +13 -1
- package/src/backend/js/stdlib/path.js +20 -6
- package/src/backend/js/stdlib/rand.js +10 -5
- package/src/backend/js/stdlib/sha256.js +12 -0
- package/src/backend/js/stdlib/shared.js +17 -0
- package/src/backend/js/stdlib/slices.js +53 -27
- package/src/backend/js/stdlib/sort.js +1 -1
- package/src/backend/js/stdlib/strconv.js +52 -11
- package/src/backend/js/stdlib/strings.js +152 -16
- package/src/backend/js/stdlib/sync.js +13 -0
- package/src/backend/js/stdlib/testing.js +2 -2
- package/src/backend/js/stdlib/time.js +93 -22
- package/src/backend/js/stdlib/unicode.js +2 -2
- package/src/backend/js/templ.js +1 -1
- package/src/backend/wasm/boundary.js +1677 -58
- package/src/backend/wasm/const-fold.js +205 -0
- package/src/backend/wasm/emit-builtins.js +1230 -0
- package/src/backend/wasm/emit-exprs.js +2096 -0
- package/src/backend/wasm/emit-maps.js +1999 -0
- package/src/backend/wasm/emit-shared.js +519 -0
- package/src/backend/wasm/emit-stdlib.js +13 -0
- package/src/backend/wasm/emit-stmts.js +2466 -0
- package/src/backend/wasm/emit.js +656 -5031
- package/src/backend/wasm/encode.js +259 -85
- package/src/backend/wasm/glue.js +19 -5
- package/src/backend/wasm/index.js +2432 -86
- package/src/backend/wasm/monomorph.js +711 -0
- package/src/backend/wasm/optimize.js +236 -0
- package/src/backend/wasm/stdlib/bytes.js +5039 -0
- package/src/backend/wasm/stdlib/core.js +8903 -0
- package/src/backend/wasm/stdlib/index.js +40 -0
- package/src/backend/wasm/stdlib/io.js +2161 -0
- package/src/backend/wasm/stdlib/packages.js +2400 -0
- package/src/backend/wasm/stdlib/pure.js +720 -0
- package/src/backend/wasm/stdlib/time.js +1200 -0
- package/src/backend/wasm/types.js +312 -36
- package/src/backend/wasm/wat.js +49 -2
- package/src/cli-core.js +276 -73
- package/src/compiler.js +162 -25
- package/src/dts-parser.js +79 -126
- package/src/index.js +88 -121
- package/src/lexer.js +80 -29
- package/src/lower/boxing.js +4 -6
- package/src/lower/captures.js +12 -23
- package/src/lower/escape.js +4 -9
- package/src/lower/functions.js +277 -5
- package/src/lower/globals.js +7 -15
- package/src/lower/index.js +5 -2
- package/src/lower/ownership.js +35 -19
- package/src/lower/range.js +2 -5
- package/src/lower/walk.js +25 -0
- package/src/minifier.js +20 -68
- package/src/parser/declarations.js +3 -6
- package/src/parser/expressions.js +3 -8
- package/src/parser/index.js +18 -3
- package/src/parser/statements.js +25 -38
- package/src/parser/types.js +1 -2
- package/src/resolver.js +3 -27
- package/src/templ-lexer.js +29 -63
- package/src/templ-parser.js +22 -45
- package/src/test-runner.js +20 -0
- package/src/typechecker/assignability.js +123 -14
- package/src/typechecker/expressions.js +244 -4
- package/src/typechecker/index.js +27 -1
- package/src/typechecker/resolve.js +71 -12
- package/src/typechecker/statements.js +0 -7
- package/src/typechecker/stdlib/core.js +292 -103
- package/src/typechecker/stdlib/extended.js +471 -4
- package/src/typechecker/stdlib/shared.js +53 -0
- package/src/typechecker/stdlib.js +2 -0
- package/src/typechecker/types.js +545 -8
- package/src/vendor.js +2 -2
|
@@ -10,15 +10,254 @@ import {
|
|
|
10
10
|
collectBoundaryMeta,
|
|
11
11
|
generateFacade,
|
|
12
12
|
scanBoundaryImports,
|
|
13
|
+
wTypeKey,
|
|
13
14
|
} from "./boundary.js";
|
|
15
|
+
import { foldConstExpr } from "./const-fold.js";
|
|
14
16
|
import { FunctionEmitter } from "./emit.js";
|
|
15
17
|
import { encodeModule, isGoFrontWasm } from "./encode.js";
|
|
16
|
-
import {
|
|
18
|
+
import { hasGenerics, monomorphise } from "./monomorph.js";
|
|
19
|
+
import { hasNativeWasmOpt, optimizeWasm } from "./optimize.js";
|
|
20
|
+
import { BINARY_MATH_IMPORTS } from "./stdlib/index.js";
|
|
21
|
+
import {
|
|
22
|
+
anonIfaceName,
|
|
23
|
+
ERROR_STRUCT,
|
|
24
|
+
getFuncSignature,
|
|
25
|
+
getInterfaceMethodList,
|
|
26
|
+
getMapKeyValTypes,
|
|
27
|
+
getReceiverTypeName,
|
|
28
|
+
goTypeKey,
|
|
29
|
+
isInterfaceType,
|
|
30
|
+
isMapType,
|
|
31
|
+
isNonEmptyInterface,
|
|
32
|
+
isTestingT,
|
|
33
|
+
toWasmType,
|
|
34
|
+
} from "./types.js";
|
|
17
35
|
import { emitWat } from "./wat.js";
|
|
18
36
|
|
|
19
|
-
export { isGoFrontWasm };
|
|
37
|
+
export { hasNativeWasmOpt, isGoFrontWasm, optimizeWasm };
|
|
38
|
+
|
|
39
|
+
const ERROR_PRODUCING_PKGS = new Set([
|
|
40
|
+
"errors",
|
|
41
|
+
"fmt",
|
|
42
|
+
"strconv",
|
|
43
|
+
"path",
|
|
44
|
+
"path/filepath",
|
|
45
|
+
"io",
|
|
46
|
+
"strings",
|
|
47
|
+
"bytes",
|
|
48
|
+
"time",
|
|
49
|
+
"sync",
|
|
50
|
+
"crypto/rand",
|
|
51
|
+
"rand",
|
|
52
|
+
"crypto/sha256",
|
|
53
|
+
"sha256",
|
|
54
|
+
"encoding/binary",
|
|
55
|
+
"binary",
|
|
56
|
+
"encoding/hex",
|
|
57
|
+
"hex",
|
|
58
|
+
"encoding/base64",
|
|
59
|
+
"base64",
|
|
60
|
+
]);
|
|
61
|
+
// Packages whose calls lower to the `env.*` imports in STDLIB_IMPORTS (the
|
|
62
|
+
// matching JS lives in WASM_STDLIB_JS). Only modules importing one of them
|
|
63
|
+
// declare the imports and ship that JS block.
|
|
64
|
+
const STDLIB_ENV_PKGS = new Set([
|
|
65
|
+
"strings",
|
|
66
|
+
"strconv",
|
|
67
|
+
"unicode/utf8",
|
|
68
|
+
"fmt",
|
|
69
|
+
"testing",
|
|
70
|
+
"unicode",
|
|
71
|
+
"math/rand",
|
|
72
|
+
"path",
|
|
73
|
+
"path/filepath",
|
|
74
|
+
"html",
|
|
75
|
+
"bytes",
|
|
76
|
+
"io",
|
|
77
|
+
"time",
|
|
78
|
+
"sync",
|
|
79
|
+
"crypto/rand",
|
|
80
|
+
"rand",
|
|
81
|
+
"crypto/sha256",
|
|
82
|
+
"sha256",
|
|
83
|
+
"encoding/binary",
|
|
84
|
+
"binary",
|
|
85
|
+
"encoding/hex",
|
|
86
|
+
"hex",
|
|
87
|
+
"encoding/base64",
|
|
88
|
+
"base64",
|
|
89
|
+
]);
|
|
90
|
+
|
|
91
|
+
function importsAnyOf(programs, pkgs) {
|
|
92
|
+
const has = (p) => (pkgs instanceof Set ? pkgs.has(p) : pkgs.includes(p));
|
|
93
|
+
return (programs ?? []).some((p) =>
|
|
94
|
+
(p.imports ?? []).some((decl) =>
|
|
95
|
+
(decl.imports ?? [decl]).some((imp) => has(imp.path)),
|
|
96
|
+
),
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function hasErrorUsage(programs) {
|
|
101
|
+
return (programs ?? []).some((p) => {
|
|
102
|
+
let found = false;
|
|
103
|
+
const scan = (n) => {
|
|
104
|
+
if (!n || found || typeof n !== "object") return;
|
|
105
|
+
if (Array.isArray(n)) {
|
|
106
|
+
for (const x of n) scan(x);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (
|
|
110
|
+
(n.kind === "Ident" ||
|
|
111
|
+
n.kind === "TypeName" ||
|
|
112
|
+
n.kind === "basic" ||
|
|
113
|
+
n.kind === "interface") &&
|
|
114
|
+
n.name === "error"
|
|
115
|
+
) {
|
|
116
|
+
found = true;
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
for (const k of Object.keys(n)) {
|
|
120
|
+
if (!k.startsWith("_")) scan(n[k]);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
scan(p);
|
|
124
|
+
return found;
|
|
125
|
+
});
|
|
126
|
+
}
|
|
20
127
|
|
|
21
|
-
|
|
128
|
+
// [name, params, results] for `env.*` imports backing stdlib calls; the JS
|
|
129
|
+
// side lives in WASM_STDLIB_JS (boundary.js). All are registered eagerly
|
|
130
|
+
// (when any STDLIB_ENV_PKGS package is imported) because the import table is
|
|
131
|
+
// locked before function bodies are emitted.
|
|
132
|
+
const S = "externref";
|
|
133
|
+
const STDLIB_IMPORTS = [
|
|
134
|
+
// strings
|
|
135
|
+
["str_trim", [S, S], [S]],
|
|
136
|
+
["str_trim_left", [S, S], [S]],
|
|
137
|
+
["str_trim_right", [S, S], [S]],
|
|
138
|
+
["str_trim_prefix", [S, S], [S]],
|
|
139
|
+
["str_trim_suffix", [S, S], [S]],
|
|
140
|
+
["str_cut", [S, S], ["i32"]],
|
|
141
|
+
["str_cut_before", [], [S]],
|
|
142
|
+
["str_cut_after", [], [S]],
|
|
143
|
+
["str_cut_prefix", [S, S], ["i32"]],
|
|
144
|
+
["str_cut_suffix", [S, S], ["i32"]],
|
|
145
|
+
["str_replace", [S, S, S, "i32"], [S]],
|
|
146
|
+
["str_contains_rune", [S, "i32"], ["i32"]],
|
|
147
|
+
["str_contains_any", [S, S], ["i32"]],
|
|
148
|
+
["str_index_byte", [S, "i32"], ["i32"]],
|
|
149
|
+
["str_index_rune", [S, "i32"], ["i32"]],
|
|
150
|
+
["str_index_any", [S, S], ["i32"]],
|
|
151
|
+
["str_last_index_byte", [S, "i32"], ["i32"]],
|
|
152
|
+
["str_last_index_any", [S, S], ["i32"]],
|
|
153
|
+
["str_title", [S], [S]],
|
|
154
|
+
["str_split", [S, S], ["i32"]],
|
|
155
|
+
["str_split_n", [S, S, "i32"], ["i32"]],
|
|
156
|
+
["str_split_after", [S, S], ["i32"]],
|
|
157
|
+
["str_split_after_n", [S, S, "i32"], ["i32"]],
|
|
158
|
+
["str_fields", [S], ["i32"]],
|
|
159
|
+
["str_part", ["i32"], [S]],
|
|
160
|
+
["str_replacer_init", [S], []],
|
|
161
|
+
["str_replacer_add", [S, S], []],
|
|
162
|
+
["str_replacer_run", [], [S]],
|
|
163
|
+
// strconv
|
|
164
|
+
["strconv_atoi", [S], ["i64"]],
|
|
165
|
+
["strconv_parse_int", [S, "i32", "i32"], ["i64"]],
|
|
166
|
+
["strconv_parse_float", [S], ["f64"]],
|
|
167
|
+
["strconv_parse_bool", [S], ["i32"]],
|
|
168
|
+
["strconv_err", [], [S]],
|
|
169
|
+
["strconv_err_kind", [], ["i32"]],
|
|
170
|
+
["strconv_format_float", ["f64", "i32", "i32", "i32"], [S]],
|
|
171
|
+
["strconv_format_int", ["i64", "i32"], [S]],
|
|
172
|
+
["strconv_format_uint", ["i64", "i32"], [S]],
|
|
173
|
+
["strconv_parse_uint", [S, "i32", "i32"], ["i64"]],
|
|
174
|
+
["strconv_quote", [S], [S]],
|
|
175
|
+
["strconv_unquote", [S], [S]],
|
|
176
|
+
// unicode/utf8
|
|
177
|
+
["utf8_rune_count", [S], ["i32"]],
|
|
178
|
+
["utf8_rune_len", ["i32"], ["i32"]],
|
|
179
|
+
["utf8_valid_string", [S], ["i32"]],
|
|
180
|
+
["utf8_decode_rune", [S, "i32"], ["i32"]],
|
|
181
|
+
["utf8_decode_size", [], ["i32"]],
|
|
182
|
+
["utf8_full_rune", [S], ["i32"]],
|
|
183
|
+
// fmt (arguments travel through the testing_arg_* buffer)
|
|
184
|
+
["fmt_sprintf", [S], [S]],
|
|
185
|
+
["fmt_printf", [S], []],
|
|
186
|
+
["fmt_format_v", [S], [S]],
|
|
187
|
+
["fmt_sscan_init", [S, "i32"], ["i32"]],
|
|
188
|
+
["fmt_sscanf_init", [S, S], ["i32"]],
|
|
189
|
+
["fmt_sscan_has_err", [], ["i32"]],
|
|
190
|
+
["fmt_sscan_err_msg", [], [S]],
|
|
191
|
+
["fmt_sscan_token_str", ["i32"], [S]],
|
|
192
|
+
["fmt_sscan_token_i64", ["i32"], ["i64"]],
|
|
193
|
+
["fmt_sscan_token_f64", ["i32"], ["f64"]],
|
|
194
|
+
["fmt_sscan_token_bool", ["i32"], ["i32"]],
|
|
195
|
+
// testing.T.Run: the harness calls back into the exported __testing_run_cb
|
|
196
|
+
["testing_run", [S, S, "anyref"], ["i32"]],
|
|
197
|
+
// unicode
|
|
198
|
+
["unicode_is_letter", ["i32"], ["i32"]],
|
|
199
|
+
["unicode_is_digit", ["i32"], ["i32"]],
|
|
200
|
+
["unicode_is_space", ["i32"], ["i32"]],
|
|
201
|
+
["unicode_is_upper", ["i32"], ["i32"]],
|
|
202
|
+
["unicode_is_lower", ["i32"], ["i32"]],
|
|
203
|
+
["unicode_is_punct", ["i32"], ["i32"]],
|
|
204
|
+
["unicode_is_control", ["i32"], ["i32"]],
|
|
205
|
+
["unicode_is_print", ["i32"], ["i32"]],
|
|
206
|
+
["unicode_is_graphic", ["i32"], ["i32"]],
|
|
207
|
+
["unicode_to_upper", ["i32"], ["i32"]],
|
|
208
|
+
["unicode_to_lower", ["i32"], ["i32"]],
|
|
209
|
+
// html
|
|
210
|
+
["html_escape", [S], [S]],
|
|
211
|
+
["html_unescape", [S], [S]],
|
|
212
|
+
// path
|
|
213
|
+
["path_base", [S], [S]],
|
|
214
|
+
["path_dir", [S], [S]],
|
|
215
|
+
["path_ext", [S], [S]],
|
|
216
|
+
["path_is_abs", [S], ["i32"]],
|
|
217
|
+
["path_clean", [S], [S]],
|
|
218
|
+
["path_split", [S], []],
|
|
219
|
+
["path_split_dir", [], [S]],
|
|
220
|
+
["path_split_file", [], [S]],
|
|
221
|
+
["path_join_init", [], []],
|
|
222
|
+
["path_join_push", [S], []],
|
|
223
|
+
["path_join_run", [], [S]],
|
|
224
|
+
["path_match", [S, S], ["i32"]],
|
|
225
|
+
["path_match_has_err", [], ["i32"]],
|
|
226
|
+
["bytes_trim", [S, S, "i32", "i32"], []],
|
|
227
|
+
["bytes_trim_start", [], ["i32"]],
|
|
228
|
+
["bytes_trim_end", [], ["i32"]],
|
|
229
|
+
// time
|
|
230
|
+
["time_now_ms", [], ["f64"]],
|
|
231
|
+
["time_timezone_offset", ["f64"], ["i32"]],
|
|
232
|
+
["time_parse", [S, S], ["f64"]],
|
|
233
|
+
["time_parse_err", [], [S]],
|
|
234
|
+
["time_parse_offset", [], ["i32"]],
|
|
235
|
+
[
|
|
236
|
+
"time_format",
|
|
237
|
+
["i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", "i32", S],
|
|
238
|
+
[S],
|
|
239
|
+
],
|
|
240
|
+
["time_duration_string", ["i64"], [S]],
|
|
241
|
+
// raw binary buffer for crypto/rand, crypto/sha256, hex, base64
|
|
242
|
+
["raw_bytes_init", ["i32"], []],
|
|
243
|
+
["raw_bytes_push", ["i32"], []],
|
|
244
|
+
["raw_bytes_len", [], ["i32"]],
|
|
245
|
+
["raw_bytes_get", ["i32"], ["i32"]],
|
|
246
|
+
// crypto/rand
|
|
247
|
+
["crypto_rand_fill", ["i32"], ["i32"]],
|
|
248
|
+
// crypto/sha256
|
|
249
|
+
["crypto_sha256_sum", [], ["i32"]],
|
|
250
|
+
// encoding/hex
|
|
251
|
+
["hex_encode_to_string", [], [S]],
|
|
252
|
+
["hex_decode_string", [S], ["i32"]],
|
|
253
|
+
["hex_get_err", [], [S]],
|
|
254
|
+
// encoding/base64
|
|
255
|
+
["base64_encode_to_string", ["i32"], [S]],
|
|
256
|
+
["base64_decode_string", ["i32", S], ["i32"]],
|
|
257
|
+
["base64_get_err", [], [S]],
|
|
258
|
+
];
|
|
259
|
+
|
|
260
|
+
class ModuleEmitter {
|
|
22
261
|
constructor(checker, lowerResult = null, programs = [], options = {}) {
|
|
23
262
|
this.checker = checker;
|
|
24
263
|
this.lowerResult = lowerResult;
|
|
@@ -34,9 +273,13 @@ export class ModuleEmitter {
|
|
|
34
273
|
|
|
35
274
|
this.structTypes = new Map(); // name -> struct info
|
|
36
275
|
this.boxTypes = new Map(); // wType key -> box info
|
|
276
|
+
this.ifaceBoxTypes = new Map(); // key -> iface box info
|
|
37
277
|
this.arrayTypes = new Map(); // wType key -> array info
|
|
38
278
|
this.sliceTypes = new Map(); // wType key -> slice info
|
|
39
279
|
this.sliceTypesByIndex = new Map(); // typeIndex -> slice info
|
|
280
|
+
this.mapTypes = new Map(); // wType key -> map info
|
|
281
|
+
this.mapTypesByIndex = new Map(); // typeIndex -> map info
|
|
282
|
+
this.mapBucketsType = null; // shared (array (mut i32))
|
|
40
283
|
this.closureTypes = new Map(); // key -> closure info
|
|
41
284
|
this.envTypes = new Map(); // key -> env info
|
|
42
285
|
|
|
@@ -54,11 +297,19 @@ export class ModuleEmitter {
|
|
|
54
297
|
this.funcMap = new Map(); // name -> global func index
|
|
55
298
|
this.funcParamTypes = new Map(); // name -> param types array
|
|
56
299
|
this.funcParamGoTypes = new Map(); // name -> param Go types array
|
|
300
|
+
this.funcReturnTypesByIdx = new Map(); // global func index -> result wasm types
|
|
57
301
|
|
|
58
302
|
this.exports = []; // export entries
|
|
59
303
|
this.stringTable = []; // string literals
|
|
60
304
|
this.stringCache = new Map();
|
|
61
305
|
|
|
306
|
+
// `errors.New` / `fmt.Errorf` / strconv parse failures allocate the
|
|
307
|
+
// runtime's own error struct; it joins the struct rec group so the
|
|
308
|
+
// `error.Error` dispatcher can `ref.test` it like any user type.
|
|
309
|
+
this.hasErrorType =
|
|
310
|
+
importsAnyOf(programs, ERROR_PRODUCING_PKGS) || hasErrorUsage(programs);
|
|
311
|
+
this.usesStdlibEnv = importsAnyOf(programs, STDLIB_ENV_PKGS);
|
|
312
|
+
|
|
62
313
|
if (programs && programs.length > 0) {
|
|
63
314
|
this.initTypes(programs);
|
|
64
315
|
}
|
|
@@ -73,6 +324,20 @@ export class ModuleEmitter {
|
|
|
73
324
|
const seen = new Set();
|
|
74
325
|
const aliases = []; // qualified key (pkg.T) -> unqualified struct name
|
|
75
326
|
|
|
327
|
+
if (this.hasErrorType) {
|
|
328
|
+
seen.add(ERROR_STRUCT);
|
|
329
|
+
rawStructs.push({
|
|
330
|
+
name: ERROR_STRUCT,
|
|
331
|
+
structType: {
|
|
332
|
+
kind: "struct",
|
|
333
|
+
fields: new Map([
|
|
334
|
+
["msg", { kind: "basic", name: "string" }],
|
|
335
|
+
["cause", { kind: "basic", name: "any" }],
|
|
336
|
+
]),
|
|
337
|
+
},
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
|
|
76
341
|
for (const p of progs) {
|
|
77
342
|
for (const d of p.decls ?? []) {
|
|
78
343
|
if (d.kind === "TypeDecl" && d.type?.kind === "StructType") {
|
|
@@ -136,7 +401,8 @@ export class ModuleEmitter {
|
|
|
136
401
|
for (let i = 0; i < rawStructs.length; i++) {
|
|
137
402
|
const s = rawStructs[i];
|
|
138
403
|
const info = this.structTypes.get(s.name);
|
|
139
|
-
const resolvedStruct =
|
|
404
|
+
const resolvedStruct =
|
|
405
|
+
s.structType ?? this.checker?.types?.get(s.name)?.underlying;
|
|
140
406
|
|
|
141
407
|
const fields = [];
|
|
142
408
|
const fieldIndexMap = new Map();
|
|
@@ -211,23 +477,95 @@ export class ModuleEmitter {
|
|
|
211
477
|
return this.structTypes.get(name) ?? null;
|
|
212
478
|
}
|
|
213
479
|
|
|
480
|
+
getAnonStructType(fields) {
|
|
481
|
+
const key = fields.map((f) => `${f.name}:${goTypeKey(f.goType)}`).join(";");
|
|
482
|
+
const name = `__anon_${key.replace(/[^a-zA-Z0-9_]/g, "_")}`;
|
|
483
|
+
if (this.structTypes.has(name)) {
|
|
484
|
+
return this.structTypes.get(name);
|
|
485
|
+
}
|
|
486
|
+
const fieldWTypes = fields.map((f) =>
|
|
487
|
+
toWasmType(f.goType, this.checker, this),
|
|
488
|
+
);
|
|
489
|
+
const typeIndex = this._getTotalTypeCount();
|
|
490
|
+
const typeEntry = {
|
|
491
|
+
form: "struct",
|
|
492
|
+
fields: fieldWTypes.map((t) => ({ type: t, mutable: true })),
|
|
493
|
+
};
|
|
494
|
+
this._pushType(typeEntry);
|
|
495
|
+
const info = {
|
|
496
|
+
name,
|
|
497
|
+
typeIndex,
|
|
498
|
+
typeEntry,
|
|
499
|
+
fields: fields.map((f, i) => ({
|
|
500
|
+
name: f.name,
|
|
501
|
+
wType: fieldWTypes[i],
|
|
502
|
+
goType: f.goType,
|
|
503
|
+
fieldIndex: i,
|
|
504
|
+
})),
|
|
505
|
+
fieldIndexMap: new Map(fields.map((f, i) => [f.name, i])),
|
|
506
|
+
embeds: [],
|
|
507
|
+
pkgName: null,
|
|
508
|
+
pkgTarget: "wasm",
|
|
509
|
+
isAnon: true,
|
|
510
|
+
};
|
|
511
|
+
this.structTypes.set(name, info);
|
|
512
|
+
return info;
|
|
513
|
+
}
|
|
514
|
+
|
|
214
515
|
getBoxType(goType) {
|
|
215
|
-
const
|
|
216
|
-
const key = typeof wType === "string" ? wType : JSON.stringify(wType);
|
|
516
|
+
const key = goTypeKey(goType);
|
|
217
517
|
if (this.boxTypes.has(key)) {
|
|
218
518
|
return this.boxTypes.get(key);
|
|
219
519
|
}
|
|
520
|
+
const wType = toWasmType(goType, this.checker, this);
|
|
220
521
|
const typeIndex = this._getTotalTypeCount();
|
|
221
522
|
const typeEntry = {
|
|
222
523
|
form: "struct",
|
|
223
524
|
fields: [{ type: wType, mutable: true }],
|
|
224
525
|
};
|
|
225
526
|
this._pushType(typeEntry);
|
|
226
|
-
const boxInfo = { typeIndex, typeEntry, wType };
|
|
527
|
+
const boxInfo = { key, typeIndex, typeEntry, wType, goType };
|
|
227
528
|
this.boxTypes.set(key, boxInfo);
|
|
228
529
|
return boxInfo;
|
|
229
530
|
}
|
|
230
531
|
|
|
532
|
+
getIfaceBoxType(goType) {
|
|
533
|
+
const key = goTypeKey(goType);
|
|
534
|
+
if (this.ifaceBoxTypes.has(key)) {
|
|
535
|
+
return this.ifaceBoxTypes.get(key);
|
|
536
|
+
}
|
|
537
|
+
const wType = toWasmType(goType, this.checker, this);
|
|
538
|
+
const tagIndex = this.ifaceBoxTypes.size;
|
|
539
|
+
const tagTypeIndex = this._getTotalTypeCount();
|
|
540
|
+
const tagFields = [];
|
|
541
|
+
for (let i = 0; i < tagIndex; i++) {
|
|
542
|
+
tagFields.push({ type: "i32", mutable: false });
|
|
543
|
+
}
|
|
544
|
+
this._pushType({
|
|
545
|
+
form: "struct",
|
|
546
|
+
fields: tagFields,
|
|
547
|
+
});
|
|
548
|
+
const typeIndex = this._getTotalTypeCount();
|
|
549
|
+
const typeEntry = {
|
|
550
|
+
form: "struct",
|
|
551
|
+
fields: [
|
|
552
|
+
{ type: wType, mutable: true },
|
|
553
|
+
{
|
|
554
|
+
type: {
|
|
555
|
+
kind: "ref",
|
|
556
|
+
nullable: true,
|
|
557
|
+
typeIndex: tagTypeIndex,
|
|
558
|
+
},
|
|
559
|
+
mutable: false,
|
|
560
|
+
},
|
|
561
|
+
],
|
|
562
|
+
};
|
|
563
|
+
this._pushType(typeEntry);
|
|
564
|
+
const boxInfo = { key, typeIndex, typeEntry, wType, goType, tagTypeIndex };
|
|
565
|
+
this.ifaceBoxTypes.set(key, boxInfo);
|
|
566
|
+
return boxInfo;
|
|
567
|
+
}
|
|
568
|
+
|
|
231
569
|
getArrayType(elemGoType) {
|
|
232
570
|
const elemWType = toWasmType(elemGoType, this.checker, this);
|
|
233
571
|
const key =
|
|
@@ -308,6 +646,107 @@ export class ModuleEmitter {
|
|
|
308
646
|
return this.sliceTypesByIndex?.get(typeIndex) ?? null;
|
|
309
647
|
}
|
|
310
648
|
|
|
649
|
+
getMapType(keyGoType, valGoType) {
|
|
650
|
+
const keyWType = toWasmType(keyGoType, this.checker, this);
|
|
651
|
+
const valWType = toWasmType(valGoType, this.checker, this);
|
|
652
|
+
const key = `${wTypeKey(keyWType)}:${wTypeKey(valWType)}`;
|
|
653
|
+
if (this.mapTypes.has(key)) {
|
|
654
|
+
return this.mapTypes.get(key);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
if (!this.mapBucketsType) {
|
|
658
|
+
const typeIndex = this._getTotalTypeCount();
|
|
659
|
+
this._pushType({
|
|
660
|
+
form: "array",
|
|
661
|
+
elemType: "i32",
|
|
662
|
+
mutable: true,
|
|
663
|
+
});
|
|
664
|
+
this.mapBucketsType = { typeIndex };
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
const entryTypeIndex = this._getTotalTypeCount();
|
|
668
|
+
this._pushType({
|
|
669
|
+
form: "struct",
|
|
670
|
+
fields: [
|
|
671
|
+
{ type: keyWType, mutable: true }, // 0: key
|
|
672
|
+
{ type: valWType, mutable: true }, // 1: val
|
|
673
|
+
{ type: "i32", mutable: true }, // 2: next (bucket chain)
|
|
674
|
+
{ type: "i32", mutable: true }, // 3: order_prev (insertion order)
|
|
675
|
+
{ type: "i32", mutable: true }, // 4: order_next (insertion order)
|
|
676
|
+
{ type: "i32", mutable: true }, // 5: active (1 = active, 0 = deleted)
|
|
677
|
+
],
|
|
678
|
+
});
|
|
679
|
+
|
|
680
|
+
const entriesTypeIndex = this._getTotalTypeCount();
|
|
681
|
+
this._pushType({
|
|
682
|
+
form: "array",
|
|
683
|
+
elemType: { kind: "ref", nullable: true, typeIndex: entryTypeIndex },
|
|
684
|
+
mutable: true,
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
const mapTypeIndex = this._getTotalTypeCount();
|
|
688
|
+
this._pushType({
|
|
689
|
+
form: "struct",
|
|
690
|
+
fields: [
|
|
691
|
+
{
|
|
692
|
+
type: {
|
|
693
|
+
kind: "ref",
|
|
694
|
+
nullable: true,
|
|
695
|
+
typeIndex: this.mapBucketsType.typeIndex,
|
|
696
|
+
},
|
|
697
|
+
mutable: true,
|
|
698
|
+
}, // 0: buckets
|
|
699
|
+
{
|
|
700
|
+
type: {
|
|
701
|
+
kind: "ref",
|
|
702
|
+
nullable: true,
|
|
703
|
+
typeIndex: entriesTypeIndex,
|
|
704
|
+
},
|
|
705
|
+
mutable: true,
|
|
706
|
+
}, // 1: entries
|
|
707
|
+
{ type: "i32", mutable: true }, // 2: len
|
|
708
|
+
{ type: "i32", mutable: true }, // 3: cap
|
|
709
|
+
{ type: "i32", mutable: true }, // 4: count
|
|
710
|
+
{ type: "i32", mutable: true }, // 5: head
|
|
711
|
+
{ type: "i32", mutable: true }, // 6: tail
|
|
712
|
+
{ type: "i32", mutable: true }, // 7: free_head
|
|
713
|
+
{ type: "i32", mutable: true }, // 8: num_buckets
|
|
714
|
+
],
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
const suffix = key.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
718
|
+
const mapInfo = {
|
|
719
|
+
key,
|
|
720
|
+
suffix,
|
|
721
|
+
typeIndex: mapTypeIndex,
|
|
722
|
+
entryTypeIndex,
|
|
723
|
+
entriesTypeIndex,
|
|
724
|
+
bucketsTypeIndex: this.mapBucketsType.typeIndex,
|
|
725
|
+
keyWType,
|
|
726
|
+
valWType,
|
|
727
|
+
keyGoType,
|
|
728
|
+
valGoType,
|
|
729
|
+
wType: { kind: "ref", nullable: true, typeIndex: mapTypeIndex },
|
|
730
|
+
makeFuncName: `__map_make_${suffix}`,
|
|
731
|
+
getFuncName: `__map_get_${suffix}`,
|
|
732
|
+
getOkFuncName: `__map_get_ok_${suffix}`,
|
|
733
|
+
setFuncName: `__map_set_${suffix}`,
|
|
734
|
+
deleteFuncName: `__map_delete_${suffix}`,
|
|
735
|
+
lenFuncName: `__map_len_${suffix}`,
|
|
736
|
+
clearFuncName: `__map_clear_${suffix}`,
|
|
737
|
+
keysFuncName: `__map_keys_${suffix}`,
|
|
738
|
+
valuesFuncName: `__map_values_${suffix}`,
|
|
739
|
+
cloneFuncName: `__map_clone_${suffix}`,
|
|
740
|
+
};
|
|
741
|
+
this.mapTypes.set(key, mapInfo);
|
|
742
|
+
this.mapTypesByIndex.set(mapTypeIndex, mapInfo);
|
|
743
|
+
return mapInfo;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
getMapTypeByIndex(typeIndex) {
|
|
747
|
+
return this.mapTypesByIndex?.get(typeIndex) ?? null;
|
|
748
|
+
}
|
|
749
|
+
|
|
311
750
|
getClosureType(goType) {
|
|
312
751
|
const sig = getFuncSignature(goType, this.checker);
|
|
313
752
|
const paramWTypes = sig.params
|
|
@@ -399,9 +838,11 @@ export class ModuleEmitter {
|
|
|
399
838
|
|
|
400
839
|
_initRuntimeImports() {
|
|
401
840
|
this._initPanicTag();
|
|
841
|
+
this._initPanicGlobal();
|
|
402
842
|
this.getPanicImportIndex(); // env.panic
|
|
403
843
|
this.internString("runtime error: index out of range");
|
|
404
844
|
this.internString("runtime error: slice bounds out of range");
|
|
845
|
+
this.internString("assignment to entry in nil map");
|
|
405
846
|
|
|
406
847
|
// String built-ins
|
|
407
848
|
this.getStringImportIndex(); // env.str
|
|
@@ -417,7 +858,51 @@ export class ModuleEmitter {
|
|
|
417
858
|
this.getStringSliceImportIndex(); // env.str_slice
|
|
418
859
|
this.getStringFromCodePointImportIndex(); // env.str_from_code_point
|
|
419
860
|
this.getStringCodePointAtImportIndex(); // env.str_code_point_at
|
|
861
|
+
this.getStringHashImportIndex(); // env.str_hash
|
|
862
|
+
this.getStringToUpperImportIndex();
|
|
863
|
+
this.getStringToLowerImportIndex();
|
|
864
|
+
this.getStringTrimSpaceImportIndex();
|
|
865
|
+
this.getStringContainsImportIndex();
|
|
866
|
+
this.getStringHasPrefixImportIndex();
|
|
867
|
+
this.getStringHasSuffixImportIndex();
|
|
868
|
+
this.getStringIndexImportIndex();
|
|
869
|
+
this.getStringLastIndexImportIndex();
|
|
870
|
+
this.getStringRepeatImportIndex();
|
|
871
|
+
this.getStringReplaceAllImportIndex();
|
|
872
|
+
this.getStringEqualFoldImportIndex();
|
|
873
|
+
this.getStringCountImportIndex();
|
|
874
|
+
this.getStrFromI64ImportIndex();
|
|
875
|
+
this.getStrFromI32ImportIndex();
|
|
876
|
+
this.getStrFromF64ImportIndex();
|
|
420
877
|
this.getIsStringImportIndex(); // env.is_string
|
|
878
|
+
this.getStrToBytesImportIndex();
|
|
879
|
+
this.getStrByteAtImportIndex();
|
|
880
|
+
this.getBytesToStrInitImportIndex();
|
|
881
|
+
this.getBytesToStrPushImportIndex();
|
|
882
|
+
this.getBytesToStrFinishImportIndex();
|
|
883
|
+
this.getTestingCallImportIndex();
|
|
884
|
+
this.getTestingNameImportIndex();
|
|
885
|
+
this.getTestingFlagImportIndex();
|
|
886
|
+
for (const t of ["i32", "i64", "f32", "f64", "externref", "anyref"]) {
|
|
887
|
+
this.getTestingArgImportIndex(t, false);
|
|
888
|
+
}
|
|
889
|
+
this.getTestingArgImportIndex("i32", true);
|
|
890
|
+
this.getFmtNewArrayIndex();
|
|
891
|
+
this.getFmtArrayPushIndex();
|
|
892
|
+
this.getFmtNewMapIndex();
|
|
893
|
+
this.getFmtMapSetIndex();
|
|
894
|
+
this.getFmtNewStructIndex();
|
|
895
|
+
this.getFmtStructSetIndex();
|
|
896
|
+
this.getFmtBoxI32Index();
|
|
897
|
+
this.getFmtBoxI64Index();
|
|
898
|
+
this.getFmtBoxF64Index();
|
|
899
|
+
this.getFmtBoxBoolIndex();
|
|
900
|
+
this.getFmtNewBoxIndex();
|
|
901
|
+
if (this.usesStdlibEnv) {
|
|
902
|
+
for (const [name, params, results] of STDLIB_IMPORTS) {
|
|
903
|
+
this.getOrAddFuncImport("env", name, params, results);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
421
906
|
|
|
422
907
|
// Logging built-ins
|
|
423
908
|
this.getPrintlnEmptyIndex(); // env.println_empty
|
|
@@ -442,12 +927,12 @@ export class ModuleEmitter {
|
|
|
442
927
|
"log2",
|
|
443
928
|
"log10",
|
|
444
929
|
"round",
|
|
930
|
+
"cbrt",
|
|
445
931
|
];
|
|
446
932
|
for (const m of unaryMath) {
|
|
447
933
|
this.getMathImportIndex(m, false);
|
|
448
934
|
}
|
|
449
|
-
const
|
|
450
|
-
for (const m of binaryMath) {
|
|
935
|
+
for (const m of BINARY_MATH_IMPORTS) {
|
|
451
936
|
this.getMathImportIndex(m, true);
|
|
452
937
|
}
|
|
453
938
|
}
|
|
@@ -461,6 +946,188 @@ export class ModuleEmitter {
|
|
|
461
946
|
this.exports.push({ name: "panicTag", kind: "tag", index: 0 });
|
|
462
947
|
}
|
|
463
948
|
|
|
949
|
+
getPanicNodeTypeIndex() {
|
|
950
|
+
if (this.panicNodeTypeIndex !== undefined) {
|
|
951
|
+
return this.panicNodeTypeIndex;
|
|
952
|
+
}
|
|
953
|
+
const typeIndex = this._getTotalTypeCount();
|
|
954
|
+
const typeEntry = {
|
|
955
|
+
form: "rec",
|
|
956
|
+
types: [
|
|
957
|
+
{
|
|
958
|
+
form: "struct",
|
|
959
|
+
fields: [
|
|
960
|
+
{
|
|
961
|
+
type: "anyref",
|
|
962
|
+
mutable: true,
|
|
963
|
+
},
|
|
964
|
+
{
|
|
965
|
+
type: "i32",
|
|
966
|
+
mutable: true,
|
|
967
|
+
},
|
|
968
|
+
{
|
|
969
|
+
type: {
|
|
970
|
+
kind: "ref",
|
|
971
|
+
nullable: true,
|
|
972
|
+
typeIndex,
|
|
973
|
+
},
|
|
974
|
+
mutable: true,
|
|
975
|
+
},
|
|
976
|
+
],
|
|
977
|
+
},
|
|
978
|
+
],
|
|
979
|
+
};
|
|
980
|
+
this._pushType(typeEntry);
|
|
981
|
+
this.panicNodeTypeIndex = typeIndex;
|
|
982
|
+
return typeIndex;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
_initPanicGlobal() {
|
|
986
|
+
const pType = this.getPanicNodeTypeIndex();
|
|
987
|
+
const gIdx = this.globals.length;
|
|
988
|
+
this.globals.push({
|
|
989
|
+
type: { kind: "ref", nullable: true, typeIndex: pType },
|
|
990
|
+
mutable: true,
|
|
991
|
+
init: [{ op: "ref.null", typeIndex: pType }],
|
|
992
|
+
});
|
|
993
|
+
this.panicGlobalIndex = gIdx;
|
|
994
|
+
this.exports.push({ name: "__panic", kind: "global", index: gIdx });
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
getPanicGlobalIndex() {
|
|
998
|
+
return this.panicGlobalIndex;
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
// `__deferArmed` is set to 1 immediately before a deferred closure is
|
|
1002
|
+
// invoked and cleared by that closure's prologue. recover() only takes
|
|
1003
|
+
// effect when the current frame observed the flag, which gives Go's
|
|
1004
|
+
// "recover must be called directly by the deferred function" rule.
|
|
1005
|
+
getDeferArmedGlobalIndex() {
|
|
1006
|
+
if (this.deferArmedGlobalIndex !== undefined) {
|
|
1007
|
+
return this.deferArmedGlobalIndex;
|
|
1008
|
+
}
|
|
1009
|
+
const gIdx = this.globals.length;
|
|
1010
|
+
this.globals.push({
|
|
1011
|
+
type: "i32",
|
|
1012
|
+
mutable: true,
|
|
1013
|
+
init: [{ op: "i32.const", value: 0 }],
|
|
1014
|
+
});
|
|
1015
|
+
this.deferArmedGlobalIndex = gIdx;
|
|
1016
|
+
return gIdx;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
getRandStateGlobalIndex() {
|
|
1020
|
+
if (this.randStateGlobalIndex !== undefined) {
|
|
1021
|
+
return this.randStateGlobalIndex;
|
|
1022
|
+
}
|
|
1023
|
+
const gIdx = this.globals.length;
|
|
1024
|
+
this.globals.push({
|
|
1025
|
+
type: "i64",
|
|
1026
|
+
mutable: true,
|
|
1027
|
+
init: [{ op: "i64.const", value: 1n }],
|
|
1028
|
+
});
|
|
1029
|
+
this.randStateGlobalIndex = gIdx;
|
|
1030
|
+
return gIdx;
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
getStrconvErrGlobalIndex(field) {
|
|
1034
|
+
if (!this.strconvErrGlobals) this.strconvErrGlobals = new Map();
|
|
1035
|
+
if (this.strconvErrGlobals.has(field)) {
|
|
1036
|
+
return this.strconvErrGlobals.get(field);
|
|
1037
|
+
}
|
|
1038
|
+
const gIdx = this.globals.length;
|
|
1039
|
+
this.globals.push({
|
|
1040
|
+
type: "anyref",
|
|
1041
|
+
mutable: true,
|
|
1042
|
+
init: [{ op: "ref.null", heapType: "any" }],
|
|
1043
|
+
});
|
|
1044
|
+
this.strconvErrGlobals.set(field, gIdx);
|
|
1045
|
+
return gIdx;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
getPathErrGlobalIndex(field) {
|
|
1049
|
+
if (!this.pathErrGlobals) this.pathErrGlobals = new Map();
|
|
1050
|
+
if (this.pathErrGlobals.has(field)) {
|
|
1051
|
+
return this.pathErrGlobals.get(field);
|
|
1052
|
+
}
|
|
1053
|
+
const gIdx = this.globals.length;
|
|
1054
|
+
this.globals.push({
|
|
1055
|
+
type: "anyref",
|
|
1056
|
+
mutable: true,
|
|
1057
|
+
init: [{ op: "ref.null", heapType: "any" }],
|
|
1058
|
+
});
|
|
1059
|
+
this.pathErrGlobals.set(field, gIdx);
|
|
1060
|
+
return gIdx;
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
getIoErrGlobalIndex(field) {
|
|
1064
|
+
if (!this.ioErrGlobals) this.ioErrGlobals = new Map();
|
|
1065
|
+
if (this.ioErrGlobals.has(field)) {
|
|
1066
|
+
return this.ioErrGlobals.get(field);
|
|
1067
|
+
}
|
|
1068
|
+
const gIdx = this.globals.length;
|
|
1069
|
+
this.globals.push({
|
|
1070
|
+
type: "anyref",
|
|
1071
|
+
mutable: true,
|
|
1072
|
+
init: [{ op: "ref.null", heapType: "any" }],
|
|
1073
|
+
});
|
|
1074
|
+
this.ioErrGlobals.set(field, gIdx);
|
|
1075
|
+
return gIdx;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
getIoDiscardGlobalIndex() {
|
|
1079
|
+
if (this.ioDiscardGlobalIndex !== undefined) {
|
|
1080
|
+
return this.ioDiscardGlobalIndex;
|
|
1081
|
+
}
|
|
1082
|
+
this.getStructType("io.discard");
|
|
1083
|
+
const gIdx = this.globals.length;
|
|
1084
|
+
this.globals.push({
|
|
1085
|
+
type: "anyref",
|
|
1086
|
+
mutable: true,
|
|
1087
|
+
init: [{ op: "ref.null", heapType: "any" }],
|
|
1088
|
+
});
|
|
1089
|
+
this.ioDiscardGlobalIndex = gIdx;
|
|
1090
|
+
return gIdx;
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
getDeferNodeTypeIndex() {
|
|
1094
|
+
if (this.deferNodeTypeIndex !== undefined) {
|
|
1095
|
+
return this.deferNodeTypeIndex;
|
|
1096
|
+
}
|
|
1097
|
+
const deferClosureSig = { kind: "Signature", params: [], results: [] };
|
|
1098
|
+
const deferClosureInfo = this.getClosureType(deferClosureSig);
|
|
1099
|
+
const typeIndex = this._getTotalTypeCount();
|
|
1100
|
+
const typeEntry = {
|
|
1101
|
+
form: "rec",
|
|
1102
|
+
types: [
|
|
1103
|
+
{
|
|
1104
|
+
form: "struct",
|
|
1105
|
+
fields: [
|
|
1106
|
+
{
|
|
1107
|
+
type: {
|
|
1108
|
+
kind: "ref",
|
|
1109
|
+
nullable: true,
|
|
1110
|
+
typeIndex: deferClosureInfo.typeIndex,
|
|
1111
|
+
},
|
|
1112
|
+
mutable: false,
|
|
1113
|
+
},
|
|
1114
|
+
{
|
|
1115
|
+
type: {
|
|
1116
|
+
kind: "ref",
|
|
1117
|
+
nullable: true,
|
|
1118
|
+
typeIndex,
|
|
1119
|
+
},
|
|
1120
|
+
mutable: true,
|
|
1121
|
+
},
|
|
1122
|
+
],
|
|
1123
|
+
},
|
|
1124
|
+
],
|
|
1125
|
+
};
|
|
1126
|
+
this._pushType(typeEntry);
|
|
1127
|
+
this.deferNodeTypeIndex = typeIndex;
|
|
1128
|
+
return typeIndex;
|
|
1129
|
+
}
|
|
1130
|
+
|
|
464
1131
|
getTypeIndex(params, results) {
|
|
465
1132
|
const key = `${params.map((p) => (typeof p === "object" ? JSON.stringify(p) : p)).join(",")}=>${results.map((r) => (typeof r === "object" ? JSON.stringify(r) : r)).join(",")}`;
|
|
466
1133
|
if (this.typeCache.has(key)) {
|
|
@@ -518,6 +1185,32 @@ export class ModuleEmitter {
|
|
|
518
1185
|
return this.resolveFuncIndex("__bounds_panic");
|
|
519
1186
|
}
|
|
520
1187
|
|
|
1188
|
+
// `gofront/shared` buffers are `(base i32, len i32)` views over the single
|
|
1189
|
+
// exported linear memory, bump-allocated by `__shared_new` during start.
|
|
1190
|
+
getSharedType() {
|
|
1191
|
+
if (this.sharedType) return this.sharedType;
|
|
1192
|
+
const typeIndex = this._getTotalTypeCount();
|
|
1193
|
+
const typeEntry = {
|
|
1194
|
+
form: "struct",
|
|
1195
|
+
fields: [
|
|
1196
|
+
{ type: "i32", mutable: false }, // byte base
|
|
1197
|
+
{ type: "i32", mutable: false }, // element count
|
|
1198
|
+
],
|
|
1199
|
+
};
|
|
1200
|
+
this._pushType(typeEntry);
|
|
1201
|
+
this.memory = { min: 1 };
|
|
1202
|
+
this.exports.push({ name: "memory", kind: "memory", index: 0 });
|
|
1203
|
+
const topGlobalIndex = this.globals.length;
|
|
1204
|
+
this.globals.push({
|
|
1205
|
+
type: "i32",
|
|
1206
|
+
mutable: true,
|
|
1207
|
+
init: [{ op: "i32.const", value: 0 }],
|
|
1208
|
+
});
|
|
1209
|
+
const wType = { kind: "ref", nullable: true, typeIndex };
|
|
1210
|
+
this.sharedType = { typeIndex, typeEntry, wType, topGlobalIndex };
|
|
1211
|
+
return this.sharedType;
|
|
1212
|
+
}
|
|
1213
|
+
|
|
521
1214
|
getSliceBoundsPanicFuncIndex() {
|
|
522
1215
|
return this.resolveFuncIndex("__slice_bounds_panic");
|
|
523
1216
|
}
|
|
@@ -526,6 +1219,90 @@ export class ModuleEmitter {
|
|
|
526
1219
|
return this.getOrAddFuncImport("env", "println_empty", [], []);
|
|
527
1220
|
}
|
|
528
1221
|
|
|
1222
|
+
getFmtNewArrayIndex() {
|
|
1223
|
+
return this.getOrAddFuncImport("env", "fmt_new_array", [], ["externref"]);
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
getFmtArrayPushIndex() {
|
|
1227
|
+
return this.getOrAddFuncImport(
|
|
1228
|
+
"env",
|
|
1229
|
+
"fmt_array_push",
|
|
1230
|
+
["externref", "externref"],
|
|
1231
|
+
[],
|
|
1232
|
+
);
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
getFmtNewMapIndex() {
|
|
1236
|
+
return this.getOrAddFuncImport("env", "fmt_new_map", [], ["externref"]);
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
getFmtMapSetIndex() {
|
|
1240
|
+
return this.getOrAddFuncImport(
|
|
1241
|
+
"env",
|
|
1242
|
+
"fmt_map_set",
|
|
1243
|
+
["externref", "externref", "externref"],
|
|
1244
|
+
[],
|
|
1245
|
+
);
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
getFmtNewStructIndex() {
|
|
1249
|
+
return this.getOrAddFuncImport(
|
|
1250
|
+
"env",
|
|
1251
|
+
"fmt_new_struct",
|
|
1252
|
+
["anyref", "i32"],
|
|
1253
|
+
["externref"],
|
|
1254
|
+
);
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
getFmtStructSetIndex() {
|
|
1258
|
+
return this.getOrAddFuncImport(
|
|
1259
|
+
"env",
|
|
1260
|
+
"fmt_struct_set",
|
|
1261
|
+
["externref", "i32", "externref"],
|
|
1262
|
+
[],
|
|
1263
|
+
);
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
getFmtBoxI32Index() {
|
|
1267
|
+
return this.getOrAddFuncImport(
|
|
1268
|
+
"env",
|
|
1269
|
+
"fmt_box_i32",
|
|
1270
|
+
["i32"],
|
|
1271
|
+
["externref"],
|
|
1272
|
+
);
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
getFmtBoxI64Index() {
|
|
1276
|
+
return this.getOrAddFuncImport(
|
|
1277
|
+
"env",
|
|
1278
|
+
"fmt_box_i64",
|
|
1279
|
+
["i64"],
|
|
1280
|
+
["externref"],
|
|
1281
|
+
);
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
getFmtBoxF64Index() {
|
|
1285
|
+
return this.getOrAddFuncImport(
|
|
1286
|
+
"env",
|
|
1287
|
+
"fmt_box_f64",
|
|
1288
|
+
["f64"],
|
|
1289
|
+
["externref"],
|
|
1290
|
+
);
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
getFmtBoxBoolIndex() {
|
|
1294
|
+
return this.getOrAddFuncImport(
|
|
1295
|
+
"env",
|
|
1296
|
+
"fmt_box_bool",
|
|
1297
|
+
["i32"],
|
|
1298
|
+
["externref"],
|
|
1299
|
+
);
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
getFmtNewBoxIndex() {
|
|
1303
|
+
return this.getOrAddFuncImport("env", "fmt_new_box", [], ["externref"]);
|
|
1304
|
+
}
|
|
1305
|
+
|
|
529
1306
|
// `*testing.T` method calls: args are pushed one by one to a JS-side
|
|
530
1307
|
// buffer, then `testing_call(t, methodNameStrIdx)` invokes the method.
|
|
531
1308
|
getTestingArgImportIndex(wType, isBool = false) {
|
|
@@ -535,7 +1312,7 @@ export class ModuleEmitter {
|
|
|
535
1312
|
wType === "externref" ? "str" : wType === "anyref" ? "any" : wType;
|
|
536
1313
|
if (!["i32", "i64", "f32", "f64", "str", "any"].includes(suffix)) {
|
|
537
1314
|
throw new Error(
|
|
538
|
-
"only primitive, string and any arguments are supported for testing.T
|
|
1315
|
+
"only primitive, string, error and any arguments are supported for fmt and testing.T calls in wasm packages (format slices/structs field by field)",
|
|
539
1316
|
);
|
|
540
1317
|
}
|
|
541
1318
|
return this.getOrAddFuncImport(
|
|
@@ -594,9 +1371,15 @@ export class ModuleEmitter {
|
|
|
594
1371
|
["externref"],
|
|
595
1372
|
[],
|
|
596
1373
|
);
|
|
597
|
-
case "anyref":
|
|
598
|
-
return this.getOrAddFuncImport("env", `${prefix}_any`, ["anyref"], []);
|
|
599
1374
|
default:
|
|
1375
|
+
if (typeof wType === "object") {
|
|
1376
|
+
return this.getOrAddFuncImport(
|
|
1377
|
+
"env",
|
|
1378
|
+
`${prefix}_any`,
|
|
1379
|
+
["anyref"],
|
|
1380
|
+
[],
|
|
1381
|
+
);
|
|
1382
|
+
}
|
|
600
1383
|
return this.getOrAddFuncImport("env", `${prefix}_i32`, ["i32"], []);
|
|
601
1384
|
}
|
|
602
1385
|
}
|
|
@@ -672,37 +1455,441 @@ export class ModuleEmitter {
|
|
|
672
1455
|
return this.getOrAddFuncImport("env", "is_string", ["anyref"], ["i32"]);
|
|
673
1456
|
}
|
|
674
1457
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
return this.getOrAddFuncImport("Math", jsName, params, ["f64"]);
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
resolveFuncIndex(name) {
|
|
681
|
-
return this.funcMap.get(name) ?? null;
|
|
1458
|
+
getStringHashImportIndex() {
|
|
1459
|
+
return this.getOrAddFuncImport("env", "str_hash", ["externref"], ["i32"]);
|
|
682
1460
|
}
|
|
683
1461
|
|
|
684
|
-
|
|
685
|
-
return this.
|
|
1462
|
+
getStringToUpperImportIndex() {
|
|
1463
|
+
return this.getOrAddFuncImport(
|
|
1464
|
+
"env",
|
|
1465
|
+
"str_to_upper",
|
|
1466
|
+
["externref"],
|
|
1467
|
+
["externref"],
|
|
1468
|
+
);
|
|
686
1469
|
}
|
|
687
1470
|
|
|
688
|
-
|
|
689
|
-
return this.
|
|
1471
|
+
getStringToLowerImportIndex() {
|
|
1472
|
+
return this.getOrAddFuncImport(
|
|
1473
|
+
"env",
|
|
1474
|
+
"str_to_lower",
|
|
1475
|
+
["externref"],
|
|
1476
|
+
["externref"],
|
|
1477
|
+
);
|
|
690
1478
|
}
|
|
691
1479
|
|
|
692
|
-
|
|
693
|
-
return this.
|
|
1480
|
+
getStringTrimSpaceImportIndex() {
|
|
1481
|
+
return this.getOrAddFuncImport(
|
|
1482
|
+
"env",
|
|
1483
|
+
"str_trim_space",
|
|
1484
|
+
["externref"],
|
|
1485
|
+
["externref"],
|
|
1486
|
+
);
|
|
694
1487
|
}
|
|
695
1488
|
|
|
696
|
-
|
|
1489
|
+
getStringContainsImportIndex() {
|
|
1490
|
+
return this.getOrAddFuncImport(
|
|
1491
|
+
"env",
|
|
1492
|
+
"str_contains",
|
|
1493
|
+
["externref", "externref"],
|
|
1494
|
+
["i32"],
|
|
1495
|
+
);
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
getStringHasPrefixImportIndex() {
|
|
1499
|
+
return this.getOrAddFuncImport(
|
|
1500
|
+
"env",
|
|
1501
|
+
"str_has_prefix",
|
|
1502
|
+
["externref", "externref"],
|
|
1503
|
+
["i32"],
|
|
1504
|
+
);
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
getStringHasSuffixImportIndex() {
|
|
1508
|
+
return this.getOrAddFuncImport(
|
|
1509
|
+
"env",
|
|
1510
|
+
"str_has_suffix",
|
|
1511
|
+
["externref", "externref"],
|
|
1512
|
+
["i32"],
|
|
1513
|
+
);
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
getStringIndexImportIndex() {
|
|
1517
|
+
return this.getOrAddFuncImport(
|
|
1518
|
+
"env",
|
|
1519
|
+
"str_index",
|
|
1520
|
+
["externref", "externref"],
|
|
1521
|
+
["i32"],
|
|
1522
|
+
);
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
getStringLastIndexImportIndex() {
|
|
1526
|
+
return this.getOrAddFuncImport(
|
|
1527
|
+
"env",
|
|
1528
|
+
"str_last_index",
|
|
1529
|
+
["externref", "externref"],
|
|
1530
|
+
["i32"],
|
|
1531
|
+
);
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
getStringRepeatImportIndex() {
|
|
1535
|
+
return this.getOrAddFuncImport(
|
|
1536
|
+
"env",
|
|
1537
|
+
"str_repeat",
|
|
1538
|
+
["externref", "i32"],
|
|
1539
|
+
["externref"],
|
|
1540
|
+
);
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
getStringReplaceAllImportIndex() {
|
|
1544
|
+
return this.getOrAddFuncImport(
|
|
1545
|
+
"env",
|
|
1546
|
+
"str_replace_all",
|
|
1547
|
+
["externref", "externref", "externref"],
|
|
1548
|
+
["externref"],
|
|
1549
|
+
);
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
getStringEqualFoldImportIndex() {
|
|
1553
|
+
return this.getOrAddFuncImport(
|
|
1554
|
+
"env",
|
|
1555
|
+
"str_equal_fold",
|
|
1556
|
+
["externref", "externref"],
|
|
1557
|
+
["i32"],
|
|
1558
|
+
);
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
getStringCountImportIndex() {
|
|
1562
|
+
return this.getOrAddFuncImport(
|
|
1563
|
+
"env",
|
|
1564
|
+
"str_count",
|
|
1565
|
+
["externref", "externref"],
|
|
1566
|
+
["i32"],
|
|
1567
|
+
);
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
getStrFromI64ImportIndex() {
|
|
1571
|
+
return this.getOrAddFuncImport(
|
|
1572
|
+
"env",
|
|
1573
|
+
"str_from_i64",
|
|
1574
|
+
["i64"],
|
|
1575
|
+
["externref"],
|
|
1576
|
+
);
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
getStrFromI32ImportIndex() {
|
|
1580
|
+
return this.getOrAddFuncImport(
|
|
1581
|
+
"env",
|
|
1582
|
+
"str_from_i32",
|
|
1583
|
+
["i32"],
|
|
1584
|
+
["externref"],
|
|
1585
|
+
);
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
getStrFromF64ImportIndex() {
|
|
1589
|
+
return this.getOrAddFuncImport(
|
|
1590
|
+
"env",
|
|
1591
|
+
"str_from_f64",
|
|
1592
|
+
["f64"],
|
|
1593
|
+
["externref"],
|
|
1594
|
+
);
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
getStrToBytesImportIndex() {
|
|
1598
|
+
return this.getOrAddFuncImport(
|
|
1599
|
+
"env",
|
|
1600
|
+
"str_to_bytes",
|
|
1601
|
+
["externref"],
|
|
1602
|
+
["i32"],
|
|
1603
|
+
);
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
getStrByteAtImportIndex() {
|
|
1607
|
+
return this.getOrAddFuncImport("env", "str_byte_at", ["i32"], ["i32"]);
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
getBytesToStrInitImportIndex() {
|
|
1611
|
+
return this.getOrAddFuncImport("env", "bytes_to_str_init", ["i32"], []);
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
getBytesToStrPushImportIndex() {
|
|
1615
|
+
return this.getOrAddFuncImport("env", "bytes_to_str_push", ["i32"], []);
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
getBytesToStrFinishImportIndex() {
|
|
1619
|
+
return this.getOrAddFuncImport(
|
|
1620
|
+
"env",
|
|
1621
|
+
"bytes_to_str_finish",
|
|
1622
|
+
[],
|
|
1623
|
+
["externref"],
|
|
1624
|
+
);
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
getMathImportIndex(jsName, isBinary = false) {
|
|
1628
|
+
const params = isBinary ? ["f64", "f64"] : ["f64"];
|
|
1629
|
+
return this.getOrAddFuncImport("Math", jsName, params, ["f64"]);
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
getMathRandomImportIndex() {
|
|
1633
|
+
return this.getOrAddFuncImport("Math", "random", [], ["f64"]);
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
// Imports from the STDLIB_IMPORTS table (strings/strconv/utf8/fmt/testing.Run).
|
|
1637
|
+
getStdlibImportIndex(name) {
|
|
1638
|
+
const idx = this.importCache.get(`env.${name}`);
|
|
1639
|
+
if (idx === undefined) throw new Error(`Unknown runtime import: ${name}`);
|
|
1640
|
+
return idx;
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
getTimezoneOffsetImportIndex() {
|
|
1644
|
+
return this.getStdlibImportIndex("time_timezone_offset");
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
// Closure type of `func(*testing.T)` (the t.Run body).
|
|
1648
|
+
getTestingRunClosureType() {
|
|
1649
|
+
return this.getClosureType({
|
|
1650
|
+
kind: "func",
|
|
1651
|
+
params: [{ kind: "pointer", base: { kind: "named", name: "testing.T" } }],
|
|
1652
|
+
returns: [],
|
|
1653
|
+
});
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
resolveFuncIndex(name) {
|
|
1657
|
+
return this.funcMap.get(name) ?? null;
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
getFuncParamTypes(name) {
|
|
1661
|
+
return this.funcParamTypes.get(name) ?? [];
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
getFuncParamGoTypes(name) {
|
|
1665
|
+
return this.funcParamGoTypes.get(name) ?? [];
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
resolveGlobal(name) {
|
|
1669
|
+
return this.globalCache.get(name) ?? null;
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
resolveConst(name) {
|
|
697
1673
|
return this.constCache.get(name) ?? null;
|
|
698
1674
|
}
|
|
699
1675
|
|
|
700
1676
|
isNonLiteralConst(name) {
|
|
701
1677
|
return this.nonLiteralConsts.has(name);
|
|
702
1678
|
}
|
|
1679
|
+
|
|
1680
|
+
// Interface method dispatch is a `ref.test` chain over every concrete type
|
|
1681
|
+
// that declares `methodName` (no itabs); the static interface type is not
|
|
1682
|
+
// needed to build the candidate list.
|
|
1683
|
+
findInterfaceCandidates(_ifaceType, methodName) {
|
|
1684
|
+
const candidates = [];
|
|
1685
|
+
const seenKeys = new Set();
|
|
1686
|
+
|
|
1687
|
+
// 1. Struct types
|
|
1688
|
+
for (const [sName, sInfo] of this.structTypes.entries()) {
|
|
1689
|
+
const directMethodName = `${sName}.${methodName}`;
|
|
1690
|
+
if (this.funcMap.has(directMethodName)) {
|
|
1691
|
+
const fIdx = this.resolveFuncIndex(directMethodName);
|
|
1692
|
+
const paramGoTypes = this.getFuncParamGoTypes(directMethodName);
|
|
1693
|
+
const paramWTypes = this.getFuncParamTypes(directMethodName);
|
|
1694
|
+
const recvGoType = paramGoTypes[0];
|
|
1695
|
+
const isPtrRecv = recvGoType?.kind === "pointer";
|
|
1696
|
+
|
|
1697
|
+
if (isPtrRecv) {
|
|
1698
|
+
const key = `ptr:${sName}`;
|
|
1699
|
+
if (!seenKeys.has(key)) {
|
|
1700
|
+
seenKeys.add(key);
|
|
1701
|
+
candidates.push({
|
|
1702
|
+
recvTypeName: sName,
|
|
1703
|
+
testTypeIndex: sInfo.typeIndex,
|
|
1704
|
+
isBoxedValue: false,
|
|
1705
|
+
needsValueDeref: false,
|
|
1706
|
+
embedPath: null,
|
|
1707
|
+
funcIndex: fIdx,
|
|
1708
|
+
targetParamTypes: paramWTypes,
|
|
1709
|
+
structInfo: sInfo,
|
|
1710
|
+
});
|
|
1711
|
+
}
|
|
1712
|
+
} else {
|
|
1713
|
+
// Value receiver: both *sName and sName match
|
|
1714
|
+
const ptrKey = `ptr:${sName}`;
|
|
1715
|
+
if (!seenKeys.has(ptrKey)) {
|
|
1716
|
+
seenKeys.add(ptrKey);
|
|
1717
|
+
candidates.push({
|
|
1718
|
+
recvTypeName: sName,
|
|
1719
|
+
testTypeIndex: sInfo.typeIndex,
|
|
1720
|
+
isBoxedValue: false,
|
|
1721
|
+
needsValueDeref: true,
|
|
1722
|
+
embedPath: null,
|
|
1723
|
+
funcIndex: fIdx,
|
|
1724
|
+
targetParamTypes: paramWTypes,
|
|
1725
|
+
targetRecvWType: paramWTypes[0],
|
|
1726
|
+
structInfo: sInfo,
|
|
1727
|
+
});
|
|
1728
|
+
}
|
|
1729
|
+
const box = this.getIfaceBoxType({ kind: "named", name: sName });
|
|
1730
|
+
const valKey = `val:${sName}`;
|
|
1731
|
+
if (box && !seenKeys.has(valKey)) {
|
|
1732
|
+
seenKeys.add(valKey);
|
|
1733
|
+
candidates.push({
|
|
1734
|
+
recvTypeName: sName,
|
|
1735
|
+
testTypeIndex: box.typeIndex,
|
|
1736
|
+
isBoxedValue: true,
|
|
1737
|
+
needsValueDeref: false,
|
|
1738
|
+
embedPath: null,
|
|
1739
|
+
funcIndex: fIdx,
|
|
1740
|
+
targetParamTypes: paramWTypes,
|
|
1741
|
+
targetRecvWType: paramWTypes[0],
|
|
1742
|
+
structInfo: sInfo,
|
|
1743
|
+
});
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
// Promoted methods on sName
|
|
1749
|
+
if (sInfo.embeds && sInfo.embeds.length > 0) {
|
|
1750
|
+
for (const embed of sInfo.embeds) {
|
|
1751
|
+
const embedMethodName = `${embed.name}.${methodName}`;
|
|
1752
|
+
if (this.funcMap.has(embedMethodName)) {
|
|
1753
|
+
const fIdx = this.resolveFuncIndex(embedMethodName);
|
|
1754
|
+
const paramGoTypes = this.getFuncParamGoTypes(embedMethodName);
|
|
1755
|
+
const paramWTypes = this.getFuncParamTypes(embedMethodName);
|
|
1756
|
+
const isPtrRecv = paramGoTypes[0]?.kind === "pointer";
|
|
1757
|
+
const embedSInfo = this.getStructType(embed.name);
|
|
1758
|
+
|
|
1759
|
+
const ptrKey = `ptr:${sName}`;
|
|
1760
|
+
if (!seenKeys.has(ptrKey)) {
|
|
1761
|
+
seenKeys.add(ptrKey);
|
|
1762
|
+
candidates.push({
|
|
1763
|
+
recvTypeName: sName,
|
|
1764
|
+
testTypeIndex: sInfo.typeIndex,
|
|
1765
|
+
isBoxedValue: false,
|
|
1766
|
+
needsValueDeref: !isPtrRecv,
|
|
1767
|
+
embedPath: [
|
|
1768
|
+
{
|
|
1769
|
+
parentTypeIndex: sInfo.typeIndex,
|
|
1770
|
+
fieldIndex: embed.fieldIndex,
|
|
1771
|
+
},
|
|
1772
|
+
],
|
|
1773
|
+
funcIndex: fIdx,
|
|
1774
|
+
targetParamTypes: paramWTypes,
|
|
1775
|
+
targetRecvWType: paramWTypes[0],
|
|
1776
|
+
structInfo: embedSInfo,
|
|
1777
|
+
});
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
if (!isPtrRecv) {
|
|
1781
|
+
const box = this.getIfaceBoxType({ kind: "named", name: sName });
|
|
1782
|
+
const valKey = `val:${sName}`;
|
|
1783
|
+
if (box && !seenKeys.has(valKey)) {
|
|
1784
|
+
seenKeys.add(valKey);
|
|
1785
|
+
candidates.push({
|
|
1786
|
+
recvTypeName: sName,
|
|
1787
|
+
testTypeIndex: box.typeIndex,
|
|
1788
|
+
isBoxedValue: true,
|
|
1789
|
+
needsValueDeref: false,
|
|
1790
|
+
embedPath: [
|
|
1791
|
+
{
|
|
1792
|
+
parentTypeIndex: sInfo.typeIndex,
|
|
1793
|
+
fieldIndex: embed.fieldIndex,
|
|
1794
|
+
},
|
|
1795
|
+
],
|
|
1796
|
+
funcIndex: fIdx,
|
|
1797
|
+
targetParamTypes: paramWTypes,
|
|
1798
|
+
targetRecvWType: paramWTypes[0],
|
|
1799
|
+
structInfo: embedSInfo,
|
|
1800
|
+
});
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
// 2. Named non-struct types
|
|
1809
|
+
for (const [fnName, fIdx] of this.funcMap.entries()) {
|
|
1810
|
+
if (fnName.endsWith(`.${methodName}`)) {
|
|
1811
|
+
const rName = fnName.slice(0, fnName.length - methodName.length - 1);
|
|
1812
|
+
if (!this.structTypes.has(rName)) {
|
|
1813
|
+
const box = this.getIfaceBoxType({ kind: "named", name: rName });
|
|
1814
|
+
const key = `named:${rName}`;
|
|
1815
|
+
if (box && !seenKeys.has(key)) {
|
|
1816
|
+
seenKeys.add(key);
|
|
1817
|
+
const paramWTypes = this.getFuncParamTypes(fnName);
|
|
1818
|
+
candidates.push({
|
|
1819
|
+
recvTypeName: rName,
|
|
1820
|
+
testTypeIndex: box.typeIndex,
|
|
1821
|
+
isBoxedValue: true,
|
|
1822
|
+
needsValueDeref: false,
|
|
1823
|
+
embedPath: null,
|
|
1824
|
+
funcIndex: fIdx,
|
|
1825
|
+
targetParamTypes: paramWTypes,
|
|
1826
|
+
});
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
return candidates;
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1835
|
+
getTypesImplementingInterface(ifaceType) {
|
|
1836
|
+
const candidates = [];
|
|
1837
|
+
const seen = new Set();
|
|
1838
|
+
const ifaceResolved =
|
|
1839
|
+
this.checker?.resolveType?.(
|
|
1840
|
+
ifaceType?.kind === "named" ? ifaceType.underlying : ifaceType,
|
|
1841
|
+
) ?? ifaceType;
|
|
1842
|
+
|
|
1843
|
+
// 1. Struct types
|
|
1844
|
+
for (const [sName, sInfo] of this.structTypes.entries()) {
|
|
1845
|
+
const namedType = this.checker?.types?.get(sName) ?? {
|
|
1846
|
+
kind: "named",
|
|
1847
|
+
name: sName,
|
|
1848
|
+
};
|
|
1849
|
+
const ptrType = { kind: "pointer", base: namedType };
|
|
1850
|
+
|
|
1851
|
+
// Check pointer to struct
|
|
1852
|
+
if (this.checker?.implements?.(ptrType, ifaceResolved)) {
|
|
1853
|
+
if (!seen.has(sInfo.typeIndex)) {
|
|
1854
|
+
seen.add(sInfo.typeIndex);
|
|
1855
|
+
candidates.push({ name: `*${sName}`, typeIndex: sInfo.typeIndex });
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1859
|
+
// Check struct value (boxed)
|
|
1860
|
+
if (this.checker?.implements?.(namedType, ifaceResolved)) {
|
|
1861
|
+
const box = this.getIfaceBoxType(namedType);
|
|
1862
|
+
if (box && !seen.has(box.typeIndex)) {
|
|
1863
|
+
seen.add(box.typeIndex);
|
|
1864
|
+
candidates.push({ name: sName, typeIndex: box.typeIndex });
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
// 2. Named non-struct types that have methods
|
|
1870
|
+
if (this.checker?.types) {
|
|
1871
|
+
for (const [tName, tVal] of this.checker.types.entries()) {
|
|
1872
|
+
if (
|
|
1873
|
+
tVal?.kind === "named" &&
|
|
1874
|
+
tVal.underlying?.kind !== "struct" &&
|
|
1875
|
+
tVal.underlying?.kind !== "interface"
|
|
1876
|
+
) {
|
|
1877
|
+
if (this.checker.implements(tVal, ifaceResolved)) {
|
|
1878
|
+
const box = this.getIfaceBoxType(tVal);
|
|
1879
|
+
if (box && !seen.has(box.typeIndex)) {
|
|
1880
|
+
seen.add(box.typeIndex);
|
|
1881
|
+
candidates.push({ name: tName, typeIndex: box.typeIndex });
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
return candidates;
|
|
1889
|
+
}
|
|
703
1890
|
}
|
|
704
1891
|
|
|
705
|
-
|
|
1892
|
+
function peepholeOptimize(instructions) {
|
|
706
1893
|
let changed = true;
|
|
707
1894
|
let current = instructions;
|
|
708
1895
|
while (changed) {
|
|
@@ -768,9 +1955,20 @@ export function compileWasmModule(
|
|
|
768
1955
|
lowerResult = null,
|
|
769
1956
|
options = {},
|
|
770
1957
|
) {
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
1958
|
+
let progs = Array.isArray(programs) ? programs : [programs];
|
|
1959
|
+
let resolvedChecker = checker;
|
|
1960
|
+
let resolvedLowerResult = lowerResult;
|
|
1961
|
+
|
|
1962
|
+
if (hasGenerics(progs)) {
|
|
1963
|
+
const mono = monomorphise(progs, checker);
|
|
1964
|
+
progs = mono.programs;
|
|
1965
|
+
resolvedChecker = mono.checker;
|
|
1966
|
+
resolvedLowerResult = lower(progs, resolvedChecker);
|
|
1967
|
+
} else if (!resolvedLowerResult) {
|
|
1968
|
+
resolvedLowerResult = lower(progs, resolvedChecker);
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1971
|
+
const mod = new ModuleEmitter(resolvedChecker, resolvedLowerResult, progs, {
|
|
774
1972
|
bundledPackages: options.bundledPackages,
|
|
775
1973
|
});
|
|
776
1974
|
|
|
@@ -785,6 +1983,7 @@ export function compileWasmModule(
|
|
|
785
1983
|
if (d.kind === "FuncDecl") {
|
|
786
1984
|
d._pkgName = pkgName;
|
|
787
1985
|
d._pkgTarget = pkgTarget;
|
|
1986
|
+
d._srcFile = p._path ?? p._filename ?? null;
|
|
788
1987
|
if (d.name === "init" || d.name.startsWith("init$")) {
|
|
789
1988
|
const renamed = initCount === 0 ? "init" : `init$${initCount}`;
|
|
790
1989
|
d.name = renamed;
|
|
@@ -812,6 +2011,8 @@ export function compileWasmModule(
|
|
|
812
2011
|
_sourceDecl: d,
|
|
813
2012
|
_pkgName: pkgName,
|
|
814
2013
|
_pkgTarget: pkgTarget,
|
|
2014
|
+
_srcFile: p._path ?? p._filename ?? null,
|
|
2015
|
+
_line: d._line,
|
|
815
2016
|
};
|
|
816
2017
|
funcDecls.push(normFn);
|
|
817
2018
|
}
|
|
@@ -846,6 +2047,8 @@ export function compileWasmModule(
|
|
|
846
2047
|
_isClosure: true,
|
|
847
2048
|
_funcLit: node,
|
|
848
2049
|
_rootFuncDecl: rootFn,
|
|
2050
|
+
_srcFile: rootFn?._srcFile ?? null,
|
|
2051
|
+
_line: typeof node._line === "number" ? node._line : rootFn?._line,
|
|
849
2052
|
};
|
|
850
2053
|
node._liftedFn = liftedFn;
|
|
851
2054
|
closures.push(liftedFn);
|
|
@@ -928,8 +2131,19 @@ export function compileWasmModule(
|
|
|
928
2131
|
}
|
|
929
2132
|
funcDecls.push(...trampolines);
|
|
930
2133
|
|
|
2134
|
+
_collectPackageConsts(progs, mod, checker);
|
|
2135
|
+
if (checker?.errors?.length > 0) {
|
|
2136
|
+
if (options.boundary) {
|
|
2137
|
+
throw new Error(checker.errors.map((e) => e.message).join("\n"));
|
|
2138
|
+
}
|
|
2139
|
+
return {
|
|
2140
|
+
wasm: null,
|
|
2141
|
+
facade: null,
|
|
2142
|
+
stringTable: null,
|
|
2143
|
+
errors: checker.errors,
|
|
2144
|
+
};
|
|
2145
|
+
}
|
|
931
2146
|
const pendingInits = _collectPackageGlobals(progs, mod, checker);
|
|
932
|
-
_collectPackageConsts(progs, mod);
|
|
933
2147
|
const globalInitFn =
|
|
934
2148
|
pendingInits.length > 0 || initNames.length > 0
|
|
935
2149
|
? _makeGlobalInitFunc(pendingInits, initNames)
|
|
@@ -956,7 +2170,370 @@ export function compileWasmModule(
|
|
|
956
2170
|
};
|
|
957
2171
|
funcDecls.push(sliceBoundsPanicFn);
|
|
958
2172
|
|
|
959
|
-
|
|
2173
|
+
const pushPanicFn = {
|
|
2174
|
+
kind: "FuncDecl",
|
|
2175
|
+
name: "__push_panic",
|
|
2176
|
+
params: [{ name: "msg", type: { kind: "basic", name: "string" } }],
|
|
2177
|
+
returnType: null,
|
|
2178
|
+
_isPushPanic: true,
|
|
2179
|
+
};
|
|
2180
|
+
funcDecls.push(pushPanicFn);
|
|
2181
|
+
|
|
2182
|
+
// 1d. Interface dispatchers
|
|
2183
|
+
_collectInterfaceDispatchers(progs, funcDecls, mod, checker);
|
|
2184
|
+
|
|
2185
|
+
// 1e. Map helpers
|
|
2186
|
+
function scanMapTypes(node) {
|
|
2187
|
+
if (!node || typeof node !== "object") return;
|
|
2188
|
+
if (Array.isArray(node)) {
|
|
2189
|
+
for (const item of node) scanMapTypes(item);
|
|
2190
|
+
return;
|
|
2191
|
+
}
|
|
2192
|
+
if (node._type && isMapType(node._type, checker)) {
|
|
2193
|
+
const { keyType, valType } = getMapKeyValTypes(node._type, checker);
|
|
2194
|
+
if (keyType && valType) {
|
|
2195
|
+
mod.getMapType(keyType, valType);
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
if (node.kind === "MapType") {
|
|
2199
|
+
const keyGoType = node.key;
|
|
2200
|
+
const valGoType = node.value ?? node.elem;
|
|
2201
|
+
if (keyGoType && valGoType) {
|
|
2202
|
+
mod.getMapType(keyGoType, valGoType);
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
for (const key of Object.keys(node)) {
|
|
2206
|
+
if (key.startsWith("_")) continue;
|
|
2207
|
+
scanMapTypes(node[key]);
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
|
|
2211
|
+
for (const fn of funcDecls) {
|
|
2212
|
+
if (fn.params) scanMapTypes(fn.params);
|
|
2213
|
+
if (fn.returnType) scanMapTypes(fn.returnType);
|
|
2214
|
+
if (fn.body) scanMapTypes(fn.body);
|
|
2215
|
+
}
|
|
2216
|
+
for (const p of progs) {
|
|
2217
|
+
for (const d of p.decls ?? []) {
|
|
2218
|
+
scanMapTypes(d);
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
|
|
2222
|
+
for (const mapInfo of mod.mapTypes.values()) {
|
|
2223
|
+
// make
|
|
2224
|
+
funcDecls.push({
|
|
2225
|
+
kind: "FuncDecl",
|
|
2226
|
+
name: mapInfo.makeFuncName,
|
|
2227
|
+
params: [{ name: "cap", type: { kind: "basic", name: "int32" } }],
|
|
2228
|
+
returnType: mapInfo.wType,
|
|
2229
|
+
body: { kind: "Block", stmts: [], list: [] },
|
|
2230
|
+
_isMapHelper: true,
|
|
2231
|
+
_mapHelperKind: "make",
|
|
2232
|
+
_mapInfo: mapInfo,
|
|
2233
|
+
_rootFuncDecl: null,
|
|
2234
|
+
});
|
|
2235
|
+
// get
|
|
2236
|
+
funcDecls.push({
|
|
2237
|
+
kind: "FuncDecl",
|
|
2238
|
+
name: mapInfo.getFuncName,
|
|
2239
|
+
params: [
|
|
2240
|
+
{ name: "m", type: mapInfo.wType },
|
|
2241
|
+
{ name: "k", type: mapInfo.keyGoType },
|
|
2242
|
+
],
|
|
2243
|
+
returnType: mapInfo.valGoType,
|
|
2244
|
+
body: { kind: "Block", stmts: [], list: [] },
|
|
2245
|
+
_isMapHelper: true,
|
|
2246
|
+
_mapHelperKind: "get",
|
|
2247
|
+
_mapInfo: mapInfo,
|
|
2248
|
+
_rootFuncDecl: null,
|
|
2249
|
+
});
|
|
2250
|
+
// get_ok
|
|
2251
|
+
funcDecls.push({
|
|
2252
|
+
kind: "FuncDecl",
|
|
2253
|
+
name: mapInfo.getOkFuncName,
|
|
2254
|
+
params: [
|
|
2255
|
+
{ name: "m", type: mapInfo.wType },
|
|
2256
|
+
{ name: "k", type: mapInfo.keyGoType },
|
|
2257
|
+
],
|
|
2258
|
+
returnType: {
|
|
2259
|
+
kind: "tuple",
|
|
2260
|
+
types: [mapInfo.valGoType, { kind: "basic", name: "bool" }],
|
|
2261
|
+
},
|
|
2262
|
+
body: { kind: "Block", stmts: [], list: [] },
|
|
2263
|
+
_isMapHelper: true,
|
|
2264
|
+
_mapHelperKind: "get_ok",
|
|
2265
|
+
_mapInfo: mapInfo,
|
|
2266
|
+
_rootFuncDecl: null,
|
|
2267
|
+
});
|
|
2268
|
+
// set
|
|
2269
|
+
funcDecls.push({
|
|
2270
|
+
kind: "FuncDecl",
|
|
2271
|
+
name: mapInfo.setFuncName,
|
|
2272
|
+
params: [
|
|
2273
|
+
{ name: "m", type: mapInfo.wType },
|
|
2274
|
+
{ name: "k", type: mapInfo.keyGoType },
|
|
2275
|
+
{ name: "v", type: mapInfo.valGoType },
|
|
2276
|
+
],
|
|
2277
|
+
returnType: null,
|
|
2278
|
+
body: { kind: "Block", stmts: [], list: [] },
|
|
2279
|
+
_isMapHelper: true,
|
|
2280
|
+
_mapHelperKind: "set",
|
|
2281
|
+
_mapInfo: mapInfo,
|
|
2282
|
+
_rootFuncDecl: null,
|
|
2283
|
+
});
|
|
2284
|
+
// delete
|
|
2285
|
+
funcDecls.push({
|
|
2286
|
+
kind: "FuncDecl",
|
|
2287
|
+
name: mapInfo.deleteFuncName,
|
|
2288
|
+
params: [
|
|
2289
|
+
{ name: "m", type: mapInfo.wType },
|
|
2290
|
+
{ name: "k", type: mapInfo.keyGoType },
|
|
2291
|
+
],
|
|
2292
|
+
returnType: null,
|
|
2293
|
+
body: { kind: "Block", stmts: [], list: [] },
|
|
2294
|
+
_isMapHelper: true,
|
|
2295
|
+
_mapHelperKind: "delete",
|
|
2296
|
+
_mapInfo: mapInfo,
|
|
2297
|
+
_rootFuncDecl: null,
|
|
2298
|
+
});
|
|
2299
|
+
// len
|
|
2300
|
+
funcDecls.push({
|
|
2301
|
+
kind: "FuncDecl",
|
|
2302
|
+
name: mapInfo.lenFuncName,
|
|
2303
|
+
params: [{ name: "m", type: mapInfo.wType }],
|
|
2304
|
+
returnType: { kind: "basic", name: "int32" },
|
|
2305
|
+
body: { kind: "Block", stmts: [], list: [] },
|
|
2306
|
+
_isMapHelper: true,
|
|
2307
|
+
_mapHelperKind: "len",
|
|
2308
|
+
_mapInfo: mapInfo,
|
|
2309
|
+
_rootFuncDecl: null,
|
|
2310
|
+
});
|
|
2311
|
+
// clear
|
|
2312
|
+
funcDecls.push({
|
|
2313
|
+
kind: "FuncDecl",
|
|
2314
|
+
name: mapInfo.clearFuncName,
|
|
2315
|
+
params: [{ name: "m", type: mapInfo.wType }],
|
|
2316
|
+
returnType: null,
|
|
2317
|
+
body: { kind: "Block", stmts: [], list: [] },
|
|
2318
|
+
_isMapHelper: true,
|
|
2319
|
+
_mapHelperKind: "clear",
|
|
2320
|
+
_mapInfo: mapInfo,
|
|
2321
|
+
_rootFuncDecl: null,
|
|
2322
|
+
});
|
|
2323
|
+
// keys
|
|
2324
|
+
funcDecls.push({
|
|
2325
|
+
kind: "FuncDecl",
|
|
2326
|
+
name: mapInfo.keysFuncName,
|
|
2327
|
+
params: [{ name: "m", type: mapInfo.wType }],
|
|
2328
|
+
returnType: { kind: "slice", elem: mapInfo.keyGoType },
|
|
2329
|
+
body: { kind: "Block", stmts: [], list: [] },
|
|
2330
|
+
_isMapHelper: true,
|
|
2331
|
+
_mapHelperKind: "keys",
|
|
2332
|
+
_mapInfo: mapInfo,
|
|
2333
|
+
_rootFuncDecl: null,
|
|
2334
|
+
});
|
|
2335
|
+
// values
|
|
2336
|
+
funcDecls.push({
|
|
2337
|
+
kind: "FuncDecl",
|
|
2338
|
+
name: mapInfo.valuesFuncName,
|
|
2339
|
+
params: [{ name: "m", type: mapInfo.wType }],
|
|
2340
|
+
returnType: { kind: "slice", elem: mapInfo.valGoType },
|
|
2341
|
+
body: { kind: "Block", stmts: [], list: [] },
|
|
2342
|
+
_isMapHelper: true,
|
|
2343
|
+
_mapHelperKind: "values",
|
|
2344
|
+
_mapInfo: mapInfo,
|
|
2345
|
+
_rootFuncDecl: null,
|
|
2346
|
+
});
|
|
2347
|
+
// clone
|
|
2348
|
+
funcDecls.push({
|
|
2349
|
+
kind: "FuncDecl",
|
|
2350
|
+
name: mapInfo.cloneFuncName,
|
|
2351
|
+
params: [{ name: "m", type: mapInfo.wType }],
|
|
2352
|
+
returnType: mapInfo.wType,
|
|
2353
|
+
body: { kind: "Block", stmts: [], list: [] },
|
|
2354
|
+
_isMapHelper: true,
|
|
2355
|
+
_mapHelperKind: "clone",
|
|
2356
|
+
_mapInfo: mapInfo,
|
|
2357
|
+
_rootFuncDecl: null,
|
|
2358
|
+
});
|
|
2359
|
+
}
|
|
2360
|
+
|
|
2361
|
+
// 1e2. `gofront/shared` linear-memory helpers (only when imported).
|
|
2362
|
+
const usesShared = progs.some((p) =>
|
|
2363
|
+
(p.imports ?? []).some((decl) =>
|
|
2364
|
+
(decl.imports ?? [decl]).some((imp) => imp.path === "gofront/shared"),
|
|
2365
|
+
),
|
|
2366
|
+
);
|
|
2367
|
+
if (usesShared) {
|
|
2368
|
+
const sharedWType = mod.getSharedType().wType;
|
|
2369
|
+
const i32 = { kind: "basic", name: "int32" };
|
|
2370
|
+
funcDecls.push({
|
|
2371
|
+
kind: "FuncDecl",
|
|
2372
|
+
name: "__shared_len_panic",
|
|
2373
|
+
params: [],
|
|
2374
|
+
returnType: null,
|
|
2375
|
+
_isRuntimePanic: true,
|
|
2376
|
+
_panicMsg: "runtime error: shared buffer length out of range",
|
|
2377
|
+
});
|
|
2378
|
+
funcDecls.push({
|
|
2379
|
+
kind: "FuncDecl",
|
|
2380
|
+
name: "__shared_new",
|
|
2381
|
+
params: [
|
|
2382
|
+
{ name: "len", type: i32 },
|
|
2383
|
+
{ name: "bytes", type: i32 },
|
|
2384
|
+
],
|
|
2385
|
+
returnType: sharedWType,
|
|
2386
|
+
body: { kind: "Block", stmts: [], list: [] },
|
|
2387
|
+
_isSharedHelper: "new",
|
|
2388
|
+
_rootFuncDecl: null,
|
|
2389
|
+
});
|
|
2390
|
+
funcDecls.push({
|
|
2391
|
+
kind: "FuncDecl",
|
|
2392
|
+
name: "__shared_sub",
|
|
2393
|
+
params: [
|
|
2394
|
+
{ name: "buf", type: sharedWType },
|
|
2395
|
+
{ name: "lo", type: i32 },
|
|
2396
|
+
{ name: "hi", type: i32 },
|
|
2397
|
+
{ name: "bytes", type: i32 },
|
|
2398
|
+
],
|
|
2399
|
+
returnType: sharedWType,
|
|
2400
|
+
body: { kind: "Block", stmts: [], list: [] },
|
|
2401
|
+
_isSharedHelper: "sub",
|
|
2402
|
+
_rootFuncDecl: null,
|
|
2403
|
+
});
|
|
2404
|
+
}
|
|
2405
|
+
|
|
2406
|
+
// 1e3. Runtime helpers: error values, interface equality, t.Run callback.
|
|
2407
|
+
mod.usesTestingRun = funcDecls.some((fn) => _usesTestingRun(fn.body));
|
|
2408
|
+
mod.usesAnyEq = _usesAnyEq(funcDecls, mod);
|
|
2409
|
+
const ANY_T = { kind: "basic", name: "any" };
|
|
2410
|
+
const STR_T = { kind: "basic", name: "string" };
|
|
2411
|
+
const BOOL_T = { kind: "basic", name: "bool" };
|
|
2412
|
+
const helper = (name, params, returnType, kind, extra = {}) => ({
|
|
2413
|
+
kind: "FuncDecl",
|
|
2414
|
+
name,
|
|
2415
|
+
params,
|
|
2416
|
+
returnType,
|
|
2417
|
+
body: { kind: "Block", stmts: [], list: [] },
|
|
2418
|
+
_isRuntimeHelper: kind,
|
|
2419
|
+
_rootFuncDecl: null,
|
|
2420
|
+
...extra,
|
|
2421
|
+
});
|
|
2422
|
+
if (mod.usesAnyEq) {
|
|
2423
|
+
funcDecls.push(
|
|
2424
|
+
helper(
|
|
2425
|
+
"__any_eq",
|
|
2426
|
+
[
|
|
2427
|
+
{ name: "a", type: ANY_T },
|
|
2428
|
+
{ name: "b", type: ANY_T },
|
|
2429
|
+
],
|
|
2430
|
+
BOOL_T,
|
|
2431
|
+
"any_eq",
|
|
2432
|
+
),
|
|
2433
|
+
);
|
|
2434
|
+
}
|
|
2435
|
+
mod.usesAnyHash = false;
|
|
2436
|
+
for (const info of mod.mapTypes.values()) {
|
|
2437
|
+
if (isInterfaceType(info.keyGoType, checker)) {
|
|
2438
|
+
mod.usesAnyHash = true;
|
|
2439
|
+
break;
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2442
|
+
if (mod.usesAnyHash) {
|
|
2443
|
+
funcDecls.push(
|
|
2444
|
+
helper(
|
|
2445
|
+
"__any_hash",
|
|
2446
|
+
[{ name: "a", type: ANY_T }],
|
|
2447
|
+
{ kind: "basic", name: "int32" },
|
|
2448
|
+
"any_hash",
|
|
2449
|
+
),
|
|
2450
|
+
);
|
|
2451
|
+
}
|
|
2452
|
+
if (mod.hasErrorType) {
|
|
2453
|
+
const errPtr = {
|
|
2454
|
+
kind: "pointer",
|
|
2455
|
+
base: { kind: "named", name: ERROR_STRUCT },
|
|
2456
|
+
};
|
|
2457
|
+
funcDecls.push(
|
|
2458
|
+
helper(
|
|
2459
|
+
`${ERROR_STRUCT}.Error`,
|
|
2460
|
+
[{ name: "e", type: errPtr }],
|
|
2461
|
+
STR_T,
|
|
2462
|
+
"error_msg",
|
|
2463
|
+
{
|
|
2464
|
+
_isMethod: true,
|
|
2465
|
+
_recvTypeName: ERROR_STRUCT,
|
|
2466
|
+
_methodName: "Error",
|
|
2467
|
+
},
|
|
2468
|
+
),
|
|
2469
|
+
helper(
|
|
2470
|
+
`${ERROR_STRUCT}.Unwrap`,
|
|
2471
|
+
[{ name: "e", type: errPtr }],
|
|
2472
|
+
ANY_T,
|
|
2473
|
+
"error_cause",
|
|
2474
|
+
{
|
|
2475
|
+
_isMethod: true,
|
|
2476
|
+
_recvTypeName: ERROR_STRUCT,
|
|
2477
|
+
_methodName: "Unwrap",
|
|
2478
|
+
},
|
|
2479
|
+
),
|
|
2480
|
+
helper(
|
|
2481
|
+
"__mk_error",
|
|
2482
|
+
[
|
|
2483
|
+
{ name: "msg", type: STR_T },
|
|
2484
|
+
{ name: "cause", type: ANY_T },
|
|
2485
|
+
],
|
|
2486
|
+
ANY_T,
|
|
2487
|
+
"mk_error",
|
|
2488
|
+
),
|
|
2489
|
+
helper("__error_str", [{ name: "e", type: ANY_T }], STR_T, "error_str"),
|
|
2490
|
+
{
|
|
2491
|
+
kind: "FuncDecl",
|
|
2492
|
+
name: "__errors_unwrap",
|
|
2493
|
+
params: [{ name: "__recv", type: ANY_T }],
|
|
2494
|
+
returnType: ANY_T,
|
|
2495
|
+
body: { kind: "Block", stmts: [], list: [] },
|
|
2496
|
+
_isInterfaceDispatcher: true,
|
|
2497
|
+
_softDispatch: true,
|
|
2498
|
+
_ifaceName: "error",
|
|
2499
|
+
_ifaceType: null,
|
|
2500
|
+
_methodName: "Unwrap",
|
|
2501
|
+
_rootFuncDecl: null,
|
|
2502
|
+
},
|
|
2503
|
+
helper(
|
|
2504
|
+
"__errors_is",
|
|
2505
|
+
[
|
|
2506
|
+
{ name: "err", type: ANY_T },
|
|
2507
|
+
{ name: "target", type: ANY_T },
|
|
2508
|
+
],
|
|
2509
|
+
BOOL_T,
|
|
2510
|
+
"errors_is",
|
|
2511
|
+
),
|
|
2512
|
+
);
|
|
2513
|
+
}
|
|
2514
|
+
if (mod.usesTestingRun) {
|
|
2515
|
+
funcDecls.push(
|
|
2516
|
+
helper(
|
|
2517
|
+
"__testing_run_cb",
|
|
2518
|
+
[
|
|
2519
|
+
{ name: "fn", type: ANY_T },
|
|
2520
|
+
{
|
|
2521
|
+
name: "t",
|
|
2522
|
+
type: {
|
|
2523
|
+
kind: "pointer",
|
|
2524
|
+
base: { kind: "named", name: "testing.T" },
|
|
2525
|
+
},
|
|
2526
|
+
},
|
|
2527
|
+
],
|
|
2528
|
+
null,
|
|
2529
|
+
"testing_run_cb",
|
|
2530
|
+
{ _forceExport: true },
|
|
2531
|
+
),
|
|
2532
|
+
);
|
|
2533
|
+
}
|
|
2534
|
+
_registerIoSyntheticMethods(funcDecls, mod, STR_T, progs);
|
|
2535
|
+
|
|
2536
|
+
// 1f. Boundary metadata (exported surface of wasm packages)
|
|
960
2537
|
const boundaryMeta = options.boundary
|
|
961
2538
|
? collectBoundaryMeta(progs, funcDecls, mod)
|
|
962
2539
|
: null;
|
|
@@ -1009,14 +2586,21 @@ export function compileWasmModule(
|
|
|
1009
2586
|
const typeIndex = mod.getTypeIndex(paramTypes, returnTypes);
|
|
1010
2587
|
fn._typeIndex = typeIndex;
|
|
1011
2588
|
fn._globalFuncIndex = globalIdx;
|
|
2589
|
+
mod.funcReturnTypesByIdx.set(globalIdx, returnTypes);
|
|
1012
2590
|
|
|
1013
2591
|
// Export if public (capitalized) or main
|
|
1014
2592
|
if (
|
|
1015
|
-
|
|
1016
|
-
(
|
|
1017
|
-
fn.
|
|
1018
|
-
|
|
1019
|
-
|
|
2593
|
+
fn._forceExport ||
|
|
2594
|
+
(!fn._isGlobalInit &&
|
|
2595
|
+
!fn._isRuntimeHelper &&
|
|
2596
|
+
!fn._isIoHelper &&
|
|
2597
|
+
!fn._isPhase7Helper &&
|
|
2598
|
+
(options.exportAll ||
|
|
2599
|
+
fn.name === "main" ||
|
|
2600
|
+
(fn.name[0] >= "A" && fn.name[0] <= "Z") ||
|
|
2601
|
+
(fn._isMethod &&
|
|
2602
|
+
fn._methodName[0] >= "A" &&
|
|
2603
|
+
fn._methodName[0] <= "Z")))
|
|
1020
2604
|
) {
|
|
1021
2605
|
mod.exports.push({ name: fn.name, kind: "func", index: globalIdx });
|
|
1022
2606
|
if (fn._isMethod && fn._exportName) {
|
|
@@ -1027,6 +2611,16 @@ export function compileWasmModule(
|
|
|
1027
2611
|
});
|
|
1028
2612
|
}
|
|
1029
2613
|
}
|
|
2614
|
+
if (
|
|
2615
|
+
fn._isPushPanic &&
|
|
2616
|
+
!mod.exports.some((e) => e.name === "__push_panic")
|
|
2617
|
+
) {
|
|
2618
|
+
mod.exports.push({
|
|
2619
|
+
name: "__push_panic",
|
|
2620
|
+
kind: "func",
|
|
2621
|
+
index: globalIdx,
|
|
2622
|
+
});
|
|
2623
|
+
}
|
|
1030
2624
|
}
|
|
1031
2625
|
|
|
1032
2626
|
// 4. Emit function bodies
|
|
@@ -1037,7 +2631,31 @@ export function compileWasmModule(
|
|
|
1037
2631
|
const funcIdx = mod.getStringImportIndex();
|
|
1038
2632
|
emitter.pushInstruction({ op: "i32.const", value: strIdx });
|
|
1039
2633
|
emitter.pushInstruction({ op: "call", funcIndex: funcIdx });
|
|
2634
|
+
emitter.pushInstruction("any.convert_extern");
|
|
1040
2635
|
emitter.emitPanicThrow();
|
|
2636
|
+
} else if (fn._isPushPanic) {
|
|
2637
|
+
const panicNodeTypeIndex = mod.getPanicNodeTypeIndex();
|
|
2638
|
+
const panicGlobal = mod.getPanicGlobalIndex();
|
|
2639
|
+
emitter.pushInstruction({ op: "local.get", index: 0 });
|
|
2640
|
+
emitter.pushInstruction("any.convert_extern");
|
|
2641
|
+
emitter.pushInstruction({ op: "i32.const", value: 0 });
|
|
2642
|
+
emitter.pushInstruction({ op: "global.get", index: panicGlobal });
|
|
2643
|
+
emitter.pushInstruction({
|
|
2644
|
+
op: "struct.new",
|
|
2645
|
+
typeIndex: panicNodeTypeIndex,
|
|
2646
|
+
});
|
|
2647
|
+
emitter.pushInstruction({ op: "global.set", index: panicGlobal });
|
|
2648
|
+
emitter.pushInstruction("return");
|
|
2649
|
+
} else if (fn._isInterfaceDispatcher) {
|
|
2650
|
+
emitter.emitInterfaceDispatcher(fn);
|
|
2651
|
+
} else if (fn._isMapHelper) {
|
|
2652
|
+
emitter.emitMapHelper(fn);
|
|
2653
|
+
} else if (fn._isSharedHelper) {
|
|
2654
|
+
emitter.emitSharedHelper(fn);
|
|
2655
|
+
} else if (fn._isRuntimeHelper) {
|
|
2656
|
+
emitter.emitRuntimeHelper(fn);
|
|
2657
|
+
} else if (fn._isIoHelper || fn._isPhase7Helper) {
|
|
2658
|
+
emitter.emitIoHelper(fn);
|
|
1041
2659
|
} else if (fn.body) {
|
|
1042
2660
|
emitter.emitFunctionBody(fn.body);
|
|
1043
2661
|
}
|
|
@@ -1061,6 +2679,10 @@ export function compileWasmModule(
|
|
|
1061
2679
|
typeIndex: fn._typeIndex,
|
|
1062
2680
|
locals: emitter.localTypes,
|
|
1063
2681
|
body,
|
|
2682
|
+
src:
|
|
2683
|
+
fn._srcFile && typeof fn._line === "number"
|
|
2684
|
+
? { file: fn._srcFile, line: fn._line }
|
|
2685
|
+
: null,
|
|
1064
2686
|
});
|
|
1065
2687
|
}
|
|
1066
2688
|
|
|
@@ -1071,6 +2693,7 @@ export function compileWasmModule(
|
|
|
1071
2693
|
facade = generateFacade(boundaryMeta, {
|
|
1072
2694
|
stringTable: mod.stringTable,
|
|
1073
2695
|
callMain: Boolean(options.callMain) && mod.funcMap.has("main"),
|
|
2696
|
+
stdlibEnv: mod.usesStdlibEnv,
|
|
1074
2697
|
});
|
|
1075
2698
|
}
|
|
1076
2699
|
|
|
@@ -1082,44 +2705,150 @@ export function compileWasmModule(
|
|
|
1082
2705
|
elements: mod.elements,
|
|
1083
2706
|
funcs: mod.funcs,
|
|
1084
2707
|
exports: mod.exports,
|
|
2708
|
+
memory: mod.memory ?? null,
|
|
1085
2709
|
start: globalInitFn ? globalInitFn._globalFuncIndex : null,
|
|
2710
|
+
sourceMappingURL: options.sourceMappingURL ?? null,
|
|
1086
2711
|
};
|
|
1087
2712
|
|
|
1088
2713
|
const wasmBytes = encodeModule(moduleIR);
|
|
1089
2714
|
const watText = options.emitWat ? emitWat(moduleIR) : null;
|
|
1090
2715
|
|
|
2716
|
+
// Function-level source positions: `sources` are the absolute Go files,
|
|
2717
|
+
// `mappings` pair each function body's byte offset with its 0-based line.
|
|
2718
|
+
const sources = [];
|
|
2719
|
+
const sourceIndex = new Map();
|
|
2720
|
+
const mappings = [];
|
|
2721
|
+
moduleIR.funcs.forEach((fn, i) => {
|
|
2722
|
+
if (!fn.src) return;
|
|
2723
|
+
let idx = sourceIndex.get(fn.src.file);
|
|
2724
|
+
if (idx === undefined) {
|
|
2725
|
+
idx = sources.length;
|
|
2726
|
+
sources.push(fn.src.file);
|
|
2727
|
+
sourceIndex.set(fn.src.file, idx);
|
|
2728
|
+
}
|
|
2729
|
+
mappings.push({
|
|
2730
|
+
offset: moduleIR.codeOffsets[i],
|
|
2731
|
+
srcFileIdx: idx,
|
|
2732
|
+
srcLine: Math.max(0, fn.src.line - 1),
|
|
2733
|
+
});
|
|
2734
|
+
});
|
|
2735
|
+
|
|
1091
2736
|
return {
|
|
1092
2737
|
wasm: wasmBytes,
|
|
1093
2738
|
wat: watText,
|
|
1094
2739
|
stringTable: mod.stringTable,
|
|
1095
2740
|
moduleIR,
|
|
1096
2741
|
facade,
|
|
2742
|
+
sourceMapInfo: { sources, mappings },
|
|
1097
2743
|
};
|
|
1098
2744
|
}
|
|
1099
2745
|
|
|
1100
|
-
// Package-level `const` declarations
|
|
1101
|
-
//
|
|
1102
|
-
//
|
|
1103
|
-
|
|
2746
|
+
// Package-level `const` declarations (iota is already substituted by the
|
|
2747
|
+
// parser). Literals and expressions the folder can reduce become BasicLits in
|
|
2748
|
+
// `constCache`; whatever is left is recorded in `nonLiteralConsts` and later
|
|
2749
|
+
// becomes a global initialised by the start function.
|
|
2750
|
+
function _collectPackageConsts(progs, mod, checker = null) {
|
|
2751
|
+
const entries = [];
|
|
1104
2752
|
for (const p of progs) {
|
|
1105
2753
|
for (const d of p.decls ?? []) {
|
|
1106
2754
|
if (d.kind !== "ConstDecl") continue;
|
|
1107
2755
|
for (const spec of d.decls ?? []) {
|
|
1108
2756
|
for (let i = 0; i < spec.names.length; i++) {
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
}
|
|
1117
|
-
if (lit?.kind === "BasicLit") mod.constCache.set(spec.names[i], lit);
|
|
1118
|
-
else if (lit) mod.nonLiteralConsts.add(spec.names[i]);
|
|
2757
|
+
if (spec.value?.[i])
|
|
2758
|
+
entries.push({
|
|
2759
|
+
name: spec.names[i],
|
|
2760
|
+
expr: spec.value[i],
|
|
2761
|
+
filename: p._filename,
|
|
2762
|
+
source: p._source,
|
|
2763
|
+
});
|
|
1119
2764
|
}
|
|
1120
2765
|
}
|
|
1121
2766
|
}
|
|
1122
2767
|
}
|
|
2768
|
+
const resolve = (name) => mod.constCache.get(name) ?? null;
|
|
2769
|
+
// Iterate to a fixpoint so a constant may refer to one declared later.
|
|
2770
|
+
let pending = entries;
|
|
2771
|
+
let progress = true;
|
|
2772
|
+
while (progress && pending.length > 0) {
|
|
2773
|
+
progress = false;
|
|
2774
|
+
const rest = [];
|
|
2775
|
+
for (const entry of pending) {
|
|
2776
|
+
let lit = null;
|
|
2777
|
+
try {
|
|
2778
|
+
lit = foldConstExpr(entry.expr, resolve);
|
|
2779
|
+
} catch (err) {
|
|
2780
|
+
if (checker?.err) {
|
|
2781
|
+
const savedFile = checker._currentFile;
|
|
2782
|
+
const savedSource = checker._currentSource;
|
|
2783
|
+
if (entry.filename) checker._currentFile = entry.filename;
|
|
2784
|
+
if (entry.source) checker._currentSource = entry.source;
|
|
2785
|
+
checker.err(err.message, entry.expr);
|
|
2786
|
+
checker._currentFile = savedFile;
|
|
2787
|
+
checker._currentSource = savedSource;
|
|
2788
|
+
} else {
|
|
2789
|
+
throw new Error(
|
|
2790
|
+
`Constant expression error for '${entry.name}': ${err.message}`,
|
|
2791
|
+
);
|
|
2792
|
+
}
|
|
2793
|
+
continue;
|
|
2794
|
+
}
|
|
2795
|
+
if (lit) {
|
|
2796
|
+
mod.constCache.set(entry.name, lit);
|
|
2797
|
+
progress = true;
|
|
2798
|
+
} else {
|
|
2799
|
+
rest.push(entry);
|
|
2800
|
+
}
|
|
2801
|
+
}
|
|
2802
|
+
pending = rest;
|
|
2803
|
+
}
|
|
2804
|
+
for (const entry of pending) mod.nonLiteralConsts.add(entry.name);
|
|
2805
|
+
}
|
|
2806
|
+
|
|
2807
|
+
// Depth-first search over AST nodes (skipping `_`-prefixed annotations).
|
|
2808
|
+
function _someNode(node, pred) {
|
|
2809
|
+
if (!node || typeof node !== "object") return false;
|
|
2810
|
+
if (Array.isArray(node)) return node.some((n) => _someNode(n, pred));
|
|
2811
|
+
if (pred(node)) return true;
|
|
2812
|
+
for (const key of Object.keys(node)) {
|
|
2813
|
+
if (key.startsWith("_")) continue;
|
|
2814
|
+
if (_someNode(node[key], pred)) return true;
|
|
2815
|
+
}
|
|
2816
|
+
return false;
|
|
2817
|
+
}
|
|
2818
|
+
|
|
2819
|
+
function _usesTestingRun(node) {
|
|
2820
|
+
return _someNode(
|
|
2821
|
+
node,
|
|
2822
|
+
(n) =>
|
|
2823
|
+
n.kind === "CallExpr" &&
|
|
2824
|
+
n.func?.kind === "SelectorExpr" &&
|
|
2825
|
+
n.func.field === "Run" &&
|
|
2826
|
+
isTestingT(n.func.expr?._type),
|
|
2827
|
+
);
|
|
2828
|
+
}
|
|
2829
|
+
|
|
2830
|
+
// `__any_eq` is only needed when some `==`/`!=` (or switch case) compares
|
|
2831
|
+
// anyref operands, a struct with an anyref field is compared, or the error
|
|
2832
|
+
// runtime (`errors.Is`) is present. `_emitAnyEq` throws if this misses a
|
|
2833
|
+
// site, so a gap surfaces as a compile error rather than a bad module.
|
|
2834
|
+
function _usesAnyEq(funcDecls, mod) {
|
|
2835
|
+
if (mod.hasErrorType) return true;
|
|
2836
|
+
for (const info of mod.mapTypes.values()) {
|
|
2837
|
+
if (isInterfaceType(info.keyGoType, mod.checker)) return true;
|
|
2838
|
+
}
|
|
2839
|
+
for (const info of mod.structTypes.values()) {
|
|
2840
|
+
if (info.fields?.some((f) => f.wType === "anyref")) return true;
|
|
2841
|
+
}
|
|
2842
|
+
const isAnyRef = (n) =>
|
|
2843
|
+
Boolean(n?._type) && toWasmType(n._type, mod.checker, mod) === "anyref";
|
|
2844
|
+
return funcDecls.some((fn) =>
|
|
2845
|
+
_someNode(fn.body, (n) => {
|
|
2846
|
+
if (n.kind === "BinaryExpr" && (n.op === "==" || n.op === "!="))
|
|
2847
|
+
return isAnyRef(n.left) || isAnyRef(n.right);
|
|
2848
|
+
if (n.kind === "SwitchStmt" && n.tag) return isAnyRef(n.tag);
|
|
2849
|
+
return false;
|
|
2850
|
+
}),
|
|
2851
|
+
);
|
|
1123
2852
|
}
|
|
1124
2853
|
|
|
1125
2854
|
function _scanImportsInBody(node, mod) {
|
|
@@ -1158,14 +2887,20 @@ function _scanImportsInBody(node, mod) {
|
|
|
1158
2887
|
Atan: "atan",
|
|
1159
2888
|
Atan2: "atan2",
|
|
1160
2889
|
Pow: "pow",
|
|
2890
|
+
Mod: "mod",
|
|
2891
|
+
Hypot: "hypot",
|
|
2892
|
+
Cbrt: "cbrt",
|
|
1161
2893
|
Exp: "exp",
|
|
1162
2894
|
Log: "log",
|
|
1163
2895
|
Log2: "log2",
|
|
1164
2896
|
Log10: "log10",
|
|
1165
2897
|
Round: "round",
|
|
2898
|
+
Sinh: "sinh",
|
|
2899
|
+
Cosh: "cosh",
|
|
2900
|
+
Tanh: "tanh",
|
|
1166
2901
|
};
|
|
1167
2902
|
if (jsMathMap[func.field]) {
|
|
1168
|
-
const isBinary =
|
|
2903
|
+
const isBinary = BINARY_MATH_IMPORTS.has(jsMathMap[func.field]);
|
|
1169
2904
|
mod.getMathImportIndex(jsMathMap[func.field], isBinary);
|
|
1170
2905
|
}
|
|
1171
2906
|
} else if (
|
|
@@ -1177,7 +2912,10 @@ function _scanImportsInBody(node, mod) {
|
|
|
1177
2912
|
if (func.field === "Name") mod.getTestingNameImportIndex();
|
|
1178
2913
|
else if (func.field === "Failed" || func.field === "Skipped")
|
|
1179
2914
|
mod.getTestingFlagImportIndex();
|
|
1180
|
-
else if (func.field
|
|
2915
|
+
else if (func.field === "Run") {
|
|
2916
|
+
// closure type for func(*testing.T) must exist before bodies are emitted
|
|
2917
|
+
mod.getTestingRunClosureType();
|
|
2918
|
+
} else {
|
|
1181
2919
|
mod.getTestingCallImportIndex();
|
|
1182
2920
|
for (const arg of args) {
|
|
1183
2921
|
mod.getTestingArgImportIndex(
|
|
@@ -1228,19 +2966,67 @@ export function compileWasm(source, options = {}) {
|
|
|
1228
2966
|
exportAll: options.exportAll ?? true,
|
|
1229
2967
|
});
|
|
1230
2968
|
|
|
2969
|
+
if (checker.errors?.length > 0) {
|
|
2970
|
+
return { wasm: null, errors: checker.errors };
|
|
2971
|
+
}
|
|
2972
|
+
|
|
1231
2973
|
return { ...res, errors: [] };
|
|
1232
2974
|
}
|
|
1233
2975
|
|
|
2976
|
+
function _isPointerGoType(t, mod) {
|
|
2977
|
+
if (!t) return false;
|
|
2978
|
+
if (t.kind === "pointer" || t.kind === "PointerType" || t.kind === "StarExpr")
|
|
2979
|
+
return true;
|
|
2980
|
+
// `type P *T` used as a field type: look through the name.
|
|
2981
|
+
const named =
|
|
2982
|
+
t.kind === "named" || t.kind === "TypeName" || t.kind === "Ident";
|
|
2983
|
+
const under = named
|
|
2984
|
+
? (t.underlying ?? mod.checker?.types?.get(t.name)?.underlying)
|
|
2985
|
+
: null;
|
|
2986
|
+
return under ? _isPointerGoType(under, mod) : false;
|
|
2987
|
+
}
|
|
2988
|
+
|
|
2989
|
+
// Constant-expression zero value of a struct global. Nested struct values are
|
|
2990
|
+
// built in place: a null ref would trap on the first field access.
|
|
2991
|
+
function _zeroStructConst(structInfo, mod) {
|
|
2992
|
+
const out = [];
|
|
2993
|
+
for (const f of structInfo.fields) out.push(..._zeroFieldConst(f, mod));
|
|
2994
|
+
out.push({ op: "struct.new", typeIndex: structInfo.typeIndex });
|
|
2995
|
+
return out;
|
|
2996
|
+
}
|
|
2997
|
+
|
|
2998
|
+
function _zeroFieldConst(f, mod) {
|
|
2999
|
+
const w = f.wType;
|
|
3000
|
+
if (w === "i64") return [{ op: "i64.const", value: 0n }];
|
|
3001
|
+
if (w === "f32" || w === "f64") return [{ op: `${w}.const`, value: 0 }];
|
|
3002
|
+
if (w === "externref") return [{ op: "ref.null", heapType: "extern" }];
|
|
3003
|
+
if (w === "anyref") return [{ op: "ref.null", heapType: "any" }];
|
|
3004
|
+
if (typeof w !== "object" || w === null)
|
|
3005
|
+
return [{ op: "i32.const", value: 0 }];
|
|
3006
|
+
const ti = w.typeIndex;
|
|
3007
|
+
const slice = typeof ti === "number" ? mod.getSliceTypeByIndex(ti) : null;
|
|
3008
|
+
if (slice) return [{ op: "global.get", index: slice.emptyGlobalIndex }];
|
|
3009
|
+
if (typeof ti === "number" && !_isPointerGoType(f.goType, mod)) {
|
|
3010
|
+
for (const s of mod.structTypes.values())
|
|
3011
|
+
if (s.typeIndex === ti) return _zeroStructConst(s, mod);
|
|
3012
|
+
}
|
|
3013
|
+
return [{ op: "ref.null", heapType: ti ?? "any" }];
|
|
3014
|
+
}
|
|
3015
|
+
|
|
1234
3016
|
function _collectPackageGlobals(progs, mod, checker) {
|
|
1235
3017
|
const pending = []; // non-constant initializers, run by the start function
|
|
3018
|
+
const pendingConsts = []; // unfoldable consts; they run before any var
|
|
1236
3019
|
for (const p of progs) {
|
|
1237
3020
|
for (const d of p.decls ?? []) {
|
|
1238
|
-
|
|
3021
|
+
const isConst = d.kind === "ConstDecl";
|
|
3022
|
+
if (d.kind !== "VarDecl" && !isConst) continue;
|
|
1239
3023
|
for (const spec of d.decls ?? d.specs ?? [d]) {
|
|
1240
3024
|
const names = spec.names ?? (spec.name ? [spec.name] : []);
|
|
1241
3025
|
const values = spec.value ?? (spec.init ? [spec.init] : []);
|
|
1242
3026
|
for (let i = 0; i < names.length; i++) {
|
|
1243
3027
|
const name = names[i];
|
|
3028
|
+
// Foldable consts live in `constCache`, not in a global.
|
|
3029
|
+
if (isConst && !mod.isNonLiteralConst(name)) continue;
|
|
1244
3030
|
const rawType = spec.type ?? values[i]?._type;
|
|
1245
3031
|
const wType = toWasmType(rawType, checker, mod);
|
|
1246
3032
|
const gIdx = mod.globals.length;
|
|
@@ -1270,34 +3056,7 @@ function _collectPackageGlobals(progs, mod, checker) {
|
|
|
1270
3056
|
(s) => s.typeIndex === wType.typeIndex,
|
|
1271
3057
|
);
|
|
1272
3058
|
if (structInfo) {
|
|
1273
|
-
initInsts =
|
|
1274
|
-
for (const f of structInfo.fields) {
|
|
1275
|
-
if (f.wType === "i64")
|
|
1276
|
-
initInsts.push({ op: "i64.const", value: 0n });
|
|
1277
|
-
else if (f.wType === "f32" || f.wType === "f64")
|
|
1278
|
-
initInsts.push({ op: `${f.wType}.const`, value: 0.0 });
|
|
1279
|
-
else if (typeof f.wType === "object") {
|
|
1280
|
-
const fieldSlice =
|
|
1281
|
-
typeof f.wType.typeIndex === "number"
|
|
1282
|
-
? mod.getSliceTypeByIndex(f.wType.typeIndex)
|
|
1283
|
-
: null;
|
|
1284
|
-
if (fieldSlice) {
|
|
1285
|
-
initInsts.push({
|
|
1286
|
-
op: "global.get",
|
|
1287
|
-
index: fieldSlice.emptyGlobalIndex,
|
|
1288
|
-
});
|
|
1289
|
-
} else {
|
|
1290
|
-
initInsts.push({
|
|
1291
|
-
op: "ref.null",
|
|
1292
|
-
heapType: f.wType.typeIndex ?? "any",
|
|
1293
|
-
});
|
|
1294
|
-
}
|
|
1295
|
-
} else initInsts.push({ op: "i32.const", value: 0 });
|
|
1296
|
-
}
|
|
1297
|
-
initInsts.push({
|
|
1298
|
-
op: "struct.new",
|
|
1299
|
-
typeIndex: structInfo.typeIndex,
|
|
1300
|
-
});
|
|
3059
|
+
initInsts = _zeroStructConst(structInfo, mod);
|
|
1301
3060
|
} else {
|
|
1302
3061
|
initInsts = [
|
|
1303
3062
|
{ op: "ref.null", heapType: wType.typeIndex ?? "any" },
|
|
@@ -1332,15 +3091,17 @@ function _collectPackageGlobals(progs, mod, checker) {
|
|
|
1332
3091
|
if (litNode && litNode.kind === "BasicLit") {
|
|
1333
3092
|
if (litNode.litKind === "INT") {
|
|
1334
3093
|
isConstInit = true;
|
|
3094
|
+
const s = String(litNode.value);
|
|
3095
|
+
const clean = /^0[0-7]+$/.test(s) ? `0o${s.slice(1)}` : s;
|
|
1335
3096
|
initInsts = [
|
|
1336
3097
|
wType === "i64"
|
|
1337
3098
|
? {
|
|
1338
3099
|
op: "i64.const",
|
|
1339
|
-
value: BigInt(sign) * BigInt(
|
|
3100
|
+
value: BigInt(sign) * BigInt(clean),
|
|
1340
3101
|
}
|
|
1341
3102
|
: {
|
|
1342
3103
|
op: "i32.const",
|
|
1343
|
-
value: (sign * Number(
|
|
3104
|
+
value: (sign * Number(clean)) | 0,
|
|
1344
3105
|
},
|
|
1345
3106
|
];
|
|
1346
3107
|
} else if (litNode.litKind === "FLOAT") {
|
|
@@ -1362,7 +3123,10 @@ function _collectPackageGlobals(progs, mod, checker) {
|
|
|
1362
3123
|
}
|
|
1363
3124
|
}
|
|
1364
3125
|
if (values[i] && !isConstInit && names.length === values.length) {
|
|
1365
|
-
|
|
3126
|
+
(isConst ? pendingConsts : pending).push({
|
|
3127
|
+
name,
|
|
3128
|
+
expr: values[i],
|
|
3129
|
+
});
|
|
1366
3130
|
}
|
|
1367
3131
|
mod.globals.push({
|
|
1368
3132
|
type: wType,
|
|
@@ -1378,7 +3142,26 @@ function _collectPackageGlobals(progs, mod, checker) {
|
|
|
1378
3142
|
}
|
|
1379
3143
|
}
|
|
1380
3144
|
}
|
|
1381
|
-
return pending;
|
|
3145
|
+
return [..._orderConstInits(pendingConsts), ...pending];
|
|
3146
|
+
}
|
|
3147
|
+
|
|
3148
|
+
// Orders runtime-initialised consts so each runs after the consts it
|
|
3149
|
+
// references (Go allows a constant to use one declared later).
|
|
3150
|
+
function _orderConstInits(entries) {
|
|
3151
|
+
const byName = new Map(entries.map((e) => [e.name, e]));
|
|
3152
|
+
const ordered = [];
|
|
3153
|
+
const state = new Map();
|
|
3154
|
+
const visit = (entry) => {
|
|
3155
|
+
if (state.has(entry.name)) return;
|
|
3156
|
+
state.set(entry.name, true);
|
|
3157
|
+
_someNode(entry.expr, (n) => {
|
|
3158
|
+
if (n.kind === "Ident" && byName.has(n.name)) visit(byName.get(n.name));
|
|
3159
|
+
return false;
|
|
3160
|
+
});
|
|
3161
|
+
ordered.push(entry);
|
|
3162
|
+
};
|
|
3163
|
+
for (const e of entries) visit(e);
|
|
3164
|
+
return ordered;
|
|
1382
3165
|
}
|
|
1383
3166
|
|
|
1384
3167
|
// Synthesises `__init_globals()` assigning every non-constant package-level
|
|
@@ -1411,3 +3194,566 @@ function _makeGlobalInitFunc(pending, initNames = []) {
|
|
|
1411
3194
|
_isGlobalInit: true,
|
|
1412
3195
|
};
|
|
1413
3196
|
}
|
|
3197
|
+
|
|
3198
|
+
function _scanInterfaceCallNodes(node, interfaceCalls, checker) {
|
|
3199
|
+
if (!node || typeof node !== "object") return;
|
|
3200
|
+
if (Array.isArray(node)) {
|
|
3201
|
+
for (const item of node)
|
|
3202
|
+
_scanInterfaceCallNodes(item, interfaceCalls, checker);
|
|
3203
|
+
return;
|
|
3204
|
+
}
|
|
3205
|
+
if (node.kind === "CallExpr" && node.func?.kind === "SelectorExpr") {
|
|
3206
|
+
const recvType = node.func.expr?._type;
|
|
3207
|
+
if (isNonEmptyInterface(recvType, checker)) {
|
|
3208
|
+
const ifaceName = getReceiverTypeName(recvType, node.func.expr) || "anon";
|
|
3209
|
+
const methodName = node.func.field;
|
|
3210
|
+
const key = `${ifaceName}.${methodName}`;
|
|
3211
|
+
if (!interfaceCalls.has(key)) {
|
|
3212
|
+
interfaceCalls.set(key, {
|
|
3213
|
+
ifaceName,
|
|
3214
|
+
ifaceType: recvType,
|
|
3215
|
+
methodName,
|
|
3216
|
+
callNode: node,
|
|
3217
|
+
});
|
|
3218
|
+
}
|
|
3219
|
+
}
|
|
3220
|
+
}
|
|
3221
|
+
for (const key of Object.keys(node)) {
|
|
3222
|
+
if (!key.startsWith("_"))
|
|
3223
|
+
_scanInterfaceCallNodes(node[key], interfaceCalls, checker);
|
|
3224
|
+
}
|
|
3225
|
+
}
|
|
3226
|
+
|
|
3227
|
+
function _scanInterfaceTypeDecls(progs, interfaceCalls, checker) {
|
|
3228
|
+
for (const p of progs) {
|
|
3229
|
+
for (const d of p.decls ?? []) {
|
|
3230
|
+
if (d.kind !== "TypeDecl") continue;
|
|
3231
|
+
const resolved = checker?.types?.get(d.name) ?? d.type;
|
|
3232
|
+
const underlying =
|
|
3233
|
+
resolved?.kind === "named"
|
|
3234
|
+
? resolved.underlying
|
|
3235
|
+
: d.type?.kind === "InterfaceType"
|
|
3236
|
+
? d.type
|
|
3237
|
+
: resolved;
|
|
3238
|
+
if (!isNonEmptyInterface(underlying, checker)) continue;
|
|
3239
|
+
const methods =
|
|
3240
|
+
underlying.methods instanceof Map
|
|
3241
|
+
? Array.from(underlying.methods.entries())
|
|
3242
|
+
: Array.isArray(underlying.methods)
|
|
3243
|
+
? underlying.methods.map((m) => [m.name ?? m, m])
|
|
3244
|
+
: [];
|
|
3245
|
+
for (const [mName, mSig] of methods) {
|
|
3246
|
+
const key = `${d.name}.${mName}`;
|
|
3247
|
+
if (!interfaceCalls.has(key)) {
|
|
3248
|
+
interfaceCalls.set(key, {
|
|
3249
|
+
ifaceName: d.name,
|
|
3250
|
+
ifaceType: resolved,
|
|
3251
|
+
methodName: mName,
|
|
3252
|
+
methodSig: mSig,
|
|
3253
|
+
});
|
|
3254
|
+
}
|
|
3255
|
+
}
|
|
3256
|
+
}
|
|
3257
|
+
}
|
|
3258
|
+
}
|
|
3259
|
+
|
|
3260
|
+
function _makeInterfaceDispatcher(info, checker) {
|
|
3261
|
+
let methodSig = info.methodSig;
|
|
3262
|
+
if (!methodSig) {
|
|
3263
|
+
const ifaceResolved =
|
|
3264
|
+
checker?.resolveType?.(
|
|
3265
|
+
info.ifaceType?.kind === "named"
|
|
3266
|
+
? info.ifaceType.underlying
|
|
3267
|
+
: info.ifaceType,
|
|
3268
|
+
) ?? info.ifaceType;
|
|
3269
|
+
if (ifaceResolved?.methods instanceof Map) {
|
|
3270
|
+
methodSig = ifaceResolved.methods.get(info.methodName);
|
|
3271
|
+
}
|
|
3272
|
+
}
|
|
3273
|
+
|
|
3274
|
+
const params = [{ name: "__recv", type: { kind: "basic", name: "any" } }];
|
|
3275
|
+
if (methodSig?.params) {
|
|
3276
|
+
for (let p = 0; p < methodSig.params.length; p++) {
|
|
3277
|
+
params.push({ name: `__arg${p}`, type: methodSig.params[p] });
|
|
3278
|
+
}
|
|
3279
|
+
} else if (info.callNode?.args) {
|
|
3280
|
+
for (let p = 0; p < info.callNode.args.length; p++) {
|
|
3281
|
+
params.push({
|
|
3282
|
+
name: `__arg${p}`,
|
|
3283
|
+
type: info.callNode.args[p]._type ?? {
|
|
3284
|
+
kind: "basic",
|
|
3285
|
+
name: "any",
|
|
3286
|
+
},
|
|
3287
|
+
});
|
|
3288
|
+
}
|
|
3289
|
+
}
|
|
3290
|
+
|
|
3291
|
+
let returnType = info.callNode?._type ?? null;
|
|
3292
|
+
if (methodSig?.returns) {
|
|
3293
|
+
returnType =
|
|
3294
|
+
methodSig.returns.length === 1
|
|
3295
|
+
? methodSig.returns[0]
|
|
3296
|
+
: methodSig.returns.length > 1
|
|
3297
|
+
? { kind: "tuple", types: methodSig.returns }
|
|
3298
|
+
: null;
|
|
3299
|
+
}
|
|
3300
|
+
|
|
3301
|
+
return {
|
|
3302
|
+
kind: "FuncDecl",
|
|
3303
|
+
name: `__dispatch_${info.ifaceName}_${info.methodName}`,
|
|
3304
|
+
params,
|
|
3305
|
+
returnType,
|
|
3306
|
+
body: { kind: "Block", stmts: [], list: [] },
|
|
3307
|
+
_isInterfaceDispatcher: true,
|
|
3308
|
+
_ifaceName: info.ifaceName,
|
|
3309
|
+
_ifaceType: info.ifaceType,
|
|
3310
|
+
_methodName: info.methodName,
|
|
3311
|
+
_rootFuncDecl: null,
|
|
3312
|
+
};
|
|
3313
|
+
}
|
|
3314
|
+
|
|
3315
|
+
function _scanFuncSigInterfaces(
|
|
3316
|
+
progs,
|
|
3317
|
+
funcDecls,
|
|
3318
|
+
interfaceCalls,
|
|
3319
|
+
checker,
|
|
3320
|
+
mod,
|
|
3321
|
+
) {
|
|
3322
|
+
const scanType = (t) => {
|
|
3323
|
+
if (!t) return;
|
|
3324
|
+
if (t.kind === "InterfaceType" || (t.kind === "interface" && !t.name)) {
|
|
3325
|
+
const mList = getInterfaceMethodList(t);
|
|
3326
|
+
if (mList.length === 0) return;
|
|
3327
|
+
const ifaceName = anonIfaceName(t, checker, mod);
|
|
3328
|
+
for (const m of mList) {
|
|
3329
|
+
const key = `${ifaceName}.${m.name}`;
|
|
3330
|
+
if (!interfaceCalls.has(key)) {
|
|
3331
|
+
interfaceCalls.set(key, {
|
|
3332
|
+
ifaceName,
|
|
3333
|
+
ifaceType: t,
|
|
3334
|
+
methodName: m.name,
|
|
3335
|
+
methodSig: {
|
|
3336
|
+
kind: "func",
|
|
3337
|
+
params: m.params,
|
|
3338
|
+
returns: m.returns,
|
|
3339
|
+
},
|
|
3340
|
+
});
|
|
3341
|
+
}
|
|
3342
|
+
}
|
|
3343
|
+
}
|
|
3344
|
+
};
|
|
3345
|
+
for (const fn of funcDecls) {
|
|
3346
|
+
for (const p of fn.params ?? []) scanType(p.type);
|
|
3347
|
+
scanType(fn.returnType);
|
|
3348
|
+
}
|
|
3349
|
+
for (const p of progs) {
|
|
3350
|
+
for (const d of p.decls ?? []) {
|
|
3351
|
+
if (d.kind === "FuncDecl") {
|
|
3352
|
+
for (const param of d.params ?? []) scanType(param.type);
|
|
3353
|
+
scanType(d.returnType);
|
|
3354
|
+
}
|
|
3355
|
+
}
|
|
3356
|
+
}
|
|
3357
|
+
}
|
|
3358
|
+
|
|
3359
|
+
function _collectInterfaceDispatchers(progs, funcDecls, mod, checker) {
|
|
3360
|
+
const interfaceCalls = new Map();
|
|
3361
|
+
for (const fn of funcDecls) {
|
|
3362
|
+
if (fn.body) _scanInterfaceCallNodes(fn.body, interfaceCalls, checker);
|
|
3363
|
+
}
|
|
3364
|
+
_scanInterfaceTypeDecls(progs, interfaceCalls, checker);
|
|
3365
|
+
_scanFuncSigInterfaces(progs, funcDecls, interfaceCalls, checker, mod);
|
|
3366
|
+
|
|
3367
|
+
// `error.Error()` is reached implicitly by fmt verbs and errors.Is, so
|
|
3368
|
+
// the dispatcher must exist even when user code never calls it.
|
|
3369
|
+
if (mod.hasErrorType && !interfaceCalls.has("error.Error")) {
|
|
3370
|
+
interfaceCalls.set("error.Error", {
|
|
3371
|
+
ifaceName: "error",
|
|
3372
|
+
ifaceType: checker?.types?.get("error") ?? {
|
|
3373
|
+
kind: "interface",
|
|
3374
|
+
name: "error",
|
|
3375
|
+
},
|
|
3376
|
+
methodName: "Error",
|
|
3377
|
+
methodSig: {
|
|
3378
|
+
kind: "func",
|
|
3379
|
+
params: [],
|
|
3380
|
+
returns: [{ kind: "basic", name: "string" }],
|
|
3381
|
+
},
|
|
3382
|
+
});
|
|
3383
|
+
}
|
|
3384
|
+
|
|
3385
|
+
const hasIo = importsAnyOf(progs, [
|
|
3386
|
+
"io",
|
|
3387
|
+
"fmt",
|
|
3388
|
+
"strings",
|
|
3389
|
+
"bytes",
|
|
3390
|
+
"crypto/sha256",
|
|
3391
|
+
"sha256",
|
|
3392
|
+
"encoding/binary",
|
|
3393
|
+
"binary",
|
|
3394
|
+
"encoding/hex",
|
|
3395
|
+
"hex",
|
|
3396
|
+
"encoding/base64",
|
|
3397
|
+
"base64",
|
|
3398
|
+
]);
|
|
3399
|
+
if (hasIo) {
|
|
3400
|
+
const byteSliceType = {
|
|
3401
|
+
kind: "slice",
|
|
3402
|
+
elem: { kind: "basic", name: "byte" },
|
|
3403
|
+
};
|
|
3404
|
+
const intErrReturns = [
|
|
3405
|
+
{ kind: "basic", name: "int" },
|
|
3406
|
+
{ kind: "interface", name: "error" },
|
|
3407
|
+
];
|
|
3408
|
+
if (!interfaceCalls.has("io.Reader.Read")) {
|
|
3409
|
+
interfaceCalls.set("io.Reader.Read", {
|
|
3410
|
+
ifaceName: "io.Reader",
|
|
3411
|
+
ifaceType: checker?.types?.get("io.Reader"),
|
|
3412
|
+
methodName: "Read",
|
|
3413
|
+
methodSig: {
|
|
3414
|
+
kind: "func",
|
|
3415
|
+
params: [byteSliceType],
|
|
3416
|
+
returns: intErrReturns,
|
|
3417
|
+
},
|
|
3418
|
+
});
|
|
3419
|
+
}
|
|
3420
|
+
if (!interfaceCalls.has("io.Writer.Write")) {
|
|
3421
|
+
interfaceCalls.set("io.Writer.Write", {
|
|
3422
|
+
ifaceName: "io.Writer",
|
|
3423
|
+
ifaceType: checker?.types?.get("io.Writer"),
|
|
3424
|
+
methodName: "Write",
|
|
3425
|
+
methodSig: {
|
|
3426
|
+
kind: "func",
|
|
3427
|
+
params: [byteSliceType],
|
|
3428
|
+
returns: intErrReturns,
|
|
3429
|
+
},
|
|
3430
|
+
});
|
|
3431
|
+
}
|
|
3432
|
+
}
|
|
3433
|
+
|
|
3434
|
+
for (const info of interfaceCalls.values()) {
|
|
3435
|
+
funcDecls.push(_makeInterfaceDispatcher(info, checker));
|
|
3436
|
+
}
|
|
3437
|
+
}
|
|
3438
|
+
|
|
3439
|
+
function _registerIoSyntheticMethods(funcDecls, mod, strType, programs) {
|
|
3440
|
+
const progs = Array.isArray(programs) ? programs : [programs];
|
|
3441
|
+
const hasStrings = importsAnyOf(progs, ["strings"]);
|
|
3442
|
+
const hasBytes = importsAnyOf(progs, ["bytes"]);
|
|
3443
|
+
const hasIo = importsAnyOf(progs, ["io"]);
|
|
3444
|
+
|
|
3445
|
+
const BYTE_SLICE_T = {
|
|
3446
|
+
kind: "slice",
|
|
3447
|
+
elem: { kind: "basic", name: "byte" },
|
|
3448
|
+
};
|
|
3449
|
+
const INT_T = { kind: "basic", name: "int" };
|
|
3450
|
+
const ERR_T = { kind: "interface", name: "error" };
|
|
3451
|
+
const INT_ERR_RET = { kind: "tuple", types: [INT_T, ERR_T] };
|
|
3452
|
+
const ioHelper = (name, params, returnType, kind, extra = {}) => ({
|
|
3453
|
+
kind: "FuncDecl",
|
|
3454
|
+
name,
|
|
3455
|
+
params,
|
|
3456
|
+
returnType,
|
|
3457
|
+
body: { kind: "Block", stmts: [], list: [] },
|
|
3458
|
+
_isIoHelper: kind,
|
|
3459
|
+
_rootFuncDecl: null,
|
|
3460
|
+
...extra,
|
|
3461
|
+
});
|
|
3462
|
+
|
|
3463
|
+
if (hasStrings && mod.structTypes.has("strings.Builder")) {
|
|
3464
|
+
const bPtr = {
|
|
3465
|
+
kind: "pointer",
|
|
3466
|
+
base: { kind: "named", name: "strings.Builder" },
|
|
3467
|
+
};
|
|
3468
|
+
funcDecls.push(
|
|
3469
|
+
ioHelper(
|
|
3470
|
+
"strings.Builder.Write",
|
|
3471
|
+
[
|
|
3472
|
+
{ name: "b", type: bPtr },
|
|
3473
|
+
{ name: "p", type: BYTE_SLICE_T },
|
|
3474
|
+
],
|
|
3475
|
+
INT_ERR_RET,
|
|
3476
|
+
"builder_write",
|
|
3477
|
+
{
|
|
3478
|
+
_isMethod: true,
|
|
3479
|
+
_recvTypeName: "strings.Builder",
|
|
3480
|
+
_methodName: "Write",
|
|
3481
|
+
},
|
|
3482
|
+
),
|
|
3483
|
+
ioHelper(
|
|
3484
|
+
"strings.Builder.WriteString",
|
|
3485
|
+
[
|
|
3486
|
+
{ name: "b", type: bPtr },
|
|
3487
|
+
{ name: "s", type: strType },
|
|
3488
|
+
],
|
|
3489
|
+
INT_ERR_RET,
|
|
3490
|
+
"builder_write_string",
|
|
3491
|
+
{
|
|
3492
|
+
_isMethod: true,
|
|
3493
|
+
_recvTypeName: "strings.Builder",
|
|
3494
|
+
_methodName: "WriteString",
|
|
3495
|
+
},
|
|
3496
|
+
),
|
|
3497
|
+
);
|
|
3498
|
+
}
|
|
3499
|
+
if (hasBytes && mod.structTypes.has("bytes.Buffer")) {
|
|
3500
|
+
const bPtr = {
|
|
3501
|
+
kind: "pointer",
|
|
3502
|
+
base: { kind: "named", name: "bytes.Buffer" },
|
|
3503
|
+
};
|
|
3504
|
+
funcDecls.push(
|
|
3505
|
+
ioHelper(
|
|
3506
|
+
"bytes.Buffer.Write",
|
|
3507
|
+
[
|
|
3508
|
+
{ name: "b", type: bPtr },
|
|
3509
|
+
{ name: "p", type: BYTE_SLICE_T },
|
|
3510
|
+
],
|
|
3511
|
+
INT_ERR_RET,
|
|
3512
|
+
"buffer_write",
|
|
3513
|
+
{
|
|
3514
|
+
_isMethod: true,
|
|
3515
|
+
_recvTypeName: "bytes.Buffer",
|
|
3516
|
+
_methodName: "Write",
|
|
3517
|
+
},
|
|
3518
|
+
),
|
|
3519
|
+
ioHelper(
|
|
3520
|
+
"bytes.Buffer.WriteString",
|
|
3521
|
+
[
|
|
3522
|
+
{ name: "b", type: bPtr },
|
|
3523
|
+
{ name: "s", type: strType },
|
|
3524
|
+
],
|
|
3525
|
+
INT_ERR_RET,
|
|
3526
|
+
"buffer_write_string",
|
|
3527
|
+
{
|
|
3528
|
+
_isMethod: true,
|
|
3529
|
+
_recvTypeName: "bytes.Buffer",
|
|
3530
|
+
_methodName: "WriteString",
|
|
3531
|
+
},
|
|
3532
|
+
),
|
|
3533
|
+
ioHelper(
|
|
3534
|
+
"bytes.Buffer.Read",
|
|
3535
|
+
[
|
|
3536
|
+
{ name: "b", type: bPtr },
|
|
3537
|
+
{ name: "p", type: BYTE_SLICE_T },
|
|
3538
|
+
],
|
|
3539
|
+
INT_ERR_RET,
|
|
3540
|
+
"buffer_read",
|
|
3541
|
+
{
|
|
3542
|
+
_isMethod: true,
|
|
3543
|
+
_recvTypeName: "bytes.Buffer",
|
|
3544
|
+
_methodName: "Read",
|
|
3545
|
+
},
|
|
3546
|
+
),
|
|
3547
|
+
);
|
|
3548
|
+
}
|
|
3549
|
+
if (hasStrings && mod.structTypes.has("strings.Reader")) {
|
|
3550
|
+
const rPtr = {
|
|
3551
|
+
kind: "pointer",
|
|
3552
|
+
base: { kind: "named", name: "strings.Reader" },
|
|
3553
|
+
};
|
|
3554
|
+
funcDecls.push(
|
|
3555
|
+
ioHelper(
|
|
3556
|
+
"strings.Reader.Read",
|
|
3557
|
+
[
|
|
3558
|
+
{ name: "r", type: rPtr },
|
|
3559
|
+
{ name: "p", type: BYTE_SLICE_T },
|
|
3560
|
+
],
|
|
3561
|
+
INT_ERR_RET,
|
|
3562
|
+
"strings_reader_read",
|
|
3563
|
+
{
|
|
3564
|
+
_isMethod: true,
|
|
3565
|
+
_recvTypeName: "strings.Reader",
|
|
3566
|
+
_methodName: "Read",
|
|
3567
|
+
},
|
|
3568
|
+
),
|
|
3569
|
+
ioHelper(
|
|
3570
|
+
"strings.Reader.Len",
|
|
3571
|
+
[{ name: "r", type: rPtr }],
|
|
3572
|
+
INT_T,
|
|
3573
|
+
"strings_reader_len",
|
|
3574
|
+
{
|
|
3575
|
+
_isMethod: true,
|
|
3576
|
+
_recvTypeName: "strings.Reader",
|
|
3577
|
+
_methodName: "Len",
|
|
3578
|
+
},
|
|
3579
|
+
),
|
|
3580
|
+
ioHelper(
|
|
3581
|
+
"strings.Reader.Reset",
|
|
3582
|
+
[
|
|
3583
|
+
{ name: "r", type: rPtr },
|
|
3584
|
+
{ name: "s", type: strType },
|
|
3585
|
+
],
|
|
3586
|
+
null,
|
|
3587
|
+
"strings_reader_reset",
|
|
3588
|
+
{
|
|
3589
|
+
_isMethod: true,
|
|
3590
|
+
_recvTypeName: "strings.Reader",
|
|
3591
|
+
_methodName: "Reset",
|
|
3592
|
+
},
|
|
3593
|
+
),
|
|
3594
|
+
);
|
|
3595
|
+
}
|
|
3596
|
+
if (hasBytes && mod.structTypes.has("bytes.Reader")) {
|
|
3597
|
+
const rPtr = {
|
|
3598
|
+
kind: "pointer",
|
|
3599
|
+
base: { kind: "named", name: "bytes.Reader" },
|
|
3600
|
+
};
|
|
3601
|
+
funcDecls.push(
|
|
3602
|
+
ioHelper(
|
|
3603
|
+
"bytes.Reader.Read",
|
|
3604
|
+
[
|
|
3605
|
+
{ name: "r", type: rPtr },
|
|
3606
|
+
{ name: "p", type: BYTE_SLICE_T },
|
|
3607
|
+
],
|
|
3608
|
+
INT_ERR_RET,
|
|
3609
|
+
"bytes_reader_read",
|
|
3610
|
+
{ _isMethod: true, _recvTypeName: "bytes.Reader", _methodName: "Read" },
|
|
3611
|
+
),
|
|
3612
|
+
ioHelper(
|
|
3613
|
+
"bytes.Reader.Len",
|
|
3614
|
+
[{ name: "r", type: rPtr }],
|
|
3615
|
+
INT_T,
|
|
3616
|
+
"bytes_reader_len",
|
|
3617
|
+
{ _isMethod: true, _recvTypeName: "bytes.Reader", _methodName: "Len" },
|
|
3618
|
+
),
|
|
3619
|
+
ioHelper(
|
|
3620
|
+
"bytes.Reader.Reset",
|
|
3621
|
+
[
|
|
3622
|
+
{ name: "r", type: rPtr },
|
|
3623
|
+
{ name: "b", type: BYTE_SLICE_T },
|
|
3624
|
+
],
|
|
3625
|
+
null,
|
|
3626
|
+
"bytes_reader_reset",
|
|
3627
|
+
{
|
|
3628
|
+
_isMethod: true,
|
|
3629
|
+
_recvTypeName: "bytes.Reader",
|
|
3630
|
+
_methodName: "Reset",
|
|
3631
|
+
},
|
|
3632
|
+
),
|
|
3633
|
+
);
|
|
3634
|
+
}
|
|
3635
|
+
if (hasIo && mod.structTypes.has("io.discard")) {
|
|
3636
|
+
const dPtr = {
|
|
3637
|
+
kind: "pointer",
|
|
3638
|
+
base: { kind: "named", name: "io.discard" },
|
|
3639
|
+
};
|
|
3640
|
+
funcDecls.push(
|
|
3641
|
+
ioHelper(
|
|
3642
|
+
"io.discard.Write",
|
|
3643
|
+
[
|
|
3644
|
+
{ name: "d", type: dPtr },
|
|
3645
|
+
{ name: "p", type: BYTE_SLICE_T },
|
|
3646
|
+
],
|
|
3647
|
+
INT_ERR_RET,
|
|
3648
|
+
"discard_write",
|
|
3649
|
+
{ _isMethod: true, _recvTypeName: "io.discard", _methodName: "Write" },
|
|
3650
|
+
),
|
|
3651
|
+
ioHelper(
|
|
3652
|
+
"io.discard.WriteString",
|
|
3653
|
+
[
|
|
3654
|
+
{ name: "d", type: dPtr },
|
|
3655
|
+
{ name: "s", type: strType },
|
|
3656
|
+
],
|
|
3657
|
+
INT_ERR_RET,
|
|
3658
|
+
"discard_write_string",
|
|
3659
|
+
{
|
|
3660
|
+
_isMethod: true,
|
|
3661
|
+
_recvTypeName: "io.discard",
|
|
3662
|
+
_methodName: "WriteString",
|
|
3663
|
+
},
|
|
3664
|
+
),
|
|
3665
|
+
);
|
|
3666
|
+
}
|
|
3667
|
+
if (hasIo && mod.structTypes.has("io.LimitedReader")) {
|
|
3668
|
+
const lrPtr = {
|
|
3669
|
+
kind: "pointer",
|
|
3670
|
+
base: { kind: "named", name: "io.LimitedReader" },
|
|
3671
|
+
};
|
|
3672
|
+
funcDecls.push(
|
|
3673
|
+
ioHelper(
|
|
3674
|
+
"io.LimitedReader.Read",
|
|
3675
|
+
[
|
|
3676
|
+
{ name: "l", type: lrPtr },
|
|
3677
|
+
{ name: "p", type: BYTE_SLICE_T },
|
|
3678
|
+
],
|
|
3679
|
+
INT_ERR_RET,
|
|
3680
|
+
"limited_reader_read",
|
|
3681
|
+
{
|
|
3682
|
+
_isMethod: true,
|
|
3683
|
+
_recvTypeName: "io.LimitedReader",
|
|
3684
|
+
_methodName: "Read",
|
|
3685
|
+
},
|
|
3686
|
+
),
|
|
3687
|
+
);
|
|
3688
|
+
}
|
|
3689
|
+
const hasSha256 = importsAnyOf(progs, ["crypto/sha256", "sha256"]);
|
|
3690
|
+
if (
|
|
3691
|
+
hasSha256 &&
|
|
3692
|
+
(mod.structTypes.has("sha256.digest") ||
|
|
3693
|
+
mod.structTypes.has("crypto/sha256.digest"))
|
|
3694
|
+
) {
|
|
3695
|
+
const dPtr = {
|
|
3696
|
+
kind: "pointer",
|
|
3697
|
+
base: { kind: "named", name: "sha256.digest" },
|
|
3698
|
+
};
|
|
3699
|
+
funcDecls.push(
|
|
3700
|
+
ioHelper(
|
|
3701
|
+
"sha256.digest.Write",
|
|
3702
|
+
[
|
|
3703
|
+
{ name: "d", type: dPtr },
|
|
3704
|
+
{ name: "p", type: BYTE_SLICE_T },
|
|
3705
|
+
],
|
|
3706
|
+
INT_ERR_RET,
|
|
3707
|
+
"sha256_write",
|
|
3708
|
+
{
|
|
3709
|
+
_isMethod: true,
|
|
3710
|
+
_recvTypeName: "sha256.digest",
|
|
3711
|
+
_methodName: "Write",
|
|
3712
|
+
},
|
|
3713
|
+
),
|
|
3714
|
+
ioHelper(
|
|
3715
|
+
"sha256.digest.Sum",
|
|
3716
|
+
[
|
|
3717
|
+
{ name: "d", type: dPtr },
|
|
3718
|
+
{ name: "b", type: BYTE_SLICE_T },
|
|
3719
|
+
],
|
|
3720
|
+
BYTE_SLICE_T,
|
|
3721
|
+
"sha256_sum",
|
|
3722
|
+
{ _isMethod: true, _recvTypeName: "sha256.digest", _methodName: "Sum" },
|
|
3723
|
+
),
|
|
3724
|
+
ioHelper(
|
|
3725
|
+
"sha256.digest.Reset",
|
|
3726
|
+
[{ name: "d", type: dPtr }],
|
|
3727
|
+
null,
|
|
3728
|
+
"sha256_reset",
|
|
3729
|
+
{
|
|
3730
|
+
_isMethod: true,
|
|
3731
|
+
_recvTypeName: "sha256.digest",
|
|
3732
|
+
_methodName: "Reset",
|
|
3733
|
+
},
|
|
3734
|
+
),
|
|
3735
|
+
ioHelper(
|
|
3736
|
+
"sha256.digest.Size",
|
|
3737
|
+
[{ name: "d", type: dPtr }],
|
|
3738
|
+
INT_T,
|
|
3739
|
+
"sha256_size",
|
|
3740
|
+
{
|
|
3741
|
+
_isMethod: true,
|
|
3742
|
+
_recvTypeName: "sha256.digest",
|
|
3743
|
+
_methodName: "Size",
|
|
3744
|
+
},
|
|
3745
|
+
),
|
|
3746
|
+
ioHelper(
|
|
3747
|
+
"sha256.digest.BlockSize",
|
|
3748
|
+
[{ name: "d", type: dPtr }],
|
|
3749
|
+
INT_T,
|
|
3750
|
+
"sha256_block_size",
|
|
3751
|
+
{
|
|
3752
|
+
_isMethod: true,
|
|
3753
|
+
_recvTypeName: "sha256.digest",
|
|
3754
|
+
_methodName: "BlockSize",
|
|
3755
|
+
},
|
|
3756
|
+
),
|
|
3757
|
+
);
|
|
3758
|
+
}
|
|
3759
|
+
}
|