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,737 @@
|
|
|
1
|
+
// Go-source stdlib pipeline: packages under stdlib/<path>/ are parsed, checked
|
|
2
|
+
// twice (probe, then mangled), linked into app.wasm and spliced into the JS
|
|
3
|
+
// bundle. compiler.js owns import resolution, so the functions that need it
|
|
4
|
+
// (and the parser/checker helpers it also exports) arrive through an injected
|
|
5
|
+
// `deps` argument: `{ gwFilesIn, resolveImports, TypeChecker }`. Importing
|
|
6
|
+
// compiler.js from here would create a cycle.
|
|
7
|
+
|
|
8
|
+
import { readFileSync } from "node:fs";
|
|
9
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
10
|
+
import { CodeGen } from "../backend/js/index.js";
|
|
11
|
+
import { Lexer } from "../lexer/lexer.js";
|
|
12
|
+
import { generateErrorsAs } from "../lower/errors-as.js";
|
|
13
|
+
import { generateFmtFormatters } from "../lower/fmt-formatters.js";
|
|
14
|
+
import { generateJsonCodecs } from "../lower/json-codecs.js";
|
|
15
|
+
import { reachableStdlibDecls, StdlibReach } from "../lower/reachability.js";
|
|
16
|
+
import { forEachChild } from "../lower/walk.js";
|
|
17
|
+
import { Parser } from "../parser/index.js";
|
|
18
|
+
import { isLocalPath, resolveStdlibSource } from "../resolver.js";
|
|
19
|
+
import { lowerStdlibRefs, registerWasmUnit } from "./wasm.js";
|
|
20
|
+
|
|
21
|
+
export {
|
|
22
|
+
mangleCollidingTypes,
|
|
23
|
+
renameLocalTypes,
|
|
24
|
+
withOriginalTypeNames,
|
|
25
|
+
} from "./type-names.js";
|
|
26
|
+
|
|
27
|
+
// ── Go-source stdlib ──────────────────────────────────────────
|
|
28
|
+
//
|
|
29
|
+
// Packages under stdlib/<path>/ are GoFront Go. Each is type-checked
|
|
30
|
+
// and compiled once per build as a `both` package (strict numerics): its JS
|
|
31
|
+
// is bundled for js/both importers and its programs are linked into app.wasm
|
|
32
|
+
// for wasm/both importers, never through the boundary facade. Top-level
|
|
33
|
+
// names are mangled to `Name$$path` so they cannot collide with user
|
|
34
|
+
// declarations in the flat JS bundle or the linked module.
|
|
35
|
+
|
|
36
|
+
function stdlibSuffix(importPath) {
|
|
37
|
+
return `$$${importPath.replaceAll("/", "$")}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Each file keeps its own `//gofront:target` (default `both`). A package
|
|
41
|
+
// with `js` files is compiled whole for `js` importers (the `js` variant) and
|
|
42
|
+
// without those files for `wasm` and `both` importers (the `shared` variant).
|
|
43
|
+
function parseStdlibFiles(dir, deps, variant = "js", target = "both") {
|
|
44
|
+
return deps
|
|
45
|
+
.gwFilesIn(dir)
|
|
46
|
+
.map((f) => {
|
|
47
|
+
const source = readFileSync(f, "utf8");
|
|
48
|
+
const filename = basename(f);
|
|
49
|
+
const tokens = new Lexer(source, filename).tokenize();
|
|
50
|
+
const ast = new Parser(tokens, filename, source).parse();
|
|
51
|
+
ast._source = source;
|
|
52
|
+
ast._path = resolve(f);
|
|
53
|
+
ast._fileTarget = tokens.target ?? "both";
|
|
54
|
+
return ast;
|
|
55
|
+
})
|
|
56
|
+
.filter((ast) => variant === "js" || ast._fileTarget !== "js")
|
|
57
|
+
.map((ast) => Object.assign(ast, { target }));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Targets of the package's files, read without parsing.
|
|
61
|
+
function stdlibFileTargets(dir, deps) {
|
|
62
|
+
return deps.gwFilesIn(dir).map((f) => {
|
|
63
|
+
const m = /^\/\/gofront:target\s+(\S+)/m.exec(readFileSync(f, "utf8"));
|
|
64
|
+
return m?.[1] ?? "both";
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Exported names declared by the `js` files (what a `wasm` or `both` importer
|
|
69
|
+
// cannot use).
|
|
70
|
+
function stdlibJsOnlyNames(dir, deps) {
|
|
71
|
+
const programs = parseStdlibFiles(dir, deps).filter(
|
|
72
|
+
(p) => p._fileTarget === "js",
|
|
73
|
+
);
|
|
74
|
+
const { values, types } = stdlibTopLevelNames(programs);
|
|
75
|
+
return new Set([...values, ...types].filter((n) => /^[A-Z]/.test(n)));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Whether every file of the package is `js`-only.
|
|
79
|
+
export function stdlibIsJsOnly(dir, deps) {
|
|
80
|
+
const targets = stdlibFileTargets(dir, deps);
|
|
81
|
+
return targets.length > 0 && targets.every((t) => t === "js");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Top-level value and type names declared by the package.
|
|
85
|
+
function stdlibTopLevelNames(programs) {
|
|
86
|
+
const values = new Set();
|
|
87
|
+
const types = new Set();
|
|
88
|
+
for (const p of programs) {
|
|
89
|
+
for (const d of p.decls ?? []) {
|
|
90
|
+
if (d.kind === "TypeDecl") types.add(d.name);
|
|
91
|
+
else if (d.kind === "FuncDecl" && d.name !== "init") values.add(d.name);
|
|
92
|
+
else if (d.kind === "VarDecl" || d.kind === "ConstDecl")
|
|
93
|
+
for (const spec of d.decls ?? [])
|
|
94
|
+
for (const n of spec.names ?? []) if (n !== "_") values.add(n);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return { values, types };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Renames the package's top-level declarations and every reference to them.
|
|
101
|
+
// `refs` holds the `file:line:col` of identifiers the first type-check
|
|
102
|
+
// resolved to package scope; type names are resolved by name (GoFront types
|
|
103
|
+
// are package-flat) except where a type parameter shadows them.
|
|
104
|
+
// With `lazy`, function and method bodies are left alone: they are mangled
|
|
105
|
+
// by `mangleBody` once a lazily checked body is first reached.
|
|
106
|
+
export function mangleStdlibAst(programs, ctx, { lazy = false } = {}) {
|
|
107
|
+
for (const p of programs) {
|
|
108
|
+
for (const d of p.decls ?? []) mangleDeclNames(d, ctx);
|
|
109
|
+
for (const d of p.decls ?? []) {
|
|
110
|
+
const deferred =
|
|
111
|
+
lazy && (d.kind === "FuncDecl" || d.kind === "MethodDecl");
|
|
112
|
+
const body = d.body;
|
|
113
|
+
if (deferred) d.body = null;
|
|
114
|
+
mangleRefs(d, p._filename, new Set(), ctx);
|
|
115
|
+
if (deferred) d.body = body;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function mangleBody(decl, file, ctx) {
|
|
121
|
+
const tparams = new Set((decl.typeParams ?? []).map((t) => t.name));
|
|
122
|
+
mangleRefs(decl.body, file, tparams, ctx);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function mangleDeclNames(d, { suffix, values }) {
|
|
126
|
+
if (d.kind === "TypeDecl" || (d.kind === "FuncDecl" && values.has(d.name))) {
|
|
127
|
+
d.name += suffix;
|
|
128
|
+
} else if (d.kind === "VarDecl" || d.kind === "ConstDecl") {
|
|
129
|
+
for (const spec of d.decls ?? [])
|
|
130
|
+
spec.names = spec.names.map((n) => (values.has(n) ? n + suffix : n));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function mangleRefs(node, file, tparams, ctx) {
|
|
135
|
+
if (!node || typeof node !== "object") return;
|
|
136
|
+
if (Array.isArray(node)) {
|
|
137
|
+
for (const n of node) mangleRefs(n, file, tparams, ctx);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const tp =
|
|
141
|
+
node.typeParams?.length > 0
|
|
142
|
+
? new Set([...tparams, ...node.typeParams.map((t) => t.name)])
|
|
143
|
+
: tparams;
|
|
144
|
+
switch (node.kind) {
|
|
145
|
+
case "TypeName":
|
|
146
|
+
case "GenericTypeName":
|
|
147
|
+
mangleTypeRef(node, tp, ctx);
|
|
148
|
+
break;
|
|
149
|
+
case "CompositeLit":
|
|
150
|
+
if (node.typeExpr?.kind === "Ident")
|
|
151
|
+
mangleTypeRef(node.typeExpr, tp, ctx);
|
|
152
|
+
break;
|
|
153
|
+
case "InstantiationExpr": // `Stack[T]{}`, `Pair[K, V](x)`
|
|
154
|
+
case "IndexExpr":
|
|
155
|
+
if (node.expr?.kind === "Ident") mangleTypeRef(node.expr, tp, ctx);
|
|
156
|
+
break;
|
|
157
|
+
case "CallExpr":
|
|
158
|
+
if (
|
|
159
|
+
(node.func?.name === "new" || node.func?.name === "make") &&
|
|
160
|
+
node.args?.[0]?.kind === "Ident"
|
|
161
|
+
) {
|
|
162
|
+
mangleTypeRef(node.args[0], tp, ctx);
|
|
163
|
+
}
|
|
164
|
+
break;
|
|
165
|
+
case "Ident":
|
|
166
|
+
if (ctx.refs.has(`${file}:${node._line}:${node._col}`))
|
|
167
|
+
mangleIdentRef(node, ctx);
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
forEachChild(node, (child) => mangleRefs(child, file, tp, ctx));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function mangleTypeRef(node, tparams, { suffix, types }) {
|
|
174
|
+
if (types.has(node.name) && !tparams.has(node.name)) {
|
|
175
|
+
node.name += suffix;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// A package-scope identifier: a package declaration gets the suffix.
|
|
180
|
+
function mangleIdentRef(node, { suffix, values, types }) {
|
|
181
|
+
if (values.has(node.name) || types.has(node.name)) {
|
|
182
|
+
node.name += suffix;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function checkStdlibPrograms(programs, pkgName, options, deps, target) {
|
|
187
|
+
const checker = new deps.TypeChecker();
|
|
188
|
+
checker.target = target;
|
|
189
|
+
checker.pkgName = pkgName;
|
|
190
|
+
checker.isStdlib = true;
|
|
191
|
+
checker.lazyBodies = true;
|
|
192
|
+
deps.resolveImports(
|
|
193
|
+
programs,
|
|
194
|
+
programs[0]._path,
|
|
195
|
+
checker,
|
|
196
|
+
new Map(),
|
|
197
|
+
new Set(),
|
|
198
|
+
[],
|
|
199
|
+
new Set(),
|
|
200
|
+
options,
|
|
201
|
+
);
|
|
202
|
+
return checker;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Errors in stdlib source are compiler bugs; the importer reports them at its
|
|
206
|
+
// import (see reportStdlibLoadError), so only the message text is kept, never
|
|
207
|
+
// a location inside stdlib/.
|
|
208
|
+
function stdlibErrorText(errors) {
|
|
209
|
+
const detail = (e) =>
|
|
210
|
+
e.message
|
|
211
|
+
.split("\n")[0]
|
|
212
|
+
.replace(/^Type error(?: in \S+)?(?: at line \d+:\d+)?:\s*/, "");
|
|
213
|
+
return [...new Set(errors.map(detail))].join("; ");
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function throwStdlibErrors(errors) {
|
|
217
|
+
if (errors.length === 0) return;
|
|
218
|
+
throw new Error(stdlibErrorText(errors));
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Pass 1: type-check the package as written and record which identifiers
|
|
222
|
+
// refer to package scope.
|
|
223
|
+
function probeStdlibPackage(_importPath, dir, options, deps, v) {
|
|
224
|
+
const programs = parseStdlibFiles(dir, deps, v.variant, v.target);
|
|
225
|
+
const pkgName = programs[0].pkg.name;
|
|
226
|
+
const { values, types } = stdlibTopLevelNames(programs);
|
|
227
|
+
const probe = checkStdlibPrograms(programs, pkgName, options, deps, v.target);
|
|
228
|
+
probe._pkgRefs = new Set();
|
|
229
|
+
throwStdlibErrors(probe.checkAll(programs));
|
|
230
|
+
return {
|
|
231
|
+
pkgName,
|
|
232
|
+
values,
|
|
233
|
+
types,
|
|
234
|
+
refs: probe._pkgRefs,
|
|
235
|
+
probe,
|
|
236
|
+
probePrograms: programs,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Function and method bodies are checked when first reached (see
|
|
241
|
+
// `StdlibReach`), in both passes: the probe records which identifiers in the
|
|
242
|
+
// body name package scope, the body is mangled with them, and the mangled
|
|
243
|
+
// body is checked. `decl._ensure()` does all three once and returns the
|
|
244
|
+
// error text, or null.
|
|
245
|
+
function installLazyBodies(info, programs, checker) {
|
|
246
|
+
const { probe, probePrograms } = info;
|
|
247
|
+
programs.forEach((p, pi) => {
|
|
248
|
+
p.decls.forEach((decl, di) => {
|
|
249
|
+
if (decl.kind !== "FuncDecl" && decl.kind !== "MethodDecl") return;
|
|
250
|
+
const probeDecl = probePrograms[pi].decls[di];
|
|
251
|
+
let result;
|
|
252
|
+
decl._ensure = () => {
|
|
253
|
+
if (result !== undefined) return result;
|
|
254
|
+
result = null; // a body that reaches itself is in progress
|
|
255
|
+
const before = [probe.errors.length, checker.errors.length];
|
|
256
|
+
probe.checkBody(probeDecl);
|
|
257
|
+
if (probe.errors.length === before[0]) {
|
|
258
|
+
mangleBody(decl, p._filename, info);
|
|
259
|
+
checker.checkBody(decl);
|
|
260
|
+
lowerStdlibRefs(decl);
|
|
261
|
+
}
|
|
262
|
+
const errors = [
|
|
263
|
+
...probe.errors.slice(before[0]),
|
|
264
|
+
...checker.errors.slice(before[1]),
|
|
265
|
+
];
|
|
266
|
+
if (errors.length > 0) result = stdlibErrorText(errors);
|
|
267
|
+
return result;
|
|
268
|
+
};
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Exported members as seen by importers (original name -> type) and the
|
|
274
|
+
// original -> mangled name map.
|
|
275
|
+
function stdlibNamespace(checker, { pkgName, values, types, suffix }, jsOnly) {
|
|
276
|
+
const members = {};
|
|
277
|
+
const mangle = new Map();
|
|
278
|
+
const consts = new Map();
|
|
279
|
+
for (const name of [...values, ...types]) {
|
|
280
|
+
const t = checker.globals.symbols.get(name + suffix);
|
|
281
|
+
if (!/^[A-Z]/.test(name) || t === undefined) continue;
|
|
282
|
+
members[name] = t;
|
|
283
|
+
mangle.set(name, name + suffix);
|
|
284
|
+
// An exported constant's folded value, so importers fold `pkg.Name`.
|
|
285
|
+
if (checker.globals._consts.has(name + suffix)) {
|
|
286
|
+
const v = checker._constValue(
|
|
287
|
+
{ kind: "Ident", name: name + suffix },
|
|
288
|
+
checker.globals,
|
|
289
|
+
);
|
|
290
|
+
if (v) consts.set(name, v);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return {
|
|
294
|
+
kind: "namespace",
|
|
295
|
+
name: pkgName,
|
|
296
|
+
members,
|
|
297
|
+
_gofront: true,
|
|
298
|
+
_target: "both",
|
|
299
|
+
_jsOnly: jsOnly,
|
|
300
|
+
_mangle: mangle,
|
|
301
|
+
_consts: consts,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Mangled type names (needed by the backends) plus `pkg.T` for exported ones.
|
|
306
|
+
function stdlibExportedTypes(checker, { pkgName, suffix }) {
|
|
307
|
+
const out = new Map();
|
|
308
|
+
for (const [k, t] of checker.types) {
|
|
309
|
+
const at = k.indexOf("$$");
|
|
310
|
+
if (at < 0) continue;
|
|
311
|
+
out.set(k, t);
|
|
312
|
+
const orig = k.slice(0, at);
|
|
313
|
+
if (k === orig + suffix && /^[A-Z]/.test(orig))
|
|
314
|
+
out.set(`${pkgName}.${orig}`, t);
|
|
315
|
+
}
|
|
316
|
+
return out;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// The compilation a package gets for an importer: `js` importers see every
|
|
320
|
+
// file, `wasm` and `both` importers only the files for both targets (cached
|
|
321
|
+
// as `<path>#shared` when that differs). Returns { key, variant, target }.
|
|
322
|
+
function stdlibVariant(importPath, dir, importerTarget, deps) {
|
|
323
|
+
const hasJs = stdlibFileTargets(dir, deps).includes("js");
|
|
324
|
+
importerTarget ??= "js";
|
|
325
|
+
if (!hasJs) return { key: importPath, variant: "js", target: "both" };
|
|
326
|
+
if (importerTarget === "js")
|
|
327
|
+
return { key: importPath, variant: "js", target: "js" };
|
|
328
|
+
return { key: `${importPath}#shared`, variant: "shared", target: "both" };
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Loads (once per build) and compiles the Go-source package at `dir`. A
|
|
332
|
+
// failure is cached too, so every importer reports it.
|
|
333
|
+
function loadStdlibPackage(
|
|
334
|
+
importPath,
|
|
335
|
+
dir,
|
|
336
|
+
options,
|
|
337
|
+
deps,
|
|
338
|
+
importerTarget = "js",
|
|
339
|
+
) {
|
|
340
|
+
const v = stdlibVariant(importPath, dir, importerTarget, deps);
|
|
341
|
+
const cache = options.stdlibCache;
|
|
342
|
+
const cached = cache.get(v.key);
|
|
343
|
+
if (cached === null)
|
|
344
|
+
throw new Error(`import cycle in Go-source stdlib: ${importPath}`);
|
|
345
|
+
if (cached instanceof Error) throw cached;
|
|
346
|
+
if (cached) return cached;
|
|
347
|
+
cache.set(v.key, null);
|
|
348
|
+
try {
|
|
349
|
+
const pkg = buildStdlibPackage(importPath, dir, options, deps, v);
|
|
350
|
+
cache.set(v.key, pkg);
|
|
351
|
+
return pkg;
|
|
352
|
+
} catch (e) {
|
|
353
|
+
cache.set(v.key, e);
|
|
354
|
+
throw e;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function buildStdlibPackage(importPath, dir, options, deps, v) {
|
|
359
|
+
const cache = options.stdlibCache;
|
|
360
|
+
const info = {
|
|
361
|
+
...probeStdlibPackage(importPath, dir, options, deps, v),
|
|
362
|
+
suffix: stdlibSuffix(importPath),
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
// Pass 2: mangle a fresh parse and check it again.
|
|
366
|
+
const programs = parseStdlibFiles(dir, deps, v.variant, v.target);
|
|
367
|
+
mangleStdlibAst(programs, info, { lazy: true });
|
|
368
|
+
const checker = checkStdlibPrograms(
|
|
369
|
+
programs,
|
|
370
|
+
info.pkgName,
|
|
371
|
+
options,
|
|
372
|
+
deps,
|
|
373
|
+
v.target,
|
|
374
|
+
);
|
|
375
|
+
throwStdlibErrors(checker.checkAll(programs));
|
|
376
|
+
lowerStdlibRefs(programs);
|
|
377
|
+
installLazyBodies(info, programs, checker);
|
|
378
|
+
|
|
379
|
+
// A user reference to a member checks the bodies it reaches, so a type
|
|
380
|
+
// error in the standard library surfaces at the call. Failures from other
|
|
381
|
+
// packages' bodies surface when the output is assembled.
|
|
382
|
+
const reach = new StdlibReach(programs);
|
|
383
|
+
const jsOnly =
|
|
384
|
+
v.variant === "shared" ? stdlibJsOnlyNames(dir, deps) : new Set();
|
|
385
|
+
const namespace = stdlibNamespace(checker, info, jsOnly);
|
|
386
|
+
namespace._path = importPath;
|
|
387
|
+
namespace._reach = (name) => {
|
|
388
|
+
const seen = reach.failures.length;
|
|
389
|
+
reach.add([{ kind: "Ident", name }]);
|
|
390
|
+
return reach.failures[seen] ?? null;
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
const pkg = {
|
|
394
|
+
importPath,
|
|
395
|
+
key: v.key,
|
|
396
|
+
checker,
|
|
397
|
+
pkgName: info.pkgName,
|
|
398
|
+
namespace,
|
|
399
|
+
exportedTypes: stdlibExportedTypes(checker, info),
|
|
400
|
+
deps: programs
|
|
401
|
+
.flatMap((p) => p.imports.flatMap((imp) => imp.imports))
|
|
402
|
+
.map(({ path }) =>
|
|
403
|
+
v.variant === "shared"
|
|
404
|
+
? (cache.get(`${path}#shared`) ?? cache.get(path))
|
|
405
|
+
: cache.get(path),
|
|
406
|
+
)
|
|
407
|
+
.filter(Boolean),
|
|
408
|
+
unit: {
|
|
409
|
+
key: `stdlib:${importPath}`,
|
|
410
|
+
pkgName: info.pkgName,
|
|
411
|
+
target: "both",
|
|
412
|
+
programs,
|
|
413
|
+
types: checker.types,
|
|
414
|
+
bundledPackages: new Set(),
|
|
415
|
+
},
|
|
416
|
+
};
|
|
417
|
+
return pkg;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// A Go-source package's JS is generated at the root compile, once every
|
|
421
|
+
// JS-emitted user program is known, so only reachable declarations are
|
|
422
|
+
// emitted (see lower/reachability.js). Importers leave a marker line.
|
|
423
|
+
const STDLIB_MARKER_RE = /^\/\*__GOFRONT_STDLIB:([^*]*)\*\/$/;
|
|
424
|
+
|
|
425
|
+
function stdlibMarker(key) {
|
|
426
|
+
return `/*__GOFRONT_STDLIB:${key}*/`;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function stdlibJs(pkg, reached, natives) {
|
|
430
|
+
const programs = pkg.unit.programs.map((p) => ({
|
|
431
|
+
...p,
|
|
432
|
+
decls: p.decls.filter((d) => reached.has(d)),
|
|
433
|
+
}));
|
|
434
|
+
if (programs.every((p) => p.decls.length === 0)) return "";
|
|
435
|
+
return new CodeGen(pkg.checker, new Map(), new Set(), {
|
|
436
|
+
target: pkg.checker.target,
|
|
437
|
+
strict: true,
|
|
438
|
+
natives,
|
|
439
|
+
}).generateAll(programs, { isTest: false });
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// The API a wasm package exposes to JS through the facade: exported func and
|
|
443
|
+
// method signatures and exported type declarations.
|
|
444
|
+
function wasmExportedSignatures(units) {
|
|
445
|
+
const out = [];
|
|
446
|
+
for (const u of units) {
|
|
447
|
+
if (u.target !== "wasm" || u.key.startsWith("stdlib:")) continue;
|
|
448
|
+
for (const d of u.programs.flatMap((p) => p.decls ?? [])) {
|
|
449
|
+
if (!/^[A-Z]/.test(d.name ?? "")) continue;
|
|
450
|
+
if (d.kind === "TypeDecl") out.push(d.type);
|
|
451
|
+
else if (d.kind === "FuncDecl" || d.kind === "MethodDecl")
|
|
452
|
+
out.push(d.params, d.returnType);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
return out;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Replaces each marker with the package's reachable code (no line at all when
|
|
459
|
+
// nothing is reachable) and shifts the source-map lines that follow.
|
|
460
|
+
function spliceStdlibJs(merged, options) {
|
|
461
|
+
const lines = merged.js.split("\n");
|
|
462
|
+
const pkgs = lines
|
|
463
|
+
.map((l) => l.match(STDLIB_MARKER_RE)?.[1])
|
|
464
|
+
.filter(Boolean)
|
|
465
|
+
.map((key) => options.stdlibCache.get(key));
|
|
466
|
+
if (pkgs.length === 0) return;
|
|
467
|
+
// The whole package carries everything its shared variant has.
|
|
468
|
+
const keys = new Set(pkgs.map((pkg) => pkg.key));
|
|
469
|
+
const emitted = (pkg) =>
|
|
470
|
+
pkg.key.endsWith("#shared") ? !keys.has(pkg.importPath) : true;
|
|
471
|
+
const reached = reachableStdlibDecls(
|
|
472
|
+
[options.jsRootPrograms, wasmExportedSignatures(options.wasmUnits)],
|
|
473
|
+
pkgs.filter(emitted).flatMap((pkg) => pkg.unit.programs),
|
|
474
|
+
);
|
|
475
|
+
const out = [];
|
|
476
|
+
const spliced = new Set();
|
|
477
|
+
for (const line of lines) {
|
|
478
|
+
const path = line.match(STDLIB_MARKER_RE)?.[1];
|
|
479
|
+
if (!path) {
|
|
480
|
+
out.push(line);
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
if (spliced.has(path)) continue;
|
|
484
|
+
spliced.add(path);
|
|
485
|
+
const pkg = options.stdlibCache.get(path);
|
|
486
|
+
const js = emitted(pkg) ? stdlibJs(pkg, reached, options.natives) : "";
|
|
487
|
+
const code = js === "" ? [] : js.split("\n");
|
|
488
|
+
const delta = code.length - 1;
|
|
489
|
+
for (const m of merged.mappings)
|
|
490
|
+
if (m.genLine > out.length) m.genLine += delta;
|
|
491
|
+
out.push(...code);
|
|
492
|
+
}
|
|
493
|
+
merged.js = out.join("\n");
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// Records a package's JS-emitted programs as reachability roots; the root
|
|
497
|
+
// compile then splices in the stdlib code they reach.
|
|
498
|
+
export function finishStdlibJs(merged, options, programs, emitTarget) {
|
|
499
|
+
if (emitTarget !== "wasm") options.jsRootPrograms.push(...programs);
|
|
500
|
+
if (!options.isDependency) spliceStdlibJs(merged, options);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// Adds a loaded package (and, first, its Go-source dependencies) to the
|
|
504
|
+
// importer's output: JS for js/both importers, a link unit for wasm/both.
|
|
505
|
+
function emitStdlibPackage(
|
|
506
|
+
pkg,
|
|
507
|
+
importerTarget,
|
|
508
|
+
preambles,
|
|
509
|
+
bundledDirs,
|
|
510
|
+
options,
|
|
511
|
+
) {
|
|
512
|
+
for (const dep of pkg.deps)
|
|
513
|
+
emitStdlibPackage(dep, importerTarget, preambles, bundledDirs, options);
|
|
514
|
+
// Every importer leaves a JS marker: the facade of a wasm package may
|
|
515
|
+
// expose a stdlib type; unreachable code splices to nothing.
|
|
516
|
+
const key = `${pkg.key}:js`;
|
|
517
|
+
if (!bundledDirs.has(key)) {
|
|
518
|
+
bundledDirs.add(key);
|
|
519
|
+
preambles.push(stdlibMarker(pkg.key));
|
|
520
|
+
}
|
|
521
|
+
if (importerTarget !== "js") registerWasmUnit(options, pkg.unit);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
function importNode({ _line, _col, line, col }) {
|
|
525
|
+
return { _line, _col, line: line ?? _line, col: col ?? _col ?? 1 };
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Reports `msg` at an import spec of program `p`.
|
|
529
|
+
function errAtImport(checker, p, spec, msg, hint = null) {
|
|
530
|
+
const saved = [checker._currentFile, checker._currentSource];
|
|
531
|
+
checker._currentFile = p._filename ?? null;
|
|
532
|
+
checker._currentSource = p._source ?? null;
|
|
533
|
+
checker.err(msg, importNode(spec), hint);
|
|
534
|
+
[checker._currentFile, checker._currentSource] = saved;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// Defines the import's namespace (and `pkg.T` type names) in the importer.
|
|
538
|
+
function bindStdlibImport(checker, p, spec, pkg) {
|
|
539
|
+
const { path, alias } = spec;
|
|
540
|
+
if (alias === "_") return;
|
|
541
|
+
if (alias === ".") {
|
|
542
|
+
errAtImport(checker, p, spec, `dot-import of '${path}' is not supported`);
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
const name = alias ?? pkg.pkgName;
|
|
546
|
+
const ns = { ...pkg.namespace, name };
|
|
547
|
+
checker.globals.define(name, ns);
|
|
548
|
+
for (const [k, t] of pkg.exportedTypes) {
|
|
549
|
+
const dot = k.indexOf(".");
|
|
550
|
+
checker.types.set(dot < 0 ? k : `${name}${k.slice(dot)}`, t);
|
|
551
|
+
}
|
|
552
|
+
if (!spec._implicit)
|
|
553
|
+
checker.trackImport(name, importNode(spec), p._filename, p._source);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// The generators behind the package's `gofront_gen.go`: each scans the
|
|
557
|
+
// checked programs for its call sites, marks them with `_genEntry` and returns
|
|
558
|
+
// null (no sites) or { body, imports, diagnostics }.
|
|
559
|
+
const GENERATORS = [
|
|
560
|
+
generateJsonCodecs,
|
|
561
|
+
generateErrorsAs,
|
|
562
|
+
generateFmtFormatters,
|
|
563
|
+
];
|
|
564
|
+
|
|
565
|
+
// Generates, imports and checks the package's generated Go (json codecs,
|
|
566
|
+
// `errors.As` functions) as one more file. Returns the generated programs:
|
|
567
|
+
// none for a package with errors or one with no generated call sites.
|
|
568
|
+
export function checkGeneratedGo(
|
|
569
|
+
programs,
|
|
570
|
+
checker,
|
|
571
|
+
preambles,
|
|
572
|
+
options,
|
|
573
|
+
deps,
|
|
574
|
+
generators = GENERATORS,
|
|
575
|
+
) {
|
|
576
|
+
if (checker.errors.length > 0) return [];
|
|
577
|
+
const parts = generators
|
|
578
|
+
.map((gen) => gen(programs, checker, checker.pkgName, options))
|
|
579
|
+
.filter(Boolean);
|
|
580
|
+
if (parts.length === 0) return [];
|
|
581
|
+
let failed = false;
|
|
582
|
+
for (const { diagnostics } of parts)
|
|
583
|
+
for (const { program, node, message, hint } of diagnostics) {
|
|
584
|
+
failed = true;
|
|
585
|
+
checker._setCurrentFile(program);
|
|
586
|
+
checker.recordBlocker("unsupported stdlib member", message);
|
|
587
|
+
checker.err(message, node, hint);
|
|
588
|
+
}
|
|
589
|
+
if (failed) return [];
|
|
590
|
+
// An import is a path or { path, alias }.
|
|
591
|
+
const specs = new Map();
|
|
592
|
+
for (const imp of parts.flatMap((p) => p.imports)) {
|
|
593
|
+
const { path, alias } = typeof imp === "string" ? { path: imp } : imp;
|
|
594
|
+
specs.set(`${alias ?? ""} ${path}`, { path, alias });
|
|
595
|
+
}
|
|
596
|
+
const imports = [...specs.values()];
|
|
597
|
+
const source = [
|
|
598
|
+
`package ${checker.pkgName}`,
|
|
599
|
+
...(imports.length > 0
|
|
600
|
+
? [
|
|
601
|
+
`import (\n${imports.map(({ path, alias }) => `\t${alias ? `${alias} ` : ""}${JSON.stringify(path)}`).join("\n")}\n)`,
|
|
602
|
+
]
|
|
603
|
+
: []),
|
|
604
|
+
...parts.map((p) => p.body),
|
|
605
|
+
"",
|
|
606
|
+
].join("\n\n");
|
|
607
|
+
const filename = "gofront_gen.go";
|
|
608
|
+
const ast = new Parser(
|
|
609
|
+
new Lexer(source, filename).tokenize(),
|
|
610
|
+
filename,
|
|
611
|
+
source,
|
|
612
|
+
).parse();
|
|
613
|
+
ast._source = source;
|
|
614
|
+
ast._generated = true;
|
|
615
|
+
ast._path = join(dirname(programs[0]._path ?? "."), filename);
|
|
616
|
+
ast.target = checker.target;
|
|
617
|
+
importStdlibPackages(
|
|
618
|
+
[ast],
|
|
619
|
+
checker,
|
|
620
|
+
preambles,
|
|
621
|
+
options.bundledDirs ?? new Set(),
|
|
622
|
+
options,
|
|
623
|
+
deps,
|
|
624
|
+
);
|
|
625
|
+
const before = checker.errors.length;
|
|
626
|
+
checker.checkAll([ast]);
|
|
627
|
+
if (checker.errors.length > before) {
|
|
628
|
+
const detail = checker.errors
|
|
629
|
+
.slice(before)
|
|
630
|
+
.map((e) => e.message)
|
|
631
|
+
.join("\n");
|
|
632
|
+
throw new Error(
|
|
633
|
+
`internal error in the generated Go; please report\n${detail}`,
|
|
634
|
+
);
|
|
635
|
+
}
|
|
636
|
+
for (const { link } of parts) link?.(checker);
|
|
637
|
+
return [ast];
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// Packages a `js` package may use without importing them, as the compiler
|
|
641
|
+
// has always allowed: a program that selects from the identifier (or, for
|
|
642
|
+
// `gom`, declares a templ component, typed as `gom.Node`) without declaring
|
|
643
|
+
// or importing it gets the import.
|
|
644
|
+
const IMPLICIT_PACKAGES = ["fmt", "gom", "regexp"];
|
|
645
|
+
|
|
646
|
+
function implicitImports(programs) {
|
|
647
|
+
const declared = (name) =>
|
|
648
|
+
programs.some(
|
|
649
|
+
(p) =>
|
|
650
|
+
p.imports.some((imp) =>
|
|
651
|
+
imp.imports.some((s) => s.path === name || s.alias === name),
|
|
652
|
+
) || stdlibTopLevelNames([p]).values.has(name),
|
|
653
|
+
);
|
|
654
|
+
const used = new Set();
|
|
655
|
+
const visit = (node) => {
|
|
656
|
+
if (!node || typeof node !== "object") return;
|
|
657
|
+
if (Array.isArray(node)) return node.forEach(visit);
|
|
658
|
+
if (node.kind === "TemplDecl") used.add("gom");
|
|
659
|
+
else if (
|
|
660
|
+
node.kind === "SelectorExpr" &&
|
|
661
|
+
IMPLICIT_PACKAGES.includes(node.expr?.name)
|
|
662
|
+
)
|
|
663
|
+
used.add(node.expr.name);
|
|
664
|
+
else if (typeof node.name === "string" && node.kind?.endsWith("TypeName"))
|
|
665
|
+
for (const name of IMPLICIT_PACKAGES)
|
|
666
|
+
if (node.name.startsWith(`${name}.`)) used.add(name);
|
|
667
|
+
forEachChild(node, visit);
|
|
668
|
+
};
|
|
669
|
+
for (const p of programs) visit(p.decls);
|
|
670
|
+
return [...used]
|
|
671
|
+
.filter((name) => !declared(name))
|
|
672
|
+
.map((path) => ({ path, _line: 1, _col: 1, _implicit: true }));
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// Packages that keep their own wording for the unavailable-in-wasm error.
|
|
676
|
+
const NOT_IN_WASM = new Map([["gom", "gom usage"]]);
|
|
677
|
+
|
|
678
|
+
// Reports an import of a `js`-only package from a `wasm` or `both` package.
|
|
679
|
+
function errJsOnlyImport(checker, p, spec) {
|
|
680
|
+
// Its uses are not a second root cause.
|
|
681
|
+
checker._unavailableImports ??= new Set();
|
|
682
|
+
checker._unavailableImports.add(spec.alias ?? spec.path.split("/").at(-1));
|
|
683
|
+
const category = NOT_IN_WASM.get(spec.path);
|
|
684
|
+
if (category) {
|
|
685
|
+
checker.recordBlocker(category, spec.path);
|
|
686
|
+
const msg = `package '${spec.path}' is not available in wasm packages`;
|
|
687
|
+
return errAtImport(checker, p, spec, msg);
|
|
688
|
+
}
|
|
689
|
+
checker.recordBlocker("unsupported stdlib import", spec.path);
|
|
690
|
+
const msg = `package '${spec.path}' is only available in js packages`;
|
|
691
|
+
errAtImport(checker, p, spec, msg, "move this code to a js package");
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// Loads and binds one import of program `p`.
|
|
695
|
+
function importStdlibSpec(
|
|
696
|
+
p,
|
|
697
|
+
spec,
|
|
698
|
+
checker,
|
|
699
|
+
preambles,
|
|
700
|
+
bundledDirs,
|
|
701
|
+
options,
|
|
702
|
+
deps,
|
|
703
|
+
) {
|
|
704
|
+
if (spec.path.startsWith("js:") || isLocalPath(spec.path)) return;
|
|
705
|
+
const dir = resolveStdlibSource(spec.path, options.stdlibRoot);
|
|
706
|
+
if (!dir) return;
|
|
707
|
+
const wasmImporter = checker.target === "wasm" || checker.target === "both";
|
|
708
|
+
if (wasmImporter && stdlibIsJsOnly(dir, deps))
|
|
709
|
+
return errJsOnlyImport(checker, p, spec);
|
|
710
|
+
let pkg;
|
|
711
|
+
try {
|
|
712
|
+
pkg = loadStdlibPackage(spec.path, dir, options, deps, checker.target);
|
|
713
|
+
} catch (e) {
|
|
714
|
+
const msg = `internal stdlib error in ${spec.path}; please report`;
|
|
715
|
+
return errAtImport(checker, p, spec, msg, e.message);
|
|
716
|
+
}
|
|
717
|
+
emitStdlibPackage(pkg, checker.target, preambles, bundledDirs, options);
|
|
718
|
+
bindStdlibImport(checker, p, spec, pkg);
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
export function importStdlibPackages(
|
|
722
|
+
programs,
|
|
723
|
+
checker,
|
|
724
|
+
preambles,
|
|
725
|
+
bundledDirs,
|
|
726
|
+
options,
|
|
727
|
+
deps,
|
|
728
|
+
) {
|
|
729
|
+
const wasmImporter = checker.target === "wasm" || checker.target === "both";
|
|
730
|
+
const implicit = wasmImporter ? [] : implicitImports(programs);
|
|
731
|
+
for (const p of programs) {
|
|
732
|
+
const specs = p.imports.flatMap((imp) => imp.imports);
|
|
733
|
+
if (p === programs[0]) specs.push(...implicit);
|
|
734
|
+
for (const spec of specs)
|
|
735
|
+
importStdlibSpec(p, spec, checker, preambles, bundledDirs, options, deps);
|
|
736
|
+
}
|
|
737
|
+
}
|