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,148 @@
|
|
|
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`). Returns whether anything was renamed.
|
|
41
|
+
let localTypeCounter = 0;
|
|
42
|
+
|
|
43
|
+
export function renameLocalTypes(programs) {
|
|
44
|
+
const counts = new Map();
|
|
45
|
+
const count = (node) => {
|
|
46
|
+
if (!node || typeof node !== "object") return;
|
|
47
|
+
if (Array.isArray(node)) return node.forEach(count);
|
|
48
|
+
if (node.kind === "TypeDecl")
|
|
49
|
+
counts.set(node.name, (counts.get(node.name) ?? 0) + 1);
|
|
50
|
+
forEachChild(node, count);
|
|
51
|
+
};
|
|
52
|
+
for (const p of programs) count(p.decls);
|
|
53
|
+
const dup = new Set([...counts].filter(([, n]) => n > 1).map(([k]) => k));
|
|
54
|
+
if (dup.size === 0) return false;
|
|
55
|
+
let changed = false;
|
|
56
|
+
// Inner scopes first, so an outer rename never touches a nested
|
|
57
|
+
// declaration that already has its own name.
|
|
58
|
+
const scan = (node) => {
|
|
59
|
+
if (!node || typeof node !== "object") return;
|
|
60
|
+
if (Array.isArray(node)) {
|
|
61
|
+
node.forEach(scan);
|
|
62
|
+
for (let i = 0; i < node.length; i++) {
|
|
63
|
+
const d = node[i];
|
|
64
|
+
if (d?.kind !== "TypeDecl" || !dup.has(d.name)) continue;
|
|
65
|
+
const renamed = new Map([
|
|
66
|
+
[d.name, `${d.name}__L${++localTypeCounter}`],
|
|
67
|
+
]);
|
|
68
|
+
for (let j = i; j < node.length; j++)
|
|
69
|
+
renameTypeRefs(node[j], renamed, new Set(), new Set());
|
|
70
|
+
changed = true;
|
|
71
|
+
}
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
forEachChild(node, scan);
|
|
75
|
+
};
|
|
76
|
+
for (const p of programs)
|
|
77
|
+
for (const d of p.decls ?? []) if (d.kind !== "TypeDecl") scan(d);
|
|
78
|
+
return changed;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// `tparams` are type parameters in scope; `values` are names a function
|
|
82
|
+
// declares as parameters or locals, which shadow a same-spelled bare `Ident`.
|
|
83
|
+
function renameTypeRefs(node, renamed, tparams, values) {
|
|
84
|
+
if (!node || typeof node !== "object") return;
|
|
85
|
+
if (Array.isArray(node)) {
|
|
86
|
+
for (const n of node) renameTypeRefs(n, renamed, tparams, values);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const tp = withTypeParams(node, tparams);
|
|
90
|
+
const vals = FUNC_KINDS.has(node.kind) ? withLocals(node, values) : values;
|
|
91
|
+
if (isTypeRef(node, vals) && renamed.has(node.name) && !tp.has(node.name))
|
|
92
|
+
node.name = renamed.get(node.name);
|
|
93
|
+
forEachChild(node, (child) => {
|
|
94
|
+
if (node.kind === "KeyValueExpr" && child === node.key) return;
|
|
95
|
+
renameTypeRefs(child, renamed, tp, vals);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const FUNC_KINDS = new Set(["FuncDecl", "MethodDecl", "FuncLit"]);
|
|
100
|
+
|
|
101
|
+
// Adds every parameter and local name declared anywhere inside a function.
|
|
102
|
+
function withLocals(fn, values) {
|
|
103
|
+
const out = new Set(values);
|
|
104
|
+
const visit = (node) => {
|
|
105
|
+
if (!node || typeof node !== "object") return;
|
|
106
|
+
if (Array.isArray(node)) return node.forEach(visit);
|
|
107
|
+
for (const n of declaredNames(node)) out.add(n);
|
|
108
|
+
forEachChild(node, visit);
|
|
109
|
+
};
|
|
110
|
+
visit(fn);
|
|
111
|
+
return out;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function declaredNames(node) {
|
|
115
|
+
const names = [];
|
|
116
|
+
if (node.kind === "DefineStmt")
|
|
117
|
+
for (const l of node.lhs) if (l.kind === "Ident") names.push(l.name);
|
|
118
|
+
if (node.kind === "VarDecl" || node.kind === "ConstDecl")
|
|
119
|
+
for (const d of node.decls ?? []) names.push(...(d.names ?? []));
|
|
120
|
+
if (node.kind === "TypeSwitchStmt" && node.assign) names.push(node.assign);
|
|
121
|
+
for (const p of node.params ?? []) if (p.name) names.push(p.name);
|
|
122
|
+
if (node.recvName) names.push(node.recvName);
|
|
123
|
+
return names;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function withTypeParams(node, tparams) {
|
|
127
|
+
if (!(node.typeParams?.length > 0)) return tparams;
|
|
128
|
+
return new Set([...tparams, ...node.typeParams.map((t) => t.name)]);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const TYPE_REF_KINDS = new Set([
|
|
132
|
+
"TypeDecl",
|
|
133
|
+
"TypeName",
|
|
134
|
+
"GenericTypeName",
|
|
135
|
+
"Ident",
|
|
136
|
+
]);
|
|
137
|
+
|
|
138
|
+
function isTypeRef(node, values) {
|
|
139
|
+
if (node.kind === "Ident") return !values.has(node.name);
|
|
140
|
+
return TYPE_REF_KINDS.has(node.kind);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Importers name a renamed type by its declared name (`b.Pt`).
|
|
144
|
+
export function withOriginalTypeNames(types, renamed) {
|
|
145
|
+
for (const [name, mangled] of renamed)
|
|
146
|
+
if (types.has(mangled)) types.set(name, types.get(mangled));
|
|
147
|
+
return types;
|
|
148
|
+
}
|
package/src/link/wasm.js
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
// Linking of `wasm`/`both` package units into one `app.wasm`: unit
|
|
2
|
+
// registration, collision checks, link-copy AST rewrites and the facade splice.
|
|
3
|
+
|
|
4
|
+
import { compileWasmModule } from "../backend/wasm/index.js";
|
|
5
|
+
import { lower } from "../lower/index.js";
|
|
6
|
+
import { reachableStdlibDecls } from "../lower/reachability.js";
|
|
7
|
+
import { forEachChild } from "../lower/walk.js";
|
|
8
|
+
import { TypeCheckError, TypeChecker } from "../typechecker/index.js";
|
|
9
|
+
|
|
10
|
+
// ── WASM linking ──────────────────────────────────────────────
|
|
11
|
+
//
|
|
12
|
+
// Every `wasm`/`both` package reached during a build registers a unit in the
|
|
13
|
+
// shared `options.wasmUnits` array. The root compile links all units into a
|
|
14
|
+
// single `app.wasm` and splices the generated JS facade into the bundle where
|
|
15
|
+
// the first wasm package's JS would have been.
|
|
16
|
+
|
|
17
|
+
const WASM_MARKER_RE = /^\/\*__GOFRONT_WASM_UNIT:[^*]*\*\/$/m;
|
|
18
|
+
|
|
19
|
+
export function wasmMarker(pkgName) {
|
|
20
|
+
return `/*__GOFRONT_WASM_UNIT:${pkgName}*/`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function registerWasmUnit(options, unit) {
|
|
24
|
+
const units = options.wasmUnits;
|
|
25
|
+
if (!units) return;
|
|
26
|
+
if (units.some((u) => u.key === unit.key)) return;
|
|
27
|
+
units.push(unit);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Registers a `wasm`/`both` package as a link unit and returns the target the
|
|
31
|
+
// package's own code is emitted for. `options.wasmTests` runs a `both`
|
|
32
|
+
// package's tests through the wasm backend (the package is then treated as
|
|
33
|
+
// `wasm` for linking so its test functions are exposed by the facade).
|
|
34
|
+
export function registerPackageUnit(options, unit) {
|
|
35
|
+
const { pkgTarget, programs } = unit;
|
|
36
|
+
if (pkgTarget !== "wasm" && pkgTarget !== "both") return pkgTarget;
|
|
37
|
+
const emitTarget =
|
|
38
|
+
options.wasmTests && options.isTest && pkgTarget === "both"
|
|
39
|
+
? "wasm"
|
|
40
|
+
: pkgTarget;
|
|
41
|
+
registerWasmUnit(options, {
|
|
42
|
+
...unit,
|
|
43
|
+
target: emitTarget,
|
|
44
|
+
programs:
|
|
45
|
+
emitTarget === pkgTarget
|
|
46
|
+
? programs
|
|
47
|
+
: programs.map((p) => ({ ...p, target: emitTarget })),
|
|
48
|
+
});
|
|
49
|
+
return emitTarget;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function linkWasmUnits(
|
|
53
|
+
units,
|
|
54
|
+
{
|
|
55
|
+
emitWat = false,
|
|
56
|
+
rootTarget = "js",
|
|
57
|
+
sourceMappingURL = null,
|
|
58
|
+
natives = true,
|
|
59
|
+
} = {},
|
|
60
|
+
) {
|
|
61
|
+
if (!units.some((u) => u.target === "wasm")) return null;
|
|
62
|
+
checkLinkCollisions(units);
|
|
63
|
+
|
|
64
|
+
const types = new Map();
|
|
65
|
+
const bundledPackages = new Set();
|
|
66
|
+
const programs = [];
|
|
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
|
+
);
|
|
76
|
+
for (const u of units) {
|
|
77
|
+
for (const b of u.bundledPackages) bundledPackages.add(b);
|
|
78
|
+
const copies = u.programs.map((p) => cloneAstForWasm(p));
|
|
79
|
+
programs.push(...copies);
|
|
80
|
+
if (u.key.startsWith("stdlib:")) stdlibPrograms.push(...copies);
|
|
81
|
+
}
|
|
82
|
+
lowerStdlibRefs(programs);
|
|
83
|
+
dequalifyBundledRefs(programs, bundledPackages);
|
|
84
|
+
const keepType = pruneStdlibPrograms(programs, stdlibPrograms);
|
|
85
|
+
for (const u of units)
|
|
86
|
+
for (const [k, v] of u.types) if (keepType(v)) types.set(k, v);
|
|
87
|
+
const checker = Object.assign(new TypeChecker(), {
|
|
88
|
+
types,
|
|
89
|
+
errors: [],
|
|
90
|
+
err(msg, node) {
|
|
91
|
+
const e = new TypeCheckError(
|
|
92
|
+
msg,
|
|
93
|
+
node,
|
|
94
|
+
this._currentFile,
|
|
95
|
+
this._currentSource,
|
|
96
|
+
);
|
|
97
|
+
this.errors.push(e);
|
|
98
|
+
return e;
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
const lowerRes = lower(programs, checker);
|
|
102
|
+
return compileWasmModule(programs, checker, lowerRes, {
|
|
103
|
+
boundary: true,
|
|
104
|
+
bundledPackages,
|
|
105
|
+
natives,
|
|
106
|
+
emitWat,
|
|
107
|
+
callMain: rootTarget === "wasm",
|
|
108
|
+
sourceMappingURL,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// The WASM pipeline rewrites ASTs in place (package dequalification,
|
|
113
|
+
// monomorphisation, variadic desugaring). Parsed ASTs are cached across
|
|
114
|
+
// builds and shared with JS codegen, so the link works on a structural copy;
|
|
115
|
+
// side tables (`_type`, …) are shared by reference.
|
|
116
|
+
function cloneAstForWasm(node, seen = new Map()) {
|
|
117
|
+
if (!node || typeof node !== "object") return node;
|
|
118
|
+
const prev = seen.get(node);
|
|
119
|
+
if (prev) return prev;
|
|
120
|
+
const copy = Array.isArray(node) ? [] : {};
|
|
121
|
+
seen.set(node, copy);
|
|
122
|
+
if (Array.isArray(node)) {
|
|
123
|
+
for (const n of node) copy.push(cloneAstForWasm(n, seen));
|
|
124
|
+
return copy;
|
|
125
|
+
}
|
|
126
|
+
for (const [k, v] of Object.entries(node)) {
|
|
127
|
+
copy[k] = k.charCodeAt(0) === 95 ? v : cloneAstForWasm(v, seen);
|
|
128
|
+
}
|
|
129
|
+
return copy;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Drops unreachable Go-source stdlib declarations from the link copy and
|
|
133
|
+
// returns a filter that drops their (mangled) named types.
|
|
134
|
+
function pruneStdlibPrograms(programs, stdlibPrograms) {
|
|
135
|
+
if (stdlibPrograms.length === 0) return () => true;
|
|
136
|
+
const std = new Set(stdlibPrograms);
|
|
137
|
+
const reached = reachableStdlibDecls(
|
|
138
|
+
programs.filter((p) => !std.has(p)),
|
|
139
|
+
stdlibPrograms,
|
|
140
|
+
);
|
|
141
|
+
const types = new Set();
|
|
142
|
+
for (const p of stdlibPrograms) {
|
|
143
|
+
p.decls = p.decls.filter((d) => reached.has(d));
|
|
144
|
+
for (const d of p.decls) if (d.kind === "TypeDecl") types.add(d.name);
|
|
145
|
+
}
|
|
146
|
+
return (t) => {
|
|
147
|
+
const name = typeof t?.name === "string" ? t.name : "";
|
|
148
|
+
return !name.includes("$$") || types.has(name.replace(/\[.*$/, ""));
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// The linked module is flat, so `util.Two` names the top-level `Two`. Done on
|
|
153
|
+
// the link copy before monomorphisation, whose re-check of all programs has no
|
|
154
|
+
// package namespaces. A local variable that shadows the package is typed and
|
|
155
|
+
// is left alone.
|
|
156
|
+
function isBundledSelector(node, bundled) {
|
|
157
|
+
const base = node.expr;
|
|
158
|
+
return (
|
|
159
|
+
base?.kind === "Ident" &&
|
|
160
|
+
bundled.has(base.name) &&
|
|
161
|
+
(base._type == null || base._type.kind === "namespace")
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function dequalifyBundledRefs(node, bundled) {
|
|
166
|
+
if (!node || typeof node !== "object") return;
|
|
167
|
+
if (Array.isArray(node)) {
|
|
168
|
+
for (const n of node) dequalifyBundledRefs(n, bundled);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (node.kind === "SelectorExpr" && isBundledSelector(node, bundled)) {
|
|
172
|
+
node.kind = "Ident";
|
|
173
|
+
node.name = node.field;
|
|
174
|
+
delete node.expr;
|
|
175
|
+
delete node.field;
|
|
176
|
+
} else if (node.kind === "TypeName" || node.kind === "GenericTypeName") {
|
|
177
|
+
const dot = node.name?.indexOf(".") ?? -1;
|
|
178
|
+
if (dot > 0 && bundled.has(node.name.slice(0, dot)))
|
|
179
|
+
node.name = node.name.slice(dot + 1);
|
|
180
|
+
}
|
|
181
|
+
forEachChild(node, (child) => dequalifyBundledRefs(child, bundled));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// The linked module is one flat namespace (like the JS bundle), so a top-level
|
|
185
|
+
// name declared by two different packages would silently resolve to whichever
|
|
186
|
+
// was emitted last. Reject it up front.
|
|
187
|
+
function topLevelDeclNames(decl) {
|
|
188
|
+
switch (decl.kind) {
|
|
189
|
+
case "FuncDecl":
|
|
190
|
+
return [decl.name];
|
|
191
|
+
case "MethodDecl":
|
|
192
|
+
return [`${decl.recvType?.name ?? "?"}.${decl.name}`];
|
|
193
|
+
case "TypeDecl":
|
|
194
|
+
return [decl.name];
|
|
195
|
+
case "VarDecl":
|
|
196
|
+
case "ConstDecl":
|
|
197
|
+
return (decl.decls ?? []).flatMap((spec) => spec.names ?? []);
|
|
198
|
+
default:
|
|
199
|
+
return [];
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function checkLinkCollisions(units) {
|
|
204
|
+
const owner = new Map(); // name -> pkgName
|
|
205
|
+
const errors = [];
|
|
206
|
+
for (const u of units) {
|
|
207
|
+
for (const p of u.programs) {
|
|
208
|
+
for (const d of p.decls ?? []) {
|
|
209
|
+
for (const name of topLevelDeclNames(d)) {
|
|
210
|
+
if (name === "_") continue;
|
|
211
|
+
const prev = owner.get(name);
|
|
212
|
+
if (prev === undefined) owner.set(name, u.pkgName);
|
|
213
|
+
else if (prev !== u.pkgName)
|
|
214
|
+
errors.push(
|
|
215
|
+
`cannot link wasm packages: '${name}' is declared in both '${prev}' and '${u.pkgName}' (linked wasm packages share one namespace)`,
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (errors.length > 0) throw new Error([...new Set(errors)].join("\n"));
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Replaces the first wasm marker with the facade (dropping the others) and
|
|
225
|
+
// shifts source-map lines that follow it.
|
|
226
|
+
function spliceFacade(merged, facade) {
|
|
227
|
+
const lines = merged.js.split("\n");
|
|
228
|
+
const first = lines.findIndex((l) => WASM_MARKER_RE.test(l));
|
|
229
|
+
if (first < 0) {
|
|
230
|
+
merged.js = `${facade}\n${merged.js}`;
|
|
231
|
+
const shift = facade.split("\n").length;
|
|
232
|
+
for (const m of merged.mappings) m.genLine += shift;
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
const facadeLines = facade.split("\n");
|
|
236
|
+
const delta = facadeLines.length - 1;
|
|
237
|
+
for (const m of merged.mappings) {
|
|
238
|
+
if (m.genLine > first) m.genLine += delta;
|
|
239
|
+
}
|
|
240
|
+
const out = [...lines.slice(0, first), ...facadeLines];
|
|
241
|
+
for (let i = first + 1; i < lines.length; i++) {
|
|
242
|
+
if (!WASM_MARKER_RE.test(lines[i])) out.push(lines[i]);
|
|
243
|
+
}
|
|
244
|
+
merged.js = out.join("\n");
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Root compiles only: link every registered unit into app.wasm and splice the
|
|
248
|
+
// facade into the merged bundle. Returns `{ wasm, wat, sourceMapInfo }`
|
|
249
|
+
// (nulls when there is nothing to link).
|
|
250
|
+
export function linkIntoBundle(
|
|
251
|
+
options,
|
|
252
|
+
merged,
|
|
253
|
+
pkgTarget,
|
|
254
|
+
{ sourceMappingURL } = {},
|
|
255
|
+
) {
|
|
256
|
+
const none = {
|
|
257
|
+
wasm: null,
|
|
258
|
+
wat: null,
|
|
259
|
+
sourceMapInfo: null,
|
|
260
|
+
stringTable: null,
|
|
261
|
+
};
|
|
262
|
+
if (options.isDependency) return none;
|
|
263
|
+
const linked = linkWasmUnits(options.wasmUnits, {
|
|
264
|
+
emitWat: options.emitWat,
|
|
265
|
+
natives: options.natives,
|
|
266
|
+
rootTarget: pkgTarget,
|
|
267
|
+
sourceMappingURL,
|
|
268
|
+
});
|
|
269
|
+
if (!linked) return none;
|
|
270
|
+
spliceFacade(merged, linked.facade);
|
|
271
|
+
return {
|
|
272
|
+
wasm: linked.wasm,
|
|
273
|
+
wat: linked.wat ?? null,
|
|
274
|
+
sourceMapInfo: linked.sourceMapInfo ?? null,
|
|
275
|
+
stringTable: linked.stringTable ?? null,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Turns `cmp.Compare` / `cmp.Ordered` references (marked `_stdName` by the
|
|
280
|
+
// checker) into plain identifiers with the mangled name, so WASM
|
|
281
|
+
// monomorphisation sees ordinary top-level references. Only for ASTs that
|
|
282
|
+
// are not cached: a stdlib package's own parse and the WASM link copy (user
|
|
283
|
+
// ASTs are re-checked on the next build; the JS backend reads `_stdName`).
|
|
284
|
+
export function lowerStdlibRefs(node) {
|
|
285
|
+
if (!node || typeof node !== "object") return;
|
|
286
|
+
if (Array.isArray(node)) {
|
|
287
|
+
for (const n of node) lowerStdlibRefs(n);
|
|
288
|
+
return;
|
|
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;
|
|
293
|
+
if (node._stdName) {
|
|
294
|
+
if (node.kind === "SelectorExpr") {
|
|
295
|
+
delete node.expr;
|
|
296
|
+
delete node.field;
|
|
297
|
+
node.kind = "Ident";
|
|
298
|
+
}
|
|
299
|
+
node.name = node._stdName;
|
|
300
|
+
delete node._stdName;
|
|
301
|
+
}
|
|
302
|
+
if (node.kind === "CallExpr" && node.func?._jsonStream) {
|
|
303
|
+
const entry = node.func._genEntry;
|
|
304
|
+
node.args.unshift(node.func.expr);
|
|
305
|
+
delete node.func.expr;
|
|
306
|
+
delete node.func.field;
|
|
307
|
+
delete node.func._jsonStream;
|
|
308
|
+
delete node.func._genEntry;
|
|
309
|
+
node.func.kind = "Ident";
|
|
310
|
+
node.func.name = entry;
|
|
311
|
+
} else if (node._genEntry && node.kind === "SelectorExpr") {
|
|
312
|
+
delete node.expr;
|
|
313
|
+
delete node.field;
|
|
314
|
+
node.kind = "Ident";
|
|
315
|
+
node.name = node._genEntry;
|
|
316
|
+
}
|
|
317
|
+
forEachChild(node, lowerStdlibRefs);
|
|
318
|
+
}
|