gofront 1.6.1 → 1.8.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 +45 -49
- package/package.json +12 -8
- package/src/backend/js/expressions.js +1627 -148
- package/src/backend/js/fmt-runtime.js +573 -0
- package/src/backend/js/index.js +508 -102
- package/src/backend/js/runtime.js +1997 -106
- package/src/backend/js/statements.js +931 -87
- package/src/backend/js/stdlib/bits.js +87 -14
- package/src/backend/js/stdlib/builder.js +41 -6
- package/src/backend/js/stdlib/bytes.js +253 -7
- package/src/backend/js/stdlib/errors.js +7 -1
- package/src/backend/js/stdlib/fmt.js +349 -10
- package/src/backend/js/stdlib/index.js +19 -2
- package/src/backend/js/stdlib/json.js +1167 -0
- package/src/backend/js/stdlib/maps.js +0 -8
- package/src/backend/js/stdlib/math.js +17 -1
- package/src/backend/js/stdlib/simd.js +34 -0
- package/src/backend/js/stdlib/slices.js +36 -6
- package/src/backend/js/stdlib/strconv.js +10 -5
- package/src/backend/js/stdlib/strings.js +83 -4
- package/src/backend/js/stdlib/testing.js +7 -0
- package/src/backend/js/stdlib/time.js +79 -55
- package/src/backend/js/stdlib/unicode.js +1 -1
- package/src/backend/js/stdlib/utf8.js +1 -1
- package/src/backend/js/templ.js +38 -76
- package/src/backend/js/type-info.js +272 -0
- package/src/backend/shared/fmt-types.js +188 -0
- package/src/backend/wasm/boundary.js +446 -72
- package/src/backend/wasm/emit-builtins.js +320 -13
- package/src/backend/wasm/emit-exprs.js +1404 -199
- package/src/backend/wasm/emit-maps.js +61 -13
- package/src/backend/wasm/emit-stmts.js +1359 -164
- package/src/backend/wasm/emit.js +518 -74
- package/src/backend/wasm/encode.js +128 -13
- package/src/backend/wasm/globals.js +452 -0
- package/src/backend/wasm/index.js +194 -2489
- package/src/backend/wasm/method-expr.js +381 -0
- package/src/backend/wasm/module-emitter.js +2240 -0
- package/src/backend/wasm/monomorph.js +62 -4
- package/src/backend/wasm/optimize.js +4 -2
- package/src/backend/wasm/prescan.js +397 -0
- package/src/backend/wasm/stdlib/bytes.js +1804 -127
- package/src/backend/wasm/stdlib/core.js +1574 -199
- package/src/backend/wasm/stdlib/index.js +3 -5
- package/src/backend/wasm/stdlib/io.js +1567 -150
- package/src/backend/wasm/stdlib/packages.js +2 -0
- package/src/backend/wasm/stdlib/simd.js +499 -0
- package/src/backend/wasm/stdlib/time.js +1041 -12
- package/src/backend/wasm/types.js +221 -15
- package/src/backend/wasm/variadic.js +93 -0
- package/src/backend/wasm/wat.js +84 -20
- package/src/{cli-core.js → cli/cli-core.js} +236 -10
- package/src/{test-runner.js → cli/test-runner.js} +332 -39
- package/src/{vendor.js → cli/vendor.js} +89 -15
- package/src/compiler.js +107 -269
- package/src/dts-parser.js +71 -14
- package/src/index.js +15 -169
- package/src/{lexer.js → lexer/lexer.js} +102 -55
- package/src/{tokens.js → lexer/tokens.js} +1 -0
- package/src/link/sourcemap.js +97 -0
- package/src/link/stdlib.js +515 -0
- package/src/link/wasm.js +294 -0
- package/src/lower/bounds.js +394 -0
- package/src/lower/boxing.js +1 -6
- package/src/lower/captures.js +176 -32
- package/src/lower/embedding.js +2 -1
- package/src/lower/index.js +26 -18
- package/src/lower/json-codecs.js +1398 -0
- package/src/lower/ownership.js +22 -1
- package/src/lower/range.js +86 -1
- package/src/lower/reachability.js +132 -0
- package/src/parser/declarations.js +58 -13
- package/src/parser/expressions.js +29 -7
- package/src/parser/index.js +2 -1
- package/src/parser/statements.js +37 -22
- package/src/parser/struct-tag.js +144 -0
- package/src/{templ-parser.js → parser/templ.js} +138 -15
- package/src/parser/types.js +130 -13
- package/src/resolver.js +22 -1
- package/src/typechecker/assignability.js +215 -11
- package/src/typechecker/const-fold.js +356 -0
- package/src/typechecker/constants.js +256 -0
- package/src/typechecker/expressions.js +960 -99
- package/src/typechecker/index.js +491 -72
- package/src/typechecker/init-order.js +330 -0
- package/src/typechecker/resolve.js +596 -52
- package/src/typechecker/statements.js +157 -17
- package/src/typechecker/stdlib/core.js +333 -55
- package/src/typechecker/stdlib/excluded.js +898 -0
- package/src/typechecker/stdlib/extended.js +359 -26
- package/src/typechecker/{stdlib.js → stdlib/index.js} +6 -4
- package/src/typechecker/stdlib/simd.js +123 -0
- package/src/typechecker/termination.js +1 -1
- package/src/typechecker/types.js +165 -26
- package/stdlib/bytes/iter.go +192 -0
- package/stdlib/bytes/search.go +240 -0
- package/stdlib/bytes/split.go +218 -0
- package/stdlib/cmp/cmp.go +65 -0
- package/stdlib/encoding/json/errors.go +31 -0
- package/stdlib/encoding/json/indent.go +71 -0
- package/stdlib/encoding/json/number.go +12 -0
- package/stdlib/encoding/json/stream.go +173 -0
- package/stdlib/errors/overlay.go +12 -0
- package/stdlib/hash/hash.go +53 -0
- package/stdlib/internal/jsonrt/dec.go +738 -0
- package/stdlib/internal/jsonrt/enc.go +366 -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 +649 -0
- package/stdlib/io/io.go +346 -0
- package/stdlib/iter/iter.go +18 -0
- package/stdlib/maps/iter.go +65 -0
- package/stdlib/math/extra.go +270 -0
- package/stdlib/slices/extra.go +73 -0
- package/stdlib/slices/iter.go +113 -0
- package/stdlib/sort/sort.go +561 -0
- package/stdlib/strconv/quote.go +341 -0
- package/stdlib/strings/extra.go +35 -0
- package/stdlib/strings/iter.go +122 -0
- package/stdlib/sync/extra.go +105 -0
- package/stdlib/unicode/unicode.go +112 -0
- package/stdlib/unicode/utf8/bytes.go +239 -0
- package/src/backend/wasm/const-fold.js +0 -205
- package/src/backend/wasm/emit-stdlib.js +0 -13
- /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/{templ-lexer.js → lexer/templ-lexer.js} +0 -0
|
@@ -33,7 +33,13 @@ export function hasGenerics(programs) {
|
|
|
33
33
|
/**
|
|
34
34
|
* Returns a stable identifier-safe string key for any Go type or AST type node.
|
|
35
35
|
*/
|
|
36
|
+
// A qualified type argument (`time.Duration`) becomes part of a JS and wasm
|
|
37
|
+
// export identifier, so its dot is replaced.
|
|
36
38
|
function typeKey(t) {
|
|
39
|
+
return rawTypeKey(t).replaceAll(".", "_");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function rawTypeKey(t) {
|
|
37
43
|
if (!t) return "any";
|
|
38
44
|
if (typeof t === "string") return t;
|
|
39
45
|
if (t.kind === "basic") return t.name;
|
|
@@ -78,7 +84,7 @@ function typeKey(t) {
|
|
|
78
84
|
/**
|
|
79
85
|
* Converts a Go type representation or AST type node into a concrete AST type node.
|
|
80
86
|
*/
|
|
81
|
-
function typeToTypeNode(t) {
|
|
87
|
+
export function typeToTypeNode(t) {
|
|
82
88
|
if (!t) return { kind: "TypeName", name: "any" };
|
|
83
89
|
if (typeof t === "string") return { kind: "TypeName", name: t };
|
|
84
90
|
if (
|
|
@@ -133,16 +139,48 @@ function getSpecializedName(baseName, typeArgs) {
|
|
|
133
139
|
return `${baseName}$${keys}`;
|
|
134
140
|
}
|
|
135
141
|
|
|
142
|
+
const KEPT_CLONE_KEYS = new Set(["_typeArgs", "_line", "_col"]);
|
|
143
|
+
|
|
136
144
|
function cloneAst(node) {
|
|
137
145
|
if (!node || typeof node !== "object") return node;
|
|
138
146
|
if (Array.isArray(node)) return node.map(cloneAst);
|
|
139
147
|
const copy = {};
|
|
140
148
|
for (const [k, v] of Object.entries(node)) {
|
|
149
|
+
// Side tables are dropped, except source positions (for diagnostics in
|
|
150
|
+
// the specialised body) and the inferred type arguments of a generic
|
|
151
|
+
// call inside a generic body: substitution needs them to specialise the
|
|
152
|
+
// callee (`isNaN(x)` inside `Compare[T]`).
|
|
153
|
+
if (k.startsWith("_") && !KEPT_CLONE_KEYS.has(k)) continue;
|
|
141
154
|
copy[k] = cloneAst(v);
|
|
142
155
|
}
|
|
156
|
+
// Named results hang off the return type node and alias its types; keep
|
|
157
|
+
// just the names so `rebindNamedResults` can rebuild them after the types
|
|
158
|
+
// are substituted.
|
|
159
|
+
const named = node.returnType?._namedReturns;
|
|
160
|
+
if (named) copy._resultNames = named.map((r) => r.name);
|
|
143
161
|
return copy;
|
|
144
162
|
}
|
|
145
163
|
|
|
164
|
+
// Re-attaches named results (`(old *T)`) to every function in a cloned and
|
|
165
|
+
// substituted tree, pointing at the substituted type nodes.
|
|
166
|
+
function rebindNamedResults(node) {
|
|
167
|
+
if (!node || typeof node !== "object") return;
|
|
168
|
+
if (Array.isArray(node)) {
|
|
169
|
+
for (const n of node) rebindNamedResults(n);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
if (node._resultNames && node.returnType) {
|
|
173
|
+
const rt = node.returnType;
|
|
174
|
+
rt._namedReturns = node._resultNames.map((name, i) => ({
|
|
175
|
+
name,
|
|
176
|
+
type: rt.kind === "TupleType" ? rt.types[i] : rt,
|
|
177
|
+
}));
|
|
178
|
+
}
|
|
179
|
+
for (const [k, v] of Object.entries(node)) {
|
|
180
|
+
if (!k.startsWith("_")) rebindNamedResults(v);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
146
184
|
/**
|
|
147
185
|
* Replaces references to type parameters in AST nodes with concrete types.
|
|
148
186
|
*/
|
|
@@ -246,6 +284,23 @@ function substituteTypesInAst(
|
|
|
246
284
|
|
|
247
285
|
// 4. CallExpr
|
|
248
286
|
if (node.kind === "CallExpr") {
|
|
287
|
+
// `T(x)` with T a type parameter becomes the conversion `int8(x)`.
|
|
288
|
+
const conv =
|
|
289
|
+
node.func?.kind === "Ident" ? typeSubstMap.get(node.func.name) : null;
|
|
290
|
+
if (conv && node.args?.length === 1) {
|
|
291
|
+
return {
|
|
292
|
+
kind: "TypeConversion",
|
|
293
|
+
targetType: cloneAst(conv),
|
|
294
|
+
expr: substituteTypesInAst(
|
|
295
|
+
node.args[0],
|
|
296
|
+
typeSubstMap,
|
|
297
|
+
onGenericUsage,
|
|
298
|
+
genericTypes,
|
|
299
|
+
genericFuncs,
|
|
300
|
+
),
|
|
301
|
+
_line: node._line,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
249
304
|
if (node.func?.kind === "Ident") {
|
|
250
305
|
let typeArgs = node._typeArgs ?? node.func._typeArgs;
|
|
251
306
|
if (typeArgs) {
|
|
@@ -593,6 +648,7 @@ export function monomorphise(programs, checker) {
|
|
|
593
648
|
genericTypes,
|
|
594
649
|
genericFuncs,
|
|
595
650
|
);
|
|
651
|
+
rebindNamedResults(monoTypeDecl);
|
|
596
652
|
specializedTypes.set(item.specializedName, monoTypeDecl);
|
|
597
653
|
|
|
598
654
|
// Specialize all methods declared on this generic type
|
|
@@ -610,6 +666,7 @@ export function monomorphise(programs, checker) {
|
|
|
610
666
|
genericTypes,
|
|
611
667
|
genericFuncs,
|
|
612
668
|
);
|
|
669
|
+
rebindNamedResults(monoMethod);
|
|
613
670
|
const mKey = `${item.specializedName}.${monoMethod.name}`;
|
|
614
671
|
specializedMethods.set(mKey, monoMethod);
|
|
615
672
|
}
|
|
@@ -634,6 +691,7 @@ export function monomorphise(programs, checker) {
|
|
|
634
691
|
genericTypes,
|
|
635
692
|
genericFuncs,
|
|
636
693
|
);
|
|
694
|
+
rebindNamedResults(monoFuncDecl);
|
|
637
695
|
specializedFuncs.set(item.specializedName, monoFuncDecl);
|
|
638
696
|
}
|
|
639
697
|
}
|
|
@@ -703,9 +761,9 @@ export function monomorphise(programs, checker) {
|
|
|
703
761
|
}
|
|
704
762
|
}
|
|
705
763
|
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
764
|
+
// One pass over all programs: declarations of every linked package are
|
|
765
|
+
// visible to the others (cross-package references are dequalified).
|
|
766
|
+
monoChecker.checkAll(progs);
|
|
709
767
|
|
|
710
768
|
return { programs: progs, checker: monoChecker };
|
|
711
769
|
}
|
|
@@ -24,8 +24,8 @@ import { join } from "node:path";
|
|
|
24
24
|
import { GOFRONT_SECTION_BYTES, isGoFrontWasm } from "./encode.js";
|
|
25
25
|
|
|
26
26
|
// Binaryen feature bitmask: GC (1024) | ReferenceTypes (256) | BulkMemory (16) | Multivalue (512) |
|
|
27
|
-
// ExceptionHandling (64) | MutableGlobals (2) | NontrappingFPToInt (4) | TailCall (128) =
|
|
28
|
-
export const DEFAULT_WASM_FEATURES =
|
|
27
|
+
// ExceptionHandling (64) | MutableGlobals (2) | NontrappingFPToInt (4) | TailCall (128) | SIMD (8) = 2014
|
|
28
|
+
export const DEFAULT_WASM_FEATURES = 2014;
|
|
29
29
|
|
|
30
30
|
let _nativeWasmOptAvailable = null;
|
|
31
31
|
let _npmWasmOptPath = null;
|
|
@@ -98,6 +98,7 @@ function runWasmOpt(
|
|
|
98
98
|
"--enable-mutable-globals",
|
|
99
99
|
"--enable-nontrapping-float-to-int",
|
|
100
100
|
"--enable-tail-call",
|
|
101
|
+
"--enable-simd",
|
|
101
102
|
];
|
|
102
103
|
if (options.gufa !== false) {
|
|
103
104
|
args.push("--gufa");
|
|
@@ -141,6 +142,7 @@ function runWasmOpt(
|
|
|
141
142
|
"--enable-mutable-globals",
|
|
142
143
|
"--enable-nontrapping-float-to-int",
|
|
143
144
|
"--enable-tail-call",
|
|
145
|
+
"--enable-simd",
|
|
144
146
|
output,
|
|
145
147
|
"-S",
|
|
146
148
|
"-o",
|
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
// src/backend/wasm/prescan.js
|
|
2
|
+
// Pre-scan helpers: walk the lowered AST to decide which imports, runtime helpers
|
|
3
|
+
// and interface dispatchers a module needs before emission starts.
|
|
4
|
+
|
|
5
|
+
import { BINARY_MATH_IMPORTS } from "./stdlib/core.js";
|
|
6
|
+
import {
|
|
7
|
+
anonIfaceName,
|
|
8
|
+
getInterfaceMethodList,
|
|
9
|
+
getReceiverTypeName,
|
|
10
|
+
isAnyType,
|
|
11
|
+
isBoolGoType,
|
|
12
|
+
isInterfaceType,
|
|
13
|
+
isNonEmptyInterface,
|
|
14
|
+
isTestingT,
|
|
15
|
+
toWasmType,
|
|
16
|
+
} from "./types.js";
|
|
17
|
+
|
|
18
|
+
export function importsAnyOf(programs, pkgs) {
|
|
19
|
+
const has = (p) => (pkgs instanceof Set ? pkgs.has(p) : pkgs.includes(p));
|
|
20
|
+
return (programs ?? []).some((p) =>
|
|
21
|
+
(p.imports ?? []).some((decl) =>
|
|
22
|
+
(decl.imports ?? [decl]).some((imp) => has(imp.path)),
|
|
23
|
+
),
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function hasErrorUsage(programs) {
|
|
28
|
+
return (programs ?? []).some((p) => {
|
|
29
|
+
let found = false;
|
|
30
|
+
const scan = (n) => {
|
|
31
|
+
if (!n || found || typeof n !== "object") return;
|
|
32
|
+
if (Array.isArray(n)) {
|
|
33
|
+
for (const x of n) scan(x);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (
|
|
37
|
+
(n.kind === "Ident" ||
|
|
38
|
+
n.kind === "TypeName" ||
|
|
39
|
+
n.kind === "basic" ||
|
|
40
|
+
n.kind === "interface") &&
|
|
41
|
+
n.name === "error"
|
|
42
|
+
) {
|
|
43
|
+
found = true;
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
for (const k of Object.keys(n)) {
|
|
47
|
+
if (!k.startsWith("_")) scan(n[k]);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
scan(p);
|
|
51
|
+
return found;
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Depth-first search over AST nodes (skipping `_`-prefixed annotations).
|
|
56
|
+
export function _someNode(node, pred) {
|
|
57
|
+
if (!node || typeof node !== "object") return false;
|
|
58
|
+
if (Array.isArray(node)) return node.some((n) => _someNode(n, pred));
|
|
59
|
+
if (pred(node)) return true;
|
|
60
|
+
for (const key of Object.keys(node)) {
|
|
61
|
+
if (key.startsWith("_")) continue;
|
|
62
|
+
if (_someNode(node[key], pred)) return true;
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function _usesTestingRun(node) {
|
|
68
|
+
return _someNode(
|
|
69
|
+
node,
|
|
70
|
+
(n) =>
|
|
71
|
+
n.kind === "CallExpr" &&
|
|
72
|
+
n.func?.kind === "SelectorExpr" &&
|
|
73
|
+
n.func.field === "Run" &&
|
|
74
|
+
isTestingT(n.func.expr?._type),
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function _usesTestingCleanup(node) {
|
|
79
|
+
return _someNode(
|
|
80
|
+
node,
|
|
81
|
+
(n) =>
|
|
82
|
+
n.kind === "CallExpr" &&
|
|
83
|
+
n.func?.kind === "SelectorExpr" &&
|
|
84
|
+
n.func.field === "Cleanup" &&
|
|
85
|
+
isTestingT(n.func.expr?._type),
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// `__any_eq` is only needed when some `==`/`!=` (or switch case) compares
|
|
90
|
+
// anyref operands, a struct with an anyref field is compared, or the error
|
|
91
|
+
// runtime (`errors.Is`) is present. `_emitAnyEq` throws if this misses a
|
|
92
|
+
// site, so a gap surfaces as a compile error rather than a bad module.
|
|
93
|
+
export function _usesAnyEq(funcDecls, mod) {
|
|
94
|
+
if (mod.hasErrorType) return true;
|
|
95
|
+
for (const info of mod.mapTypes.values()) {
|
|
96
|
+
if (isInterfaceType(info.keyGoType, mod.checker)) return true;
|
|
97
|
+
}
|
|
98
|
+
for (const info of mod.structTypes.values()) {
|
|
99
|
+
if (info.fields?.some((f) => f.wType === "anyref")) return true;
|
|
100
|
+
}
|
|
101
|
+
const isAnyRef = (n) =>
|
|
102
|
+
Boolean(n?._type) && toWasmType(n._type, mod.checker, mod) === "anyref";
|
|
103
|
+
return funcDecls.some((fn) =>
|
|
104
|
+
_someNode(fn.body, (n) => {
|
|
105
|
+
if (n.kind === "BinaryExpr" && (n.op === "==" || n.op === "!="))
|
|
106
|
+
return isAnyRef(n.left) || isAnyRef(n.right);
|
|
107
|
+
if (n.kind === "SwitchStmt" && n.tag) return isAnyRef(n.tag);
|
|
108
|
+
return false;
|
|
109
|
+
}),
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// An `fmt` call with an `any`/interface operand or a `xs...` spread needs
|
|
114
|
+
// `__any_fmt`. `emitFmtCall` falls back to scalar-only dispatch without it.
|
|
115
|
+
export function _usesFmtAny(funcDecls, mod) {
|
|
116
|
+
return funcDecls.some((fn) =>
|
|
117
|
+
_someNode(
|
|
118
|
+
fn.body,
|
|
119
|
+
(n) =>
|
|
120
|
+
n.kind === "CallExpr" &&
|
|
121
|
+
n.func?.kind === "SelectorExpr" &&
|
|
122
|
+
n.func.expr?.kind === "Ident" &&
|
|
123
|
+
n.func.expr.name === "fmt" &&
|
|
124
|
+
n.args?.some(
|
|
125
|
+
(a) =>
|
|
126
|
+
a._spread ||
|
|
127
|
+
isAnyType(a._type, mod.checker) ||
|
|
128
|
+
isInterfaceType(a._type, mod.checker),
|
|
129
|
+
),
|
|
130
|
+
),
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Whether a value of this static type may print a 64-bit unsigned integer
|
|
135
|
+
// (directly, inside a container or struct, or behind `any`/an interface).
|
|
136
|
+
export function typeMayHoldU64(t, mod, seen = new Set(), depth = 0) {
|
|
137
|
+
if (!t || typeof t !== "object" || seen.has(t) || depth > 8) return false;
|
|
138
|
+
seen.add(t);
|
|
139
|
+
if (isAnyType(t, mod.checker) || isInterfaceType(t, mod.checker)) return true;
|
|
140
|
+
const sub = (x) => typeMayHoldU64(x, mod, seen, depth + 1);
|
|
141
|
+
switch (t.kind) {
|
|
142
|
+
case "basic":
|
|
143
|
+
return t.name === "uint64" || t.name === "uint" || t.name === "uintptr";
|
|
144
|
+
case "TypeName":
|
|
145
|
+
case "Ident":
|
|
146
|
+
return sub(
|
|
147
|
+
mod.checker?.resolveType?.(t) ?? { kind: "basic", name: t.name },
|
|
148
|
+
);
|
|
149
|
+
case "named":
|
|
150
|
+
return sub(t.underlying);
|
|
151
|
+
case "tuple":
|
|
152
|
+
return (t.types ?? []).some(sub);
|
|
153
|
+
case "pointer":
|
|
154
|
+
return sub(t.base);
|
|
155
|
+
case "slice":
|
|
156
|
+
case "array":
|
|
157
|
+
return sub(t.elem);
|
|
158
|
+
case "map":
|
|
159
|
+
return sub(t.key) || sub(t.value);
|
|
160
|
+
case "struct":
|
|
161
|
+
return [...(t.fields?.values?.() ?? [])].some(sub);
|
|
162
|
+
default:
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function _scanImportsInBody(node, mod) {
|
|
168
|
+
if (!node || typeof node !== "object") return;
|
|
169
|
+
|
|
170
|
+
if (node.kind === "CallExpr") {
|
|
171
|
+
const { func, args } = node;
|
|
172
|
+
if ((args ?? []).some((a) => a._spread || typeMayHoldU64(a._type, mod))) {
|
|
173
|
+
mod.getFmtBoxU64Index();
|
|
174
|
+
}
|
|
175
|
+
if (
|
|
176
|
+
func.kind === "Ident" &&
|
|
177
|
+
(func.name === "print" || func.name === "println")
|
|
178
|
+
) {
|
|
179
|
+
const isPrintln = func.name === "println";
|
|
180
|
+
if (args.length === 0 && isPrintln) {
|
|
181
|
+
mod.getPrintlnEmptyIndex();
|
|
182
|
+
}
|
|
183
|
+
for (let i = 0; i < args.length; i++) {
|
|
184
|
+
const arg = args[i];
|
|
185
|
+
const isLast = i === args.length - 1 && isPrintln;
|
|
186
|
+
const isBool =
|
|
187
|
+
isBoolGoType(arg._type) ||
|
|
188
|
+
(arg.kind === "BasicLit" && arg.litKind === "BOOL");
|
|
189
|
+
const wType = toWasmType(arg._type, mod.checker);
|
|
190
|
+
mod.getLogImportIndex(wType, isLast, isBool);
|
|
191
|
+
}
|
|
192
|
+
} else if (
|
|
193
|
+
func.kind === "SelectorExpr" &&
|
|
194
|
+
func.expr?.name === "math" &&
|
|
195
|
+
func.field
|
|
196
|
+
) {
|
|
197
|
+
const jsMathMap = {
|
|
198
|
+
Sin: "sin",
|
|
199
|
+
Cos: "cos",
|
|
200
|
+
Tan: "tan",
|
|
201
|
+
Asin: "asin",
|
|
202
|
+
Acos: "acos",
|
|
203
|
+
Atan: "atan",
|
|
204
|
+
Atan2: "atan2",
|
|
205
|
+
Pow: "pow",
|
|
206
|
+
Mod: "mod",
|
|
207
|
+
Hypot: "hypot",
|
|
208
|
+
Cbrt: "cbrt",
|
|
209
|
+
Exp: "exp",
|
|
210
|
+
Log: "log",
|
|
211
|
+
Log1p: "log1p",
|
|
212
|
+
Log2: "log2",
|
|
213
|
+
Log10: "log10",
|
|
214
|
+
Expm1: "expm1",
|
|
215
|
+
Round: "round",
|
|
216
|
+
Sinh: "sinh",
|
|
217
|
+
Cosh: "cosh",
|
|
218
|
+
Tanh: "tanh",
|
|
219
|
+
};
|
|
220
|
+
if (jsMathMap[func.field]) {
|
|
221
|
+
const isBinary = BINARY_MATH_IMPORTS.has(jsMathMap[func.field]);
|
|
222
|
+
mod.getMathImportIndex(jsMathMap[func.field], isBinary);
|
|
223
|
+
}
|
|
224
|
+
} else if (
|
|
225
|
+
func.kind === "SelectorExpr" &&
|
|
226
|
+
isTestingT(func.expr?._type) &&
|
|
227
|
+
func.field
|
|
228
|
+
) {
|
|
229
|
+
mod.internString(func.field);
|
|
230
|
+
if (func.field === "Name") mod.getTestingNameImportIndex();
|
|
231
|
+
else if (func.field === "Failed" || func.field === "Skipped")
|
|
232
|
+
mod.getTestingFlagImportIndex();
|
|
233
|
+
else if (func.field === "Run") {
|
|
234
|
+
// closure type for func(*testing.T) must exist before bodies are emitted
|
|
235
|
+
mod.getTestingRunClosureType();
|
|
236
|
+
} else if (func.field === "Cleanup") {
|
|
237
|
+
mod.getTestingCleanupClosureType();
|
|
238
|
+
} else {
|
|
239
|
+
mod.getTestingCallImportIndex();
|
|
240
|
+
for (const arg of args) {
|
|
241
|
+
mod.getTestingArgImportIndex(
|
|
242
|
+
toWasmType(arg._type, mod.checker),
|
|
243
|
+
isBoolGoType(arg._type) ||
|
|
244
|
+
(arg.kind === "BasicLit" && arg.litKind === "BOOL"),
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (node.kind === "BasicLit" && node.litKind === "STRING") {
|
|
252
|
+
mod.getStringImportIndex();
|
|
253
|
+
const strVal = String(node.value ?? "");
|
|
254
|
+
mod.internString(strVal);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Recurse child properties
|
|
258
|
+
for (const key of Object.keys(node)) {
|
|
259
|
+
if (key.startsWith("_")) continue;
|
|
260
|
+
const child = node[key];
|
|
261
|
+
if (Array.isArray(child)) {
|
|
262
|
+
for (const item of child) _scanImportsInBody(item, mod);
|
|
263
|
+
} else if (child && typeof child === "object") {
|
|
264
|
+
_scanImportsInBody(child, mod);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export function _scanInterfaceCallNodes(node, interfaceCalls, checker) {
|
|
270
|
+
if (!node || typeof node !== "object") return;
|
|
271
|
+
if (Array.isArray(node)) {
|
|
272
|
+
for (const item of node)
|
|
273
|
+
_scanInterfaceCallNodes(item, interfaceCalls, checker);
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
if (node.kind === "CallExpr" && node.func?.kind === "SelectorExpr") {
|
|
277
|
+
const recvType = node.func.expr?._type;
|
|
278
|
+
if (isNonEmptyInterface(recvType, checker)) {
|
|
279
|
+
const ifaceName =
|
|
280
|
+
getReceiverTypeName(recvType, node.func.expr) ||
|
|
281
|
+
anonIfaceName(recvType);
|
|
282
|
+
const methodName = node.func.field;
|
|
283
|
+
const key = `${ifaceName}.${methodName}`;
|
|
284
|
+
if (!interfaceCalls.has(key)) {
|
|
285
|
+
interfaceCalls.set(key, {
|
|
286
|
+
ifaceName,
|
|
287
|
+
ifaceType: recvType,
|
|
288
|
+
methodName,
|
|
289
|
+
callNode: node,
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
for (const key of Object.keys(node)) {
|
|
295
|
+
if (!key.startsWith("_"))
|
|
296
|
+
_scanInterfaceCallNodes(node[key], interfaceCalls, checker);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export function _scanInterfaceTypeDecls(progs, interfaceCalls, checker) {
|
|
301
|
+
for (const p of progs) {
|
|
302
|
+
for (const d of p.decls ?? []) {
|
|
303
|
+
if (d.kind !== "TypeDecl") continue;
|
|
304
|
+
const resolved = checker?.types?.get(d.name) ?? d.type;
|
|
305
|
+
const underlying =
|
|
306
|
+
resolved?.kind === "named"
|
|
307
|
+
? resolved.underlying
|
|
308
|
+
: d.type?.kind === "InterfaceType"
|
|
309
|
+
? d.type
|
|
310
|
+
: resolved;
|
|
311
|
+
if (!isNonEmptyInterface(underlying, checker)) continue;
|
|
312
|
+
const methods =
|
|
313
|
+
underlying.methods instanceof Map
|
|
314
|
+
? Array.from(underlying.methods.entries())
|
|
315
|
+
: Array.isArray(underlying.methods)
|
|
316
|
+
? underlying.methods.map((m) => [m.name ?? m, m])
|
|
317
|
+
: [];
|
|
318
|
+
for (const [mName, mSig] of methods) {
|
|
319
|
+
const key = `${d.name}.${mName}`;
|
|
320
|
+
if (!interfaceCalls.has(key)) {
|
|
321
|
+
interfaceCalls.set(key, {
|
|
322
|
+
ifaceName: d.name,
|
|
323
|
+
ifaceType: resolved,
|
|
324
|
+
methodName: mName,
|
|
325
|
+
methodSig: mSig,
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// A qualified interface (`fmt.Stringer`) in a signature is a boundary type
|
|
334
|
+
// when the function is exported, so its dispatchers must exist. Returns true
|
|
335
|
+
// when `t` names such a type.
|
|
336
|
+
function _registerQualifiedIface(t, interfaceCalls, checker) {
|
|
337
|
+
if (t.kind !== "TypeName" || !t.name?.includes(".")) return false;
|
|
338
|
+
const resolved = checker?.types?.get(t.name);
|
|
339
|
+
const iface = resolved?.underlying ?? resolved;
|
|
340
|
+
if (!resolved || !isNonEmptyInterface(iface, checker)) return true;
|
|
341
|
+
for (const m of getInterfaceMethodList(iface)) {
|
|
342
|
+
const key = `${t.name}.${m.name}`;
|
|
343
|
+
if (interfaceCalls.has(key)) continue;
|
|
344
|
+
interfaceCalls.set(key, {
|
|
345
|
+
ifaceName: t.name,
|
|
346
|
+
ifaceType: resolved,
|
|
347
|
+
methodName: m.name,
|
|
348
|
+
methodSig: { kind: "func", params: m.params, returns: m.returns },
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
return true;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export function _scanFuncSigInterfaces(
|
|
355
|
+
progs,
|
|
356
|
+
funcDecls,
|
|
357
|
+
interfaceCalls,
|
|
358
|
+
checker,
|
|
359
|
+
mod,
|
|
360
|
+
) {
|
|
361
|
+
const scanType = (t) => {
|
|
362
|
+
if (!t) return;
|
|
363
|
+
if (_registerQualifiedIface(t, interfaceCalls, checker)) return;
|
|
364
|
+
if (t.kind === "InterfaceType" || (t.kind === "interface" && !t.name)) {
|
|
365
|
+
const mList = getInterfaceMethodList(t);
|
|
366
|
+
if (mList.length === 0) return;
|
|
367
|
+
const ifaceName = anonIfaceName(t, checker, mod);
|
|
368
|
+
for (const m of mList) {
|
|
369
|
+
const key = `${ifaceName}.${m.name}`;
|
|
370
|
+
if (!interfaceCalls.has(key)) {
|
|
371
|
+
interfaceCalls.set(key, {
|
|
372
|
+
ifaceName,
|
|
373
|
+
ifaceType: t,
|
|
374
|
+
methodName: m.name,
|
|
375
|
+
methodSig: {
|
|
376
|
+
kind: "func",
|
|
377
|
+
params: m.params,
|
|
378
|
+
returns: m.returns,
|
|
379
|
+
},
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
for (const fn of funcDecls) {
|
|
386
|
+
for (const p of fn.params ?? []) scanType(p.type);
|
|
387
|
+
scanType(fn.returnType);
|
|
388
|
+
}
|
|
389
|
+
for (const p of progs) {
|
|
390
|
+
for (const d of p.decls ?? []) {
|
|
391
|
+
if (d.kind === "FuncDecl") {
|
|
392
|
+
for (const param of d.params ?? []) scanType(param.type);
|
|
393
|
+
scanType(d.returnType);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|