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,392 @@
|
|
|
1
|
+
// src/backend/wasm/method-expr.js
|
|
2
|
+
// Method expressions (`T.M`, `(*T).M`) and method values (`x.M`) in WASM.
|
|
3
|
+
// A call through a method expression, `(*T).M(p, a)`, becomes the ordinary
|
|
4
|
+
// method call `p.M(a)`. Any other use becomes a func literal that forwards
|
|
5
|
+
// to the method, `func(recv *T, a0 A) R { return recv.M(a0) }`, which the
|
|
6
|
+
// closure lifting then emits like any other func value. A method value
|
|
7
|
+
// binds its receiver when it is evaluated (copying a value receiver, taking
|
|
8
|
+
// the address of an addressable one for a pointer receiver):
|
|
9
|
+
// `func(recv$ *T) func(A) R { return func(a0 A) R { return recv$.M(a0) } }(&x)`. Runs after monomorphisation, so the
|
|
10
|
+
// receiver and method types are concrete, on the WASM pipeline's own copy of
|
|
11
|
+
// the programs.
|
|
12
|
+
|
|
13
|
+
import { typeToTypeNode as toNode } from "./monomorph.js";
|
|
14
|
+
|
|
15
|
+
// Func types stay semantic types: `typeToTypeNode` has no func form and
|
|
16
|
+
// would turn them into `any`.
|
|
17
|
+
function typeToTypeNode(t) {
|
|
18
|
+
return t?.kind === "func" ? t : toNode(t);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function isVoid(t) {
|
|
22
|
+
return !t || (t.kind === "basic" && t.name === "void");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function returnTypeNode(t) {
|
|
26
|
+
if (isVoid(t)) return null;
|
|
27
|
+
if (t.kind === "tuple")
|
|
28
|
+
return { kind: "TupleType", types: t.types.map(typeToTypeNode) };
|
|
29
|
+
return typeToTypeNode(t);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function ident(name, type, at) {
|
|
33
|
+
return { kind: "Ident", name, _type: type, _line: at._line, _col: at._col };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// `recv.M(args)` for the method expression `sel` (type `func(recv, …)`).
|
|
37
|
+
function methodCall(sel, recvExpr, args, fnType) {
|
|
38
|
+
const result = fnType.returns?.[0];
|
|
39
|
+
return {
|
|
40
|
+
kind: "CallExpr",
|
|
41
|
+
func: {
|
|
42
|
+
kind: "SelectorExpr",
|
|
43
|
+
expr: recvExpr,
|
|
44
|
+
field: sel.field,
|
|
45
|
+
_type: {
|
|
46
|
+
kind: "func",
|
|
47
|
+
params: fnType.params.slice(1),
|
|
48
|
+
returns: fnType.returns,
|
|
49
|
+
variadic: fnType.variadic,
|
|
50
|
+
},
|
|
51
|
+
_line: sel._line,
|
|
52
|
+
_col: sel._col,
|
|
53
|
+
},
|
|
54
|
+
args,
|
|
55
|
+
_type: isVoid(result) ? { kind: "basic", name: "void" } : result,
|
|
56
|
+
_line: sel._line,
|
|
57
|
+
_col: sel._col,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Each method value's outer literal gets its own receiver name: captures are
|
|
62
|
+
// resolved by name, so two method values of different receiver types in one
|
|
63
|
+
// function must not share `recv$`.
|
|
64
|
+
let recvCounter = 0;
|
|
65
|
+
let bundled = null;
|
|
66
|
+
|
|
67
|
+
function forwardingFuncLit(sel, recvName = "recv$") {
|
|
68
|
+
const fnType = sel._type;
|
|
69
|
+
const [recvType, ...paramTypes] = fnType.params;
|
|
70
|
+
const params = [
|
|
71
|
+
{ name: recvName, type: typeToTypeNode(recvType) },
|
|
72
|
+
...paramTypes.map((t, i) => ({
|
|
73
|
+
name: `a$${i}`,
|
|
74
|
+
type: typeToTypeNode(t),
|
|
75
|
+
variadic: fnType.variadic && i === paramTypes.length - 1,
|
|
76
|
+
})),
|
|
77
|
+
];
|
|
78
|
+
const args = paramTypes.map((t, i) => {
|
|
79
|
+
const a = ident(`a$${i}`, t, sel);
|
|
80
|
+
if (fnType.variadic && i === paramTypes.length - 1) a._spread = true;
|
|
81
|
+
return a;
|
|
82
|
+
});
|
|
83
|
+
const call = methodCall(sel, ident(recvName, recvType, sel), args, fnType);
|
|
84
|
+
const result = fnType.returns?.[0];
|
|
85
|
+
const stmts = isVoid(result)
|
|
86
|
+
? [{ kind: "ExprStmt", expr: call }]
|
|
87
|
+
: [{ kind: "ReturnStmt", values: [call] }];
|
|
88
|
+
return {
|
|
89
|
+
kind: "FuncLit",
|
|
90
|
+
params,
|
|
91
|
+
returnType: returnTypeNode(result),
|
|
92
|
+
body: { kind: "Block", stmts },
|
|
93
|
+
_type: fnType,
|
|
94
|
+
_line: sel._line,
|
|
95
|
+
_col: sel._col,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function isInterfaceRecv(t) {
|
|
100
|
+
return (t?.underlying ?? t)?.kind === "interface";
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function methodValueLit(sel) {
|
|
104
|
+
const fnType = sel._type;
|
|
105
|
+
let recvType = sel.expr._type;
|
|
106
|
+
let recvArg = sel.expr;
|
|
107
|
+
if (fnType._ptrRecv && recvType?.kind !== "pointer") {
|
|
108
|
+
recvType = { kind: "pointer", base: recvType };
|
|
109
|
+
recvArg = {
|
|
110
|
+
kind: "UnaryExpr",
|
|
111
|
+
op: "&",
|
|
112
|
+
operand: sel.expr,
|
|
113
|
+
_type: recvType,
|
|
114
|
+
_line: sel._line,
|
|
115
|
+
_col: sel._col,
|
|
116
|
+
};
|
|
117
|
+
} else if (sel._derefRecv && recvType?.kind === "pointer") {
|
|
118
|
+
// `p.M` with a value receiver binds a copy of `*p` now.
|
|
119
|
+
recvType = recvType.base;
|
|
120
|
+
recvArg = {
|
|
121
|
+
kind: "UnaryExpr",
|
|
122
|
+
op: "*",
|
|
123
|
+
operand: sel.expr,
|
|
124
|
+
_type: recvType,
|
|
125
|
+
_line: sel._line,
|
|
126
|
+
_col: sel._col,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
const params = fnType.params ?? [];
|
|
130
|
+
const recvName = `recv$${recvCounter++}`;
|
|
131
|
+
const inner = forwardingFuncLit(
|
|
132
|
+
{
|
|
133
|
+
...sel,
|
|
134
|
+
_type: { ...fnType, params: [recvType, ...params] },
|
|
135
|
+
},
|
|
136
|
+
recvName,
|
|
137
|
+
);
|
|
138
|
+
// The receiver is the outer parameter; the inner literal takes the rest.
|
|
139
|
+
inner.params = inner.params.slice(1);
|
|
140
|
+
inner._type = fnType;
|
|
141
|
+
const outerType = { kind: "func", params: [recvType], returns: [fnType] };
|
|
142
|
+
return {
|
|
143
|
+
kind: "CallExpr",
|
|
144
|
+
func: {
|
|
145
|
+
kind: "FuncLit",
|
|
146
|
+
params: [{ name: recvName, type: typeToTypeNode(recvType) }],
|
|
147
|
+
returnType: typeToTypeNode(fnType),
|
|
148
|
+
body: {
|
|
149
|
+
kind: "Block",
|
|
150
|
+
stmts: [
|
|
151
|
+
// A method value on a nil interface panics when it is
|
|
152
|
+
// evaluated, not when it is called.
|
|
153
|
+
...(isInterfaceRecv(recvType)
|
|
154
|
+
? [{ kind: "NilCheckStmt", expr: ident(recvName, recvType, sel) }]
|
|
155
|
+
: []),
|
|
156
|
+
{ kind: "ReturnStmt", values: [inner] },
|
|
157
|
+
],
|
|
158
|
+
},
|
|
159
|
+
_type: outerType,
|
|
160
|
+
_line: sel._line,
|
|
161
|
+
_col: sel._col,
|
|
162
|
+
},
|
|
163
|
+
args: [recvArg],
|
|
164
|
+
_type: fnType,
|
|
165
|
+
_line: sel._line,
|
|
166
|
+
_col: sel._col,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// True for a stdlib function used as a value: `pkg.Fn` on an imported
|
|
171
|
+
// package that is not bundled GoFront source (those resolve as plain
|
|
172
|
+
// functions), with a concrete (non-generic) func type.
|
|
173
|
+
function isStdlibFuncValue(node) {
|
|
174
|
+
if (node?.kind !== "SelectorExpr" || node._type?.kind !== "func")
|
|
175
|
+
return false;
|
|
176
|
+
const ns = node.expr;
|
|
177
|
+
if (ns?.kind !== "Ident" || ns._type?.kind !== "namespace") return false;
|
|
178
|
+
return !bundled?.has(ns.name) && !hasTypeParam(node._type);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function hasTypeParam(t) {
|
|
182
|
+
const text = JSON.stringify([t.params, t.returns], (k, v) =>
|
|
183
|
+
k.startsWith("_") || k === "underlying" ? undefined : v,
|
|
184
|
+
);
|
|
185
|
+
return text.includes('"typeParam"') || text.includes('"typeparam"');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Largest number of variadic operands a forwarded stdlib function value
|
|
189
|
+
// accepts: stdlib emitters take their operands unpacked, so the forwarder
|
|
190
|
+
// dispatches on the operand count.
|
|
191
|
+
const MAX_FORWARDED_VARIADIC = 8;
|
|
192
|
+
|
|
193
|
+
const INT_TYPE = { kind: "basic", name: "int" };
|
|
194
|
+
|
|
195
|
+
function intLit(n, at) {
|
|
196
|
+
return {
|
|
197
|
+
kind: "BasicLit",
|
|
198
|
+
litKind: "INT",
|
|
199
|
+
value: String(n),
|
|
200
|
+
_type: INT_TYPE,
|
|
201
|
+
_line: at._line,
|
|
202
|
+
_col: at._col,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function lineInfo(at) {
|
|
207
|
+
return { _line: at._line, _col: at._col };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function typedCall(func, args, type, at) {
|
|
211
|
+
return { kind: "CallExpr", func, args, _type: type, ...lineInfo(at) };
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// The statements calling `sel(args)` and returning its result.
|
|
215
|
+
function callStmts(sel, args) {
|
|
216
|
+
const result = sel._type.returns?.[0];
|
|
217
|
+
const type = isVoid(result) ? { kind: "basic", name: "void" } : result;
|
|
218
|
+
const call = typedCall(sel, args, type, sel);
|
|
219
|
+
return isVoid(result)
|
|
220
|
+
? [
|
|
221
|
+
{ kind: "ExprStmt", expr: call },
|
|
222
|
+
{ kind: "ReturnStmt", values: [] },
|
|
223
|
+
]
|
|
224
|
+
: [{ kind: "ReturnStmt", values: [call] }];
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// `if len(rest) == k { return pkg.Fn(fixed..., rest[0..k-1]) }` for one k.
|
|
228
|
+
function operandCountBranch(sel, fixed, rest, elemType, k) {
|
|
229
|
+
const at = sel;
|
|
230
|
+
const elems = Array.from({ length: k }, (_, i) => ({
|
|
231
|
+
kind: "IndexExpr",
|
|
232
|
+
expr: rest,
|
|
233
|
+
index: intLit(i, at),
|
|
234
|
+
_type: elemType,
|
|
235
|
+
...lineInfo(at),
|
|
236
|
+
}));
|
|
237
|
+
const len = typedCall(ident("len", null, at), [rest], INT_TYPE, at);
|
|
238
|
+
const body = callStmts(sel, [...fixed, ...elems]);
|
|
239
|
+
return {
|
|
240
|
+
kind: "IfStmt",
|
|
241
|
+
init: null,
|
|
242
|
+
cond: {
|
|
243
|
+
kind: "BinaryExpr",
|
|
244
|
+
left: len,
|
|
245
|
+
op: "==",
|
|
246
|
+
right: intLit(k, at),
|
|
247
|
+
_type: { kind: "basic", name: "bool" },
|
|
248
|
+
...lineInfo(at),
|
|
249
|
+
},
|
|
250
|
+
body: { kind: "Block", stmts: body, list: body },
|
|
251
|
+
elseBody: null,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function tooManyOperands(sel) {
|
|
256
|
+
const msg = {
|
|
257
|
+
kind: "BasicLit",
|
|
258
|
+
litKind: "STRING",
|
|
259
|
+
value: `too many arguments in call to ${sel.expr.name}.${sel.field}`,
|
|
260
|
+
_type: { kind: "basic", name: "string" },
|
|
261
|
+
};
|
|
262
|
+
const panic = typedCall(
|
|
263
|
+
ident("panic", null, sel),
|
|
264
|
+
[msg],
|
|
265
|
+
{ kind: "basic", name: "void" },
|
|
266
|
+
sel,
|
|
267
|
+
);
|
|
268
|
+
return [{ kind: "ExprStmt", expr: panic }];
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function variadicDispatch(sel, fixed, elemType) {
|
|
272
|
+
const rest = ident(
|
|
273
|
+
`a$${fixed.length}`,
|
|
274
|
+
{ kind: "slice", elem: elemType },
|
|
275
|
+
sel,
|
|
276
|
+
);
|
|
277
|
+
const stmts = [];
|
|
278
|
+
for (let k = 0; k <= MAX_FORWARDED_VARIADIC; k++)
|
|
279
|
+
stmts.push(operandCountBranch(sel, fixed, rest, elemType, k));
|
|
280
|
+
return [...stmts, ...tooManyOperands(sel)];
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// `func(a0 A, a1 B) R { return pkg.Fn(a0, a1) }` for the stdlib function
|
|
284
|
+
// value `pkg.Fn`. A variadic one dispatches on the operand count:
|
|
285
|
+
// `if len(rest) == 0 { return pkg.Fn(a0) }; if len(rest) == 1 { ... }`.
|
|
286
|
+
function stdlibFuncLit(sel) {
|
|
287
|
+
const fnType = sel._type;
|
|
288
|
+
const ps = fnType.params ?? [];
|
|
289
|
+
const nFixed = fnType.variadic ? ps.length - 1 : ps.length;
|
|
290
|
+
const params = ps.map((t, i) => ({
|
|
291
|
+
name: `a$${i}`,
|
|
292
|
+
type: typeToTypeNode(t),
|
|
293
|
+
variadic: i === nFixed,
|
|
294
|
+
}));
|
|
295
|
+
const fixed = ps.slice(0, nFixed).map((t, i) => ident(`a$${i}`, t, sel));
|
|
296
|
+
const stmts = fnType.variadic
|
|
297
|
+
? variadicDispatch(sel, fixed, ps[nFixed])
|
|
298
|
+
: callStmts(sel, fixed);
|
|
299
|
+
return {
|
|
300
|
+
kind: "FuncLit",
|
|
301
|
+
params,
|
|
302
|
+
returnType: returnTypeNode(fnType.returns?.[0]),
|
|
303
|
+
body: { kind: "Block", stmts, list: stmts },
|
|
304
|
+
_type: fnType,
|
|
305
|
+
...lineInfo(sel),
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function rewrite(node, isCallee) {
|
|
310
|
+
return rewriteMethods(node, isCallee) ?? rewriteStdlibValue(node, isCallee);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// The forwarder passes plain operands and returns one value, so a stdlib
|
|
314
|
+
// function that takes a callback or returns several values cannot be one.
|
|
315
|
+
function assertForwardable(sel) {
|
|
316
|
+
const t = sel._type;
|
|
317
|
+
// Stdlib signatures type a callback as `any`, and the forwarder cannot
|
|
318
|
+
// pass an `any` operand to an emitter that expects a slice or function.
|
|
319
|
+
const fixed = t.variadic ? (t.params ?? []).slice(0, -1) : (t.params ?? []);
|
|
320
|
+
const hasCallback = fixed.some(
|
|
321
|
+
(p) => p?.kind === "func" || (p?.kind === "basic" && p.name === "any"),
|
|
322
|
+
);
|
|
323
|
+
if (hasCallback || (t.returns?.length ?? 0) > 1)
|
|
324
|
+
throw new Error(
|
|
325
|
+
`${sel.expr.name}.${sel.field} cannot be used as a value in WASM (it takes a callback or returns several values)`,
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function rewriteStdlibValue(node, isCallee) {
|
|
330
|
+
if (isCallee || !isStdlibFuncValue(node)) return null;
|
|
331
|
+
assertForwardable(node);
|
|
332
|
+
return stdlibFuncLit(node);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function rewriteMethods(node, isCallee) {
|
|
336
|
+
if (node?.kind === "SelectorExpr" && node._isMethodExpr && node._type)
|
|
337
|
+
return forwardingFuncLit(node);
|
|
338
|
+
if (
|
|
339
|
+
node?.kind === "SelectorExpr" &&
|
|
340
|
+
node._isMethodValue &&
|
|
341
|
+
node._type?.kind === "func" &&
|
|
342
|
+
!isCallee
|
|
343
|
+
)
|
|
344
|
+
return methodValueLit(node);
|
|
345
|
+
if (
|
|
346
|
+
node?.kind === "CallExpr" &&
|
|
347
|
+
node.func?.kind === "SelectorExpr" &&
|
|
348
|
+
node.func._isMethodExpr &&
|
|
349
|
+
node.func._type &&
|
|
350
|
+
node.args?.length > 0 &&
|
|
351
|
+
!node._multiForward
|
|
352
|
+
) {
|
|
353
|
+
const [recv, ...rest] = node.args;
|
|
354
|
+
const call = methodCall(node.func, recv, rest, node.func._type);
|
|
355
|
+
call._type = node._type;
|
|
356
|
+
return call;
|
|
357
|
+
}
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/** Rewrites method expressions in place; true if anything changed. */
|
|
362
|
+
export function desugarMethodExprs(node, bundledPackages = null) {
|
|
363
|
+
recvCounter = 0;
|
|
364
|
+
bundled = bundledPackages;
|
|
365
|
+
return desugar(node);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function desugar(node) {
|
|
369
|
+
if (!node || typeof node !== "object") return false;
|
|
370
|
+
let changed = false;
|
|
371
|
+
if (Array.isArray(node)) {
|
|
372
|
+
for (let i = 0; i < node.length; i++) {
|
|
373
|
+
const r = rewrite(node[i]);
|
|
374
|
+
if (r) {
|
|
375
|
+
node[i] = r;
|
|
376
|
+
changed = true;
|
|
377
|
+
}
|
|
378
|
+
changed = desugar(node[i]) || changed;
|
|
379
|
+
}
|
|
380
|
+
return changed;
|
|
381
|
+
}
|
|
382
|
+
for (const key of Object.keys(node)) {
|
|
383
|
+
if (key.charCodeAt(0) === 95) continue; // "_" side tables
|
|
384
|
+
const r = rewrite(node[key], node.kind === "CallExpr" && key === "func");
|
|
385
|
+
if (r) {
|
|
386
|
+
node[key] = r;
|
|
387
|
+
changed = true;
|
|
388
|
+
}
|
|
389
|
+
changed = desugar(node[key]) || changed;
|
|
390
|
+
}
|
|
391
|
+
return changed;
|
|
392
|
+
}
|