gofront 1.8.0 → 1.9.1
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 +3 -3
- package/package.json +6 -5
- package/src/backend/js/expressions.js +1157 -408
- package/src/backend/js/index.js +327 -153
- package/src/backend/js/natives.js +383 -0
- package/src/backend/js/runtime.js +559 -2552
- package/src/backend/js/statements.js +670 -158
- package/src/backend/js/stdlib/index.js +45 -93
- package/src/backend/js/templ.js +32 -45
- package/src/backend/js/type-info.js +298 -19
- package/src/backend/shared/fixed-float.js +20 -0
- package/src/backend/shared/fmt-types.js +91 -170
- package/src/backend/shared/host-unicode.js +36 -0
- package/src/backend/wasm/boundary.js +490 -965
- package/src/backend/wasm/emit-builtins.js +884 -256
- package/src/backend/wasm/emit-exprs.js +185 -77
- package/src/backend/wasm/emit-maps.js +17 -367
- package/src/backend/wasm/emit-shared.js +4 -1
- package/src/backend/wasm/emit-stmts.js +524 -240
- package/src/backend/wasm/emit.js +176 -38
- package/src/backend/wasm/globals.js +48 -8
- package/src/backend/wasm/glue.js +2 -4
- package/src/backend/wasm/index.js +44 -530
- package/src/backend/wasm/method-expr.js +12 -1
- package/src/backend/wasm/module-emitter.js +299 -770
- package/src/backend/wasm/monomorph.js +139 -8
- package/src/backend/wasm/natives.js +600 -0
- package/src/backend/wasm/prescan.js +5 -153
- package/src/backend/wasm/prune-funcs.js +89 -0
- package/src/backend/wasm/stdlib/index.js +2 -32
- package/src/backend/wasm/types.js +97 -20
- package/src/backend/wasm/variadic.js +9 -4
- package/src/cli/cli-core.js +5 -1
- package/src/cli/test-runner.js +115 -342
- package/src/compiler.js +113 -130
- package/src/lexer/lexer.js +106 -11
- package/src/lexer/templ-lexer.js +61 -18
- package/src/lexer/tokens.js +1 -0
- package/src/link/stdlib.js +355 -132
- package/src/link/type-names.js +204 -0
- package/src/link/wasm.js +30 -6
- package/src/lower/bounds.js +153 -0
- package/src/lower/captures.js +92 -33
- package/src/lower/embedding.js +62 -5
- package/src/lower/errors-as.js +196 -0
- package/src/lower/fmt-formatters.js +929 -0
- package/src/lower/functions.js +225 -7
- package/src/lower/gen-types.js +293 -0
- package/src/lower/index.js +14 -2
- package/src/lower/json-codecs.js +334 -245
- package/src/lower/ownership.js +25 -7
- package/src/lower/reachability.js +69 -51
- package/src/lower/walk.js +12 -0
- package/src/parser/declarations.js +30 -5
- package/src/parser/expressions.js +111 -57
- package/src/parser/statements.js +108 -0
- package/src/parser/templ.js +44 -5
- package/src/parser/types.js +12 -0
- package/src/resolver.js +1 -2
- package/src/typechecker/assignability.js +214 -19
- package/src/typechecker/const-fold.js +70 -27
- package/src/typechecker/constants.js +156 -8
- package/src/typechecker/expressions.js +490 -108
- package/src/typechecker/index.js +346 -42
- package/src/typechecker/init-order.js +136 -40
- package/src/typechecker/operand-text.js +41 -0
- package/src/typechecker/resolve.js +490 -65
- package/src/typechecker/statements.js +288 -27
- package/src/typechecker/stdlib/excluded.js +9 -5
- package/src/typechecker/stdlib/index.js +25 -9
- package/src/typechecker/termination.js +10 -1
- package/src/typechecker/types.js +287 -575
- package/stdlib/bytes/buffer.go +479 -0
- package/stdlib/bytes/bytes.go +1010 -0
- package/stdlib/bytes/reader.go +161 -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/json.go +62 -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/html/html.go +251 -0
- package/stdlib/internal/fmtrt/fmtrt.go +163 -0
- package/stdlib/internal/jsonrt/dec.go +171 -38
- package/stdlib/internal/jsonrt/enc.go +186 -36
- package/stdlib/internal/jsonrt/scanner.go +17 -8
- package/stdlib/io/io.go +157 -1
- package/stdlib/maps/maps.go +131 -0
- package/stdlib/math/bits/bits.go +507 -0
- package/stdlib/math/extra.go +16 -14
- 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 +391 -452
- 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 +235 -50
- 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 +141 -3
- package/stdlib/unicode/utf8/utf8.go +464 -0
- package/src/backend/js/fmt-runtime.js +0 -573
- 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 -119
- package/src/backend/js/stdlib/builder.js +0 -98
- package/src/backend/js/stdlib/bytes.js +0 -368
- package/src/backend/js/stdlib/errors.js +0 -45
- package/src/backend/js/stdlib/fmt.js +0 -517
- 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 -1167
- package/src/backend/js/stdlib/maps.js +0 -37
- package/src/backend/js/stdlib/math.js +0 -85
- 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 -34
- package/src/backend/js/stdlib/slices.js +0 -107
- package/src/backend/js/stdlib/sort.js +0 -30
- package/src/backend/js/stdlib/strconv.js +0 -79
- package/src/backend/js/stdlib/strings.js +0 -301
- package/src/backend/js/stdlib/sync.js +0 -13
- package/src/backend/js/stdlib/testing.js +0 -41
- package/src/backend/js/stdlib/time.js +0 -152
- package/src/backend/js/stdlib/unicode.js +0 -25
- package/src/backend/js/stdlib/utf8.js +0 -26
- package/src/backend/wasm/stdlib/bytes.js +0 -6716
- package/src/backend/wasm/stdlib/core.js +0 -10278
- package/src/backend/wasm/stdlib/io.js +0 -3578
- package/src/backend/wasm/stdlib/packages.js +0 -2402
- package/src/backend/wasm/stdlib/pure.js +0 -720
- package/src/backend/wasm/stdlib/time.js +0 -2229
- package/src/typechecker/stdlib/core.js +0 -1079
- package/src/typechecker/stdlib/extended.js +0 -1210
- package/stdlib/bytes/iter.go +0 -192
- package/stdlib/bytes/search.go +0 -240
- package/stdlib/bytes/split.go +0 -218
- package/stdlib/errors/overlay.go +0 -12
- package/stdlib/maps/iter.go +0 -65
- package/stdlib/slices/extra.go +0 -73
- package/stdlib/slices/iter.go +0 -113
- package/stdlib/strings/extra.go +0 -35
- package/stdlib/strings/iter.go +0 -122
- package/stdlib/sync/extra.go +0 -105
- package/stdlib/unicode/utf8/bytes.go +0 -239
|
@@ -1,74 +1,56 @@
|
|
|
1
1
|
// CodeGen stdlib call generation — installed as a mixin on CodeGen.prototype.
|
|
2
|
-
//
|
|
2
|
+
// Dispatches `gofront/shared` and `gofront/simd` calls to inline JS.
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import { binaryMethods } from "./binary.js";
|
|
6
|
-
import { bitsMethods } from "./bits.js";
|
|
7
|
-
import { builderMethods } from "./builder.js";
|
|
8
|
-
import { bytesMethods } from "./bytes.js";
|
|
9
|
-
import { errorsMethods } from "./errors.js";
|
|
10
|
-
import { fmtMethods } from "./fmt.js";
|
|
11
|
-
import { gomMethods } from "./gom.js";
|
|
12
|
-
import { hexMethods } from "./hex.js";
|
|
13
|
-
import { htmlMethods } from "./html.js";
|
|
14
|
-
import { ioMethods } from "./io.js";
|
|
15
|
-
import { jsonMethods } from "./json.js";
|
|
16
|
-
import { mapsMethods } from "./maps.js";
|
|
17
|
-
import { mathMethods } from "./math.js";
|
|
18
|
-
import { osMethods } from "./os.js";
|
|
19
|
-
import { pathMethods } from "./path.js";
|
|
20
|
-
import { randMethods } from "./rand.js";
|
|
21
|
-
import { regexpMethods } from "./regexp.js";
|
|
22
|
-
import { sha256Methods } from "./sha256.js";
|
|
23
|
-
import { sharedMethods } from "./shared.js";
|
|
24
|
-
import { simdMethods } from "./simd.js";
|
|
25
|
-
import { slicesMethods } from "./slices.js";
|
|
26
|
-
import { sortMethods } from "./sort.js";
|
|
27
|
-
import { strconvMethods } from "./strconv.js";
|
|
28
|
-
import { stringsMethods } from "./strings.js";
|
|
29
|
-
import { syncMethods } from "./sync.js";
|
|
30
|
-
import { testingMethods } from "./testing.js";
|
|
31
|
-
import { timeMethods } from "./time.js";
|
|
32
|
-
import { unicodeMethods } from "./unicode.js";
|
|
33
|
-
import { utf8Methods } from "./utf8.js";
|
|
4
|
+
import { SHARED_KINDS } from "../../../typechecker/stdlib/shared.js";
|
|
34
5
|
|
|
35
6
|
/** @typedef {import('../index.js').CodeGen} CodeGen */
|
|
36
7
|
|
|
37
8
|
// Stdlib namespace → handler method name. Used by _genStdlibCall to dispatch
|
|
38
9
|
// dynamically without creating static call edges.
|
|
39
10
|
const STDLIB_METHOD_MAP = {
|
|
40
|
-
fmt: "_genFmt",
|
|
41
|
-
strings: "_genStrings",
|
|
42
|
-
bytes: "_genBytes",
|
|
43
|
-
strconv: "_genStrconv",
|
|
44
|
-
sort: "_genSort",
|
|
45
|
-
math: "_genMath",
|
|
46
|
-
bits: "_genBits",
|
|
47
|
-
unicode: "_genUnicode",
|
|
48
|
-
os: "_genOs",
|
|
49
|
-
errors: "_genErrors",
|
|
50
|
-
time: "_genTime",
|
|
51
|
-
regexp: "_genRegexp",
|
|
52
|
-
slices: "_genSlices",
|
|
53
|
-
maps: "_genMaps",
|
|
54
|
-
html: "_genHtml",
|
|
55
|
-
io: "_genIo",
|
|
56
|
-
rand: "_genRand",
|
|
57
|
-
utf8: "_genUtf8",
|
|
58
|
-
path: "_genPath",
|
|
59
|
-
filepath: "_genPath",
|
|
60
|
-
testing: "_genTesting",
|
|
61
11
|
shared: "_genShared",
|
|
62
|
-
sync: "_genSync",
|
|
63
|
-
sha256: "_genSha256",
|
|
64
|
-
binary: "_genBinary",
|
|
65
|
-
hex: "_genHex",
|
|
66
|
-
base64: "_genBase64",
|
|
67
|
-
json: "_genJson",
|
|
68
12
|
simd: "_genSimd",
|
|
69
13
|
};
|
|
70
14
|
|
|
71
15
|
const dispatchMethods = {
|
|
16
|
+
// `gofront/shared`: in js packages a shared buffer is just a TypedArray (no
|
|
17
|
+
// linear memory is involved); in wasm packages the facade hands out
|
|
18
|
+
// zero-copy views onto the module memory.
|
|
19
|
+
_genShared(fn, a) {
|
|
20
|
+
if (!fn.startsWith("New")) return undefined;
|
|
21
|
+
const info = SHARED_KINDS[fn.slice(3)];
|
|
22
|
+
if (!info) return undefined;
|
|
23
|
+
return `new ${info.ctor}(${a()[0]})`;
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
// `gofront/simd` in JS packages.
|
|
27
|
+
_genSimd(fn, a) {
|
|
28
|
+
this.useHelper("simd");
|
|
29
|
+
const args = a();
|
|
30
|
+
switch (fn) {
|
|
31
|
+
case "Splat":
|
|
32
|
+
return `__simd_splat(${args[0]})`;
|
|
33
|
+
case "NewF32x4":
|
|
34
|
+
return `__simd_f32x4(${args[0]}, ${args[1]}, ${args[2]}, ${args[3]})`;
|
|
35
|
+
case "Select":
|
|
36
|
+
return `__simd_select(${args[0]}, ${args[1]}, ${args[2]})`;
|
|
37
|
+
case "Shuffle":
|
|
38
|
+
return `__simd_shuffle(${args.join(", ")})`;
|
|
39
|
+
case "LoadF32x4":
|
|
40
|
+
return `__simd_load(${args[0]}, ${args[1]})`;
|
|
41
|
+
case "StoreF32x4":
|
|
42
|
+
return `__simd_store(${args[0]}, ${args[1]}, ${args[2]})`;
|
|
43
|
+
case "LoadF32x4Slice":
|
|
44
|
+
return `__simd_load_slice(${args[0]}, ${args[1]})`;
|
|
45
|
+
case "StoreF32x4Slice":
|
|
46
|
+
return `__simd_store_slice(${args[0]}, ${args[1]}, ${args[2]})`;
|
|
47
|
+
case "Pad4":
|
|
48
|
+
return `(Math.floor((${args[0]} + 3) / 4) * 4)`;
|
|
49
|
+
default:
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
|
|
72
54
|
typeComment(typeNode) {
|
|
73
55
|
if (!typeNode) return "unknown";
|
|
74
56
|
switch (typeNode.kind) {
|
|
@@ -86,18 +68,18 @@ const dispatchMethods = {
|
|
|
86
68
|
// Dispatch table for stdlib namespace calls (pkg.Func → inline JS).
|
|
87
69
|
// Returns the generated JS string, or undefined if not handled.
|
|
88
70
|
_genStdlibCall(ns, fn, expr) {
|
|
89
|
-
if (ns === "gom") return this._genGom(fn, expr);
|
|
90
71
|
const method = STDLIB_METHOD_MAP[ns];
|
|
91
72
|
if (!method) return undefined;
|
|
92
|
-
//
|
|
93
|
-
|
|
73
|
+
// A member written in Go is an ordinary call of its mangled name.
|
|
74
|
+
if (expr.func?._stdName) return undefined;
|
|
75
|
+
// Stdlib shims take raw values, not the interface box of a named value.
|
|
94
76
|
const isNamespace = expr.func?.expr?._type?.kind === "namespace";
|
|
95
|
-
if (isNamespace
|
|
77
|
+
if (isNamespace) {
|
|
96
78
|
for (const e of expr.args) if (e) e._noNamedBox = true;
|
|
97
79
|
}
|
|
98
80
|
const a = () => expr.args.map((e) => this.genExpr(e));
|
|
99
81
|
const res = this[method](fn, a, expr);
|
|
100
|
-
if (res === undefined && isNamespace
|
|
82
|
+
if (res === undefined && isNamespace) {
|
|
101
83
|
for (const e of expr.args) if (e) delete e._noNamedBox;
|
|
102
84
|
}
|
|
103
85
|
return res;
|
|
@@ -106,35 +88,5 @@ const dispatchMethods = {
|
|
|
106
88
|
|
|
107
89
|
/** @type {ThisType<CodeGen>} */
|
|
108
90
|
export const stdlibGenMethods = {
|
|
109
|
-
...bitsMethods,
|
|
110
|
-
...builderMethods,
|
|
111
|
-
...bytesMethods,
|
|
112
|
-
...errorsMethods,
|
|
113
|
-
...fmtMethods,
|
|
114
|
-
...gomMethods,
|
|
115
|
-
...htmlMethods,
|
|
116
|
-
...ioMethods,
|
|
117
|
-
...mapsMethods,
|
|
118
|
-
...mathMethods,
|
|
119
|
-
...osMethods,
|
|
120
|
-
...pathMethods,
|
|
121
|
-
...randMethods,
|
|
122
|
-
...regexpMethods,
|
|
123
|
-
...sharedMethods,
|
|
124
|
-
...simdMethods,
|
|
125
|
-
...slicesMethods,
|
|
126
|
-
...sortMethods,
|
|
127
|
-
...strconvMethods,
|
|
128
|
-
...stringsMethods,
|
|
129
|
-
...timeMethods,
|
|
130
|
-
...unicodeMethods,
|
|
131
|
-
...utf8Methods,
|
|
132
|
-
...testingMethods,
|
|
133
|
-
...syncMethods,
|
|
134
|
-
...sha256Methods,
|
|
135
|
-
...binaryMethods,
|
|
136
|
-
...hexMethods,
|
|
137
|
-
...base64Methods,
|
|
138
|
-
...jsonMethods,
|
|
139
91
|
...dispatchMethods,
|
|
140
92
|
};
|
package/src/backend/js/templ.js
CHANGED
|
@@ -13,6 +13,8 @@ export const templGenMethods = {
|
|
|
13
13
|
)
|
|
14
14
|
.join(", ");
|
|
15
15
|
this.line(`function ${decl.name}(${params}) {`);
|
|
16
|
+
const prevBoxed = this._boxedVars;
|
|
17
|
+
this._boxedVars = new Set(this._boxedGlobals);
|
|
16
18
|
this.indented(() => {
|
|
17
19
|
// `@W(xs...)` passes `{ __sp: xs }` like any Go variadic call.
|
|
18
20
|
const last = decl.params[decl.params.length - 1];
|
|
@@ -26,6 +28,7 @@ export const templGenMethods = {
|
|
|
26
28
|
});
|
|
27
29
|
this.line(`}};`);
|
|
28
30
|
});
|
|
31
|
+
this._boxedVars = prevBoxed;
|
|
29
32
|
this.line("}");
|
|
30
33
|
},
|
|
31
34
|
|
|
@@ -86,11 +89,17 @@ export const templGenMethods = {
|
|
|
86
89
|
);
|
|
87
90
|
case "TemplComponent":
|
|
88
91
|
return this._genTemplComponentMount(node, p);
|
|
89
|
-
case "TemplChildren":
|
|
90
|
-
//
|
|
92
|
+
case "TemplChildren": {
|
|
93
|
+
// `{ name... }`: mount each node of a variadic gom.Node parameter
|
|
94
|
+
// (nil mounts nothing). `children` may also be absent altogether.
|
|
95
|
+
const slot =
|
|
96
|
+
node.name === "children"
|
|
97
|
+
? `(typeof children!=="undefined"?children:null)`
|
|
98
|
+
: this._genTemplExpr(node, "null");
|
|
91
99
|
return this.line(
|
|
92
|
-
`
|
|
100
|
+
`for (const ___c of ${slot} ?? []) ___c?.Mount?.(${p});`,
|
|
93
101
|
);
|
|
102
|
+
}
|
|
94
103
|
case "TemplIf":
|
|
95
104
|
return this._genTemplIf(node, p);
|
|
96
105
|
case "TemplFor":
|
|
@@ -200,15 +209,18 @@ export const templGenMethods = {
|
|
|
200
209
|
_genTemplSwitch(node, p) {
|
|
201
210
|
const { cases } = node;
|
|
202
211
|
if (cases.length === 0) return;
|
|
203
|
-
|
|
212
|
+
// `switch { case cond: }` compares each condition with `true`.
|
|
213
|
+
const exprJs = this._genTemplExpr(node, "true");
|
|
204
214
|
this.line(`switch (${exprJs}) {`);
|
|
205
215
|
this.indented(() => {
|
|
206
216
|
for (const c of cases) {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
217
|
+
if (c.caseTokens === null) this.line("default: {");
|
|
218
|
+
else {
|
|
219
|
+
// One JS label per value of `case a, b:`.
|
|
220
|
+
if (c.exprError) throw c.exprError;
|
|
221
|
+
const labels = (c.exprs ?? []).map((e) => `case ${this.genExpr(e)}:`);
|
|
222
|
+
this.line(`${labels.join(" ") || 'case "":'} {`);
|
|
223
|
+
}
|
|
212
224
|
this.indented(() => this._genTemplNodes(c.body, p));
|
|
213
225
|
this.line(`break; }`);
|
|
214
226
|
}
|
|
@@ -216,48 +228,23 @@ export const templGenMethods = {
|
|
|
216
228
|
this.line(`}`);
|
|
217
229
|
},
|
|
218
230
|
|
|
231
|
+
// The header is a parsed Go `for` statement, so it goes through the regular
|
|
232
|
+
// loop generator; its body is a statement that mounts the templ nodes.
|
|
219
233
|
_genTemplFor(node, p) {
|
|
220
234
|
if (node.forError) throw node.forError;
|
|
221
235
|
if (!node.forStmt)
|
|
222
236
|
throw new Error("templ for header was not parsed by TemplParser");
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
const condJs = forStmt.cond ? this.genExpr(forStmt.cond) : "true";
|
|
231
|
-
this.line(`while (${condJs}) {`);
|
|
232
|
-
this.indented(() => this._genTemplNodes(templBody, mountParent));
|
|
233
|
-
this.line("}");
|
|
234
|
-
return;
|
|
235
|
-
}
|
|
236
|
-
// Range for: for key, val := range iter
|
|
237
|
-
const range = init.rhs[0];
|
|
238
|
-
const iterJs = this.genExpr(range.expr);
|
|
239
|
-
const lhs = init.lhs.map((e) => e.name ?? "_");
|
|
240
|
-
const keyName = lhs[0] ?? "_";
|
|
241
|
-
const valName = lhs[1] ?? null;
|
|
242
|
-
|
|
243
|
-
const loopHeader = this._genTemplRangeHeader(keyName, valName, iterJs);
|
|
244
|
-
this.line(loopHeader);
|
|
245
|
-
this.indented(() => this._genTemplNodes(templBody, mountParent));
|
|
246
|
-
this.line("}");
|
|
237
|
+
const mount = { kind: "TemplMount", parent: p };
|
|
238
|
+
// Hidden from the generic AST walkers: they know no templ node kinds.
|
|
239
|
+
Object.defineProperty(mount, "nodes", { value: node.body });
|
|
240
|
+
this.genFor({
|
|
241
|
+
...node.forStmt,
|
|
242
|
+
body: { kind: "Block", stmts: [mount] },
|
|
243
|
+
});
|
|
247
244
|
},
|
|
248
245
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
return `for (const ${keyName === "_" ? "_$" : keyName} of ${iterJs}) {`;
|
|
252
|
-
}
|
|
253
|
-
if (keyName === "_" && valName === "_")
|
|
254
|
-
return `for (const _$ of ${iterJs}) {`;
|
|
255
|
-
if (keyName === "_") return `for (const ${valName} of ${iterJs}) {`;
|
|
256
|
-
if (valName === "_")
|
|
257
|
-
return `for (const [${keyName}] of Object.entries(${iterJs})) {`;
|
|
258
|
-
// Default to .entries() for arrays, Object.entries for objects
|
|
259
|
-
this.useHelper("sliceGuard");
|
|
260
|
-
return `for (const [${keyName}, ${valName}] of __s(${iterJs}).entries()) {`;
|
|
246
|
+
_genTemplMountStmt(stmt) {
|
|
247
|
+
this._genTemplNodes(stmt.nodes, stmt.parent);
|
|
261
248
|
},
|
|
262
249
|
|
|
263
250
|
// Emit JS for the Go expression TemplParser parsed onto `node`. `emptyJs` is
|
|
@@ -7,8 +7,14 @@
|
|
|
7
7
|
// type's kind, identity and a type test (see `__ti` in HELPER_GENERICS).
|
|
8
8
|
|
|
9
9
|
import { forEachChild } from "../../lower/index.js";
|
|
10
|
-
import {
|
|
11
|
-
|
|
10
|
+
import {
|
|
11
|
+
isAny,
|
|
12
|
+
isTypedArraySlice,
|
|
13
|
+
typedArrayConstructorForElem,
|
|
14
|
+
typeStr,
|
|
15
|
+
} from "../../typechecker/types.js";
|
|
16
|
+
import { goTypeName } from "../shared/fmt-types.js";
|
|
17
|
+
import { isGenericComposite, mentionsTypeParam } from "./expressions.js";
|
|
12
18
|
|
|
13
19
|
// True when type node `node` names one of the type parameters `names`.
|
|
14
20
|
function typeNodeMentions(node, names) {
|
|
@@ -68,8 +74,24 @@ export const typeInfoGenMethods = {
|
|
|
68
74
|
return found;
|
|
69
75
|
},
|
|
70
76
|
|
|
77
|
+
// `x.M(...)` where x has a type parameter type: a raw value of a named
|
|
78
|
+
// non-struct type argument must be boxed to reach its methods.
|
|
79
|
+
_isTypeParamMethodCall(call, names) {
|
|
80
|
+
const t = call.func?.expr?._type;
|
|
81
|
+
return (
|
|
82
|
+
call.func?.kind === "SelectorExpr" &&
|
|
83
|
+
!call.func._isMethodExpr &&
|
|
84
|
+
t?.kind === "typeParam" &&
|
|
85
|
+
(!names || names.has(t.name))
|
|
86
|
+
);
|
|
87
|
+
},
|
|
88
|
+
|
|
71
89
|
_nodeUsesTypeInfo(node, names) {
|
|
72
90
|
if (node._ifaceKeyDyn && names.has(node._ifaceKeyParam)) return true;
|
|
91
|
+
// A map or slice entering an interface is tagged by its instantiated type.
|
|
92
|
+
if (isGenericComposite(node._ifaceSrc)) return true;
|
|
93
|
+
if (node._ifaceSrc?.kind === "typeParam" && names.has(node._ifaceSrc.name))
|
|
94
|
+
return true;
|
|
73
95
|
switch (node.kind) {
|
|
74
96
|
case "TypeAssertExpr":
|
|
75
97
|
return typeNodeMentions(node.type, names);
|
|
@@ -79,11 +101,23 @@ export const typeInfoGenMethods = {
|
|
|
79
101
|
return typeNodeMentions(node.targetType, names);
|
|
80
102
|
case "BinaryExpr":
|
|
81
103
|
return (
|
|
82
|
-
(node.op === "==" || node.op === "!=") &&
|
|
83
|
-
|
|
104
|
+
((node.op === "==" || node.op === "!=") &&
|
|
105
|
+
node.left._type?.kind === "typeParam") ||
|
|
106
|
+
(node._type?.kind === "typeParam" && names.has(node._type.name))
|
|
107
|
+
);
|
|
108
|
+
case "AssignStmt":
|
|
109
|
+
return (
|
|
110
|
+
node.op !== "=" &&
|
|
111
|
+
node.lhs?.[0]?._type?.kind === "typeParam" &&
|
|
112
|
+
names.has(node.lhs[0]._type.name)
|
|
113
|
+
);
|
|
114
|
+
case "IncDecStmt":
|
|
115
|
+
return (
|
|
116
|
+
node.expr?._type?.kind === "typeParam" &&
|
|
117
|
+
names.has(node.expr._type.name)
|
|
84
118
|
);
|
|
85
119
|
case "CallExpr":
|
|
86
|
-
return this._callUsesTypeInfo(node);
|
|
120
|
+
return this._callUsesTypeInfo(node, names);
|
|
87
121
|
case "CompositeLit":
|
|
88
122
|
case "GenericTypeName":
|
|
89
123
|
return typeArgsMention(node);
|
|
@@ -92,7 +126,8 @@ export const typeInfoGenMethods = {
|
|
|
92
126
|
}
|
|
93
127
|
},
|
|
94
128
|
|
|
95
|
-
_callUsesTypeInfo(call) {
|
|
129
|
+
_callUsesTypeInfo(call, names) {
|
|
130
|
+
if (this._isTypeParamMethodCall(call, names)) return true;
|
|
96
131
|
if (call._isTypeConversion)
|
|
97
132
|
return call._conversionTargetType?.kind === "typeParam";
|
|
98
133
|
// A fmt operand of a type parameter type is formatted by its instantiated
|
|
@@ -146,16 +181,45 @@ export const typeInfoGenMethods = {
|
|
|
146
181
|
const check = this._typeCheckForType(t, "__v");
|
|
147
182
|
const is = check ? `(__v) => ${check}` : "null";
|
|
148
183
|
const unbox = this._boxesInInterface(t) ? "true" : "false";
|
|
149
|
-
const box = this._ifaceKeyBox(t);
|
|
184
|
+
const box = this._ifaceKeyBox(t) ?? this._ptrMethodBox(t);
|
|
150
185
|
const fmtBox = this._fmtBoxFor(t);
|
|
151
|
-
const tail = fmtBox
|
|
152
|
-
? `, ${box ?? "null"}, ${fmtBox}`
|
|
153
|
-
: box
|
|
154
|
-
? `, ${box}`
|
|
155
|
-
: "";
|
|
186
|
+
const tail = this._typeInfoTail(t, box, fmtBox);
|
|
156
187
|
return `__ti(${base}, ${JSON.stringify(k)}, ${JSON.stringify(typeStr(t))}, ${is}, ${unbox}${tail})`;
|
|
157
188
|
},
|
|
158
189
|
|
|
190
|
+
// The optional trailing arguments of `__ti` (`b`, `fb`, `nm`). `typeStr`
|
|
191
|
+
// drops the `...` of a variadic func type, so a func type also carries its
|
|
192
|
+
// spelled name (see `__tnm`).
|
|
193
|
+
_typeInfoTail(t, box, fmtBox) {
|
|
194
|
+
if (t.kind === "func")
|
|
195
|
+
return `, ${box ?? "null"}, ${fmtBox ?? "null"}, ${JSON.stringify(this._dynTypeName(t))}`;
|
|
196
|
+
if (fmtBox) return `, ${box ?? "null"}, ${fmtBox}`;
|
|
197
|
+
return box ? `, ${box}` : "";
|
|
198
|
+
},
|
|
199
|
+
|
|
200
|
+
// How `__ik` tells the dynamic type of an interface map key apart where
|
|
201
|
+
// the JS value cannot: the numeric type name for types sharing a JS
|
|
202
|
+
// representation with `int`/`float64`/`int64`, `*` for pointers (keyed by
|
|
203
|
+
// identity) and `!type` for types Go cannot hash.
|
|
204
|
+
_ifaceKeyTag(t) {
|
|
205
|
+
if (!t || mentionsTypeParam(t)) return null;
|
|
206
|
+
if (t.kind === "named") {
|
|
207
|
+
// A defined slice, map or func type: a literal of it carries no tag.
|
|
208
|
+
const u = this._underlyingOf(t)?.kind;
|
|
209
|
+
return u === "slice" || u === "map" || u === "func"
|
|
210
|
+
? `!${goTypeName(t)}`
|
|
211
|
+
: null;
|
|
212
|
+
}
|
|
213
|
+
if (t.kind === "pointer") return "*";
|
|
214
|
+
if (t.kind === "slice" || t.kind === "map" || t.kind === "func")
|
|
215
|
+
return `!${this._dynTypeName(t)}`;
|
|
216
|
+
if (t.kind !== "basic") return null;
|
|
217
|
+
const name = this._dynTypeName(t);
|
|
218
|
+
if (["int", "float64", "int64", "string", "bool"].includes(name))
|
|
219
|
+
return null;
|
|
220
|
+
return /^(u?int\d*|uintptr|float32)$/.test(name) ? name : null;
|
|
221
|
+
},
|
|
222
|
+
|
|
159
223
|
// A function boxing a plain value of named basic type `t`, as converting
|
|
160
224
|
// it to an interface does; null when `t` is not boxed.
|
|
161
225
|
_ifaceKeyBox(t) {
|
|
@@ -163,9 +227,27 @@ export const typeInfoGenMethods = {
|
|
|
163
227
|
return `(__v) => ${this._genNamedIfaceBox("__v", t, false)}`;
|
|
164
228
|
},
|
|
165
229
|
|
|
230
|
+
// A function boxing a pointer to a named non-struct type with methods, as
|
|
231
|
+
// converting it to an interface does; null for any other type.
|
|
232
|
+
_ptrMethodBox(t) {
|
|
233
|
+
if (t?.kind !== "pointer" || !this._boxesInInterface(t)) return null;
|
|
234
|
+
return `(__v) => ${this._genNamedIfaceBox("__v", t.base, true)}`;
|
|
235
|
+
},
|
|
236
|
+
|
|
166
237
|
// A function giving a plain value of type `t` the type information fmt
|
|
167
|
-
// needs once it is an `any` operand
|
|
238
|
+
// needs once it is an `any` operand; null when it needs none.
|
|
168
239
|
_fmtBoxFor(t) {
|
|
240
|
+
const base = this._fmtBoxBase(t);
|
|
241
|
+
// An unnamed slice, map or pointer also carries its type tag, as
|
|
242
|
+
// converting it to an `any` gives it, so a type switch tells it apart.
|
|
243
|
+
const tag = this._dynTypeTag(t);
|
|
244
|
+
if (!tag) return base;
|
|
245
|
+
this.useHelper("ifaceBox");
|
|
246
|
+
const ptr = this._dynTagFlags(t);
|
|
247
|
+
return `(__v) => __ity(${base ? `(${base})(__v)` : "__v"}, ${JSON.stringify(tag)}${ptr})`;
|
|
248
|
+
},
|
|
249
|
+
|
|
250
|
+
_fmtBoxBase(t) {
|
|
169
251
|
if (!t || t.kind === "typeParam" || mentionsTypeParam(t)) return null;
|
|
170
252
|
if (this._ifaceKeyBox(t)) return null; // boxed by the key box already
|
|
171
253
|
const u = this._underlyingOf(t);
|
|
@@ -173,15 +255,212 @@ export const typeInfoGenMethods = {
|
|
|
173
255
|
this.useHelper("ifaceBox");
|
|
174
256
|
return "(__v) => __ifp(__v)";
|
|
175
257
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
258
|
+
// A struct value is marked as a value, so a pointer boxed earlier by the
|
|
259
|
+
// same generic code does not make it read as `*T`.
|
|
260
|
+
if (u?.kind === "struct" && t.kind !== "pointer") {
|
|
261
|
+
this.useHelper("ifaceBox");
|
|
262
|
+
return "(__v) => __ifv(__v)";
|
|
263
|
+
}
|
|
179
264
|
if (u?.kind === "map") {
|
|
180
265
|
this.useHelper("ifaceBox");
|
|
181
|
-
return
|
|
266
|
+
return "(__v) => __ifm(__v)";
|
|
267
|
+
}
|
|
268
|
+
if (u?.kind === "slice") return "(__v) => (__v ?? [])";
|
|
269
|
+
return null;
|
|
270
|
+
},
|
|
271
|
+
|
|
272
|
+
// Extra `__ity` arguments: `, true` marks a pointer and `, false, true` a
|
|
273
|
+
// defined slice type, whose nil value keeps its type as a tagged sentinel.
|
|
274
|
+
_dynTagFlags(t) {
|
|
275
|
+
if (t?.kind === "pointer") return ", true";
|
|
276
|
+
if (this._isNamedComposite(t) && this._underlyingOf(t)?.kind === "slice")
|
|
277
|
+
return ", false, true";
|
|
278
|
+
return "";
|
|
279
|
+
},
|
|
280
|
+
|
|
281
|
+
// JS expression for the run-time type tag of a map, slice or array type `t`
|
|
282
|
+
// that mentions type parameters, built from their type information (see
|
|
283
|
+
// `__ti`); null when some part cannot be spelled.
|
|
284
|
+
_genericMapTag(t) {
|
|
285
|
+
const part = (x) => {
|
|
286
|
+
if (!mentionsTypeParam(x)) return JSON.stringify(this._dynTypeName(x));
|
|
287
|
+
switch (x.kind) {
|
|
288
|
+
case "typeParam": {
|
|
289
|
+
const f = this._typeParamZeroFactory(x.name);
|
|
290
|
+
return f ? `__tnm(${f})` : null;
|
|
291
|
+
}
|
|
292
|
+
case "slice": {
|
|
293
|
+
const e = part(x.elem);
|
|
294
|
+
return e && `"[]" + ${e}`;
|
|
295
|
+
}
|
|
296
|
+
case "array": {
|
|
297
|
+
const e = part(x.elem);
|
|
298
|
+
return e && `"[${x.size}]" + ${e}`;
|
|
299
|
+
}
|
|
300
|
+
case "pointer": {
|
|
301
|
+
const e = part(x.base);
|
|
302
|
+
return e && `"*" + ${e}`;
|
|
303
|
+
}
|
|
304
|
+
case "map": {
|
|
305
|
+
const k = part(x.key);
|
|
306
|
+
const v = part(x.value);
|
|
307
|
+
return k && v && `"map[" + ${k} + "]" + ${v}`;
|
|
308
|
+
}
|
|
309
|
+
default:
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
return part(t);
|
|
314
|
+
},
|
|
315
|
+
|
|
316
|
+
// A defined func type whose values carry their name as a run-time tag
|
|
317
|
+
// (one with methods is boxed as a class instead).
|
|
318
|
+
_isTaggedFuncType(t) {
|
|
319
|
+
return (
|
|
320
|
+
t?.kind === "named" &&
|
|
321
|
+
!mentionsTypeParam(t) &&
|
|
322
|
+
!t.typeArgs?.length &&
|
|
323
|
+
this._underlyingOf(t)?.kind === "func" &&
|
|
324
|
+
!this._namedBoxInfo(t)
|
|
325
|
+
);
|
|
326
|
+
},
|
|
327
|
+
|
|
328
|
+
// The run-time type tag of an unnamed slice, map or non-struct pointer
|
|
329
|
+
// entering an interface, or null when the value needs none. The tag lets
|
|
330
|
+
// assertions and type switches tell `[]int32` from `[]uint8`, `*int` from
|
|
331
|
+
// `*string`, and a nil `*int` from the nil interface.
|
|
332
|
+
_dynTypeTag(t) {
|
|
333
|
+
if (!t || mentionsTypeParam(t)) return null;
|
|
334
|
+
if (
|
|
335
|
+
t.kind === "slice" ||
|
|
336
|
+
t.kind === "map" ||
|
|
337
|
+
t.kind === "array" ||
|
|
338
|
+
t.kind === "struct" ||
|
|
339
|
+
t.kind === "func"
|
|
340
|
+
)
|
|
341
|
+
return this._dynTypeName(t);
|
|
342
|
+
if (this._isNamedComposite(t)) return t.name;
|
|
343
|
+
if (t.kind === "pointer") {
|
|
344
|
+
const base = t.base;
|
|
345
|
+
if (base?.kind === "struct" && !base.name) return this._dynTypeName(t);
|
|
346
|
+
if (this._underlyingOf(base)?.kind === "struct") return null;
|
|
347
|
+
if (this._boxesInInterface(t)) return null;
|
|
348
|
+
return this._dynTypeName(t);
|
|
349
|
+
}
|
|
350
|
+
return null;
|
|
351
|
+
},
|
|
352
|
+
|
|
353
|
+
// Whether Go cannot hash a value of type `t` (it contains a slice, map or
|
|
354
|
+
// func), so using it as an interface map key panics.
|
|
355
|
+
_isUnhashableType(t, depth = 0) {
|
|
356
|
+
if (!t || depth > 8 || mentionsTypeParam(t)) return false;
|
|
357
|
+
const u = this._underlyingOf(t) ?? t;
|
|
358
|
+
switch (u.kind) {
|
|
359
|
+
case "slice":
|
|
360
|
+
case "map":
|
|
361
|
+
case "func":
|
|
362
|
+
return true;
|
|
363
|
+
case "array":
|
|
364
|
+
return this._isUnhashableType(u.elem, depth + 1);
|
|
365
|
+
case "struct":
|
|
366
|
+
return [...(u.fields?.values() ?? [])].some((f) =>
|
|
367
|
+
this._isUnhashableType(f, depth + 1),
|
|
368
|
+
);
|
|
369
|
+
default:
|
|
370
|
+
return false;
|
|
371
|
+
}
|
|
372
|
+
},
|
|
373
|
+
|
|
374
|
+
// A named slice, array or map type: its values are plain JS arrays and
|
|
375
|
+
// objects, told apart in an interface by a tag carrying the type's name.
|
|
376
|
+
_isNamedComposite(t) {
|
|
377
|
+
if (t?.kind !== "named" || this._namedBoxInfo(t)) return false;
|
|
378
|
+
const kind = this._underlyingOf(t)?.kind;
|
|
379
|
+
return kind === "slice" || kind === "array" || kind === "map";
|
|
380
|
+
},
|
|
381
|
+
|
|
382
|
+
// `typeStr` with the spellings of identical types unified.
|
|
383
|
+
_dynTypeName(t) {
|
|
384
|
+
return this._dynTypeStr(t)
|
|
385
|
+
.replace(/\bbyte\b/g, "uint8")
|
|
386
|
+
.replace(/\brune\b/g, "int32")
|
|
387
|
+
.replace(/\bany\b/g, "interface {}")
|
|
388
|
+
.replace(/interface\{\.\.\.\}/g, "interface {}");
|
|
389
|
+
},
|
|
390
|
+
|
|
391
|
+
// `typeStr`, but an unnamed struct lists its fields, so two anonymous
|
|
392
|
+
// structs do not share a tag.
|
|
393
|
+
_dynTypeStr(t) {
|
|
394
|
+
const s = (x) => this._dynTypeStr(x);
|
|
395
|
+
switch (t?.kind) {
|
|
396
|
+
case "slice":
|
|
397
|
+
return `[]${s(t.elem)}`;
|
|
398
|
+
case "array":
|
|
399
|
+
return `[${t.size?.value ?? t.size ?? "..."}]${s(t.elem)}`;
|
|
400
|
+
case "map":
|
|
401
|
+
return `map[${s(t.key)}]${s(t.value)}`;
|
|
402
|
+
case "pointer":
|
|
403
|
+
return `*${s(t.base)}`;
|
|
404
|
+
case "func": {
|
|
405
|
+
// Go's spelling: no result is `func()`, not `func() void`.
|
|
406
|
+
const rs = (t.returns ?? []).filter(
|
|
407
|
+
(r) => !(r?.kind === "basic" && r.name === "void"),
|
|
408
|
+
);
|
|
409
|
+
const ps = (t.params ?? []).map((p, i) =>
|
|
410
|
+
t.variadic && i === t.params.length - 1 ? `...${s(p)}` : s(p),
|
|
411
|
+
);
|
|
412
|
+
const res =
|
|
413
|
+
rs.length === 0
|
|
414
|
+
? ""
|
|
415
|
+
: rs.length === 1
|
|
416
|
+
? ` ${s(rs[0])}`
|
|
417
|
+
: ` (${rs.map(s).join(", ")})`;
|
|
418
|
+
return `func(${ps.join(", ")})${res}`;
|
|
419
|
+
}
|
|
420
|
+
case "struct":
|
|
421
|
+
if (t.name || !t._structFields) return typeStr(t);
|
|
422
|
+
return `struct{${t._structFields
|
|
423
|
+
.map(
|
|
424
|
+
(f) =>
|
|
425
|
+
`${(f.names ?? []).join(",")} ${s(f.type)}${f.tag ? ` ${JSON.stringify(f.tag)}` : ""}${f.embedded ? " embedded" : ""}`,
|
|
426
|
+
)
|
|
427
|
+
.join("; ")}}`;
|
|
428
|
+
default:
|
|
429
|
+
return typeStr(t);
|
|
430
|
+
}
|
|
431
|
+
},
|
|
432
|
+
|
|
433
|
+
// Run-time test that `val` holds a value of the unnamed composite type
|
|
434
|
+
// (or named func type) of assertion target `typeNode`; null when the
|
|
435
|
+
// checker recorded no type for it.
|
|
436
|
+
_dynTypeCheck(typeNode, val) {
|
|
437
|
+
const t = typeNode?._assertType;
|
|
438
|
+
if (!t) return null;
|
|
439
|
+
const u = this._underlyingOf(t);
|
|
440
|
+
const shape = mentionsTypeParam(t);
|
|
441
|
+
const tag = JSON.stringify(shape ? "" : this._dynTypeName(t));
|
|
442
|
+
this.useHelper("ifaceBox");
|
|
443
|
+
const args = (kind, ctor = "null", n = "0") =>
|
|
444
|
+
`__isty(${val}, ${tag}, "${kind}", ${ctor}, ${n})`;
|
|
445
|
+
if (u?.kind === "func") return args("func");
|
|
446
|
+
if (t.kind === "named") return null;
|
|
447
|
+
if (u?.kind === "map") return args("map");
|
|
448
|
+
if (u?.kind === "struct") return args("struct");
|
|
449
|
+
if (u?.kind === "array")
|
|
450
|
+
return args("array", "null", String(u.size?.value ?? u.size ?? 0));
|
|
451
|
+
if (u?.kind === "slice") {
|
|
452
|
+
const ctor = isTypedArraySlice(u)
|
|
453
|
+
? typedArrayConstructorForElem(u.elem)
|
|
454
|
+
: null;
|
|
455
|
+
return args("slice", ctor ?? "null");
|
|
456
|
+
}
|
|
457
|
+
if (u?.kind === "pointer") {
|
|
458
|
+
if (u.base?.kind === "struct" && !u.base.name) return args("struct");
|
|
459
|
+
if (this._underlyingOf(u.base)?.kind === "struct") return null;
|
|
460
|
+
if (this._boxesInInterface(u)) return null;
|
|
461
|
+
return args("ptr");
|
|
182
462
|
}
|
|
183
|
-
|
|
184
|
-
return desc ? `(__v) => ${tagged("__v")}` : null;
|
|
463
|
+
return null;
|
|
185
464
|
},
|
|
186
465
|
|
|
187
466
|
_underlyingOf(t) {
|