gofront 1.7.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -14
- package/package.json +10 -38
- package/src/backend/js/expressions.js +2060 -389
- package/src/backend/js/index.js +586 -174
- package/src/backend/js/natives.js +383 -0
- package/src/backend/js/runtime.js +936 -1644
- package/src/backend/js/statements.js +1349 -132
- package/src/backend/js/stdlib/index.js +51 -94
- package/src/backend/js/templ.js +67 -118
- package/src/backend/js/type-info.js +436 -0
- package/src/backend/shared/fixed-float.js +20 -0
- package/src/backend/shared/fmt-types.js +80 -0
- package/src/backend/shared/host-unicode.js +36 -0
- package/src/backend/wasm/boundary.js +753 -872
- package/src/backend/wasm/emit-builtins.js +914 -237
- package/src/backend/wasm/emit-exprs.js +1456 -195
- package/src/backend/wasm/emit-maps.js +78 -380
- package/src/backend/wasm/emit-stmts.js +1659 -214
- package/src/backend/wasm/emit.js +614 -88
- package/src/backend/wasm/encode.js +43 -13
- package/src/backend/wasm/globals.js +491 -0
- package/src/backend/wasm/glue.js +2 -4
- package/src/backend/wasm/index.js +406 -3195
- package/src/backend/wasm/method-expr.js +392 -0
- package/src/backend/wasm/module-emitter.js +1615 -0
- package/src/backend/wasm/monomorph.js +164 -9
- package/src/backend/wasm/natives.js +596 -0
- package/src/backend/wasm/prescan.js +249 -0
- package/src/backend/wasm/prune-funcs.js +89 -0
- package/src/backend/wasm/stdlib/index.js +2 -37
- package/src/backend/wasm/stdlib/simd.js +293 -28
- package/src/backend/wasm/types.js +260 -27
- package/src/backend/wasm/variadic.js +93 -0
- package/src/backend/wasm/wat.js +47 -20
- package/src/{cli-core.js → cli/cli-core.js} +241 -11
- package/src/{test-runner.js → cli/test-runner.js} +188 -122
- package/src/{vendor.js → cli/vendor.js} +3 -4
- package/src/compiler.js +202 -386
- package/src/index.js +15 -169
- package/src/{lexer.js → lexer/lexer.js} +203 -62
- package/src/{templ-lexer.js → lexer/templ-lexer.js} +61 -18
- package/src/{tokens.js → lexer/tokens.js} +2 -0
- package/src/link/sourcemap.js +97 -0
- package/src/link/stdlib.js +737 -0
- package/src/link/type-names.js +148 -0
- package/src/link/wasm.js +318 -0
- package/src/lower/bounds.js +547 -0
- package/src/lower/boxing.js +1 -1
- package/src/lower/captures.js +267 -64
- package/src/lower/embedding.js +32 -3
- package/src/lower/errors-as.js +196 -0
- package/src/lower/fmt-formatters.js +905 -0
- package/src/lower/functions.js +2 -0
- package/src/lower/gen-types.js +274 -0
- package/src/lower/index.js +33 -19
- package/src/lower/json-codecs.js +1379 -0
- package/src/lower/ownership.js +47 -8
- package/src/lower/range.js +86 -1
- package/src/lower/reachability.js +150 -0
- package/src/lower/walk.js +12 -0
- package/src/parser/declarations.js +83 -15
- package/src/parser/expressions.js +110 -18
- package/src/parser/index.js +2 -1
- package/src/parser/statements.js +38 -22
- package/src/{templ-parser.js → parser/templ.js} +179 -17
- package/src/parser/types.js +70 -9
- package/src/resolver.js +21 -1
- package/src/typechecker/assignability.js +291 -19
- package/src/typechecker/const-fold.js +399 -0
- package/src/typechecker/constants.js +282 -0
- package/src/typechecker/expressions.js +1220 -183
- package/src/typechecker/index.js +659 -92
- package/src/typechecker/init-order.js +426 -0
- package/src/typechecker/operand-text.js +40 -0
- package/src/typechecker/resolve.js +613 -62
- package/src/typechecker/statements.js +212 -20
- package/src/typechecker/stdlib/excluded.js +902 -0
- package/src/typechecker/stdlib/index.js +38 -0
- package/src/typechecker/stdlib/simd.js +65 -15
- package/src/typechecker/termination.js +11 -2
- package/src/typechecker/types.js +268 -514
- package/stdlib/bytes/buffer.go +479 -0
- package/stdlib/bytes/bytes.go +1010 -0
- package/stdlib/bytes/reader.go +161 -0
- package/stdlib/cmp/cmp.go +65 -0
- package/stdlib/context/context.go +115 -0
- package/stdlib/crypto/rand/rand.go +34 -0
- package/stdlib/crypto/sha256/sha256.go +249 -0
- package/stdlib/encoding/base64/base64.go +331 -0
- package/stdlib/encoding/binary/binary.go +332 -0
- package/stdlib/encoding/hex/hex.go +140 -0
- package/stdlib/encoding/json/errors.go +31 -0
- package/stdlib/encoding/json/indent.go +71 -0
- package/stdlib/encoding/json/json.go +62 -0
- package/stdlib/encoding/json/number.go +12 -0
- package/stdlib/encoding/json/stream.go +173 -0
- package/stdlib/errors/errors.go +174 -0
- package/stdlib/fmt/errors.go +110 -0
- package/stdlib/fmt/format.go +732 -0
- package/stdlib/fmt/print.go +1232 -0
- package/stdlib/gom/gom.go +293 -0
- package/stdlib/hash/hash.go +53 -0
- package/stdlib/html/html.go +251 -0
- package/stdlib/internal/fmtrt/fmtrt.go +163 -0
- package/stdlib/internal/jsonrt/dec.go +871 -0
- package/stdlib/internal/jsonrt/enc.go +516 -0
- package/stdlib/internal/jsonrt/indent.go +135 -0
- package/stdlib/internal/jsonrt/number.go +27 -0
- package/stdlib/internal/jsonrt/reader.go +73 -0
- package/stdlib/internal/jsonrt/scanner.go +658 -0
- package/stdlib/io/io.go +502 -0
- package/stdlib/iter/iter.go +18 -0
- package/stdlib/maps/maps.go +131 -0
- package/stdlib/math/bits/bits.go +507 -0
- package/stdlib/math/extra.go +272 -0
- package/stdlib/math/math.go +439 -0
- package/stdlib/math/rand/rand.go +297 -0
- package/stdlib/os/os.go +93 -0
- package/stdlib/path/filepath/filepath.go +47 -0
- package/stdlib/path/path.go +394 -0
- package/stdlib/regexp/regexp.go +231 -0
- package/stdlib/slices/slices.go +526 -0
- package/stdlib/slices/sort.go +209 -0
- package/stdlib/slices/zsort_cmp.go +480 -0
- package/stdlib/slices/zsort_ordered.go +482 -0
- package/stdlib/sort/sort.go +500 -0
- package/stdlib/sort/zsort_func.go +480 -0
- package/stdlib/sort/zsort_iface.go +480 -0
- package/stdlib/strconv/float.go +1699 -0
- package/stdlib/strconv/isprint.go +752 -0
- package/stdlib/strconv/quote.go +526 -0
- package/stdlib/strconv/strconv.go +616 -0
- package/stdlib/strings/builder.go +95 -0
- package/stdlib/strings/reader.go +163 -0
- package/stdlib/strings/replacer.go +102 -0
- package/stdlib/strings/strings.go +807 -0
- package/stdlib/sync/sync.go +268 -0
- package/stdlib/testing/async_js.go +61 -0
- package/stdlib/testing/benchmark.go +297 -0
- package/stdlib/testing/testing.go +523 -0
- package/stdlib/time/duration.go +233 -0
- package/stdlib/time/format.go +745 -0
- package/stdlib/time/parse.go +790 -0
- package/stdlib/time/sleep_js.go +63 -0
- package/stdlib/time/time.go +834 -0
- package/stdlib/unicode/unicode.go +250 -0
- package/stdlib/unicode/utf8/utf8.go +464 -0
- package/src/backend/js/stdlib/base64.js +0 -12
- package/src/backend/js/stdlib/binary.js +0 -102
- package/src/backend/js/stdlib/bits.js +0 -46
- package/src/backend/js/stdlib/builder.js +0 -63
- package/src/backend/js/stdlib/bytes.js +0 -122
- package/src/backend/js/stdlib/errors.js +0 -44
- package/src/backend/js/stdlib/fmt.js +0 -178
- package/src/backend/js/stdlib/gom.js +0 -155
- package/src/backend/js/stdlib/hex.js +0 -17
- package/src/backend/js/stdlib/html.js +0 -20
- package/src/backend/js/stdlib/io.js +0 -137
- package/src/backend/js/stdlib/json.js +0 -972
- package/src/backend/js/stdlib/maps.js +0 -45
- package/src/backend/js/stdlib/math.js +0 -69
- package/src/backend/js/stdlib/os.js +0 -13
- package/src/backend/js/stdlib/path.js +0 -48
- package/src/backend/js/stdlib/rand.js +0 -32
- package/src/backend/js/stdlib/regexp.js +0 -54
- package/src/backend/js/stdlib/sha256.js +0 -12
- package/src/backend/js/stdlib/shared.js +0 -17
- package/src/backend/js/stdlib/simd.js +0 -24
- package/src/backend/js/stdlib/slices.js +0 -77
- package/src/backend/js/stdlib/sort.js +0 -30
- package/src/backend/js/stdlib/strconv.js +0 -74
- package/src/backend/js/stdlib/strings.js +0 -222
- package/src/backend/js/stdlib/sync.js +0 -13
- package/src/backend/js/stdlib/testing.js +0 -34
- package/src/backend/js/stdlib/time.js +0 -128
- package/src/backend/js/stdlib/unicode.js +0 -25
- package/src/backend/js/stdlib/utf8.js +0 -26
- package/src/backend/wasm/const-fold.js +0 -205
- package/src/backend/wasm/emit-stdlib.js +0 -13
- package/src/backend/wasm/stdlib/bytes.js +0 -5039
- package/src/backend/wasm/stdlib/core.js +0 -8903
- package/src/backend/wasm/stdlib/io.js +0 -2161
- package/src/backend/wasm/stdlib/packages.js +0 -2400
- package/src/backend/wasm/stdlib/pure.js +0 -720
- package/src/backend/wasm/stdlib/time.js +0 -1200
- package/src/typechecker/stdlib/core.js +0 -801
- package/src/typechecker/stdlib/extended.js +0 -914
- package/src/typechecker/stdlib.js +0 -22
- /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
- /package/src/{colors.js → cli/colors.js} +0 -0
- /package/src/{dev-server.js → cli/dev-server.js} +0 -0
- /package/src/{minifier.js → cli/minifier.js} +0 -0
- /package/src/{project-config.js → cli/project-config.js} +0 -0
- /package/src/{pwa.js → cli/pwa.js} +0 -0
|
@@ -1,2113 +1,327 @@
|
|
|
1
1
|
// src/backend/wasm/index.js
|
|
2
2
|
// WASM Backend compiler entry point: lowered AST + types -> Module IR -> binary / WAT.
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
import { lower, lowerFunction } from "../../lower/index.js";
|
|
5
|
+
import {
|
|
6
|
+
getOrCreateIteratorFuncLit,
|
|
7
|
+
isIteratorRangeFor,
|
|
8
|
+
} from "../../lower/range.js";
|
|
8
9
|
import {
|
|
9
10
|
addBoundaryHelpers,
|
|
10
11
|
collectBoundaryMeta,
|
|
11
12
|
generateFacade,
|
|
12
13
|
scanBoundaryImports,
|
|
13
|
-
wTypeKey,
|
|
14
14
|
} from "./boundary.js";
|
|
15
|
-
import { foldConstExpr } from "./const-fold.js";
|
|
16
15
|
import { FunctionEmitter } from "./emit.js";
|
|
17
16
|
import { encodeModule, isGoFrontWasm } from "./encode.js";
|
|
17
|
+
import {
|
|
18
|
+
_collectPackageConsts,
|
|
19
|
+
_collectPackageGlobals,
|
|
20
|
+
_makeGlobalInitFunc,
|
|
21
|
+
_makeInterfaceDispatcher,
|
|
22
|
+
} from "./globals.js";
|
|
23
|
+
import { desugarMethodExprs } from "./method-expr.js";
|
|
24
|
+
import { ModuleEmitter } from "./module-emitter.js";
|
|
18
25
|
import { hasGenerics, monomorphise } from "./monomorph.js";
|
|
26
|
+
import { emitWasmIntrinsic, prepareWasmIntrinsic } from "./natives.js";
|
|
19
27
|
import { hasNativeWasmOpt, optimizeWasm } from "./optimize.js";
|
|
20
|
-
import { BINARY_MATH_IMPORTS } from "./stdlib/index.js";
|
|
21
28
|
import {
|
|
22
|
-
|
|
29
|
+
_scanFuncSigInterfaces,
|
|
30
|
+
_scanImportsInBody,
|
|
31
|
+
_scanInterfaceCallNodes,
|
|
32
|
+
_scanInterfaceTypeDecls,
|
|
33
|
+
_someNode,
|
|
34
|
+
_usesAnyEq,
|
|
35
|
+
} from "./prescan.js";
|
|
36
|
+
import { pruneUnusedFuncs } from "./prune-funcs.js";
|
|
37
|
+
import {
|
|
23
38
|
ERROR_STRUCT,
|
|
24
|
-
getFuncSignature,
|
|
25
|
-
getInterfaceMethodList,
|
|
26
39
|
getMapKeyValTypes,
|
|
27
|
-
getReceiverTypeName,
|
|
28
|
-
goTypeKey,
|
|
29
40
|
isInterfaceType,
|
|
30
41
|
isMapType,
|
|
31
|
-
isNonEmptyInterface,
|
|
32
|
-
isTestingT,
|
|
33
42
|
toWasmType,
|
|
34
43
|
} from "./types.js";
|
|
44
|
+
import { desugarVariadics } from "./variadic.js";
|
|
35
45
|
import { emitWat } from "./wat.js";
|
|
36
46
|
|
|
37
47
|
export { hasNativeWasmOpt, isGoFrontWasm, optimizeWasm };
|
|
38
48
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
"
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
);
|
|
49
|
+
// Body of `__index_panic(i i64, n i32)`: builds Go's message with the string
|
|
50
|
+
// imports and raises it like any other runtime panic.
|
|
51
|
+
function emitIndexPanicBody(mod, emitter) {
|
|
52
|
+
const str = (text) => {
|
|
53
|
+
emitter.pushInstruction({ op: "i32.const", value: mod.internString(text) });
|
|
54
|
+
emitter.pushInstruction({
|
|
55
|
+
op: "call",
|
|
56
|
+
funcIndex: mod.getStringImportIndex(),
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
const concat = () =>
|
|
60
|
+
emitter.pushInstruction({
|
|
61
|
+
op: "call",
|
|
62
|
+
funcIndex: mod.getStringConcatImportIndex(),
|
|
63
|
+
});
|
|
64
|
+
str("runtime error: index out of range [");
|
|
65
|
+
emitter.pushInstruction({ op: "local.get", index: 0 });
|
|
66
|
+
emitter.pushInstruction({
|
|
67
|
+
op: "call",
|
|
68
|
+
funcIndex: mod.getStrFromI64ImportIndex(),
|
|
69
|
+
});
|
|
70
|
+
concat();
|
|
71
|
+
emitter.pushInstruction({ op: "local.get", index: 0 });
|
|
72
|
+
emitter.pushInstruction({ op: "i64.const", value: 0n });
|
|
73
|
+
emitter.pushInstruction("i64.lt_s");
|
|
74
|
+
emitter.pushInstruction({ op: "if", blockType: "externref" });
|
|
75
|
+
str("]");
|
|
76
|
+
emitter.pushInstruction("else");
|
|
77
|
+
str("] with length ");
|
|
78
|
+
emitter.pushInstruction({ op: "local.get", index: 1 });
|
|
79
|
+
emitter.pushInstruction({
|
|
80
|
+
op: "call",
|
|
81
|
+
funcIndex: mod.getStrFromI32ImportIndex(),
|
|
82
|
+
});
|
|
83
|
+
concat();
|
|
84
|
+
emitter.pushInstruction("end");
|
|
85
|
+
concat();
|
|
86
|
+
emitter.emitRuntimePanicThrow();
|
|
98
87
|
}
|
|
99
88
|
|
|
100
|
-
function
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
89
|
+
function peepholeOptimize(instructions) {
|
|
90
|
+
let changed = true;
|
|
91
|
+
let current = instructions;
|
|
92
|
+
while (changed) {
|
|
93
|
+
changed = false;
|
|
94
|
+
const next = [];
|
|
95
|
+
for (let i = 0; i < current.length; i++) {
|
|
96
|
+
const c = current[i];
|
|
97
|
+
const n = current[i + 1];
|
|
98
|
+
|
|
99
|
+
// Pattern 1: local.get X; local.set X -> nothing
|
|
109
100
|
if (
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
n.kind === "interface") &&
|
|
114
|
-
n.name === "error"
|
|
101
|
+
c?.op === "local.get" &&
|
|
102
|
+
n?.op === "local.set" &&
|
|
103
|
+
c.index === n.index
|
|
115
104
|
) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
for (const k of Object.keys(n)) {
|
|
120
|
-
if (!k.startsWith("_")) scan(n[k]);
|
|
105
|
+
changed = true;
|
|
106
|
+
i++; // skip both
|
|
107
|
+
continue;
|
|
121
108
|
}
|
|
122
|
-
};
|
|
123
|
-
scan(p);
|
|
124
|
-
return found;
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
|
|
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 {
|
|
261
|
-
constructor(checker, lowerResult = null, programs = [], options = {}) {
|
|
262
|
-
this.checker = checker;
|
|
263
|
-
this.lowerResult = lowerResult;
|
|
264
|
-
// Local package names whose code is linked into this module; selectors
|
|
265
|
-
// like `mathx.NewVec3` are de-qualified to `NewVec3` by the emitter.
|
|
266
|
-
this.bundledPackages = options.bundledPackages ?? new Set();
|
|
267
|
-
this.constCache = new Map(); // name -> BasicLit (package-level literal consts)
|
|
268
|
-
this.nonLiteralConsts = new Set(); // package-level consts the backend cannot evaluate yet
|
|
269
|
-
|
|
270
|
-
this.types = []; // type entries
|
|
271
|
-
this.typeCache = new Map(); // signature string -> index
|
|
272
|
-
this._openRec = null; // rec group receiving new types while structs are resolved
|
|
273
|
-
|
|
274
|
-
this.structTypes = new Map(); // name -> struct info
|
|
275
|
-
this.boxTypes = new Map(); // wType key -> box info
|
|
276
|
-
this.ifaceBoxTypes = new Map(); // key -> iface box info
|
|
277
|
-
this.arrayTypes = new Map(); // wType key -> array info
|
|
278
|
-
this.sliceTypes = new Map(); // wType key -> slice info
|
|
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))
|
|
283
|
-
this.closureTypes = new Map(); // key -> closure info
|
|
284
|
-
this.envTypes = new Map(); // key -> env info
|
|
285
|
-
|
|
286
|
-
this.elements = []; // declarative elements for ref.func
|
|
287
|
-
this.elementSet = new Set();
|
|
288
|
-
|
|
289
|
-
this.imports = []; // import entries
|
|
290
|
-
this.importCache = new Map(); // key -> func index
|
|
291
|
-
|
|
292
|
-
this.tags = []; // exception tags
|
|
293
|
-
this.globals = []; // global entries
|
|
294
|
-
this.globalCache = new Map(); // name -> index
|
|
295
109
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
110
|
+
// Pattern 2: local.set X; local.get X -> local.tee X
|
|
111
|
+
if (
|
|
112
|
+
c?.op === "local.set" &&
|
|
113
|
+
n?.op === "local.get" &&
|
|
114
|
+
c.index === n.index
|
|
115
|
+
) {
|
|
116
|
+
changed = true;
|
|
117
|
+
next.push({ op: "local.tee", index: c.index });
|
|
118
|
+
i++; // skip n (local.get)
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
301
121
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
122
|
+
// Pattern 3: local.tee X; local.set X -> local.set X
|
|
123
|
+
if (
|
|
124
|
+
c?.op === "local.tee" &&
|
|
125
|
+
n?.op === "local.set" &&
|
|
126
|
+
c.index === n.index
|
|
127
|
+
) {
|
|
128
|
+
changed = true;
|
|
129
|
+
next.push({ op: "local.set", index: c.index });
|
|
130
|
+
i++;
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
305
133
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
134
|
+
// Pattern 4: local.tee X; drop -> local.set X
|
|
135
|
+
if (c?.op === "local.tee" && (n === "drop" || n?.op === "drop")) {
|
|
136
|
+
changed = true;
|
|
137
|
+
next.push({ op: "local.set", index: c.index });
|
|
138
|
+
i++;
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
312
141
|
|
|
313
|
-
|
|
314
|
-
this.initTypes(programs);
|
|
142
|
+
next.push(c);
|
|
315
143
|
}
|
|
316
|
-
|
|
317
|
-
this._importsLocked = false;
|
|
318
|
-
this._initRuntimeImports();
|
|
144
|
+
current = next;
|
|
319
145
|
}
|
|
146
|
+
return current;
|
|
147
|
+
}
|
|
320
148
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
149
|
+
export function compileWasmModule(
|
|
150
|
+
programs,
|
|
151
|
+
checker,
|
|
152
|
+
lowerResult = null,
|
|
153
|
+
options = {},
|
|
154
|
+
) {
|
|
155
|
+
let progs = Array.isArray(programs) ? programs : [programs];
|
|
156
|
+
let resolvedChecker = checker;
|
|
157
|
+
let resolvedLowerResult = lowerResult;
|
|
326
158
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
159
|
+
if (hasGenerics(progs)) {
|
|
160
|
+
const mono = monomorphise(progs, checker);
|
|
161
|
+
progs = mono.programs;
|
|
162
|
+
resolvedChecker = mono.checker;
|
|
163
|
+
resolvedLowerResult = null;
|
|
164
|
+
}
|
|
165
|
+
// Variadic parameters become slices (after monomorphisation: concrete types).
|
|
166
|
+
// Method expressions become method calls or forwarding func literals,
|
|
167
|
+
// before variadics so a variadic method's forwarder packs its arguments.
|
|
168
|
+
const methodExprs = desugarMethodExprs(progs, options.bundledPackages);
|
|
169
|
+
if (desugarVariadics(progs) || methodExprs || !resolvedLowerResult) {
|
|
170
|
+
resolvedLowerResult = lower(progs, resolvedChecker);
|
|
171
|
+
}
|
|
172
|
+
checker = resolvedChecker;
|
|
173
|
+
lowerResult = resolvedLowerResult;
|
|
340
174
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
seen.add(d.name);
|
|
346
|
-
rawStructs.push({
|
|
347
|
-
name: d.name,
|
|
348
|
-
astFields: d.type.fields ?? [],
|
|
349
|
-
pkgName: p.pkg?.name ?? "main",
|
|
350
|
-
pkgTarget: p.target ?? "wasm",
|
|
351
|
-
});
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
}
|
|
175
|
+
const mod = new ModuleEmitter(resolvedChecker, resolvedLowerResult, progs, {
|
|
176
|
+
bundledPackages: options.bundledPackages,
|
|
177
|
+
natives: options.natives,
|
|
178
|
+
});
|
|
356
179
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
180
|
+
// 1. Collect all functions, methods, and package-level globals
|
|
181
|
+
const funcDecls = [];
|
|
182
|
+
let initCount = 0;
|
|
183
|
+
const initNames = [];
|
|
184
|
+
for (const p of progs) {
|
|
185
|
+
const pkgName = p.pkg?.name ?? "main";
|
|
186
|
+
const pkgTarget = p.target ?? "wasm";
|
|
187
|
+
for (const d of p.decls ?? []) {
|
|
188
|
+
if (d.kind === "FuncDecl") {
|
|
189
|
+
d._pkgName = pkgName;
|
|
190
|
+
d._pkgTarget = pkgTarget;
|
|
191
|
+
d._srcFile = p._path ?? p._filename ?? null;
|
|
192
|
+
if (d.name === "init" || d.name.startsWith("init$")) {
|
|
193
|
+
const renamed = initCount === 0 ? "init" : `init$${initCount}`;
|
|
194
|
+
d.name = renamed;
|
|
195
|
+
initNames.push(renamed);
|
|
196
|
+
initCount++;
|
|
366
197
|
}
|
|
198
|
+
funcDecls.push(d);
|
|
199
|
+
} else if (d.kind === "MethodDecl") {
|
|
200
|
+
const recvTypeName =
|
|
201
|
+
d.recvType?.name ??
|
|
202
|
+
(d.recvType?.kind === "TypeName" ? d.recvType.name : "Recv");
|
|
203
|
+
const recvGoType = d.recvPointer
|
|
204
|
+
? { kind: "pointer", base: { kind: "named", name: recvTypeName } }
|
|
205
|
+
: { kind: "named", name: recvTypeName };
|
|
206
|
+
const normFn = {
|
|
207
|
+
kind: "FuncDecl",
|
|
208
|
+
name: `${recvTypeName}.${d.name}`,
|
|
209
|
+
params: [{ name: d.recvName, type: recvGoType }, ...(d.params ?? [])],
|
|
210
|
+
returnType: d.returnType,
|
|
211
|
+
body: d.body,
|
|
212
|
+
_isMethod: true,
|
|
213
|
+
_recvTypeName: recvTypeName,
|
|
214
|
+
_methodName: d.name,
|
|
215
|
+
_exportName: `${recvTypeName}_${d.name}`,
|
|
216
|
+
_sourceDecl: d,
|
|
217
|
+
_pkgName: pkgName,
|
|
218
|
+
_pkgTarget: pkgTarget,
|
|
219
|
+
_srcFile: p._path ?? p._filename ?? null,
|
|
220
|
+
_line: d._line,
|
|
221
|
+
};
|
|
222
|
+
funcDecls.push(normFn);
|
|
367
223
|
}
|
|
368
224
|
}
|
|
225
|
+
}
|
|
369
226
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
// group so struct indices (base + i) stay valid and may be referenced
|
|
374
|
-
// from the auxiliary types in any order.
|
|
375
|
-
const base = this._getTotalTypeCount();
|
|
376
|
-
const structEntries = rawStructs.map(() => ({
|
|
377
|
-
form: "struct",
|
|
378
|
-
fields: [],
|
|
379
|
-
}));
|
|
380
|
-
this.types.push({ form: "rec", types: structEntries });
|
|
381
|
-
this._openRec = structEntries;
|
|
227
|
+
// 1b. Scan and lift all closures (FuncLits)
|
|
228
|
+
let closureCounter = 0;
|
|
229
|
+
const closures = [];
|
|
382
230
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
fields: [],
|
|
389
|
-
fieldIndexMap: new Map(),
|
|
390
|
-
embeds: [],
|
|
391
|
-
pkgName: s.pkgName ?? null,
|
|
392
|
-
pkgTarget: s.pkgTarget ?? "wasm",
|
|
393
|
-
});
|
|
231
|
+
function scanClosures(node, rootFn) {
|
|
232
|
+
if (!node || typeof node !== "object") return;
|
|
233
|
+
if (Array.isArray(node)) {
|
|
234
|
+
for (const item of node) scanClosures(item, rootFn);
|
|
235
|
+
return;
|
|
394
236
|
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
237
|
+
if (isIteratorRangeFor(node)) {
|
|
238
|
+
const yieldFuncLit = getOrCreateIteratorFuncLit(node);
|
|
239
|
+
scanClosures(yieldFuncLit, rootFn);
|
|
240
|
+
// A func literal ranged over directly is a closure of its own.
|
|
241
|
+
scanClosures(node.cond?.expr ?? node.init?.rhs?.[0]?.expr, rootFn);
|
|
242
|
+
return;
|
|
399
243
|
}
|
|
244
|
+
if (node.kind === "FuncLit") {
|
|
245
|
+
closureCounter++;
|
|
246
|
+
const closureName = `_closure$${closureCounter}`;
|
|
247
|
+
node._liftedName = closureName;
|
|
248
|
+
node._rootFuncDecl = rootFn;
|
|
400
249
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
: [];
|
|
421
|
-
for (const n of names) {
|
|
422
|
-
const wType = toWasmType(f.type, this.checker, this);
|
|
423
|
-
fields.push({
|
|
424
|
-
name: n,
|
|
425
|
-
goType: f.type,
|
|
426
|
-
wType,
|
|
427
|
-
embedded: Boolean(f.embedded),
|
|
428
|
-
});
|
|
429
|
-
fieldIndexMap.set(n, fIdx);
|
|
430
|
-
if (f.embedded) {
|
|
431
|
-
embeds.push({
|
|
432
|
-
name: n,
|
|
433
|
-
fieldIndex: fIdx,
|
|
434
|
-
});
|
|
435
|
-
}
|
|
436
|
-
fIdx++;
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
} else if (resolvedStruct?.fields) {
|
|
440
|
-
let fIdx = 0;
|
|
441
|
-
for (const [fName, fType] of resolvedStruct.fields.entries()) {
|
|
442
|
-
const wType = toWasmType(fType, this.checker, this);
|
|
443
|
-
fields.push({ name: fName, goType: fType, wType });
|
|
444
|
-
fieldIndexMap.set(fName, fIdx);
|
|
445
|
-
fIdx++;
|
|
446
|
-
}
|
|
447
|
-
if (resolvedStruct?._embeds) {
|
|
448
|
-
for (const embed of resolvedStruct._embeds) {
|
|
449
|
-
const embedName = embed.kind === "named" ? embed.name : null;
|
|
450
|
-
if (embedName && fieldIndexMap.has(embedName)) {
|
|
451
|
-
embeds.push({
|
|
452
|
-
name: embedName,
|
|
453
|
-
fieldIndex: fieldIndexMap.get(embedName),
|
|
454
|
-
});
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
info.fields = fields;
|
|
461
|
-
info.fieldIndexMap = fieldIndexMap;
|
|
462
|
-
info.embeds = embeds;
|
|
250
|
+
const liftedFn = {
|
|
251
|
+
kind: "FuncDecl",
|
|
252
|
+
name: closureName,
|
|
253
|
+
params: [
|
|
254
|
+
{ name: "__env", type: { kind: "basic", name: "any" } },
|
|
255
|
+
...(node.params ?? []),
|
|
256
|
+
],
|
|
257
|
+
returnType: node.returnType,
|
|
258
|
+
body: node.body,
|
|
259
|
+
_isClosure: true,
|
|
260
|
+
_isYieldClosure: Boolean(node._isYieldClosure),
|
|
261
|
+
_iterStateName: node._iterStateName ?? null,
|
|
262
|
+
_funcLit: node,
|
|
263
|
+
_rootFuncDecl: rootFn,
|
|
264
|
+
_srcFile: rootFn?._srcFile ?? null,
|
|
265
|
+
_line: typeof node._line === "number" ? node._line : rootFn?._line,
|
|
266
|
+
};
|
|
267
|
+
node._liftedFn = liftedFn;
|
|
268
|
+
closures.push(liftedFn);
|
|
463
269
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
info.typeEntry = typeEntry;
|
|
270
|
+
scanClosures(node.body, rootFn);
|
|
271
|
+
return;
|
|
467
272
|
}
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
_pushType(typeEntry) {
|
|
473
|
-
(this._openRec ?? this.types).push(typeEntry);
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
getStructType(name) {
|
|
477
|
-
return this.structTypes.get(name) ?? null;
|
|
478
|
-
}
|
|
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);
|
|
273
|
+
for (const key of Object.keys(node)) {
|
|
274
|
+
if (key.startsWith("_")) continue;
|
|
275
|
+
scanClosures(node[key], rootFn);
|
|
485
276
|
}
|
|
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
277
|
}
|
|
514
278
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
if (
|
|
518
|
-
|
|
279
|
+
for (const fn of funcDecls) {
|
|
280
|
+
fn._rootFuncDecl = fn;
|
|
281
|
+
if (fn.body) {
|
|
282
|
+
scanClosures(fn.body, fn);
|
|
519
283
|
}
|
|
520
|
-
const wType = toWasmType(goType, this.checker, this);
|
|
521
|
-
const typeIndex = this._getTotalTypeCount();
|
|
522
|
-
const typeEntry = {
|
|
523
|
-
form: "struct",
|
|
524
|
-
fields: [{ type: wType, mutable: true }],
|
|
525
|
-
};
|
|
526
|
-
this._pushType(typeEntry);
|
|
527
|
-
const boxInfo = { key, typeIndex, typeEntry, wType, goType };
|
|
528
|
-
this.boxTypes.set(key, boxInfo);
|
|
529
|
-
return boxInfo;
|
|
530
284
|
}
|
|
285
|
+
funcDecls.push(...closures);
|
|
531
286
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
tagFields.push({ type: "i32", mutable: false });
|
|
287
|
+
// 1c. Add trampolines for named functions (for when they are passed as function values)
|
|
288
|
+
const topLevelFuncs = new Map();
|
|
289
|
+
for (const fn of funcDecls) {
|
|
290
|
+
if (
|
|
291
|
+
!fn._isClosure &&
|
|
292
|
+
!fn._isMethod &&
|
|
293
|
+
fn.name !== "main" &&
|
|
294
|
+
!fn.name.startsWith("_")
|
|
295
|
+
) {
|
|
296
|
+
topLevelFuncs.set(fn.name, fn);
|
|
543
297
|
}
|
|
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
298
|
}
|
|
299
|
+
mod.topLevelFuncMap = topLevelFuncs;
|
|
568
300
|
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
const
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
301
|
+
const trampolines = [];
|
|
302
|
+
for (const [name, targetFn] of topLevelFuncs.entries()) {
|
|
303
|
+
const trampName = `_tramp$${name}`;
|
|
304
|
+
const callExpr = {
|
|
305
|
+
kind: "CallExpr",
|
|
306
|
+
func: { kind: "Ident", name: targetFn.name },
|
|
307
|
+
args: (targetFn.params ?? []).map((p) => ({
|
|
308
|
+
kind: "Ident",
|
|
309
|
+
name: p.name,
|
|
310
|
+
_type: p.type,
|
|
311
|
+
})),
|
|
312
|
+
_type: targetFn.returnType ?? { kind: "basic", name: "void" },
|
|
581
313
|
};
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
getSliceType(elemGoType) {
|
|
589
|
-
const elemWType = toWasmType(elemGoType, this.checker, this);
|
|
590
|
-
const key =
|
|
591
|
-
typeof elemWType === "string" ? elemWType : JSON.stringify(elemWType);
|
|
592
|
-
if (this.sliceTypes.has(key)) {
|
|
593
|
-
return this.sliceTypes.get(key);
|
|
594
|
-
}
|
|
595
|
-
const arrInfo = this.getArrayType(elemGoType);
|
|
596
|
-
const typeIndex = this._getTotalTypeCount();
|
|
597
|
-
const typeEntry = {
|
|
598
|
-
form: "struct",
|
|
599
|
-
fields: [
|
|
600
|
-
{
|
|
601
|
-
type: {
|
|
602
|
-
kind: "ref",
|
|
603
|
-
nullable: true,
|
|
604
|
-
typeIndex: arrInfo.typeIndex,
|
|
314
|
+
const bodyStmts = targetFn.returnType
|
|
315
|
+
? [
|
|
316
|
+
{
|
|
317
|
+
kind: "ReturnStmt",
|
|
318
|
+
values: [callExpr],
|
|
605
319
|
},
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
],
|
|
612
|
-
};
|
|
613
|
-
this._pushType(typeEntry);
|
|
614
|
-
|
|
615
|
-
const emptyGlobalIndex = this.globals.length;
|
|
616
|
-
this.globals.push({
|
|
617
|
-
type: {
|
|
618
|
-
kind: "ref",
|
|
619
|
-
nullable: true,
|
|
620
|
-
typeIndex,
|
|
621
|
-
},
|
|
622
|
-
mutable: false,
|
|
623
|
-
init: [
|
|
624
|
-
{ op: "ref.null", heapType: arrInfo.typeIndex },
|
|
625
|
-
{ op: "i32.const", value: 0 },
|
|
626
|
-
{ op: "i32.const", value: 0 },
|
|
627
|
-
{ op: "i32.const", value: 0 },
|
|
628
|
-
{ op: "struct.new", typeIndex },
|
|
629
|
-
],
|
|
630
|
-
});
|
|
631
|
-
|
|
632
|
-
const sliceInfo = {
|
|
633
|
-
typeIndex,
|
|
634
|
-
typeEntry,
|
|
635
|
-
arrInfo,
|
|
636
|
-
elemWType,
|
|
637
|
-
elemGoType,
|
|
638
|
-
emptyGlobalIndex,
|
|
639
|
-
};
|
|
640
|
-
this.sliceTypes.set(key, sliceInfo);
|
|
641
|
-
this.sliceTypesByIndex.set(typeIndex, sliceInfo);
|
|
642
|
-
return sliceInfo;
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
getSliceTypeByIndex(typeIndex) {
|
|
646
|
-
return this.sliceTypesByIndex?.get(typeIndex) ?? null;
|
|
647
|
-
}
|
|
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
|
-
|
|
750
|
-
getClosureType(goType) {
|
|
751
|
-
const sig = getFuncSignature(goType, this.checker);
|
|
752
|
-
const paramWTypes = sig.params
|
|
753
|
-
.map((p) => toWasmType(p, this.checker, this))
|
|
754
|
-
.filter(Boolean);
|
|
755
|
-
const returnWTypes = sig.returns
|
|
756
|
-
.map((r) => toWasmType(r, this.checker, this))
|
|
757
|
-
.filter(Boolean);
|
|
758
|
-
|
|
759
|
-
const key = `${paramWTypes.map((p) => (typeof p === "object" ? JSON.stringify(p) : p)).join(",")}=>${returnWTypes.map((r) => (typeof r === "object" ? JSON.stringify(r) : r)).join(",")}`;
|
|
760
|
-
if (this.closureTypes.has(key)) {
|
|
761
|
-
return this.closureTypes.get(key);
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
// 1. Function signature type index: (param anyref, ...paramWTypes) -> (...returnWTypes)
|
|
765
|
-
const funcTypeIndex = this.getTypeIndex(
|
|
766
|
-
["anyref", ...paramWTypes],
|
|
767
|
-
returnWTypes,
|
|
768
|
-
);
|
|
769
|
-
|
|
770
|
-
// 2. Closure struct type index: struct { fn: (ref funcTypeIndex), env: anyref }
|
|
771
|
-
const typeIndex = this._getTotalTypeCount();
|
|
772
|
-
const typeEntry = {
|
|
773
|
-
form: "struct",
|
|
774
|
-
fields: [
|
|
775
|
-
{
|
|
776
|
-
type: {
|
|
777
|
-
kind: "ref",
|
|
778
|
-
nullable: false,
|
|
779
|
-
typeIndex: funcTypeIndex,
|
|
780
|
-
},
|
|
781
|
-
mutable: false,
|
|
782
|
-
},
|
|
783
|
-
{
|
|
784
|
-
type: "anyref",
|
|
785
|
-
mutable: true,
|
|
786
|
-
},
|
|
787
|
-
],
|
|
788
|
-
};
|
|
789
|
-
this._pushType(typeEntry);
|
|
790
|
-
|
|
791
|
-
const closureInfo = {
|
|
792
|
-
typeIndex,
|
|
793
|
-
typeEntry,
|
|
794
|
-
funcTypeIndex,
|
|
795
|
-
paramWTypes,
|
|
796
|
-
returnWTypes,
|
|
797
|
-
sig,
|
|
798
|
-
};
|
|
799
|
-
this.closureTypes.set(key, closureInfo);
|
|
800
|
-
return closureInfo;
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
getEnvType(fieldTypes) {
|
|
804
|
-
const key = fieldTypes
|
|
805
|
-
.map((f) =>
|
|
806
|
-
typeof f.wType === "object" ? JSON.stringify(f.wType) : f.wType,
|
|
807
|
-
)
|
|
808
|
-
.join(",");
|
|
809
|
-
if (this.envTypes.has(key)) {
|
|
810
|
-
return this.envTypes.get(key);
|
|
811
|
-
}
|
|
812
|
-
const typeIndex = this._getTotalTypeCount();
|
|
813
|
-
const typeEntry = {
|
|
814
|
-
form: "struct",
|
|
815
|
-
fields: fieldTypes.map((f) => ({ type: f.wType, mutable: true })),
|
|
816
|
-
};
|
|
817
|
-
this._pushType(typeEntry);
|
|
818
|
-
const envInfo = { typeIndex, typeEntry, fieldTypes };
|
|
819
|
-
this.envTypes.set(key, envInfo);
|
|
820
|
-
return envInfo;
|
|
821
|
-
}
|
|
822
|
-
|
|
823
|
-
addRefFuncElement(funcIdx) {
|
|
824
|
-
if (!this.elementSet.has(funcIdx)) {
|
|
825
|
-
this.elementSet.add(funcIdx);
|
|
826
|
-
this.elements.push(funcIdx);
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
_getTotalTypeCount() {
|
|
831
|
-
let count = 0;
|
|
832
|
-
for (const entry of this.types) {
|
|
833
|
-
if (entry.form === "rec") count += entry.types.length;
|
|
834
|
-
else count += 1;
|
|
835
|
-
}
|
|
836
|
-
return count;
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
_initRuntimeImports() {
|
|
840
|
-
this._initPanicTag();
|
|
841
|
-
this._initPanicGlobal();
|
|
842
|
-
this.getPanicImportIndex(); // env.panic
|
|
843
|
-
this.internString("runtime error: index out of range");
|
|
844
|
-
this.internString("runtime error: slice bounds out of range");
|
|
845
|
-
this.internString("assignment to entry in nil map");
|
|
846
|
-
|
|
847
|
-
// String built-ins
|
|
848
|
-
this.getStringImportIndex(); // env.str
|
|
849
|
-
this.getStringLenImportIndex(); // env.str_len
|
|
850
|
-
this.getStringConcatImportIndex(); // env.str_concat
|
|
851
|
-
this.getStringCmpImportIndex("=="); // env.str_eq
|
|
852
|
-
this.getStringCmpImportIndex("!="); // env.str_ne
|
|
853
|
-
this.getStringCmpImportIndex("<"); // env.str_lt
|
|
854
|
-
this.getStringCmpImportIndex("<="); // env.str_le
|
|
855
|
-
this.getStringCmpImportIndex(">"); // env.str_gt
|
|
856
|
-
this.getStringCmpImportIndex(">="); // env.str_ge
|
|
857
|
-
this.getStringGetImportIndex(); // env.str_get
|
|
858
|
-
this.getStringSliceImportIndex(); // env.str_slice
|
|
859
|
-
this.getStringFromCodePointImportIndex(); // env.str_from_code_point
|
|
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();
|
|
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
|
-
}
|
|
906
|
-
|
|
907
|
-
// Logging built-ins
|
|
908
|
-
this.getPrintlnEmptyIndex(); // env.println_empty
|
|
909
|
-
const logTypes = ["i32", "i64", "f32", "f64", "externref", "anyref"];
|
|
910
|
-
for (const t of logTypes) {
|
|
911
|
-
this.getLogImportIndex(t, false, false); // env.print_*
|
|
912
|
-
this.getLogImportIndex(t, true, false); // env.println_*
|
|
913
|
-
}
|
|
914
|
-
this.getLogImportIndex("i32", false, true); // env.print_bool
|
|
915
|
-
this.getLogImportIndex("i32", true, true); // env.println_bool
|
|
916
|
-
|
|
917
|
-
// Math built-ins
|
|
918
|
-
const unaryMath = [
|
|
919
|
-
"sin",
|
|
920
|
-
"cos",
|
|
921
|
-
"tan",
|
|
922
|
-
"asin",
|
|
923
|
-
"acos",
|
|
924
|
-
"atan",
|
|
925
|
-
"exp",
|
|
926
|
-
"log",
|
|
927
|
-
"log2",
|
|
928
|
-
"log10",
|
|
929
|
-
"round",
|
|
930
|
-
"cbrt",
|
|
931
|
-
];
|
|
932
|
-
for (const m of unaryMath) {
|
|
933
|
-
this.getMathImportIndex(m, false);
|
|
934
|
-
}
|
|
935
|
-
for (const m of BINARY_MATH_IMPORTS) {
|
|
936
|
-
this.getMathImportIndex(m, true);
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
_initPanicTag() {
|
|
941
|
-
// Type: (externref) -> ()
|
|
942
|
-
const typeIdx = this.getTypeIndex(["externref"], []);
|
|
943
|
-
// Tag 0
|
|
944
|
-
this.tags.push({ typeIndex: typeIdx });
|
|
945
|
-
// Export tag 0 as "panicTag"
|
|
946
|
-
this.exports.push({ name: "panicTag", kind: "tag", index: 0 });
|
|
947
|
-
}
|
|
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
|
-
|
|
1131
|
-
getTypeIndex(params, results) {
|
|
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(",")}`;
|
|
1133
|
-
if (this.typeCache.has(key)) {
|
|
1134
|
-
return this.typeCache.get(key);
|
|
1135
|
-
}
|
|
1136
|
-
const idx = this._getTotalTypeCount();
|
|
1137
|
-
this._pushType({ form: "func", params, results });
|
|
1138
|
-
this.typeCache.set(key, idx);
|
|
1139
|
-
return idx;
|
|
1140
|
-
}
|
|
1141
|
-
|
|
1142
|
-
internString(str) {
|
|
1143
|
-
if (this.stringCache.has(str)) {
|
|
1144
|
-
return this.stringCache.get(str);
|
|
1145
|
-
}
|
|
1146
|
-
const idx = this.stringTable.length;
|
|
1147
|
-
this.stringTable.push(str);
|
|
1148
|
-
this.stringCache.set(str, idx);
|
|
1149
|
-
return idx;
|
|
1150
|
-
}
|
|
1151
|
-
|
|
1152
|
-
getOrAddFuncImport(module, name, params, results) {
|
|
1153
|
-
const key = `${module}.${name}`;
|
|
1154
|
-
if (this.importCache.has(key)) {
|
|
1155
|
-
return this.importCache.get(key);
|
|
1156
|
-
}
|
|
1157
|
-
if (this._importsLocked) {
|
|
1158
|
-
throw new Error(`Unexpected import added after lock: ${key}`);
|
|
1159
|
-
}
|
|
1160
|
-
const typeIndex = this.getTypeIndex(params, results);
|
|
1161
|
-
const globalFuncIdx = this.imports.filter((i) => i.kind === "func").length;
|
|
1162
|
-
this.imports.push({
|
|
1163
|
-
module,
|
|
1164
|
-
name,
|
|
1165
|
-
kind: "func",
|
|
1166
|
-
typeIndex,
|
|
1167
|
-
});
|
|
1168
|
-
this.importCache.set(key, globalFuncIdx);
|
|
1169
|
-
return globalFuncIdx;
|
|
1170
|
-
}
|
|
1171
|
-
|
|
1172
|
-
getStringImportIndex() {
|
|
1173
|
-
return this.getOrAddFuncImport("env", "str", ["i32"], ["externref"]);
|
|
1174
|
-
}
|
|
1175
|
-
|
|
1176
|
-
// Panics are raised by a JS import that throws a plain `Error`, so wasm
|
|
1177
|
-
// frames unwind without a wasm `throw` and the JS caller needs no
|
|
1178
|
-
// try/catch guard around exports. (V8 cannot inline JS→wasm calls made
|
|
1179
|
-
// inside a try block, and a non-inlined call boxes every float argument.)
|
|
1180
|
-
getPanicImportIndex() {
|
|
1181
|
-
return this.getOrAddFuncImport("env", "panic", ["externref"], []);
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
|
-
getBoundsPanicFuncIndex() {
|
|
1185
|
-
return this.resolveFuncIndex("__bounds_panic");
|
|
1186
|
-
}
|
|
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
|
-
|
|
1214
|
-
getSliceBoundsPanicFuncIndex() {
|
|
1215
|
-
return this.resolveFuncIndex("__slice_bounds_panic");
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
getPrintlnEmptyIndex() {
|
|
1219
|
-
return this.getOrAddFuncImport("env", "println_empty", [], []);
|
|
1220
|
-
}
|
|
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
|
-
|
|
1306
|
-
// `*testing.T` method calls: args are pushed one by one to a JS-side
|
|
1307
|
-
// buffer, then `testing_call(t, methodNameStrIdx)` invokes the method.
|
|
1308
|
-
getTestingArgImportIndex(wType, isBool = false) {
|
|
1309
|
-
if (isBool)
|
|
1310
|
-
return this.getOrAddFuncImport("env", "testing_arg_bool", ["i32"], []);
|
|
1311
|
-
const suffix =
|
|
1312
|
-
wType === "externref" ? "str" : wType === "anyref" ? "any" : wType;
|
|
1313
|
-
if (!["i32", "i64", "f32", "f64", "str", "any"].includes(suffix)) {
|
|
1314
|
-
throw new Error(
|
|
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)",
|
|
1316
|
-
);
|
|
1317
|
-
}
|
|
1318
|
-
return this.getOrAddFuncImport(
|
|
1319
|
-
"env",
|
|
1320
|
-
`testing_arg_${suffix}`,
|
|
1321
|
-
[suffix === "str" ? "externref" : suffix === "any" ? "anyref" : wType],
|
|
1322
|
-
[],
|
|
1323
|
-
);
|
|
1324
|
-
}
|
|
1325
|
-
|
|
1326
|
-
getTestingCallImportIndex() {
|
|
1327
|
-
return this.getOrAddFuncImport(
|
|
1328
|
-
"env",
|
|
1329
|
-
"testing_call",
|
|
1330
|
-
["externref", "i32"],
|
|
1331
|
-
[],
|
|
1332
|
-
);
|
|
1333
|
-
}
|
|
1334
|
-
|
|
1335
|
-
getTestingNameImportIndex() {
|
|
1336
|
-
return this.getOrAddFuncImport(
|
|
1337
|
-
"env",
|
|
1338
|
-
"testing_name",
|
|
1339
|
-
["externref"],
|
|
1340
|
-
["externref"],
|
|
1341
|
-
);
|
|
1342
|
-
}
|
|
1343
|
-
|
|
1344
|
-
getTestingFlagImportIndex() {
|
|
1345
|
-
return this.getOrAddFuncImport(
|
|
1346
|
-
"env",
|
|
1347
|
-
"testing_flag",
|
|
1348
|
-
["externref", "i32"],
|
|
1349
|
-
["i32"],
|
|
1350
|
-
);
|
|
1351
|
-
}
|
|
1352
|
-
|
|
1353
|
-
getLogImportIndex(wType, isLast = false, isBool = false) {
|
|
1354
|
-
const prefix = isLast ? "println" : "print";
|
|
1355
|
-
if (isBool) {
|
|
1356
|
-
return this.getOrAddFuncImport("env", `${prefix}_bool`, ["i32"], []);
|
|
1357
|
-
}
|
|
1358
|
-
switch (wType) {
|
|
1359
|
-
case "i32":
|
|
1360
|
-
return this.getOrAddFuncImport("env", `${prefix}_i32`, ["i32"], []);
|
|
1361
|
-
case "i64":
|
|
1362
|
-
return this.getOrAddFuncImport("env", `${prefix}_i64`, ["i64"], []);
|
|
1363
|
-
case "f32":
|
|
1364
|
-
return this.getOrAddFuncImport("env", `${prefix}_f32`, ["f32"], []);
|
|
1365
|
-
case "f64":
|
|
1366
|
-
return this.getOrAddFuncImport("env", `${prefix}_f64`, ["f64"], []);
|
|
1367
|
-
case "externref":
|
|
1368
|
-
return this.getOrAddFuncImport(
|
|
1369
|
-
"env",
|
|
1370
|
-
`${prefix}_str`,
|
|
1371
|
-
["externref"],
|
|
1372
|
-
[],
|
|
1373
|
-
);
|
|
1374
|
-
default:
|
|
1375
|
-
if (typeof wType === "object") {
|
|
1376
|
-
return this.getOrAddFuncImport(
|
|
1377
|
-
"env",
|
|
1378
|
-
`${prefix}_any`,
|
|
1379
|
-
["anyref"],
|
|
1380
|
-
[],
|
|
1381
|
-
);
|
|
1382
|
-
}
|
|
1383
|
-
return this.getOrAddFuncImport("env", `${prefix}_i32`, ["i32"], []);
|
|
1384
|
-
}
|
|
1385
|
-
}
|
|
1386
|
-
|
|
1387
|
-
getStringLenImportIndex() {
|
|
1388
|
-
return this.getOrAddFuncImport("env", "str_len", ["externref"], ["i32"]);
|
|
1389
|
-
}
|
|
1390
|
-
|
|
1391
|
-
getStringConcatImportIndex() {
|
|
1392
|
-
return this.getOrAddFuncImport(
|
|
1393
|
-
"env",
|
|
1394
|
-
"str_concat",
|
|
1395
|
-
["externref", "externref"],
|
|
1396
|
-
["externref"],
|
|
1397
|
-
);
|
|
1398
|
-
}
|
|
1399
|
-
|
|
1400
|
-
getStringCmpImportIndex(op) {
|
|
1401
|
-
const opMap = {
|
|
1402
|
-
"==": "str_eq",
|
|
1403
|
-
"!=": "str_ne",
|
|
1404
|
-
"<": "str_lt",
|
|
1405
|
-
"<=": "str_le",
|
|
1406
|
-
">": "str_gt",
|
|
1407
|
-
">=": "str_ge",
|
|
1408
|
-
};
|
|
1409
|
-
const fn = opMap[op] ?? "str_eq";
|
|
1410
|
-
return this.getOrAddFuncImport(
|
|
1411
|
-
"env",
|
|
1412
|
-
fn,
|
|
1413
|
-
["externref", "externref"],
|
|
1414
|
-
["i32"],
|
|
1415
|
-
);
|
|
1416
|
-
}
|
|
1417
|
-
|
|
1418
|
-
getStringGetImportIndex() {
|
|
1419
|
-
return this.getOrAddFuncImport(
|
|
1420
|
-
"env",
|
|
1421
|
-
"str_get",
|
|
1422
|
-
["externref", "i32"],
|
|
1423
|
-
["i32"],
|
|
1424
|
-
);
|
|
1425
|
-
}
|
|
1426
|
-
|
|
1427
|
-
getStringSliceImportIndex() {
|
|
1428
|
-
return this.getOrAddFuncImport(
|
|
1429
|
-
"env",
|
|
1430
|
-
"str_slice",
|
|
1431
|
-
["externref", "i32", "i32"],
|
|
1432
|
-
["externref"],
|
|
1433
|
-
);
|
|
1434
|
-
}
|
|
1435
|
-
|
|
1436
|
-
getStringFromCodePointImportIndex() {
|
|
1437
|
-
return this.getOrAddFuncImport(
|
|
1438
|
-
"env",
|
|
1439
|
-
"str_from_code_point",
|
|
1440
|
-
["i32"],
|
|
1441
|
-
["externref"],
|
|
1442
|
-
);
|
|
1443
|
-
}
|
|
1444
|
-
|
|
1445
|
-
getStringCodePointAtImportIndex() {
|
|
1446
|
-
return this.getOrAddFuncImport(
|
|
1447
|
-
"env",
|
|
1448
|
-
"str_code_point_at",
|
|
1449
|
-
["externref", "i32"],
|
|
1450
|
-
["i32"],
|
|
1451
|
-
);
|
|
1452
|
-
}
|
|
1453
|
-
|
|
1454
|
-
getIsStringImportIndex() {
|
|
1455
|
-
return this.getOrAddFuncImport("env", "is_string", ["anyref"], ["i32"]);
|
|
1456
|
-
}
|
|
1457
|
-
|
|
1458
|
-
getStringHashImportIndex() {
|
|
1459
|
-
return this.getOrAddFuncImport("env", "str_hash", ["externref"], ["i32"]);
|
|
1460
|
-
}
|
|
1461
|
-
|
|
1462
|
-
getStringToUpperImportIndex() {
|
|
1463
|
-
return this.getOrAddFuncImport(
|
|
1464
|
-
"env",
|
|
1465
|
-
"str_to_upper",
|
|
1466
|
-
["externref"],
|
|
1467
|
-
["externref"],
|
|
1468
|
-
);
|
|
1469
|
-
}
|
|
1470
|
-
|
|
1471
|
-
getStringToLowerImportIndex() {
|
|
1472
|
-
return this.getOrAddFuncImport(
|
|
1473
|
-
"env",
|
|
1474
|
-
"str_to_lower",
|
|
1475
|
-
["externref"],
|
|
1476
|
-
["externref"],
|
|
1477
|
-
);
|
|
1478
|
-
}
|
|
1479
|
-
|
|
1480
|
-
getStringTrimSpaceImportIndex() {
|
|
1481
|
-
return this.getOrAddFuncImport(
|
|
1482
|
-
"env",
|
|
1483
|
-
"str_trim_space",
|
|
1484
|
-
["externref"],
|
|
1485
|
-
["externref"],
|
|
1486
|
-
);
|
|
1487
|
-
}
|
|
1488
|
-
|
|
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) {
|
|
1673
|
-
return this.constCache.get(name) ?? null;
|
|
1674
|
-
}
|
|
1675
|
-
|
|
1676
|
-
isNonLiteralConst(name) {
|
|
1677
|
-
return this.nonLiteralConsts.has(name);
|
|
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
|
-
}
|
|
1890
|
-
}
|
|
1891
|
-
|
|
1892
|
-
function peepholeOptimize(instructions) {
|
|
1893
|
-
let changed = true;
|
|
1894
|
-
let current = instructions;
|
|
1895
|
-
while (changed) {
|
|
1896
|
-
changed = false;
|
|
1897
|
-
const next = [];
|
|
1898
|
-
for (let i = 0; i < current.length; i++) {
|
|
1899
|
-
const c = current[i];
|
|
1900
|
-
const n = current[i + 1];
|
|
1901
|
-
|
|
1902
|
-
// Pattern 1: local.get X; local.set X -> nothing
|
|
1903
|
-
if (
|
|
1904
|
-
c?.op === "local.get" &&
|
|
1905
|
-
n?.op === "local.set" &&
|
|
1906
|
-
c.index === n.index
|
|
1907
|
-
) {
|
|
1908
|
-
changed = true;
|
|
1909
|
-
i++; // skip both
|
|
1910
|
-
continue;
|
|
1911
|
-
}
|
|
1912
|
-
|
|
1913
|
-
// Pattern 2: local.set X; local.get X -> local.tee X
|
|
1914
|
-
if (
|
|
1915
|
-
c?.op === "local.set" &&
|
|
1916
|
-
n?.op === "local.get" &&
|
|
1917
|
-
c.index === n.index
|
|
1918
|
-
) {
|
|
1919
|
-
changed = true;
|
|
1920
|
-
next.push({ op: "local.tee", index: c.index });
|
|
1921
|
-
i++; // skip n (local.get)
|
|
1922
|
-
continue;
|
|
1923
|
-
}
|
|
1924
|
-
|
|
1925
|
-
// Pattern 3: local.tee X; local.set X -> local.set X
|
|
1926
|
-
if (
|
|
1927
|
-
c?.op === "local.tee" &&
|
|
1928
|
-
n?.op === "local.set" &&
|
|
1929
|
-
c.index === n.index
|
|
1930
|
-
) {
|
|
1931
|
-
changed = true;
|
|
1932
|
-
next.push({ op: "local.set", index: c.index });
|
|
1933
|
-
i++;
|
|
1934
|
-
continue;
|
|
1935
|
-
}
|
|
1936
|
-
|
|
1937
|
-
// Pattern 4: local.tee X; drop -> local.set X
|
|
1938
|
-
if (c?.op === "local.tee" && (n === "drop" || n?.op === "drop")) {
|
|
1939
|
-
changed = true;
|
|
1940
|
-
next.push({ op: "local.set", index: c.index });
|
|
1941
|
-
i++;
|
|
1942
|
-
continue;
|
|
1943
|
-
}
|
|
1944
|
-
|
|
1945
|
-
next.push(c);
|
|
1946
|
-
}
|
|
1947
|
-
current = next;
|
|
1948
|
-
}
|
|
1949
|
-
return current;
|
|
1950
|
-
}
|
|
1951
|
-
|
|
1952
|
-
export function compileWasmModule(
|
|
1953
|
-
programs,
|
|
1954
|
-
checker,
|
|
1955
|
-
lowerResult = null,
|
|
1956
|
-
options = {},
|
|
1957
|
-
) {
|
|
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, {
|
|
1972
|
-
bundledPackages: options.bundledPackages,
|
|
1973
|
-
});
|
|
1974
|
-
|
|
1975
|
-
// 1. Collect all functions, methods, and package-level globals
|
|
1976
|
-
const funcDecls = [];
|
|
1977
|
-
let initCount = 0;
|
|
1978
|
-
const initNames = [];
|
|
1979
|
-
for (const p of progs) {
|
|
1980
|
-
const pkgName = p.pkg?.name ?? "main";
|
|
1981
|
-
const pkgTarget = p.target ?? "wasm";
|
|
1982
|
-
for (const d of p.decls ?? []) {
|
|
1983
|
-
if (d.kind === "FuncDecl") {
|
|
1984
|
-
d._pkgName = pkgName;
|
|
1985
|
-
d._pkgTarget = pkgTarget;
|
|
1986
|
-
d._srcFile = p._path ?? p._filename ?? null;
|
|
1987
|
-
if (d.name === "init" || d.name.startsWith("init$")) {
|
|
1988
|
-
const renamed = initCount === 0 ? "init" : `init$${initCount}`;
|
|
1989
|
-
d.name = renamed;
|
|
1990
|
-
initNames.push(renamed);
|
|
1991
|
-
initCount++;
|
|
1992
|
-
}
|
|
1993
|
-
funcDecls.push(d);
|
|
1994
|
-
} else if (d.kind === "MethodDecl") {
|
|
1995
|
-
const recvTypeName =
|
|
1996
|
-
d.recvType?.name ??
|
|
1997
|
-
(d.recvType?.kind === "TypeName" ? d.recvType.name : "Recv");
|
|
1998
|
-
const recvGoType = d.recvPointer
|
|
1999
|
-
? { kind: "pointer", base: { kind: "named", name: recvTypeName } }
|
|
2000
|
-
: { kind: "named", name: recvTypeName };
|
|
2001
|
-
const normFn = {
|
|
2002
|
-
kind: "FuncDecl",
|
|
2003
|
-
name: `${recvTypeName}.${d.name}`,
|
|
2004
|
-
params: [{ name: d.recvName, type: recvGoType }, ...(d.params ?? [])],
|
|
2005
|
-
returnType: d.returnType,
|
|
2006
|
-
body: d.body,
|
|
2007
|
-
_isMethod: true,
|
|
2008
|
-
_recvTypeName: recvTypeName,
|
|
2009
|
-
_methodName: d.name,
|
|
2010
|
-
_exportName: `${recvTypeName}_${d.name}`,
|
|
2011
|
-
_sourceDecl: d,
|
|
2012
|
-
_pkgName: pkgName,
|
|
2013
|
-
_pkgTarget: pkgTarget,
|
|
2014
|
-
_srcFile: p._path ?? p._filename ?? null,
|
|
2015
|
-
_line: d._line,
|
|
2016
|
-
};
|
|
2017
|
-
funcDecls.push(normFn);
|
|
2018
|
-
}
|
|
2019
|
-
}
|
|
2020
|
-
}
|
|
2021
|
-
|
|
2022
|
-
// 1b. Scan and lift all closures (FuncLits)
|
|
2023
|
-
let closureCounter = 0;
|
|
2024
|
-
const closures = [];
|
|
2025
|
-
|
|
2026
|
-
function scanClosures(node, rootFn) {
|
|
2027
|
-
if (!node || typeof node !== "object") return;
|
|
2028
|
-
if (Array.isArray(node)) {
|
|
2029
|
-
for (const item of node) scanClosures(item, rootFn);
|
|
2030
|
-
return;
|
|
2031
|
-
}
|
|
2032
|
-
if (node.kind === "FuncLit") {
|
|
2033
|
-
closureCounter++;
|
|
2034
|
-
const closureName = `_closure$${closureCounter}`;
|
|
2035
|
-
node._liftedName = closureName;
|
|
2036
|
-
node._rootFuncDecl = rootFn;
|
|
2037
|
-
|
|
2038
|
-
const liftedFn = {
|
|
2039
|
-
kind: "FuncDecl",
|
|
2040
|
-
name: closureName,
|
|
2041
|
-
params: [
|
|
2042
|
-
{ name: "__env", type: { kind: "basic", name: "any" } },
|
|
2043
|
-
...(node.params ?? []),
|
|
2044
|
-
],
|
|
2045
|
-
returnType: node.returnType,
|
|
2046
|
-
body: node.body,
|
|
2047
|
-
_isClosure: true,
|
|
2048
|
-
_funcLit: node,
|
|
2049
|
-
_rootFuncDecl: rootFn,
|
|
2050
|
-
_srcFile: rootFn?._srcFile ?? null,
|
|
2051
|
-
_line: typeof node._line === "number" ? node._line : rootFn?._line,
|
|
2052
|
-
};
|
|
2053
|
-
node._liftedFn = liftedFn;
|
|
2054
|
-
closures.push(liftedFn);
|
|
2055
|
-
|
|
2056
|
-
scanClosures(node.body, rootFn);
|
|
2057
|
-
return;
|
|
2058
|
-
}
|
|
2059
|
-
for (const key of Object.keys(node)) {
|
|
2060
|
-
if (key.startsWith("_")) continue;
|
|
2061
|
-
scanClosures(node[key], rootFn);
|
|
2062
|
-
}
|
|
2063
|
-
}
|
|
2064
|
-
|
|
2065
|
-
for (const fn of funcDecls) {
|
|
2066
|
-
fn._rootFuncDecl = fn;
|
|
2067
|
-
if (fn.body) {
|
|
2068
|
-
scanClosures(fn.body, fn);
|
|
2069
|
-
}
|
|
2070
|
-
}
|
|
2071
|
-
funcDecls.push(...closures);
|
|
2072
|
-
|
|
2073
|
-
// 1c. Add trampolines for named functions (for when they are passed as function values)
|
|
2074
|
-
const topLevelFuncs = new Map();
|
|
2075
|
-
for (const fn of funcDecls) {
|
|
2076
|
-
if (
|
|
2077
|
-
!fn._isClosure &&
|
|
2078
|
-
!fn._isMethod &&
|
|
2079
|
-
fn.name !== "main" &&
|
|
2080
|
-
!fn.name.startsWith("_")
|
|
2081
|
-
) {
|
|
2082
|
-
topLevelFuncs.set(fn.name, fn);
|
|
2083
|
-
}
|
|
2084
|
-
}
|
|
2085
|
-
mod.topLevelFuncMap = topLevelFuncs;
|
|
2086
|
-
|
|
2087
|
-
const trampolines = [];
|
|
2088
|
-
for (const [name, targetFn] of topLevelFuncs.entries()) {
|
|
2089
|
-
const trampName = `_tramp$${name}`;
|
|
2090
|
-
const callExpr = {
|
|
2091
|
-
kind: "CallExpr",
|
|
2092
|
-
func: { kind: "Ident", name: targetFn.name },
|
|
2093
|
-
args: (targetFn.params ?? []).map((p) => ({
|
|
2094
|
-
kind: "Ident",
|
|
2095
|
-
name: p.name,
|
|
2096
|
-
_type: p.type,
|
|
2097
|
-
})),
|
|
2098
|
-
_type: targetFn.returnType ?? { kind: "basic", name: "void" },
|
|
2099
|
-
};
|
|
2100
|
-
const bodyStmts = targetFn.returnType
|
|
2101
|
-
? [
|
|
2102
|
-
{
|
|
2103
|
-
kind: "ReturnStmt",
|
|
2104
|
-
values: [callExpr],
|
|
2105
|
-
},
|
|
2106
|
-
]
|
|
2107
|
-
: [
|
|
2108
|
-
{ kind: "ExprStmt", expr: callExpr },
|
|
2109
|
-
{ kind: "ReturnStmt", values: [] },
|
|
2110
|
-
];
|
|
320
|
+
]
|
|
321
|
+
: [
|
|
322
|
+
{ kind: "ExprStmt", expr: callExpr },
|
|
323
|
+
{ kind: "ReturnStmt", values: [] },
|
|
324
|
+
];
|
|
2111
325
|
|
|
2112
326
|
const trampFn = {
|
|
2113
327
|
kind: "FuncDecl",
|
|
@@ -2148,7 +362,15 @@ export function compileWasmModule(
|
|
|
2148
362
|
pendingInits.length > 0 || initNames.length > 0
|
|
2149
363
|
? _makeGlobalInitFunc(pendingInits, initNames)
|
|
2150
364
|
: null;
|
|
2151
|
-
if (globalInitFn)
|
|
365
|
+
if (globalInitFn) {
|
|
366
|
+
// Func literals in package-level initializers are lifted like any other.
|
|
367
|
+
globalInitFn._rootFuncDecl = globalInitFn;
|
|
368
|
+
// Captured variables of those literals need the same analysis.
|
|
369
|
+
lowerFunction(lowerResult, globalInitFn);
|
|
370
|
+
const before = closures.length;
|
|
371
|
+
scanClosures(globalInitFn.body, globalInitFn);
|
|
372
|
+
funcDecls.push(globalInitFn, ...closures.slice(before));
|
|
373
|
+
}
|
|
2152
374
|
|
|
2153
375
|
const boundsPanicFn = {
|
|
2154
376
|
kind: "FuncDecl",
|
|
@@ -2160,6 +382,30 @@ export function compileWasmModule(
|
|
|
2160
382
|
};
|
|
2161
383
|
funcDecls.push(boundsPanicFn);
|
|
2162
384
|
|
|
385
|
+
// Go's index panic: `index out of range [i] with length n`, or
|
|
386
|
+
// `index out of range [i]` for a negative index.
|
|
387
|
+
funcDecls.push({
|
|
388
|
+
kind: "FuncDecl",
|
|
389
|
+
name: "__index_panic",
|
|
390
|
+
params: [
|
|
391
|
+
{ name: "i", type: { kind: "basic", name: "int64" } },
|
|
392
|
+
{ name: "n", type: { kind: "basic", name: "int32" } },
|
|
393
|
+
],
|
|
394
|
+
returnType: null,
|
|
395
|
+
_isIndexPanic: true,
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
// Target of the per-function `nil` block (`_openNilBlock` in emit.js).
|
|
399
|
+
funcDecls.push({
|
|
400
|
+
kind: "FuncDecl",
|
|
401
|
+
name: "__nil_panic",
|
|
402
|
+
params: [],
|
|
403
|
+
returnType: null,
|
|
404
|
+
_isRuntimePanic: true,
|
|
405
|
+
_panicMsg:
|
|
406
|
+
"runtime error: invalid memory address or nil pointer dereference",
|
|
407
|
+
});
|
|
408
|
+
|
|
2163
409
|
const sliceBoundsPanicFn = {
|
|
2164
410
|
kind: "FuncDecl",
|
|
2165
411
|
name: "__slice_bounds_panic",
|
|
@@ -2344,18 +590,6 @@ export function compileWasmModule(
|
|
|
2344
590
|
_mapInfo: mapInfo,
|
|
2345
591
|
_rootFuncDecl: null,
|
|
2346
592
|
});
|
|
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
593
|
}
|
|
2360
594
|
|
|
2361
595
|
// 1e2. `gofront/shared` linear-memory helpers (only when imported).
|
|
@@ -2403,8 +637,7 @@ export function compileWasmModule(
|
|
|
2403
637
|
});
|
|
2404
638
|
}
|
|
2405
639
|
|
|
2406
|
-
// 1e3. Runtime helpers: error values
|
|
2407
|
-
mod.usesTestingRun = funcDecls.some((fn) => _usesTestingRun(fn.body));
|
|
640
|
+
// 1e3. Runtime helpers: error values and interface equality.
|
|
2408
641
|
mod.usesAnyEq = _usesAnyEq(funcDecls, mod);
|
|
2409
642
|
const ANY_T = { kind: "basic", name: "any" };
|
|
2410
643
|
const STR_T = { kind: "basic", name: "string" };
|
|
@@ -2432,6 +665,25 @@ export function compileWasmModule(
|
|
|
2432
665
|
),
|
|
2433
666
|
);
|
|
2434
667
|
}
|
|
668
|
+
// `x.(T)` outside comma-ok form panics with the dynamic type's name.
|
|
669
|
+
mod.usesTypeAssertFail = funcDecls.some((fn) =>
|
|
670
|
+
_someNode(fn.body, (n) => n.kind === "TypeAssertExpr" && !n._commaOk),
|
|
671
|
+
);
|
|
672
|
+
if (mod.usesTypeAssertFail) {
|
|
673
|
+
funcDecls.push(
|
|
674
|
+
helper(
|
|
675
|
+
"__ta_fail",
|
|
676
|
+
[
|
|
677
|
+
{ name: "v", type: ANY_T },
|
|
678
|
+
{ name: "src", type: STR_T },
|
|
679
|
+
{ name: "target", type: STR_T },
|
|
680
|
+
{ name: "iface", type: BOOL_T },
|
|
681
|
+
],
|
|
682
|
+
null,
|
|
683
|
+
"ta_fail",
|
|
684
|
+
),
|
|
685
|
+
);
|
|
686
|
+
}
|
|
2435
687
|
mod.usesAnyHash = false;
|
|
2436
688
|
for (const info of mod.mapTypes.values()) {
|
|
2437
689
|
if (isInterfaceType(info.keyGoType, checker)) {
|
|
@@ -2466,72 +718,10 @@ export function compileWasmModule(
|
|
|
2466
718
|
_methodName: "Error",
|
|
2467
719
|
},
|
|
2468
720
|
),
|
|
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
|
-
),
|
|
721
|
+
helper("__mk_error", [{ name: "msg", type: STR_T }], ANY_T, "mk_error"),
|
|
2489
722
|
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
723
|
);
|
|
2533
724
|
}
|
|
2534
|
-
_registerIoSyntheticMethods(funcDecls, mod, STR_T, progs);
|
|
2535
725
|
|
|
2536
726
|
// 1f. Boundary metadata (exported surface of wasm packages)
|
|
2537
727
|
const boundaryMeta = options.boundary
|
|
@@ -2540,6 +730,7 @@ export function compileWasmModule(
|
|
|
2540
730
|
|
|
2541
731
|
// 2. Pre-scan for needed imports so import func indices are fixed
|
|
2542
732
|
for (const fn of funcDecls) {
|
|
733
|
+
if (fn.intrinsic) prepareWasmIntrinsic(mod, fn);
|
|
2543
734
|
_scanImportsInBody(fn.body, mod);
|
|
2544
735
|
}
|
|
2545
736
|
if (boundaryMeta) scanBoundaryImports(mod, boundaryMeta);
|
|
@@ -2593,8 +784,11 @@ export function compileWasmModule(
|
|
|
2593
784
|
fn._forceExport ||
|
|
2594
785
|
(!fn._isGlobalInit &&
|
|
2595
786
|
!fn._isRuntimeHelper &&
|
|
2596
|
-
!fn.
|
|
2597
|
-
!fn.
|
|
787
|
+
!fn._isMapHelper &&
|
|
788
|
+
!fn._isInterfaceDispatcher &&
|
|
789
|
+
!fn._isSharedHelper &&
|
|
790
|
+
!fn._isRuntimePanic &&
|
|
791
|
+
!fn._isIndexPanic &&
|
|
2598
792
|
(options.exportAll ||
|
|
2599
793
|
fn.name === "main" ||
|
|
2600
794
|
(fn.name[0] >= "A" && fn.name[0] <= "Z") ||
|
|
@@ -2626,13 +820,14 @@ export function compileWasmModule(
|
|
|
2626
820
|
// 4. Emit function bodies
|
|
2627
821
|
for (const fn of funcDecls) {
|
|
2628
822
|
const emitter = new FunctionEmitter(mod, fn, fn._globalFuncIndex);
|
|
2629
|
-
if (fn.
|
|
823
|
+
if (fn._isIndexPanic) {
|
|
824
|
+
emitIndexPanicBody(mod, emitter);
|
|
825
|
+
} else if (fn._isRuntimePanic) {
|
|
2630
826
|
const strIdx = mod.internString(fn._panicMsg);
|
|
2631
827
|
const funcIdx = mod.getStringImportIndex();
|
|
2632
828
|
emitter.pushInstruction({ op: "i32.const", value: strIdx });
|
|
2633
829
|
emitter.pushInstruction({ op: "call", funcIndex: funcIdx });
|
|
2634
|
-
emitter.
|
|
2635
|
-
emitter.emitPanicThrow();
|
|
830
|
+
emitter.emitRuntimePanicThrow();
|
|
2636
831
|
} else if (fn._isPushPanic) {
|
|
2637
832
|
const panicNodeTypeIndex = mod.getPanicNodeTypeIndex();
|
|
2638
833
|
const panicGlobal = mod.getPanicGlobalIndex();
|
|
@@ -2651,709 +846,96 @@ export function compileWasmModule(
|
|
|
2651
846
|
} else if (fn._isMapHelper) {
|
|
2652
847
|
emitter.emitMapHelper(fn);
|
|
2653
848
|
} else if (fn._isSharedHelper) {
|
|
2654
|
-
emitter.emitSharedHelper(fn);
|
|
2655
|
-
} else if (fn._isRuntimeHelper) {
|
|
2656
|
-
emitter.emitRuntimeHelper(fn);
|
|
2657
|
-
} else if (fn.
|
|
2658
|
-
emitter
|
|
2659
|
-
} else if (fn.body) {
|
|
2660
|
-
emitter.emitFunctionBody(fn.body);
|
|
2661
|
-
}
|
|
2662
|
-
// If last instruction is not return or unreachable, auto-emit return or unreachable
|
|
2663
|
-
let body = emitter.body;
|
|
2664
|
-
if (
|
|
2665
|
-
body.length === 0 ||
|
|
2666
|
-
(body[body.length - 1].op !== "return" &&
|
|
2667
|
-
body[body.length - 1].op !== "unreachable")
|
|
2668
|
-
) {
|
|
2669
|
-
if (emitter.returnTypes.length === 0) {
|
|
2670
|
-
body.push({ op: "return" });
|
|
2671
|
-
} else {
|
|
2672
|
-
body.push({ op: "unreachable" });
|
|
2673
|
-
}
|
|
2674
|
-
}
|
|
2675
|
-
|
|
2676
|
-
body = peepholeOptimize(body);
|
|
2677
|
-
|
|
2678
|
-
mod.funcs.push({
|
|
2679
|
-
typeIndex: fn._typeIndex,
|
|
2680
|
-
locals: emitter.localTypes,
|
|
2681
|
-
body,
|
|
2682
|
-
src:
|
|
2683
|
-
fn._srcFile && typeof fn._line === "number"
|
|
2684
|
-
? { file: fn._srcFile, line: fn._line }
|
|
2685
|
-
: null,
|
|
2686
|
-
});
|
|
2687
|
-
}
|
|
2688
|
-
|
|
2689
|
-
// 5. Boundary marshalling helpers + JS facade
|
|
2690
|
-
let facade = null;
|
|
2691
|
-
if (boundaryMeta) {
|
|
2692
|
-
addBoundaryHelpers(mod, boundaryMeta);
|
|
2693
|
-
facade = generateFacade(boundaryMeta, {
|
|
2694
|
-
stringTable: mod.stringTable,
|
|
2695
|
-
callMain: Boolean(options.callMain) && mod.funcMap.has("main"),
|
|
2696
|
-
stdlibEnv: mod.usesStdlibEnv,
|
|
2697
|
-
});
|
|
2698
|
-
}
|
|
2699
|
-
|
|
2700
|
-
const moduleIR = {
|
|
2701
|
-
types: mod.types,
|
|
2702
|
-
imports: mod.imports,
|
|
2703
|
-
tags: mod.tags,
|
|
2704
|
-
globals: mod.globals,
|
|
2705
|
-
elements: mod.elements,
|
|
2706
|
-
funcs: mod.funcs,
|
|
2707
|
-
exports: mod.exports,
|
|
2708
|
-
memory: mod.memory ?? null,
|
|
2709
|
-
start: globalInitFn ? globalInitFn._globalFuncIndex : null,
|
|
2710
|
-
sourceMappingURL: options.sourceMappingURL ?? null,
|
|
2711
|
-
};
|
|
2712
|
-
|
|
2713
|
-
const wasmBytes = encodeModule(moduleIR);
|
|
2714
|
-
const watText = options.emitWat ? emitWat(moduleIR) : null;
|
|
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
|
-
|
|
2736
|
-
return {
|
|
2737
|
-
wasm: wasmBytes,
|
|
2738
|
-
wat: watText,
|
|
2739
|
-
stringTable: mod.stringTable,
|
|
2740
|
-
moduleIR,
|
|
2741
|
-
facade,
|
|
2742
|
-
sourceMapInfo: { sources, mappings },
|
|
2743
|
-
};
|
|
2744
|
-
}
|
|
2745
|
-
|
|
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 = [];
|
|
2752
|
-
for (const p of progs) {
|
|
2753
|
-
for (const d of p.decls ?? []) {
|
|
2754
|
-
if (d.kind !== "ConstDecl") continue;
|
|
2755
|
-
for (const spec of d.decls ?? []) {
|
|
2756
|
-
for (let i = 0; i < spec.names.length; 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
|
-
});
|
|
2764
|
-
}
|
|
2765
|
-
}
|
|
2766
|
-
}
|
|
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
|
-
);
|
|
2852
|
-
}
|
|
2853
|
-
|
|
2854
|
-
function _scanImportsInBody(node, mod) {
|
|
2855
|
-
if (!node || typeof node !== "object") return;
|
|
2856
|
-
|
|
2857
|
-
if (node.kind === "CallExpr") {
|
|
2858
|
-
const { func, args } = node;
|
|
2859
|
-
if (
|
|
2860
|
-
func.kind === "Ident" &&
|
|
2861
|
-
(func.name === "print" || func.name === "println")
|
|
2862
|
-
) {
|
|
2863
|
-
const isPrintln = func.name === "println";
|
|
2864
|
-
if (args.length === 0 && isPrintln) {
|
|
2865
|
-
mod.getPrintlnEmptyIndex();
|
|
2866
|
-
}
|
|
2867
|
-
for (let i = 0; i < args.length; i++) {
|
|
2868
|
-
const arg = args[i];
|
|
2869
|
-
const isLast = i === args.length - 1 && isPrintln;
|
|
2870
|
-
const isBool =
|
|
2871
|
-
arg._type?.name === "bool" ||
|
|
2872
|
-
(arg.kind === "BasicLit" && arg.litKind === "BOOL");
|
|
2873
|
-
const wType = toWasmType(arg._type, mod.checker);
|
|
2874
|
-
mod.getLogImportIndex(wType, isLast, isBool);
|
|
2875
|
-
}
|
|
2876
|
-
} else if (
|
|
2877
|
-
func.kind === "SelectorExpr" &&
|
|
2878
|
-
func.expr?.name === "math" &&
|
|
2879
|
-
func.field
|
|
2880
|
-
) {
|
|
2881
|
-
const jsMathMap = {
|
|
2882
|
-
Sin: "sin",
|
|
2883
|
-
Cos: "cos",
|
|
2884
|
-
Tan: "tan",
|
|
2885
|
-
Asin: "asin",
|
|
2886
|
-
Acos: "acos",
|
|
2887
|
-
Atan: "atan",
|
|
2888
|
-
Atan2: "atan2",
|
|
2889
|
-
Pow: "pow",
|
|
2890
|
-
Mod: "mod",
|
|
2891
|
-
Hypot: "hypot",
|
|
2892
|
-
Cbrt: "cbrt",
|
|
2893
|
-
Exp: "exp",
|
|
2894
|
-
Log: "log",
|
|
2895
|
-
Log2: "log2",
|
|
2896
|
-
Log10: "log10",
|
|
2897
|
-
Round: "round",
|
|
2898
|
-
Sinh: "sinh",
|
|
2899
|
-
Cosh: "cosh",
|
|
2900
|
-
Tanh: "tanh",
|
|
2901
|
-
};
|
|
2902
|
-
if (jsMathMap[func.field]) {
|
|
2903
|
-
const isBinary = BINARY_MATH_IMPORTS.has(jsMathMap[func.field]);
|
|
2904
|
-
mod.getMathImportIndex(jsMathMap[func.field], isBinary);
|
|
2905
|
-
}
|
|
2906
|
-
} else if (
|
|
2907
|
-
func.kind === "SelectorExpr" &&
|
|
2908
|
-
isTestingT(func.expr?._type) &&
|
|
2909
|
-
func.field
|
|
2910
|
-
) {
|
|
2911
|
-
mod.internString(func.field);
|
|
2912
|
-
if (func.field === "Name") mod.getTestingNameImportIndex();
|
|
2913
|
-
else if (func.field === "Failed" || func.field === "Skipped")
|
|
2914
|
-
mod.getTestingFlagImportIndex();
|
|
2915
|
-
else if (func.field === "Run") {
|
|
2916
|
-
// closure type for func(*testing.T) must exist before bodies are emitted
|
|
2917
|
-
mod.getTestingRunClosureType();
|
|
2918
|
-
} else {
|
|
2919
|
-
mod.getTestingCallImportIndex();
|
|
2920
|
-
for (const arg of args) {
|
|
2921
|
-
mod.getTestingArgImportIndex(
|
|
2922
|
-
toWasmType(arg._type, mod.checker),
|
|
2923
|
-
arg._type?.name === "bool" ||
|
|
2924
|
-
(arg.kind === "BasicLit" && arg.litKind === "BOOL"),
|
|
2925
|
-
);
|
|
2926
|
-
}
|
|
2927
|
-
}
|
|
2928
|
-
}
|
|
2929
|
-
}
|
|
2930
|
-
|
|
2931
|
-
if (node.kind === "BasicLit" && node.litKind === "STRING") {
|
|
2932
|
-
mod.getStringImportIndex();
|
|
2933
|
-
const strVal = String(node.value ?? "");
|
|
2934
|
-
mod.internString(strVal);
|
|
2935
|
-
}
|
|
2936
|
-
|
|
2937
|
-
// Recurse child properties
|
|
2938
|
-
for (const key of Object.keys(node)) {
|
|
2939
|
-
if (key.startsWith("_")) continue;
|
|
2940
|
-
const child = node[key];
|
|
2941
|
-
if (Array.isArray(child)) {
|
|
2942
|
-
for (const item of child) _scanImportsInBody(item, mod);
|
|
2943
|
-
} else if (child && typeof child === "object") {
|
|
2944
|
-
_scanImportsInBody(child, mod);
|
|
2945
|
-
}
|
|
2946
|
-
}
|
|
2947
|
-
}
|
|
2948
|
-
|
|
2949
|
-
export function compileWasm(source, options = {}) {
|
|
2950
|
-
const filename = options.filename ?? "main.go";
|
|
2951
|
-
const tokens = new Lexer(source, filename).tokenize();
|
|
2952
|
-
const ast = new Parser(tokens, filename, source).parse();
|
|
2953
|
-
|
|
2954
|
-
const checker = new TypeChecker();
|
|
2955
|
-
checker.target = "wasm";
|
|
2956
|
-
checker.pkgName = ast.pkg?.name ?? "main";
|
|
2957
|
-
|
|
2958
|
-
const errors = checker.check(ast);
|
|
2959
|
-
if (errors.length > 0) {
|
|
2960
|
-
return { wasm: null, errors };
|
|
2961
|
-
}
|
|
2962
|
-
|
|
2963
|
-
const lowerRes = lower([ast], checker);
|
|
2964
|
-
const res = compileWasmModule([ast], checker, lowerRes, {
|
|
2965
|
-
...options,
|
|
2966
|
-
exportAll: options.exportAll ?? true,
|
|
2967
|
-
});
|
|
2968
|
-
|
|
2969
|
-
if (checker.errors?.length > 0) {
|
|
2970
|
-
return { wasm: null, errors: checker.errors };
|
|
2971
|
-
}
|
|
2972
|
-
|
|
2973
|
-
return { ...res, errors: [] };
|
|
2974
|
-
}
|
|
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
|
-
|
|
3016
|
-
function _collectPackageGlobals(progs, mod, checker) {
|
|
3017
|
-
const pending = []; // non-constant initializers, run by the start function
|
|
3018
|
-
const pendingConsts = []; // unfoldable consts; they run before any var
|
|
3019
|
-
for (const p of progs) {
|
|
3020
|
-
for (const d of p.decls ?? []) {
|
|
3021
|
-
const isConst = d.kind === "ConstDecl";
|
|
3022
|
-
if (d.kind !== "VarDecl" && !isConst) continue;
|
|
3023
|
-
for (const spec of d.decls ?? d.specs ?? [d]) {
|
|
3024
|
-
const names = spec.names ?? (spec.name ? [spec.name] : []);
|
|
3025
|
-
const values = spec.value ?? (spec.init ? [spec.init] : []);
|
|
3026
|
-
for (let i = 0; i < names.length; i++) {
|
|
3027
|
-
const name = names[i];
|
|
3028
|
-
// Foldable consts live in `constCache`, not in a global.
|
|
3029
|
-
if (isConst && !mod.isNonLiteralConst(name)) continue;
|
|
3030
|
-
const rawType = spec.type ?? values[i]?._type;
|
|
3031
|
-
const wType = toWasmType(rawType, checker, mod);
|
|
3032
|
-
const gIdx = mod.globals.length;
|
|
3033
|
-
let initInsts = [{ op: `${wType}.const`, value: 0 }];
|
|
3034
|
-
if (wType === "i64") initInsts = [{ op: "i64.const", value: 0n }];
|
|
3035
|
-
else if (wType === "f32" || wType === "f64")
|
|
3036
|
-
initInsts = [{ op: `${wType}.const`, value: 0.0 }];
|
|
3037
|
-
else if (
|
|
3038
|
-
wType === "externref" ||
|
|
3039
|
-
wType === "anyref" ||
|
|
3040
|
-
typeof wType === "object"
|
|
3041
|
-
) {
|
|
3042
|
-
const sliceInfo =
|
|
3043
|
-
typeof wType === "object" && typeof wType.typeIndex === "number"
|
|
3044
|
-
? mod.getSliceTypeByIndex(wType.typeIndex)
|
|
3045
|
-
: null;
|
|
3046
|
-
if (sliceInfo) {
|
|
3047
|
-
initInsts = [
|
|
3048
|
-
{ op: "global.get", index: sliceInfo.emptyGlobalIndex },
|
|
3049
|
-
];
|
|
3050
|
-
} else if (
|
|
3051
|
-
typeof wType === "object" &&
|
|
3052
|
-
typeof wType.typeIndex === "number" &&
|
|
3053
|
-
rawType?.kind !== "pointer"
|
|
3054
|
-
) {
|
|
3055
|
-
const structInfo = Array.from(mod.structTypes.values()).find(
|
|
3056
|
-
(s) => s.typeIndex === wType.typeIndex,
|
|
3057
|
-
);
|
|
3058
|
-
if (structInfo) {
|
|
3059
|
-
initInsts = _zeroStructConst(structInfo, mod);
|
|
3060
|
-
} else {
|
|
3061
|
-
initInsts = [
|
|
3062
|
-
{ op: "ref.null", heapType: wType.typeIndex ?? "any" },
|
|
3063
|
-
];
|
|
3064
|
-
}
|
|
3065
|
-
} else {
|
|
3066
|
-
initInsts = [
|
|
3067
|
-
{
|
|
3068
|
-
op: "ref.null",
|
|
3069
|
-
heapType:
|
|
3070
|
-
typeof wType === "object"
|
|
3071
|
-
? (wType.heapType ?? wType.typeIndex ?? "any")
|
|
3072
|
-
: wType.replace("ref", ""),
|
|
3073
|
-
},
|
|
3074
|
-
];
|
|
3075
|
-
}
|
|
3076
|
-
}
|
|
3077
|
-
|
|
3078
|
-
let litNode = values?.[i];
|
|
3079
|
-
let sign = 1;
|
|
3080
|
-
if (
|
|
3081
|
-
litNode &&
|
|
3082
|
-
litNode.kind === "UnaryExpr" &&
|
|
3083
|
-
litNode.op === "-" &&
|
|
3084
|
-
litNode.operand?.kind === "BasicLit"
|
|
3085
|
-
) {
|
|
3086
|
-
litNode = litNode.operand;
|
|
3087
|
-
sign = -1;
|
|
3088
|
-
}
|
|
3089
|
-
|
|
3090
|
-
let isConstInit = false;
|
|
3091
|
-
if (litNode && litNode.kind === "BasicLit") {
|
|
3092
|
-
if (litNode.litKind === "INT") {
|
|
3093
|
-
isConstInit = true;
|
|
3094
|
-
const s = String(litNode.value);
|
|
3095
|
-
const clean = /^0[0-7]+$/.test(s) ? `0o${s.slice(1)}` : s;
|
|
3096
|
-
initInsts = [
|
|
3097
|
-
wType === "i64"
|
|
3098
|
-
? {
|
|
3099
|
-
op: "i64.const",
|
|
3100
|
-
value: BigInt(sign) * BigInt(clean),
|
|
3101
|
-
}
|
|
3102
|
-
: {
|
|
3103
|
-
op: "i32.const",
|
|
3104
|
-
value: (sign * Number(clean)) | 0,
|
|
3105
|
-
},
|
|
3106
|
-
];
|
|
3107
|
-
} else if (litNode.litKind === "FLOAT") {
|
|
3108
|
-
isConstInit = true;
|
|
3109
|
-
initInsts = [
|
|
3110
|
-
{
|
|
3111
|
-
op: `${wType}.const`,
|
|
3112
|
-
value: sign * Number(litNode.value),
|
|
3113
|
-
},
|
|
3114
|
-
];
|
|
3115
|
-
} else if (litNode.litKind === "BOOL") {
|
|
3116
|
-
isConstInit = true;
|
|
3117
|
-
initInsts = [
|
|
3118
|
-
{
|
|
3119
|
-
op: "i32.const",
|
|
3120
|
-
value: litNode.value === "true" ? 1 : 0,
|
|
3121
|
-
},
|
|
3122
|
-
];
|
|
3123
|
-
}
|
|
3124
|
-
}
|
|
3125
|
-
if (values[i] && !isConstInit && names.length === values.length) {
|
|
3126
|
-
(isConst ? pendingConsts : pending).push({
|
|
3127
|
-
name,
|
|
3128
|
-
expr: values[i],
|
|
3129
|
-
});
|
|
3130
|
-
}
|
|
3131
|
-
mod.globals.push({
|
|
3132
|
-
type: wType,
|
|
3133
|
-
mutable: true,
|
|
3134
|
-
init: initInsts,
|
|
3135
|
-
});
|
|
3136
|
-
mod.globalCache.set(name, {
|
|
3137
|
-
index: gIdx,
|
|
3138
|
-
type: wType,
|
|
3139
|
-
goType: rawType,
|
|
3140
|
-
});
|
|
3141
|
-
}
|
|
3142
|
-
}
|
|
3143
|
-
}
|
|
3144
|
-
}
|
|
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;
|
|
3165
|
-
}
|
|
3166
|
-
|
|
3167
|
-
// Synthesises `__init_globals()` assigning every non-constant package-level
|
|
3168
|
-
// initializer in declaration order and invoking package `init()` functions;
|
|
3169
|
-
// registered as the module start function.
|
|
3170
|
-
function _makeGlobalInitFunc(pending, initNames = []) {
|
|
3171
|
-
const stmts = pending.map(({ name, expr }) => ({
|
|
3172
|
-
kind: "AssignStmt",
|
|
3173
|
-
lhs: [{ kind: "Ident", name, _type: expr._type }],
|
|
3174
|
-
op: "=",
|
|
3175
|
-
rhs: [expr],
|
|
3176
|
-
}));
|
|
3177
|
-
for (const initName of initNames) {
|
|
3178
|
-
stmts.push({
|
|
3179
|
-
kind: "ExprStmt",
|
|
3180
|
-
expr: {
|
|
3181
|
-
kind: "CallExpr",
|
|
3182
|
-
func: { kind: "Ident", name: initName },
|
|
3183
|
-
args: [],
|
|
3184
|
-
},
|
|
3185
|
-
});
|
|
3186
|
-
}
|
|
3187
|
-
stmts.push({ kind: "ReturnStmt", values: [] });
|
|
3188
|
-
return {
|
|
3189
|
-
kind: "FuncDecl",
|
|
3190
|
-
name: "__init_globals",
|
|
3191
|
-
params: [],
|
|
3192
|
-
returnType: null,
|
|
3193
|
-
body: { kind: "Block", stmts, list: stmts },
|
|
3194
|
-
_isGlobalInit: true,
|
|
3195
|
-
};
|
|
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
|
-
}
|
|
849
|
+
emitter.emitSharedHelper(fn);
|
|
850
|
+
} else if (fn._isRuntimeHelper) {
|
|
851
|
+
emitter.emitRuntimeHelper(fn);
|
|
852
|
+
} else if (fn.intrinsic) {
|
|
853
|
+
emitWasmIntrinsic(emitter, fn);
|
|
854
|
+
} else if (fn.body) {
|
|
855
|
+
emitter.emitFunctionBody(fn.body);
|
|
856
|
+
}
|
|
857
|
+
// If last instruction is not return or unreachable, auto-emit return or unreachable
|
|
858
|
+
let body = emitter.body;
|
|
859
|
+
if (
|
|
860
|
+
body.length === 0 ||
|
|
861
|
+
(body[body.length - 1].op !== "return" &&
|
|
862
|
+
body[body.length - 1].op !== "unreachable")
|
|
863
|
+
) {
|
|
864
|
+
if (emitter.returnTypes.length === 0) {
|
|
865
|
+
body.push({ op: "return" });
|
|
866
|
+
} else {
|
|
867
|
+
body.push({ op: "unreachable" });
|
|
3255
868
|
}
|
|
3256
869
|
}
|
|
3257
|
-
}
|
|
3258
|
-
}
|
|
3259
870
|
|
|
3260
|
-
|
|
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
|
-
}
|
|
871
|
+
body = peepholeOptimize(body);
|
|
3273
872
|
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
type: info.callNode.args[p]._type ?? {
|
|
3284
|
-
kind: "basic",
|
|
3285
|
-
name: "any",
|
|
3286
|
-
},
|
|
3287
|
-
});
|
|
3288
|
-
}
|
|
873
|
+
mod.funcs.push({
|
|
874
|
+
typeIndex: fn._typeIndex,
|
|
875
|
+
locals: emitter.localTypes,
|
|
876
|
+
body,
|
|
877
|
+
src:
|
|
878
|
+
fn._srcFile && typeof fn._line === "number"
|
|
879
|
+
? { file: fn._srcFile, line: fn._line }
|
|
880
|
+
: null,
|
|
881
|
+
});
|
|
3289
882
|
}
|
|
3290
883
|
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
884
|
+
// 5. Boundary marshalling helpers + JS facade
|
|
885
|
+
let facade = null;
|
|
886
|
+
if (boundaryMeta) {
|
|
887
|
+
addBoundaryHelpers(mod, boundaryMeta);
|
|
888
|
+
facade = generateFacade(boundaryMeta, {
|
|
889
|
+
stringTable: mod.stringTable,
|
|
890
|
+
callMain: Boolean(options.callMain) && mod.funcMap.has("main"),
|
|
891
|
+
});
|
|
3299
892
|
}
|
|
3300
893
|
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
894
|
+
const moduleIR = {
|
|
895
|
+
types: mod.types,
|
|
896
|
+
imports: mod.imports,
|
|
897
|
+
tags: mod.tags,
|
|
898
|
+
globals: mod.globals,
|
|
899
|
+
elements: mod.elements,
|
|
900
|
+
funcs: mod.funcs,
|
|
901
|
+
exports: mod.exports,
|
|
902
|
+
memory: mod.memory ?? null,
|
|
903
|
+
start: globalInitFn ? globalInitFn._globalFuncIndex : null,
|
|
904
|
+
sourceMappingURL: options.sourceMappingURL ?? null,
|
|
3312
905
|
};
|
|
3313
|
-
}
|
|
3314
906
|
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
const
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
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
|
-
}
|
|
907
|
+
pruneUnusedFuncs(moduleIR);
|
|
908
|
+
const wasmBytes = encodeModule(moduleIR);
|
|
909
|
+
const watText = options.emitWat ? emitWat(moduleIR) : null;
|
|
910
|
+
|
|
911
|
+
// Function-level source positions: `sources` are the absolute Go files,
|
|
912
|
+
// `mappings` pair each function body's byte offset with its 0-based line.
|
|
913
|
+
const sources = [];
|
|
914
|
+
const sourceIndex = new Map();
|
|
915
|
+
const mappings = [];
|
|
916
|
+
moduleIR.funcs.forEach((fn, i) => {
|
|
917
|
+
if (!fn.src) return;
|
|
918
|
+
let idx = sourceIndex.get(fn.src.file);
|
|
919
|
+
if (idx === undefined) {
|
|
920
|
+
idx = sources.length;
|
|
921
|
+
sources.push(fn.src.file);
|
|
922
|
+
sourceIndex.set(fn.src.file, idx);
|
|
3343
923
|
}
|
|
924
|
+
mappings.push({
|
|
925
|
+
offset: moduleIR.codeOffsets[i],
|
|
926
|
+
srcFileIdx: idx,
|
|
927
|
+
srcLine: Math.max(0, fn.src.line - 1),
|
|
928
|
+
});
|
|
929
|
+
});
|
|
930
|
+
|
|
931
|
+
return {
|
|
932
|
+
wasm: wasmBytes,
|
|
933
|
+
wat: watText,
|
|
934
|
+
stringTable: mod.stringTable,
|
|
935
|
+
moduleIR,
|
|
936
|
+
facade,
|
|
937
|
+
sourceMapInfo: { sources, mappings },
|
|
3344
938
|
};
|
|
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
939
|
}
|
|
3358
940
|
|
|
3359
941
|
function _collectInterfaceDispatchers(progs, funcDecls, mod, checker) {
|
|
@@ -3382,378 +964,7 @@ function _collectInterfaceDispatchers(progs, funcDecls, mod, checker) {
|
|
|
3382
964
|
});
|
|
3383
965
|
}
|
|
3384
966
|
|
|
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
967
|
for (const info of interfaceCalls.values()) {
|
|
3435
968
|
funcDecls.push(_makeInterfaceDispatcher(info, checker));
|
|
3436
969
|
}
|
|
3437
970
|
}
|
|
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
|
-
}
|