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
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
// src/backend/wasm/prescan.js
|
|
2
|
+
// Pre-scan helpers: walk the lowered AST to decide which imports, runtime helpers
|
|
3
|
+
// and interface dispatchers a module needs before emission starts.
|
|
4
|
+
|
|
5
|
+
import { prepareWasmNative } from "./natives.js";
|
|
6
|
+
import {
|
|
7
|
+
anonIfaceName,
|
|
8
|
+
getInterfaceMethodList,
|
|
9
|
+
getReceiverTypeName,
|
|
10
|
+
isInterfaceType,
|
|
11
|
+
isNonEmptyInterface,
|
|
12
|
+
printOperandKind,
|
|
13
|
+
toWasmType,
|
|
14
|
+
} from "./types.js";
|
|
15
|
+
|
|
16
|
+
export function hasErrorUsage(programs) {
|
|
17
|
+
return (programs ?? []).some((p) => {
|
|
18
|
+
let found = false;
|
|
19
|
+
const scan = (n) => {
|
|
20
|
+
if (!n || found || typeof n !== "object") return;
|
|
21
|
+
if (Array.isArray(n)) {
|
|
22
|
+
for (const x of n) scan(x);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (
|
|
26
|
+
(n.kind === "Ident" ||
|
|
27
|
+
n.kind === "TypeName" ||
|
|
28
|
+
n.kind === "basic" ||
|
|
29
|
+
n.kind === "interface") &&
|
|
30
|
+
n.name === "error"
|
|
31
|
+
) {
|
|
32
|
+
found = true;
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
for (const k of Object.keys(n)) {
|
|
36
|
+
if (!k.startsWith("_")) scan(n[k]);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
scan(p);
|
|
40
|
+
return found;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Depth-first search over AST nodes (skipping `_`-prefixed annotations).
|
|
45
|
+
export function _someNode(node, pred) {
|
|
46
|
+
if (!node || typeof node !== "object") return false;
|
|
47
|
+
if (Array.isArray(node)) return node.some((n) => _someNode(n, pred));
|
|
48
|
+
if (pred(node)) return true;
|
|
49
|
+
for (const key of Object.keys(node)) {
|
|
50
|
+
if (key.startsWith("_")) continue;
|
|
51
|
+
if (_someNode(node[key], pred)) return true;
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// `__any_eq` is only needed when some `==`/`!=` (or switch case) compares
|
|
57
|
+
// anyref operands, a struct with an anyref field is compared, or the error
|
|
58
|
+
// runtime (`errors.Is`) is present. `_emitAnyEq` throws if this misses a
|
|
59
|
+
// site, so a gap surfaces as a compile error rather than a bad module.
|
|
60
|
+
export function _usesAnyEq(funcDecls, mod) {
|
|
61
|
+
if (mod.hasErrorType) return true;
|
|
62
|
+
for (const info of mod.mapTypes.values()) {
|
|
63
|
+
if (isInterfaceType(info.keyGoType, mod.checker)) return true;
|
|
64
|
+
}
|
|
65
|
+
for (const info of mod.structTypes.values()) {
|
|
66
|
+
if (info.fields?.some((f) => f.wType === "anyref")) return true;
|
|
67
|
+
}
|
|
68
|
+
const isAnyRef = (n) =>
|
|
69
|
+
Boolean(n?._type) && toWasmType(n._type, mod.checker, mod) === "anyref";
|
|
70
|
+
return funcDecls.some((fn) =>
|
|
71
|
+
_someNode(fn.body, (n) => {
|
|
72
|
+
if (n.kind === "BinaryExpr" && (n.op === "==" || n.op === "!="))
|
|
73
|
+
return isAnyRef(n.left) || isAnyRef(n.right);
|
|
74
|
+
if (n.kind === "SwitchStmt" && n.tag) return isAnyRef(n.tag);
|
|
75
|
+
return false;
|
|
76
|
+
}),
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function _scanImportsInBody(node, mod) {
|
|
81
|
+
if (!node || typeof node !== "object") return;
|
|
82
|
+
|
|
83
|
+
if (node.kind === "CallExpr") {
|
|
84
|
+
const { func, args } = node;
|
|
85
|
+
prepareWasmNative(mod, node);
|
|
86
|
+
if (
|
|
87
|
+
func.kind === "Ident" &&
|
|
88
|
+
(func.name === "print" || func.name === "println")
|
|
89
|
+
) {
|
|
90
|
+
const isPrintln = func.name === "println";
|
|
91
|
+
if (args.length === 0 && isPrintln) {
|
|
92
|
+
mod.getPrintlnEmptyIndex();
|
|
93
|
+
}
|
|
94
|
+
for (let i = 0; i < args.length; i++) {
|
|
95
|
+
const isLast = i === args.length - 1 && isPrintln;
|
|
96
|
+
const { wType, isBool } = printOperandKind(args[i], mod.checker);
|
|
97
|
+
mod.getLogImportIndex(wType, isLast, isBool);
|
|
98
|
+
if (!isPrintln) mod.getPrintResetIndex();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (node.kind === "BasicLit" && node.litKind === "STRING") {
|
|
104
|
+
mod.getStringImportIndex();
|
|
105
|
+
const strVal = String(node.value ?? "");
|
|
106
|
+
mod.internString(strVal);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Recurse child properties
|
|
110
|
+
for (const key of Object.keys(node)) {
|
|
111
|
+
if (key.startsWith("_")) continue;
|
|
112
|
+
const child = node[key];
|
|
113
|
+
if (Array.isArray(child)) {
|
|
114
|
+
for (const item of child) _scanImportsInBody(item, mod);
|
|
115
|
+
} else if (child && typeof child === "object") {
|
|
116
|
+
_scanImportsInBody(child, mod);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function _scanInterfaceCallNodes(node, interfaceCalls, checker) {
|
|
122
|
+
if (!node || typeof node !== "object") return;
|
|
123
|
+
if (Array.isArray(node)) {
|
|
124
|
+
for (const item of node)
|
|
125
|
+
_scanInterfaceCallNodes(item, interfaceCalls, checker);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (node.kind === "CallExpr" && node.func?.kind === "SelectorExpr") {
|
|
129
|
+
const recvType = node.func.expr?._type;
|
|
130
|
+
if (isNonEmptyInterface(recvType, checker)) {
|
|
131
|
+
const ifaceName =
|
|
132
|
+
getReceiverTypeName(recvType, node.func.expr) ||
|
|
133
|
+
anonIfaceName(recvType);
|
|
134
|
+
const methodName = node.func.field;
|
|
135
|
+
const key = `${ifaceName}.${methodName}`;
|
|
136
|
+
if (!interfaceCalls.has(key)) {
|
|
137
|
+
interfaceCalls.set(key, {
|
|
138
|
+
ifaceName,
|
|
139
|
+
ifaceType: recvType,
|
|
140
|
+
methodName,
|
|
141
|
+
callNode: node,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
for (const key of Object.keys(node)) {
|
|
147
|
+
if (!key.startsWith("_"))
|
|
148
|
+
_scanInterfaceCallNodes(node[key], interfaceCalls, checker);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function _scanInterfaceTypeDecls(progs, interfaceCalls, checker) {
|
|
153
|
+
for (const p of progs) {
|
|
154
|
+
for (const d of p.decls ?? []) {
|
|
155
|
+
if (d.kind !== "TypeDecl") continue;
|
|
156
|
+
const resolved = checker?.types?.get(d.name) ?? d.type;
|
|
157
|
+
const underlying =
|
|
158
|
+
resolved?.kind === "named"
|
|
159
|
+
? resolved.underlying
|
|
160
|
+
: d.type?.kind === "InterfaceType"
|
|
161
|
+
? d.type
|
|
162
|
+
: resolved;
|
|
163
|
+
if (!isNonEmptyInterface(underlying, checker)) continue;
|
|
164
|
+
const methods =
|
|
165
|
+
underlying.methods instanceof Map
|
|
166
|
+
? Array.from(underlying.methods.entries())
|
|
167
|
+
: Array.isArray(underlying.methods)
|
|
168
|
+
? underlying.methods.map((m) => [m.name ?? m, m])
|
|
169
|
+
: [];
|
|
170
|
+
for (const [mName, mSig] of methods) {
|
|
171
|
+
const key = `${d.name}.${mName}`;
|
|
172
|
+
if (!interfaceCalls.has(key)) {
|
|
173
|
+
interfaceCalls.set(key, {
|
|
174
|
+
ifaceName: d.name,
|
|
175
|
+
ifaceType: resolved,
|
|
176
|
+
methodName: mName,
|
|
177
|
+
methodSig: mSig,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// A qualified interface (`fmt.Stringer`) in a signature is a boundary type
|
|
186
|
+
// when the function is exported, so its dispatchers must exist. Returns true
|
|
187
|
+
// when `t` names such a type.
|
|
188
|
+
function _registerQualifiedIface(t, interfaceCalls, checker) {
|
|
189
|
+
if (t.kind !== "TypeName" || !t.name?.includes(".")) return false;
|
|
190
|
+
const resolved = checker?.types?.get(t.name);
|
|
191
|
+
const iface = resolved?.underlying ?? resolved;
|
|
192
|
+
if (!resolved || !isNonEmptyInterface(iface, checker)) return true;
|
|
193
|
+
for (const m of getInterfaceMethodList(iface)) {
|
|
194
|
+
const key = `${t.name}.${m.name}`;
|
|
195
|
+
if (interfaceCalls.has(key)) continue;
|
|
196
|
+
interfaceCalls.set(key, {
|
|
197
|
+
ifaceName: t.name,
|
|
198
|
+
ifaceType: resolved,
|
|
199
|
+
methodName: m.name,
|
|
200
|
+
methodSig: { kind: "func", params: m.params, returns: m.returns },
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function _scanFuncSigInterfaces(
|
|
207
|
+
progs,
|
|
208
|
+
funcDecls,
|
|
209
|
+
interfaceCalls,
|
|
210
|
+
checker,
|
|
211
|
+
mod,
|
|
212
|
+
) {
|
|
213
|
+
const scanType = (t) => {
|
|
214
|
+
if (!t) return;
|
|
215
|
+
if (_registerQualifiedIface(t, interfaceCalls, checker)) return;
|
|
216
|
+
if (t.kind === "InterfaceType" || (t.kind === "interface" && !t.name)) {
|
|
217
|
+
const mList = getInterfaceMethodList(t);
|
|
218
|
+
if (mList.length === 0) return;
|
|
219
|
+
const ifaceName = anonIfaceName(t, checker, mod);
|
|
220
|
+
for (const m of mList) {
|
|
221
|
+
const key = `${ifaceName}.${m.name}`;
|
|
222
|
+
if (!interfaceCalls.has(key)) {
|
|
223
|
+
interfaceCalls.set(key, {
|
|
224
|
+
ifaceName,
|
|
225
|
+
ifaceType: t,
|
|
226
|
+
methodName: m.name,
|
|
227
|
+
methodSig: {
|
|
228
|
+
kind: "func",
|
|
229
|
+
params: m.params,
|
|
230
|
+
returns: m.returns,
|
|
231
|
+
},
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
for (const fn of funcDecls) {
|
|
238
|
+
for (const p of fn.params ?? []) scanType(p.type);
|
|
239
|
+
scanType(fn.returnType);
|
|
240
|
+
}
|
|
241
|
+
for (const p of progs) {
|
|
242
|
+
for (const d of p.decls ?? []) {
|
|
243
|
+
if (d.kind === "FuncDecl") {
|
|
244
|
+
for (const param of d.params ?? []) scanType(param.type);
|
|
245
|
+
scanType(d.returnType);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// src/backend/was./prune-funcs.js
|
|
2
|
+
// Drops function imports and defined functions that no export, element
|
|
3
|
+
// segment, global initialiser or start function reaches, and renumbers the function index space to match. Imports
|
|
4
|
+
// are registered eagerly because the table is locked before bodies are
|
|
5
|
+
// emitted, so most modules declare far more than they call.
|
|
6
|
+
|
|
7
|
+
const FUNC_REF_OPS = new Set(["call", "return_call", "ref.func"]);
|
|
8
|
+
|
|
9
|
+
const funcRef = (inst) => inst.funcIndex ?? inst.index ?? 0;
|
|
10
|
+
|
|
11
|
+
function funcRefsOf(insts, visit) {
|
|
12
|
+
for (const inst of insts ?? []) {
|
|
13
|
+
if (FUNC_REF_OPS.has(inst.op)) visit(funcRef(inst));
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Calls `visit` for every function the module reaches without a body: exports,
|
|
18
|
+
// element segments, global initialisers and the start function.
|
|
19
|
+
function forEachRoot(moduleIR, visit) {
|
|
20
|
+
for (const g of moduleIR.globals ?? []) funcRefsOf(g.init, visit);
|
|
21
|
+
for (const idx of moduleIR.elements ?? []) visit(idx);
|
|
22
|
+
for (const exp of moduleIR.exports ?? []) {
|
|
23
|
+
if (exp.kind === "func") visit(exp.index);
|
|
24
|
+
}
|
|
25
|
+
if (typeof moduleIR.start === "number") visit(moduleIR.start);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function remapInst(inst, remap) {
|
|
29
|
+
if (!FUNC_REF_OPS.has(inst.op)) return inst;
|
|
30
|
+
const next = remap[funcRef(inst)];
|
|
31
|
+
return "funcIndex" in inst
|
|
32
|
+
? { ...inst, funcIndex: next }
|
|
33
|
+
: { ...inst, index: next };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Marks every function index reachable from the roots through call bodies.
|
|
37
|
+
function reachableFuncs(moduleIR, count, total) {
|
|
38
|
+
const used = new Array(total).fill(false);
|
|
39
|
+
const work = [];
|
|
40
|
+
const mark = (idx) => {
|
|
41
|
+
if (idx < total && !used[idx]) {
|
|
42
|
+
used[idx] = true;
|
|
43
|
+
work.push(idx);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
forEachRoot(moduleIR, mark);
|
|
47
|
+
while (work.length > 0) {
|
|
48
|
+
const idx = work.pop();
|
|
49
|
+
if (idx >= count) funcRefsOf(moduleIR.funcs[idx - count].body, mark);
|
|
50
|
+
}
|
|
51
|
+
return used;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function applyRemap(moduleIR, remap) {
|
|
55
|
+
for (const fn of moduleIR.funcs) {
|
|
56
|
+
fn.body = fn.body.map((inst) => remapInst(inst, remap));
|
|
57
|
+
}
|
|
58
|
+
for (const g of moduleIR.globals ?? []) {
|
|
59
|
+
if (g.init) g.init = g.init.map((inst) => remapInst(inst, remap));
|
|
60
|
+
}
|
|
61
|
+
if (moduleIR.elements) {
|
|
62
|
+
moduleIR.elements = moduleIR.elements.map((idx) => remap[idx]);
|
|
63
|
+
}
|
|
64
|
+
for (const exp of moduleIR.exports ?? []) {
|
|
65
|
+
if (exp.kind === "func") exp.index = remap[exp.index];
|
|
66
|
+
}
|
|
67
|
+
if (typeof moduleIR.start === "number") {
|
|
68
|
+
moduleIR.start = remap[moduleIR.start];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function pruneUnusedFuncs(moduleIR) {
|
|
73
|
+
const imports = moduleIR.imports ?? [];
|
|
74
|
+
const funcImports = imports.filter((imp) => imp.kind === "func");
|
|
75
|
+
const count = funcImports.length;
|
|
76
|
+
const total = count + moduleIR.funcs.length;
|
|
77
|
+
|
|
78
|
+
const used = reachableFuncs(moduleIR, count, total);
|
|
79
|
+
if (used.every(Boolean)) return;
|
|
80
|
+
|
|
81
|
+
const remap = new Array(total);
|
|
82
|
+
let next = 0;
|
|
83
|
+
for (let i = 0; i < total; i++) if (used[i]) remap[i] = next++;
|
|
84
|
+
|
|
85
|
+
const dropped = new Set(funcImports.filter((_, i) => !used[i]));
|
|
86
|
+
moduleIR.imports = imports.filter((imp) => !dropped.has(imp));
|
|
87
|
+
moduleIR.funcs = moduleIR.funcs.filter((_, i) => used[count + i]);
|
|
88
|
+
applyRemap(moduleIR, remap);
|
|
89
|
+
}
|
|
@@ -1,43 +1,8 @@
|
|
|
1
1
|
// src/backend/wasm/stdlib/index.js
|
|
2
2
|
// Standard library emitters for the WebAssembly backend.
|
|
3
|
-
//
|
|
4
|
-
// Organized by domain:
|
|
5
|
-
// - core.js: math, math/bits, maps, slices, strings, strconv, fmt, errors, sort, unicode/utf8, testing
|
|
6
|
-
// - bytes.js: bytes package functions and bytes.Buffer methods
|
|
7
|
-
// - pure.js: stateless utilities (unicode, math/rand, html, path/filepath)
|
|
8
|
-
// - io.js: io constants/functions, readers/writers, fmt Fprint/Sscan, synthetic IO methods
|
|
9
|
-
// - time.js: time package (Duration, Time struct, components, Format/Parse, clock)
|
|
10
3
|
|
|
11
|
-
import { BytesEmitter } from "./bytes.js";
|
|
12
|
-
import { BINARY_MATH_IMPORTS, StdlibCoreEmitter } from "./core.js";
|
|
13
|
-
import { IoEmitter } from "./io.js";
|
|
14
|
-
import { ExtendedPackagesEmitter } from "./packages.js";
|
|
15
|
-
import { PurePackagesEmitter } from "./pure.js";
|
|
16
4
|
import { SimdEmitter } from "./simd.js";
|
|
17
|
-
import { TimeEmitter } from "./time.js";
|
|
18
5
|
|
|
19
|
-
|
|
20
|
-
export {
|
|
21
|
-
BINARY_MATH_IMPORTS,
|
|
22
|
-
BytesEmitter,
|
|
23
|
-
ExtendedPackagesEmitter,
|
|
24
|
-
IoEmitter,
|
|
25
|
-
IoEmitter as Phase7Emitter,
|
|
26
|
-
PurePackagesEmitter,
|
|
27
|
-
PurePackagesEmitter as Phase6Emitter,
|
|
28
|
-
SimdEmitter,
|
|
29
|
-
StdlibCoreEmitter,
|
|
30
|
-
StdlibCoreEmitter as StdlibEmitter,
|
|
31
|
-
TimeEmitter,
|
|
32
|
-
TimeEmitter as Phase8Emitter,
|
|
33
|
-
};
|
|
6
|
+
export { SimdEmitter };
|
|
34
7
|
|
|
35
|
-
export const stdlibEmitters = [
|
|
36
|
-
StdlibCoreEmitter,
|
|
37
|
-
BytesEmitter,
|
|
38
|
-
PurePackagesEmitter,
|
|
39
|
-
IoEmitter,
|
|
40
|
-
TimeEmitter,
|
|
41
|
-
ExtendedPackagesEmitter,
|
|
42
|
-
SimdEmitter,
|
|
43
|
-
];
|
|
8
|
+
export const stdlibEmitters = [SimdEmitter];
|