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
package/src/lower/functions.js
CHANGED
|
@@ -271,6 +271,8 @@ function normalizeDeferStmt(stmt, parentList = null, index = -1, ctx = null) {
|
|
|
271
271
|
...call,
|
|
272
272
|
func: funcExpr,
|
|
273
273
|
args: callArgs,
|
|
274
|
+
// A deferred native call runs the Go body, as on the JS backend.
|
|
275
|
+
_noNative: true,
|
|
274
276
|
_multiForward: tempIdents.length > 0 ? false : call._multiForward,
|
|
275
277
|
};
|
|
276
278
|
const synthFuncLit = {
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
// src/lower/gen-types.js
|
|
2
|
+
// Spells checked types as Go source for the generated per-package file
|
|
3
|
+
// (src/lower/json-codecs.js, src/lower/errors-as.js,
|
|
4
|
+
// src/lower/fmt-formatters.js), and holds what those generators share: the
|
|
5
|
+
// imports the spelled code needs and the enumeration of the program's types.
|
|
6
|
+
|
|
7
|
+
import { defaultType } from "../typechecker/types.js";
|
|
8
|
+
import { forEachChild } from "./walk.js";
|
|
9
|
+
|
|
10
|
+
export function under(t) {
|
|
11
|
+
return t?.kind === "named" ? under(t.underlying) : t;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const isEmptyIface = (t) =>
|
|
15
|
+
(t?.kind === "basic" && t.name === "any") ||
|
|
16
|
+
(under(t)?.kind === "interface" && under(t).methods?.size === 0);
|
|
17
|
+
|
|
18
|
+
// Arrays and maps of these share one WebAssembly representation (and so one
|
|
19
|
+
// type-switch case), so only literals may register them: a `[2]byte` conversion
|
|
20
|
+
// must not shadow a `[2]int32` that is stored in an interface. A slice of
|
|
21
|
+
// them is tagged with its Go type in an interface and registers like any other.
|
|
22
|
+
export const SHARED_REPR = new Set([
|
|
23
|
+
"bool",
|
|
24
|
+
"int8",
|
|
25
|
+
"int16",
|
|
26
|
+
"int32",
|
|
27
|
+
"uint8",
|
|
28
|
+
"uint16",
|
|
29
|
+
"uint32",
|
|
30
|
+
"byte",
|
|
31
|
+
"rune",
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
export function hasSharedRepr(t, depth = 0) {
|
|
35
|
+
if (!t || depth > 8) return false;
|
|
36
|
+
const u = under(t);
|
|
37
|
+
if (u?.kind === "basic") return SHARED_REPR.has(u.name);
|
|
38
|
+
if (u?.kind === "slice" || u?.kind === "array")
|
|
39
|
+
return hasSharedRepr(u.elem, depth + 1);
|
|
40
|
+
if (u?.kind === "map")
|
|
41
|
+
return hasSharedRepr(u.key, depth + 1) || hasSharedRepr(u.value, depth + 1);
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// A method's results; a multi-value result is one tuple type.
|
|
46
|
+
export function results(m) {
|
|
47
|
+
const r = (m.returns ?? []).filter(
|
|
48
|
+
(t) => !(t?.kind === "basic" && t.name === "void"),
|
|
49
|
+
);
|
|
50
|
+
return r.length === 1 && r[0]?.kind === "tuple" ? r[0].types : r;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Calls every `fn(node)` for the AST nodes under `root`.
|
|
54
|
+
export function walkNodes(root, fn) {
|
|
55
|
+
if (!root || typeof root !== "object") return;
|
|
56
|
+
if (Array.isArray(root)) {
|
|
57
|
+
for (const n of root) walkNodes(n, fn);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
fn(root);
|
|
61
|
+
forEachChild(root, (c) => walkNodes(c, fn));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// True when `t` mentions a type parameter (a generic declaration, or a type
|
|
65
|
+
// built from one inside a generic function).
|
|
66
|
+
export function mentionsTypeParam(t, seen = new Set()) {
|
|
67
|
+
if (!t || seen.has(t)) return false;
|
|
68
|
+
seen.add(t);
|
|
69
|
+
const m = (x) => mentionsTypeParam(x, seen);
|
|
70
|
+
switch (t.kind) {
|
|
71
|
+
case "typeParam":
|
|
72
|
+
return true;
|
|
73
|
+
case "named":
|
|
74
|
+
return (t.typeArgs ?? []).some(m) || m(t.underlying);
|
|
75
|
+
case "pointer":
|
|
76
|
+
return m(t.base);
|
|
77
|
+
case "slice":
|
|
78
|
+
case "array":
|
|
79
|
+
return m(t.elem);
|
|
80
|
+
case "map":
|
|
81
|
+
return m(t.key) || m(t.value);
|
|
82
|
+
case "struct":
|
|
83
|
+
return (t._structFields ?? []).some((f) => m(f.type));
|
|
84
|
+
default:
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// The last element of an import path: the package's name.
|
|
90
|
+
const canonicalName = (modPath) => modPath.split("/").pop();
|
|
91
|
+
|
|
92
|
+
// The names the programs declare at package level.
|
|
93
|
+
function declaredNames(programs) {
|
|
94
|
+
const names = new Set();
|
|
95
|
+
for (const p of programs)
|
|
96
|
+
for (const d of p.decls ?? []) {
|
|
97
|
+
if (d.kind === "TypeDecl" || d.kind === "FuncDecl") names.add(d.name);
|
|
98
|
+
else if (d.kind === "VarDecl" || d.kind === "ConstDecl")
|
|
99
|
+
for (const spec of d.decls ?? [])
|
|
100
|
+
for (const n of spec.names ?? []) names.add(n);
|
|
101
|
+
}
|
|
102
|
+
return names;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Every name an import answers to (its alias, its package name) -> its path.
|
|
106
|
+
function importPaths(programs) {
|
|
107
|
+
const paths = new Map();
|
|
108
|
+
for (const p of programs)
|
|
109
|
+
for (const imp of p.imports ?? [])
|
|
110
|
+
for (const spec of imp.imports) {
|
|
111
|
+
if (spec.alias === "_" || spec.alias === ".") continue;
|
|
112
|
+
paths.set(canonicalName(spec.path), spec.path);
|
|
113
|
+
if (spec.alias) paths.set(spec.alias, spec.path);
|
|
114
|
+
}
|
|
115
|
+
return paths;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// The generated file's imports for the qualifiers (name -> path) its code
|
|
119
|
+
// spells: a path, or { path, alias } for a qualifier renamed to stay clear of
|
|
120
|
+
// a package-level name.
|
|
121
|
+
export function importsFor(qualifiers) {
|
|
122
|
+
return [...qualifiers].map(([name, path]) =>
|
|
123
|
+
name === canonicalName(path) ? path : { path, alias: name },
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Every type a closed-world generator may need to describe: the entries
|
|
128
|
+
// { type, node, via } for the type arguments of generic calls (`typeArg`),
|
|
129
|
+
// the type of every expression (`expr`) and the package's declared named
|
|
130
|
+
// types (`declared`, node null).
|
|
131
|
+
export function collectProgramTypes(programs, checker) {
|
|
132
|
+
const found = [];
|
|
133
|
+
for (const p of programs)
|
|
134
|
+
walkNodes(p.decls, (node) => {
|
|
135
|
+
for (const a of node._typeArgs ?? [])
|
|
136
|
+
found.push({ type: defaultType(a), node, via: "typeArg" });
|
|
137
|
+
if (node._type)
|
|
138
|
+
found.push({ type: defaultType(node._type), node, via: "expr" });
|
|
139
|
+
});
|
|
140
|
+
for (const [name, t] of checker.types) {
|
|
141
|
+
if (t?.kind !== "named" || t.name !== name) continue;
|
|
142
|
+
if (name.includes(".") || name.includes("$$")) continue;
|
|
143
|
+
if (t._genericOf || t.typeArgs?.length) continue;
|
|
144
|
+
found.push({ type: t, node: null, via: "declared" });
|
|
145
|
+
}
|
|
146
|
+
return found;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export class TypeSpeller {
|
|
150
|
+
// `qualifiers` receives name -> import path for each package the spelled
|
|
151
|
+
// code names.
|
|
152
|
+
constructor(pkgName, qualifiers, programs = []) {
|
|
153
|
+
this.pkgName = pkgName;
|
|
154
|
+
this.qualifiers = qualifiers;
|
|
155
|
+
this.programs = programs;
|
|
156
|
+
this.importPaths = importPaths(programs);
|
|
157
|
+
this.declared = declaredNames(programs);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Go source for the type.
|
|
161
|
+
spell(t) {
|
|
162
|
+
const sp = (x) => this.spell(x);
|
|
163
|
+
const byKind = {
|
|
164
|
+
basic: () => t.name,
|
|
165
|
+
named: () => this.spellNamed(t),
|
|
166
|
+
pointer: () => `*${sp(t.base)}`,
|
|
167
|
+
slice: () => `[]${sp(t.elem)}`,
|
|
168
|
+
array: () => `[${t.size}]${sp(t.elem)}`,
|
|
169
|
+
map: () => `map[${sp(t.key)}]${sp(t.value)}`,
|
|
170
|
+
struct: () => this.spellStruct(t),
|
|
171
|
+
interface: () => (t.name === "error" ? "error" : "any"),
|
|
172
|
+
func: () => this.funcString(t, sp),
|
|
173
|
+
};
|
|
174
|
+
if (!byKind[t.kind])
|
|
175
|
+
throw new Error(`json: cannot spell type kind ${t.kind}`);
|
|
176
|
+
return byKind[t.kind]();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// The package name `%T` and errors print for the bare-named type `t`: this
|
|
180
|
+
// package, or the local package an importer reaches it through as `p.Name`.
|
|
181
|
+
declaringPackage(t) {
|
|
182
|
+
if (this.declared.has(t.name) || !this.checker) return this.pkgName;
|
|
183
|
+
for (const [key, v] of this.checker.types) {
|
|
184
|
+
const dot = key.indexOf(".");
|
|
185
|
+
if (v !== t || dot <= 0 || key.slice(dot + 1) !== t.name) continue;
|
|
186
|
+
const modPath = this.importPaths.get(key.slice(0, dot));
|
|
187
|
+
if (modPath) return canonicalName(modPath);
|
|
188
|
+
}
|
|
189
|
+
return this.pkgName;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// The name the generated file uses for the package at `modPath`: its
|
|
193
|
+
// package name, or `_gf_<name>` when the package declares that name itself
|
|
194
|
+
// or another path took it.
|
|
195
|
+
qualifierFor(modPath) {
|
|
196
|
+
for (const [name, path] of this.qualifiers)
|
|
197
|
+
if (path === modPath) return name;
|
|
198
|
+
const base = canonicalName(modPath);
|
|
199
|
+
const taken = (n) => this.declared.has(n) || this.qualifiers.has(n);
|
|
200
|
+
let name = base;
|
|
201
|
+
for (let i = 1; taken(name); i++) name = `_gf_${base}${i > 1 ? i : ""}`;
|
|
202
|
+
this.qualifiers.set(name, modPath);
|
|
203
|
+
return name;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
spellNamed(t) {
|
|
207
|
+
let name = t.name;
|
|
208
|
+
const at = name.indexOf("$$");
|
|
209
|
+
const dot = name.indexOf(".");
|
|
210
|
+
if (at >= 0) {
|
|
211
|
+
const orig = name.slice(0, at);
|
|
212
|
+
const modPath = name.slice(at + 2).replaceAll("$", "/");
|
|
213
|
+
name = `${this.qualifierFor(modPath)}.${orig}`;
|
|
214
|
+
} else if (dot > 0) {
|
|
215
|
+
const modPath = this.importPaths.get(name.slice(0, dot));
|
|
216
|
+
if (modPath) name = `${this.qualifierFor(modPath)}${name.slice(dot)}`;
|
|
217
|
+
}
|
|
218
|
+
return t.typeArgs?.length
|
|
219
|
+
? `${name}[${t.typeArgs.map((a) => this.spell(a)).join(", ")}]`
|
|
220
|
+
: name;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
spellStruct(t) {
|
|
224
|
+
const fields = (t._structFields ?? []).map((f) => {
|
|
225
|
+
const tag = f.tag ? ` \`${f.tag}\`` : "";
|
|
226
|
+
if (f.embedded) return `${this.spell(f.type)}${tag}`;
|
|
227
|
+
return `${f.names.join(", ")} ${this.spell(f.type)}${tag}`;
|
|
228
|
+
});
|
|
229
|
+
return `struct { ${fields.join("; ")} }`;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
funcString(t, name) {
|
|
233
|
+
const rs = results(t);
|
|
234
|
+
const res =
|
|
235
|
+
rs.length === 0
|
|
236
|
+
? ""
|
|
237
|
+
: rs.length === 1
|
|
238
|
+
? ` ${name(rs[0])}`
|
|
239
|
+
: ` (${rs.map(name).join(", ")})`;
|
|
240
|
+
// The signature keeps a variadic parameter's element type.
|
|
241
|
+
const params = t.params.map((p, i) =>
|
|
242
|
+
t.variadic && i === t.params.length - 1 ? `...${name(p)}` : name(p),
|
|
243
|
+
);
|
|
244
|
+
return `func(${params.join(", ")})${res}`;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// True when the type can be written as Go source in the generated file.
|
|
248
|
+
// Subclasses decide what a named type, an interface and a struct field
|
|
249
|
+
// need.
|
|
250
|
+
spellable(t, depth = 0, seen = new Set()) {
|
|
251
|
+
if (!t || depth > 8) return false;
|
|
252
|
+
const ok = (x) => this.spellable(x, depth + 1, seen);
|
|
253
|
+
const byKind = {
|
|
254
|
+
basic: () => t.name !== "void" && t.name !== "nil",
|
|
255
|
+
named: () => this.spellableNamed(t, ok, seen),
|
|
256
|
+
pointer: () => ok(t.base),
|
|
257
|
+
slice: () => ok(t.elem),
|
|
258
|
+
array: () => ok(t.elem),
|
|
259
|
+
map: () => ok(t.key) && ok(t.value),
|
|
260
|
+
interface: () => this.spellableInterface(t),
|
|
261
|
+
struct: () => this.spellableStruct(t, ok),
|
|
262
|
+
func: () => t.params.every(ok) && results(t).every(ok),
|
|
263
|
+
};
|
|
264
|
+
return byKind[t.kind]?.() ?? false;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
spellableInterface(_t) {
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
spellableStruct(t, ok) {
|
|
272
|
+
return (t._structFields ?? []).every((f) => ok(f.type));
|
|
273
|
+
}
|
|
274
|
+
}
|
package/src/lower/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/lower/index.js
|
|
2
2
|
// Lowering step: analyzes AST and types to populate side-tables for backends (JS and WASM).
|
|
3
3
|
|
|
4
|
+
export * from "./bounds.js";
|
|
4
5
|
export * from "./boxing.js";
|
|
5
6
|
export * from "./captures.js";
|
|
6
7
|
export * from "./embedding.js";
|
|
@@ -17,7 +18,9 @@ import { computeEmbeddedStubs } from "./embedding.js";
|
|
|
17
18
|
import { paramEscapes } from "./escape.js";
|
|
18
19
|
import { extractNamedReturns, hasDefer, normalizeDefers } from "./functions.js";
|
|
19
20
|
import { computeGlobalAnalysis } from "./globals.js";
|
|
20
|
-
import { OwnershipContext } from "./ownership.js";
|
|
21
|
+
import { OwnershipContext, takesElementAddress } from "./ownership.js";
|
|
22
|
+
|
|
23
|
+
export { takesElementAddress };
|
|
21
24
|
|
|
22
25
|
class LowerResult {
|
|
23
26
|
constructor() {
|
|
@@ -39,9 +42,35 @@ class LowerResult {
|
|
|
39
42
|
this.initPathFunctions = new Set();
|
|
40
43
|
// Map<ASTNode, Array<string>>
|
|
41
44
|
this.cachedGlobalsByFunc = new Map();
|
|
45
|
+
// True when some code takes `&xs[i]` (see takesElementAddress)
|
|
46
|
+
this.elemAddrTaken = false;
|
|
42
47
|
}
|
|
43
48
|
}
|
|
44
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Runs the per-function analyses for `d` and records them in `res`. Also
|
|
52
|
+
* used for synthesized functions (the package-level initializer).
|
|
53
|
+
*/
|
|
54
|
+
export function lowerFunction(res, d) {
|
|
55
|
+
if (!d.body) return;
|
|
56
|
+
const boxed = scanAddressTaken(d.body, new Set());
|
|
57
|
+
res.boxedVars.set(d, boxed);
|
|
58
|
+
res.ownership.set(d, new OwnershipContext(d.body));
|
|
59
|
+
res.functions.set(d, {
|
|
60
|
+
hasDefer: hasDefer(d.body),
|
|
61
|
+
namedReturns: extractNamedReturns(d),
|
|
62
|
+
});
|
|
63
|
+
res.captures.set(d, analyzeCaptures(d));
|
|
64
|
+
|
|
65
|
+
const escapingParams = new Set();
|
|
66
|
+
for (const p of d.params ?? []) {
|
|
67
|
+
if (p.name && p.name !== "_" && paramEscapes(d.body, p.name)) {
|
|
68
|
+
escapingParams.add(p.name);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
res.escapes.set(d, escapingParams);
|
|
72
|
+
}
|
|
73
|
+
|
|
45
74
|
/**
|
|
46
75
|
* Runs lowering analyses across an AST or collection of programs, returning side-tables.
|
|
47
76
|
* @param {Object} programOrPrograms
|
|
@@ -56,6 +85,8 @@ export function lower(programOrPrograms, checker = null) {
|
|
|
56
85
|
|
|
57
86
|
normalizeDefers(programs);
|
|
58
87
|
|
|
88
|
+
res.elemAddrTaken = programs.some((p) => takesElementAddress(p?.decls));
|
|
89
|
+
|
|
59
90
|
// 1. Aggregate methods across all files in the package
|
|
60
91
|
const methodMap = new Map();
|
|
61
92
|
for (const program of programs) {
|
|
@@ -74,24 +105,7 @@ export function lower(programOrPrograms, checker = null) {
|
|
|
74
105
|
|
|
75
106
|
for (const d of program.decls) {
|
|
76
107
|
if (d.kind === "FuncDecl" || d.kind === "MethodDecl") {
|
|
77
|
-
|
|
78
|
-
const boxed = scanAddressTaken(d.body, new Set());
|
|
79
|
-
res.boxedVars.set(d, boxed);
|
|
80
|
-
res.ownership.set(d, new OwnershipContext(d.body));
|
|
81
|
-
res.functions.set(d, {
|
|
82
|
-
hasDefer: hasDefer(d.body),
|
|
83
|
-
namedReturns: extractNamedReturns(d),
|
|
84
|
-
});
|
|
85
|
-
res.captures.set(d, analyzeCaptures(d));
|
|
86
|
-
|
|
87
|
-
const escapingParams = new Set();
|
|
88
|
-
for (const p of d.params ?? []) {
|
|
89
|
-
if (p.name && p.name !== "_" && paramEscapes(d.body, p.name)) {
|
|
90
|
-
escapingParams.add(p.name);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
res.escapes.set(d, escapingParams);
|
|
94
|
-
}
|
|
108
|
+
lowerFunction(res, d);
|
|
95
109
|
}
|
|
96
110
|
}
|
|
97
111
|
|