gofront 1.8.0 → 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/package.json +6 -5
- package/src/backend/js/expressions.js +1157 -408
- package/src/backend/js/index.js +327 -153
- package/src/backend/js/natives.js +383 -0
- package/src/backend/js/runtime.js +559 -2552
- package/src/backend/js/statements.js +670 -158
- package/src/backend/js/stdlib/index.js +45 -93
- package/src/backend/js/templ.js +32 -45
- package/src/backend/js/type-info.js +298 -19
- package/src/backend/shared/fixed-float.js +20 -0
- package/src/backend/shared/fmt-types.js +91 -170
- package/src/backend/shared/host-unicode.js +36 -0
- package/src/backend/wasm/boundary.js +490 -965
- package/src/backend/wasm/emit-builtins.js +884 -256
- package/src/backend/wasm/emit-exprs.js +185 -77
- package/src/backend/wasm/emit-maps.js +17 -367
- package/src/backend/wasm/emit-shared.js +4 -1
- package/src/backend/wasm/emit-stmts.js +524 -240
- package/src/backend/wasm/emit.js +176 -38
- package/src/backend/wasm/globals.js +48 -8
- package/src/backend/wasm/glue.js +2 -4
- package/src/backend/wasm/index.js +44 -530
- package/src/backend/wasm/method-expr.js +12 -1
- package/src/backend/wasm/module-emitter.js +299 -770
- package/src/backend/wasm/monomorph.js +139 -8
- package/src/backend/wasm/natives.js +600 -0
- package/src/backend/wasm/prescan.js +5 -153
- package/src/backend/wasm/prune-funcs.js +89 -0
- package/src/backend/wasm/stdlib/index.js +2 -32
- package/src/backend/wasm/types.js +97 -20
- package/src/backend/wasm/variadic.js +9 -4
- package/src/cli/cli-core.js +5 -1
- package/src/cli/test-runner.js +115 -342
- package/src/compiler.js +113 -130
- package/src/lexer/lexer.js +106 -11
- package/src/lexer/templ-lexer.js +61 -18
- package/src/lexer/tokens.js +1 -0
- package/src/link/stdlib.js +355 -132
- package/src/link/type-names.js +204 -0
- package/src/link/wasm.js +30 -6
- package/src/lower/bounds.js +153 -0
- package/src/lower/captures.js +92 -33
- package/src/lower/embedding.js +62 -5
- package/src/lower/errors-as.js +196 -0
- package/src/lower/fmt-formatters.js +929 -0
- package/src/lower/functions.js +225 -7
- package/src/lower/gen-types.js +293 -0
- package/src/lower/index.js +14 -2
- package/src/lower/json-codecs.js +334 -245
- package/src/lower/ownership.js +25 -7
- package/src/lower/reachability.js +69 -51
- package/src/lower/walk.js +12 -0
- package/src/parser/declarations.js +30 -5
- package/src/parser/expressions.js +111 -57
- package/src/parser/statements.js +108 -0
- package/src/parser/templ.js +44 -5
- package/src/parser/types.js +12 -0
- package/src/resolver.js +1 -2
- package/src/typechecker/assignability.js +214 -19
- package/src/typechecker/const-fold.js +70 -27
- package/src/typechecker/constants.js +156 -8
- package/src/typechecker/expressions.js +490 -108
- package/src/typechecker/index.js +346 -42
- package/src/typechecker/init-order.js +136 -40
- package/src/typechecker/operand-text.js +41 -0
- package/src/typechecker/resolve.js +490 -65
- package/src/typechecker/statements.js +288 -27
- package/src/typechecker/stdlib/excluded.js +9 -5
- package/src/typechecker/stdlib/index.js +25 -9
- package/src/typechecker/termination.js +10 -1
- package/src/typechecker/types.js +287 -575
- package/stdlib/bytes/buffer.go +479 -0
- package/stdlib/bytes/bytes.go +1010 -0
- package/stdlib/bytes/reader.go +161 -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/json.go +62 -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/html/html.go +251 -0
- package/stdlib/internal/fmtrt/fmtrt.go +163 -0
- package/stdlib/internal/jsonrt/dec.go +171 -38
- package/stdlib/internal/jsonrt/enc.go +186 -36
- package/stdlib/internal/jsonrt/scanner.go +17 -8
- package/stdlib/io/io.go +157 -1
- package/stdlib/maps/maps.go +131 -0
- package/stdlib/math/bits/bits.go +507 -0
- package/stdlib/math/extra.go +16 -14
- 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 +391 -452
- 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 +235 -50
- 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 +141 -3
- package/stdlib/unicode/utf8/utf8.go +464 -0
- package/src/backend/js/fmt-runtime.js +0 -573
- 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 -119
- package/src/backend/js/stdlib/builder.js +0 -98
- package/src/backend/js/stdlib/bytes.js +0 -368
- package/src/backend/js/stdlib/errors.js +0 -45
- package/src/backend/js/stdlib/fmt.js +0 -517
- 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 -1167
- package/src/backend/js/stdlib/maps.js +0 -37
- package/src/backend/js/stdlib/math.js +0 -85
- 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 -34
- package/src/backend/js/stdlib/slices.js +0 -107
- package/src/backend/js/stdlib/sort.js +0 -30
- package/src/backend/js/stdlib/strconv.js +0 -79
- package/src/backend/js/stdlib/strings.js +0 -301
- package/src/backend/js/stdlib/sync.js +0 -13
- package/src/backend/js/stdlib/testing.js +0 -41
- package/src/backend/js/stdlib/time.js +0 -152
- package/src/backend/js/stdlib/unicode.js +0 -25
- package/src/backend/js/stdlib/utf8.js +0 -26
- package/src/backend/wasm/stdlib/bytes.js +0 -6716
- package/src/backend/wasm/stdlib/core.js +0 -10278
- package/src/backend/wasm/stdlib/io.js +0 -3578
- package/src/backend/wasm/stdlib/packages.js +0 -2402
- package/src/backend/wasm/stdlib/pure.js +0 -720
- package/src/backend/wasm/stdlib/time.js +0 -2229
- package/src/typechecker/stdlib/core.js +0 -1079
- package/src/typechecker/stdlib/extended.js +0 -1210
- package/stdlib/bytes/iter.go +0 -192
- package/stdlib/bytes/search.go +0 -240
- package/stdlib/bytes/split.go +0 -218
- package/stdlib/errors/overlay.go +0 -12
- package/stdlib/maps/iter.go +0 -65
- package/stdlib/slices/extra.go +0 -73
- package/stdlib/slices/iter.go +0 -113
- package/stdlib/strings/extra.go +0 -35
- package/stdlib/strings/iter.go +0 -122
- package/stdlib/sync/extra.go +0 -105
- package/stdlib/unicode/utf8/bytes.go +0 -239
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
// Same-named types in different local packages.
|
|
2
|
+
//
|
|
3
|
+
// Local packages are inlined into one flat JS module, so `a.Pt` and `b.Pt`
|
|
4
|
+
// would both become `class Pt`. A package that declares a type another
|
|
5
|
+
// package already owns gets that type renamed to `Pt$$b` before it is
|
|
6
|
+
// type-checked, the convention Go-source stdlib packages use: diagnostics and
|
|
7
|
+
// `%T` show it as `b.Pt`, and importers reach it through `b.Pt` as usual.
|
|
8
|
+
|
|
9
|
+
import { forEachChild } from "../lower/walk.js";
|
|
10
|
+
|
|
11
|
+
function topLevelTypeNames(programs) {
|
|
12
|
+
const names = [];
|
|
13
|
+
for (const p of programs)
|
|
14
|
+
for (const d of p.decls ?? [])
|
|
15
|
+
if (d.kind === "TypeDecl") names.push(d.name);
|
|
16
|
+
return names;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Registers the package's type names in `owners` (name -> package key) and
|
|
20
|
+
// renames those another package registered first. Returns Map<name, mangled>
|
|
21
|
+
// for the renamed types, empty when nothing collides.
|
|
22
|
+
export function mangleCollidingTypes(programs, pkgKey, pkgName, owners) {
|
|
23
|
+
const renamed = new Map();
|
|
24
|
+
for (const name of topLevelTypeNames(programs)) {
|
|
25
|
+
const owner = owners.get(name);
|
|
26
|
+
if (owner === undefined) owners.set(name, pkgKey);
|
|
27
|
+
else if (owner !== pkgKey) renamed.set(name, `${name}$$${pkgName}`);
|
|
28
|
+
}
|
|
29
|
+
if (renamed.size > 0)
|
|
30
|
+
for (const p of programs)
|
|
31
|
+
for (const d of p.decls ?? [])
|
|
32
|
+
renameTypeRefs(d, renamed, new Set(), new Set());
|
|
33
|
+
return renamed;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Function-local types are scoped to their block, but the checker and both
|
|
37
|
+
// backends key types by bare name. A local type whose name another type of the
|
|
38
|
+
// package also uses (a package-level type or a local of another function)
|
|
39
|
+
// is renamed to `Name__L<n>` for its scope; `%T` and diagnostics drop the
|
|
40
|
+
// suffix (see `displayTypeName`). `reserved` holds type names other packages
|
|
41
|
+
// own: a local type spelled like one is renamed too, so it cannot clash with
|
|
42
|
+
// the imported type in the flat module. Returns whether anything was renamed.
|
|
43
|
+
let localTypeCounter = 0;
|
|
44
|
+
|
|
45
|
+
export function renameLocalTypes(programs, reserved = new Set()) {
|
|
46
|
+
const counts = new Map();
|
|
47
|
+
const count = (node) => {
|
|
48
|
+
if (!node || typeof node !== "object") return;
|
|
49
|
+
if (Array.isArray(node)) return node.forEach(count);
|
|
50
|
+
if (node.kind === "TypeDecl")
|
|
51
|
+
counts.set(node.name, (counts.get(node.name) ?? 0) + 1);
|
|
52
|
+
forEachChild(node, count);
|
|
53
|
+
};
|
|
54
|
+
for (const p of programs) count(p.decls);
|
|
55
|
+
const dup = new Set(
|
|
56
|
+
[...counts].filter(([k, n]) => n > 1 || reserved.has(k)).map(([k]) => k),
|
|
57
|
+
);
|
|
58
|
+
let changed = false;
|
|
59
|
+
// Inner scopes first, so an outer rename never touches a nested
|
|
60
|
+
// declaration that already has its own name. `visible` holds the value
|
|
61
|
+
// names declared so far in the enclosing scopes.
|
|
62
|
+
const scan = (node, visible) => {
|
|
63
|
+
if (!node || typeof node !== "object") return;
|
|
64
|
+
if (Array.isArray(node)) {
|
|
65
|
+
const seen = new Set(visible);
|
|
66
|
+
for (let i = 0; i < node.length; i++) {
|
|
67
|
+
const d = node[i];
|
|
68
|
+
scan(d, seen);
|
|
69
|
+
if (d?.kind === "TypeDecl") {
|
|
70
|
+
// A value of the same name would clash with the type in JS.
|
|
71
|
+
const stop = node.findIndex(
|
|
72
|
+
(s, j) => j > i && declaresValue(s, d.name),
|
|
73
|
+
);
|
|
74
|
+
if (dup.has(d.name) || seen.has(d.name) || stop >= 0) {
|
|
75
|
+
const renamed = new Map([
|
|
76
|
+
[d.name, `${d.name}__L${++localTypeCounter}`],
|
|
77
|
+
]);
|
|
78
|
+
const end = stop >= 0 ? stop : node.length - 1;
|
|
79
|
+
for (let j = i; j <= end; j++) {
|
|
80
|
+
// The shadowing declaration's own right-hand side and type
|
|
81
|
+
// are evaluated before its new value is in scope.
|
|
82
|
+
const target =
|
|
83
|
+
j === stop && node[j].kind === "DefineStmt"
|
|
84
|
+
? node[j].rhs
|
|
85
|
+
: node[j];
|
|
86
|
+
renameTypeRefs(target, renamed, new Set(), new Set());
|
|
87
|
+
}
|
|
88
|
+
changed = true;
|
|
89
|
+
}
|
|
90
|
+
} else if (d) for (const n of valueNames(d)) seen.add(n);
|
|
91
|
+
}
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const here = FUNC_KINDS.has(node.kind)
|
|
95
|
+
? new Set([...visible, ...declaredParams(node)])
|
|
96
|
+
: visible;
|
|
97
|
+
forEachChild(node, (c) => scan(c, here));
|
|
98
|
+
};
|
|
99
|
+
for (const p of programs)
|
|
100
|
+
for (const d of p.decls ?? [])
|
|
101
|
+
if (d.kind !== "TypeDecl") scan(d, new Set());
|
|
102
|
+
return changed;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const VALUE_DECL_KINDS = new Set(["DefineStmt", "VarDecl", "ConstDecl"]);
|
|
106
|
+
|
|
107
|
+
// Names a statement declares as values in its own scope.
|
|
108
|
+
function valueNames(stmt) {
|
|
109
|
+
return VALUE_DECL_KINDS.has(stmt.kind) ? declaredNames(stmt) : [];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function declaresValue(stmt, name) {
|
|
113
|
+
return valueNames(stmt).includes(name);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function declaredParams(fn) {
|
|
117
|
+
const names = (fn.params ?? []).map((p) => p.name).filter(Boolean);
|
|
118
|
+
if (fn.recvName) names.push(fn.recvName);
|
|
119
|
+
return names;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// A package compiled later that declares a type this package only declares
|
|
123
|
+
// inside a function is renamed by `mangleCollidingTypes`, so `Pt` of one package
|
|
124
|
+
// and a local `Pt` of another never meet in the flat module.
|
|
125
|
+
export function registerLocalTypeOwners(programs, pkgKey, owners) {
|
|
126
|
+
if (!owners) return;
|
|
127
|
+
const visit = (node) => {
|
|
128
|
+
if (!node || typeof node !== "object") return;
|
|
129
|
+
if (Array.isArray(node)) return node.forEach(visit);
|
|
130
|
+
if (node.kind === "TypeDecl" && !owners.has(node.name))
|
|
131
|
+
owners.set(node.name, pkgKey);
|
|
132
|
+
forEachChild(node, visit);
|
|
133
|
+
};
|
|
134
|
+
for (const p of programs) visit(p.decls);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// `tparams` are type parameters in scope; `values` are names a function
|
|
138
|
+
// declares as parameters or locals, which shadow a same-spelled bare `Ident`.
|
|
139
|
+
function renameTypeRefs(node, renamed, tparams, values) {
|
|
140
|
+
if (!node || typeof node !== "object") return;
|
|
141
|
+
if (Array.isArray(node)) {
|
|
142
|
+
for (const n of node) renameTypeRefs(n, renamed, tparams, values);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const tp = withTypeParams(node, tparams);
|
|
146
|
+
const vals = FUNC_KINDS.has(node.kind) ? withLocals(node, values) : values;
|
|
147
|
+
if (isTypeRef(node, vals) && renamed.has(node.name) && !tp.has(node.name))
|
|
148
|
+
node.name = renamed.get(node.name);
|
|
149
|
+
forEachChild(node, (child) => {
|
|
150
|
+
if (node.kind === "KeyValueExpr" && child === node.key) return;
|
|
151
|
+
renameTypeRefs(child, renamed, tp, vals);
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const FUNC_KINDS = new Set(["FuncDecl", "MethodDecl", "FuncLit"]);
|
|
156
|
+
|
|
157
|
+
// Adds every parameter and local name declared anywhere inside a function.
|
|
158
|
+
function withLocals(fn, values) {
|
|
159
|
+
const out = new Set(values);
|
|
160
|
+
const visit = (node) => {
|
|
161
|
+
if (!node || typeof node !== "object") return;
|
|
162
|
+
if (Array.isArray(node)) return node.forEach(visit);
|
|
163
|
+
for (const n of declaredNames(node)) out.add(n);
|
|
164
|
+
forEachChild(node, visit);
|
|
165
|
+
};
|
|
166
|
+
visit(fn);
|
|
167
|
+
return out;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function declaredNames(node) {
|
|
171
|
+
const names = [];
|
|
172
|
+
if (node.kind === "DefineStmt")
|
|
173
|
+
for (const l of node.lhs) if (l.kind === "Ident") names.push(l.name);
|
|
174
|
+
if (node.kind === "VarDecl" || node.kind === "ConstDecl")
|
|
175
|
+
for (const d of node.decls ?? []) names.push(...(d.names ?? []));
|
|
176
|
+
if (node.kind === "TypeSwitchStmt" && node.assign) names.push(node.assign);
|
|
177
|
+
for (const p of node.params ?? []) if (p.name) names.push(p.name);
|
|
178
|
+
if (node.recvName) names.push(node.recvName);
|
|
179
|
+
return names;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function withTypeParams(node, tparams) {
|
|
183
|
+
if (!(node.typeParams?.length > 0)) return tparams;
|
|
184
|
+
return new Set([...tparams, ...node.typeParams.map((t) => t.name)]);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const TYPE_REF_KINDS = new Set([
|
|
188
|
+
"TypeDecl",
|
|
189
|
+
"TypeName",
|
|
190
|
+
"GenericTypeName",
|
|
191
|
+
"Ident",
|
|
192
|
+
]);
|
|
193
|
+
|
|
194
|
+
function isTypeRef(node, values) {
|
|
195
|
+
if (node.kind === "Ident") return !values.has(node.name);
|
|
196
|
+
return TYPE_REF_KINDS.has(node.kind);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Importers name a renamed type by its declared name (`b.Pt`).
|
|
200
|
+
export function withOriginalTypeNames(types, renamed) {
|
|
201
|
+
for (const [name, mangled] of renamed)
|
|
202
|
+
if (types.has(mangled)) types.set(name, types.get(mangled));
|
|
203
|
+
return types;
|
|
204
|
+
}
|
package/src/link/wasm.js
CHANGED
|
@@ -51,7 +51,12 @@ export function registerPackageUnit(options, unit) {
|
|
|
51
51
|
|
|
52
52
|
function linkWasmUnits(
|
|
53
53
|
units,
|
|
54
|
-
{
|
|
54
|
+
{
|
|
55
|
+
emitWat = false,
|
|
56
|
+
rootTarget = "js",
|
|
57
|
+
sourceMappingURL = null,
|
|
58
|
+
natives = true,
|
|
59
|
+
} = {},
|
|
55
60
|
) {
|
|
56
61
|
if (!units.some((u) => u.target === "wasm")) return null;
|
|
57
62
|
checkLinkCollisions(units);
|
|
@@ -60,6 +65,14 @@ function linkWasmUnits(
|
|
|
60
65
|
const bundledPackages = new Set();
|
|
61
66
|
const programs = [];
|
|
62
67
|
const stdlibPrograms = [];
|
|
68
|
+
// Check the stdlib bodies the linked code reaches before the ASTs are
|
|
69
|
+
// copied; lazily checked bodies are annotated and mangled in place.
|
|
70
|
+
reachableStdlibDecls(
|
|
71
|
+
units
|
|
72
|
+
.filter((u) => !u.key.startsWith("stdlib:"))
|
|
73
|
+
.flatMap((u) => u.programs),
|
|
74
|
+
units.filter((u) => u.key.startsWith("stdlib:")).flatMap((u) => u.programs),
|
|
75
|
+
);
|
|
63
76
|
for (const u of units) {
|
|
64
77
|
for (const b of u.bundledPackages) bundledPackages.add(b);
|
|
65
78
|
const copies = u.programs.map((p) => cloneAstForWasm(p));
|
|
@@ -89,6 +102,7 @@ function linkWasmUnits(
|
|
|
89
102
|
return compileWasmModule(programs, checker, lowerRes, {
|
|
90
103
|
boundary: true,
|
|
91
104
|
bundledPackages,
|
|
105
|
+
natives,
|
|
92
106
|
emitWat,
|
|
93
107
|
callMain: rootTarget === "wasm",
|
|
94
108
|
sourceMappingURL,
|
|
@@ -239,10 +253,16 @@ export function linkIntoBundle(
|
|
|
239
253
|
pkgTarget,
|
|
240
254
|
{ sourceMappingURL } = {},
|
|
241
255
|
) {
|
|
242
|
-
const none = {
|
|
256
|
+
const none = {
|
|
257
|
+
wasm: null,
|
|
258
|
+
wat: null,
|
|
259
|
+
sourceMapInfo: null,
|
|
260
|
+
stringTable: null,
|
|
261
|
+
};
|
|
243
262
|
if (options.isDependency) return none;
|
|
244
263
|
const linked = linkWasmUnits(options.wasmUnits, {
|
|
245
264
|
emitWat: options.emitWat,
|
|
265
|
+
natives: options.natives,
|
|
246
266
|
rootTarget: pkgTarget,
|
|
247
267
|
sourceMappingURL,
|
|
248
268
|
});
|
|
@@ -252,6 +272,7 @@ export function linkIntoBundle(
|
|
|
252
272
|
wasm: linked.wasm,
|
|
253
273
|
wat: linked.wat ?? null,
|
|
254
274
|
sourceMapInfo: linked.sourceMapInfo ?? null,
|
|
275
|
+
stringTable: linked.stringTable ?? null,
|
|
255
276
|
};
|
|
256
277
|
}
|
|
257
278
|
|
|
@@ -266,6 +287,9 @@ export function lowerStdlibRefs(node) {
|
|
|
266
287
|
for (const n of node) lowerStdlibRefs(n);
|
|
267
288
|
return;
|
|
268
289
|
}
|
|
290
|
+
// A generated entry replaces the stdlib declaration the checker resolved
|
|
291
|
+
// (errors.As) the call to.
|
|
292
|
+
if (node._genEntry && node._stdName) delete node._stdName;
|
|
269
293
|
if (node._stdName) {
|
|
270
294
|
if (node.kind === "SelectorExpr") {
|
|
271
295
|
delete node.expr;
|
|
@@ -276,19 +300,19 @@ export function lowerStdlibRefs(node) {
|
|
|
276
300
|
delete node._stdName;
|
|
277
301
|
}
|
|
278
302
|
if (node.kind === "CallExpr" && node.func?._jsonStream) {
|
|
279
|
-
const entry = node.func.
|
|
303
|
+
const entry = node.func._genEntry;
|
|
280
304
|
node.args.unshift(node.func.expr);
|
|
281
305
|
delete node.func.expr;
|
|
282
306
|
delete node.func.field;
|
|
283
307
|
delete node.func._jsonStream;
|
|
284
|
-
delete node.func.
|
|
308
|
+
delete node.func._genEntry;
|
|
285
309
|
node.func.kind = "Ident";
|
|
286
310
|
node.func.name = entry;
|
|
287
|
-
} else if (node.
|
|
311
|
+
} else if (node._genEntry && node.kind === "SelectorExpr") {
|
|
288
312
|
delete node.expr;
|
|
289
313
|
delete node.field;
|
|
290
314
|
node.kind = "Ident";
|
|
291
|
-
node.name = node.
|
|
315
|
+
node.name = node._genEntry;
|
|
292
316
|
}
|
|
293
317
|
forEachChild(node, lowerStdlibRefs);
|
|
294
318
|
}
|
package/src/lower/bounds.js
CHANGED
|
@@ -385,6 +385,159 @@ function paramNames(decl) {
|
|
|
385
385
|
return names;
|
|
386
386
|
}
|
|
387
387
|
|
|
388
|
+
// ── Nil-check elision ───────────────────────────────────────────
|
|
389
|
+
//
|
|
390
|
+
// A pointer parameter or receiver that is declared once and never assigned
|
|
391
|
+
// or address-taken stays the same pointer for the whole function. Strict mode
|
|
392
|
+
// checks it for nil at a field access (`__nilcall`); marking later accesses
|
|
393
|
+
// `_nonNil` drops the repeats, and a receiver of a method that never compares
|
|
394
|
+
// itself to nil cannot be nil at all (a nil receiver fails at the call).
|
|
395
|
+
|
|
396
|
+
function isPointerIdent(node, names) {
|
|
397
|
+
return (
|
|
398
|
+
isIdent(node) &&
|
|
399
|
+
names.has(node.name) &&
|
|
400
|
+
underlying(node._type)?.kind === "pointer"
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function derefBase(node) {
|
|
405
|
+
if (node.kind === "SelectorExpr") return node.expr;
|
|
406
|
+
if (node.kind === "UnaryExpr" && node.op === "*") return node.operand;
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Names whose address is taken anywhere under `body`.
|
|
411
|
+
function addressTakenNames(body) {
|
|
412
|
+
const taken = new Set();
|
|
413
|
+
const visit = (node) => {
|
|
414
|
+
if (!node || typeof node !== "object") return;
|
|
415
|
+
if (Array.isArray(node)) {
|
|
416
|
+
for (const n of node) visit(n);
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
if (node.kind === "UnaryExpr" && node.op === "&" && isIdent(node.operand))
|
|
420
|
+
taken.add(node.operand.name);
|
|
421
|
+
forEachChild(node, visit);
|
|
422
|
+
};
|
|
423
|
+
visit(body);
|
|
424
|
+
return taken;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
function hasLabelOrGoto(body) {
|
|
428
|
+
let hit = false;
|
|
429
|
+
const visit = (node) => {
|
|
430
|
+
if (hit || !node || typeof node !== "object") return;
|
|
431
|
+
if (Array.isArray(node)) {
|
|
432
|
+
for (const n of node) visit(n);
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
if (
|
|
436
|
+
node.kind === "LabeledStmt" ||
|
|
437
|
+
(node.kind === "BranchStmt" && node.tok === "goto")
|
|
438
|
+
)
|
|
439
|
+
hit = true;
|
|
440
|
+
forEachChild(node, visit);
|
|
441
|
+
};
|
|
442
|
+
visit(body);
|
|
443
|
+
return hit;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// The children of `node` that run whenever `node` runs, or null when all do.
|
|
447
|
+
function alwaysRunChildren(node) {
|
|
448
|
+
switch (node.kind) {
|
|
449
|
+
case "FuncLit":
|
|
450
|
+
case "DeferStmt":
|
|
451
|
+
case "GoStmt":
|
|
452
|
+
return [];
|
|
453
|
+
case "IfStmt":
|
|
454
|
+
return [node.init, node.cond];
|
|
455
|
+
case "ForStmt":
|
|
456
|
+
return [node.init];
|
|
457
|
+
case "SwitchStmt":
|
|
458
|
+
case "TypeSwitchStmt":
|
|
459
|
+
return [node.init, node.tag];
|
|
460
|
+
case "BinaryExpr":
|
|
461
|
+
return node.op === "&&" || node.op === "||" ? [node.left] : null;
|
|
462
|
+
default:
|
|
463
|
+
return null;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Marks derefs of `names` under `root` that run whenever `root` runs and
|
|
468
|
+
// follow the first one per name (`seen`) or are in `always`.
|
|
469
|
+
function markRepeats(root, names, seen, always) {
|
|
470
|
+
const visit = (node) => {
|
|
471
|
+
if (!node || typeof node !== "object") return;
|
|
472
|
+
if (Array.isArray(node)) {
|
|
473
|
+
for (const n of node) visit(n);
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
const kids = alwaysRunChildren(node);
|
|
477
|
+
if (kids) {
|
|
478
|
+
for (const k of kids) visit(k);
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
const base = derefBase(node);
|
|
482
|
+
if (base && isPointerIdent(base, names)) {
|
|
483
|
+
if (seen.has(base.name) || always.has(base.name)) node._nonNil = true;
|
|
484
|
+
else seen.add(base.name);
|
|
485
|
+
}
|
|
486
|
+
forEachChild(node, visit);
|
|
487
|
+
};
|
|
488
|
+
visit(root);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// Marks every deref of `names` under `root` (receivers that cannot be nil).
|
|
492
|
+
function markAll(root, names) {
|
|
493
|
+
const visit = (node) => {
|
|
494
|
+
if (!node || typeof node !== "object") return;
|
|
495
|
+
if (Array.isArray(node)) {
|
|
496
|
+
for (const n of node) visit(n);
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
const base = derefBase(node);
|
|
500
|
+
if (base && isPointerIdent(base, names)) node._nonNil = true;
|
|
501
|
+
forEachChild(node, visit);
|
|
502
|
+
};
|
|
503
|
+
visit(root);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
function markNilFunc(decl, nilSafeRecv) {
|
|
507
|
+
const body = decl.body;
|
|
508
|
+
const params = paramNames(decl);
|
|
509
|
+
const stats = nameStats(params, body);
|
|
510
|
+
const taken = addressTakenNames(body);
|
|
511
|
+
const stable = (n) =>
|
|
512
|
+
n && stats.declared.get(n) === 1 && !stats.assigned.has(n) && !taken.has(n);
|
|
513
|
+
const always = new Set();
|
|
514
|
+
if (decl.kind === "MethodDecl" && decl.recvPointer && !nilSafeRecv(decl))
|
|
515
|
+
if (stable(decl.recvName)) always.add(decl.recvName);
|
|
516
|
+
const candidates = new Set(
|
|
517
|
+
(decl.params ?? []).map((p) => p.name).filter(stable),
|
|
518
|
+
);
|
|
519
|
+
for (const n of always) candidates.add(n);
|
|
520
|
+
if (candidates.size === 0) return;
|
|
521
|
+
if (always.size > 0) markAll(body, always);
|
|
522
|
+
if (hasLabelOrGoto(body)) return;
|
|
523
|
+
const seen = new Set();
|
|
524
|
+
for (const stmt of body.stmts ?? [])
|
|
525
|
+
markRepeats(stmt, candidates, seen, always);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Marks pointer derefs that need no nil check in strict mode: those of a
|
|
530
|
+
* receiver that cannot be nil and repeats after the first unconditional
|
|
531
|
+
* deref of an unchanged pointer parameter. `nilSafeRecv(decl)` tells
|
|
532
|
+
* whether a method accepts a nil receiver.
|
|
533
|
+
*/
|
|
534
|
+
export function markRedundantNilChecks(program, nilSafeRecv) {
|
|
535
|
+
for (const d of program?.decls ?? []) {
|
|
536
|
+
if ((d.kind === "FuncDecl" || d.kind === "MethodDecl") && d.body)
|
|
537
|
+
markNilFunc(d, nilSafeRecv);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
388
541
|
/** Marks provably in-range IndexExprs `_inBounds` in every function. */
|
|
389
542
|
export function markInBoundsIndexes(program) {
|
|
390
543
|
for (const d of program?.decls ?? []) {
|
package/src/lower/captures.js
CHANGED
|
@@ -13,6 +13,9 @@ export class CaptureAnalysis {
|
|
|
13
13
|
constructor() {
|
|
14
14
|
// Map<FuncLit, Set<string>>: free variables captured by each closure
|
|
15
15
|
this.capturesByClosure = new Map();
|
|
16
|
+
// Map<FuncLit, Map<string, Type>>: the type of each captured variable,
|
|
17
|
+
// read off the closure's first reference to it.
|
|
18
|
+
this.captureTypes = new Map();
|
|
16
19
|
// Set<string>: variables in the enclosing scope that are captured AND mutated
|
|
17
20
|
this.mutatedCaptures = new Set();
|
|
18
21
|
}
|
|
@@ -48,48 +51,94 @@ export function collectDeclaredNames(node, names = new Set()) {
|
|
|
48
51
|
}
|
|
49
52
|
|
|
50
53
|
/**
|
|
51
|
-
* Finds
|
|
54
|
+
* Finds the identifiers a function literal body reads from outside itself. A
|
|
55
|
+
* name is local only from its declaration onwards and only inside its block,
|
|
56
|
+
* so `m := f(m)` reads the enclosing `m`, and `{ m := 1 }; use(m)` reads it
|
|
57
|
+
* too. `scopes` is the stack of name sets visible inside the literal;
|
|
58
|
+
* `types` receives the type of the first reference to each free name.
|
|
52
59
|
*/
|
|
53
|
-
function collectReferencedNames(node,
|
|
60
|
+
function collectReferencedNames(node, scopes, refs = new Set(), types) {
|
|
54
61
|
if (!node || typeof node !== "object") return refs;
|
|
55
62
|
if (Array.isArray(node)) {
|
|
56
|
-
for (const item of node) collectReferencedNames(item,
|
|
57
|
-
return refs;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// In struct literals, field names in KeyValueExpr are not variable references
|
|
61
|
-
if (node.kind === "KeyValueExpr") {
|
|
62
|
-
collectReferencedNames(node.value, localDecls, refs);
|
|
63
|
+
for (const item of node) collectReferencedNames(item, scopes, refs, types);
|
|
63
64
|
return refs;
|
|
64
65
|
}
|
|
66
|
+
const walk = (child) => collectReferencedNames(child, scopes, refs, types);
|
|
67
|
+
const scoped = (fn) => {
|
|
68
|
+
scopes.push(new Set());
|
|
69
|
+
fn();
|
|
70
|
+
scopes.pop();
|
|
71
|
+
};
|
|
72
|
+
const declare = (name) => {
|
|
73
|
+
if (name && name !== "_") scopes[scopes.length - 1].add(name);
|
|
74
|
+
};
|
|
65
75
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
76
|
+
switch (node.kind) {
|
|
77
|
+
// In struct literals, field names in KeyValueExpr are not variable references
|
|
78
|
+
case "KeyValueExpr":
|
|
79
|
+
walk(node.value);
|
|
80
|
+
return refs;
|
|
81
|
+
// Map keys are expressions that may read captured variables; struct
|
|
82
|
+
// field names are not references.
|
|
83
|
+
case "CompositeLit": {
|
|
84
|
+
const t = node._type?.underlying ?? node._type;
|
|
85
|
+
if (t && t.kind !== "struct") {
|
|
86
|
+
for (const el of node.elems ?? []) {
|
|
87
|
+
if (el?.kind === "KeyValueExpr") walk(el.key);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
break;
|
|
73
91
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
92
|
+
case "FuncLit":
|
|
93
|
+
scoped(() => {
|
|
94
|
+
for (const p of node.params ?? []) declare(p.name);
|
|
95
|
+
for (const r of node.returnType?._namedReturns ?? []) declare(r.name);
|
|
96
|
+
forEachChild(node, walk);
|
|
97
|
+
});
|
|
98
|
+
return refs;
|
|
99
|
+
case "DefineStmt":
|
|
100
|
+
walk(node.rhs);
|
|
101
|
+
for (const l of node.lhs ?? []) {
|
|
102
|
+
if (l.kind === "Ident") declare(l.name);
|
|
103
|
+
else walk(l);
|
|
104
|
+
}
|
|
105
|
+
return refs;
|
|
106
|
+
case "VarDecl":
|
|
107
|
+
case "ConstDecl":
|
|
108
|
+
for (const spec of node.decls ?? []) {
|
|
109
|
+
walk(spec.value ?? spec.init);
|
|
110
|
+
for (const name of spec.names ?? []) declare(name);
|
|
111
|
+
}
|
|
112
|
+
return refs;
|
|
113
|
+
case "TypeSwitchStmt":
|
|
114
|
+
scoped(() => {
|
|
115
|
+
walk(node.expr);
|
|
116
|
+
for (const c of node.cases ?? []) {
|
|
117
|
+
scoped(() => {
|
|
118
|
+
declare(node.assign);
|
|
119
|
+
walk(c.stmts);
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
return refs;
|
|
124
|
+
case "Block":
|
|
125
|
+
case "CaseClause":
|
|
126
|
+
case "IfStmt":
|
|
127
|
+
case "ForStmt":
|
|
128
|
+
case "SwitchStmt":
|
|
129
|
+
scoped(() => forEachChild(node, walk));
|
|
130
|
+
return refs;
|
|
131
|
+
case "Ident":
|
|
132
|
+
if (node.name !== "_" && !scopes.some((sc) => sc.has(node.name))) {
|
|
133
|
+
refs.add(node.name);
|
|
134
|
+
if (node._type && types && !types.has(node.name))
|
|
135
|
+
types.set(node.name, node._type);
|
|
136
|
+
}
|
|
137
|
+
return refs;
|
|
87
138
|
}
|
|
88
139
|
|
|
89
140
|
// For nested closures, free variables are also referenced
|
|
90
|
-
forEachChild(node,
|
|
91
|
-
collectReferencedNames(child, localDecls, refs),
|
|
92
|
-
);
|
|
141
|
+
forEachChild(node, walk);
|
|
93
142
|
return refs;
|
|
94
143
|
}
|
|
95
144
|
|
|
@@ -226,6 +275,7 @@ export function analyzeCaptures(fnDecl) {
|
|
|
226
275
|
for (const r of node.returnType?._namedReturns ?? []) {
|
|
227
276
|
if (r.name && r.name !== "_") closureLocals.add(r.name);
|
|
228
277
|
}
|
|
278
|
+
const paramScope = new Set(closureLocals);
|
|
229
279
|
collectDeclaredNames(node.body, closureLocals);
|
|
230
280
|
|
|
231
281
|
const allOuterScope = new Set();
|
|
@@ -233,14 +283,23 @@ export function analyzeCaptures(fnDecl) {
|
|
|
233
283
|
for (const name of scope) allOuterScope.add(name);
|
|
234
284
|
}
|
|
235
285
|
|
|
236
|
-
const
|
|
286
|
+
const refTypes = new Map();
|
|
287
|
+
const allRefs = collectReferencedNames(
|
|
288
|
+
node.body,
|
|
289
|
+
[paramScope],
|
|
290
|
+
new Set(),
|
|
291
|
+
refTypes,
|
|
292
|
+
);
|
|
237
293
|
const captured = new Set();
|
|
294
|
+
const capturedTypes = new Map();
|
|
238
295
|
for (const ref of allRefs) {
|
|
239
296
|
if (allOuterScope.has(ref)) {
|
|
240
297
|
captured.add(ref);
|
|
298
|
+
if (refTypes.has(ref)) capturedTypes.set(ref, refTypes.get(ref));
|
|
241
299
|
}
|
|
242
300
|
}
|
|
243
301
|
result.capturesByClosure.set(node, captured);
|
|
302
|
+
result.captureTypes.set(node, capturedTypes);
|
|
244
303
|
|
|
245
304
|
for (const varName of captured) {
|
|
246
305
|
if (mutatedVars.has(varName)) {
|