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
|
@@ -2,21 +2,27 @@ import {
|
|
|
2
2
|
closureMutates,
|
|
3
3
|
fnMutates,
|
|
4
4
|
nodeMutatesVar,
|
|
5
|
-
nodeWritesVar,
|
|
6
5
|
rootIdentName,
|
|
7
6
|
someChild,
|
|
8
7
|
} from "../../lower/index.js";
|
|
8
|
+
import { hasCallOrReceive } from "../../lower/walk.js";
|
|
9
9
|
import { constIntValue } from "../../typechecker/const-fold.js";
|
|
10
10
|
import {
|
|
11
11
|
BROWSER_GLOBALS,
|
|
12
12
|
compositeLitIndices,
|
|
13
13
|
ERROR,
|
|
14
|
+
embeddedFieldName,
|
|
15
|
+
is64BitInt,
|
|
16
|
+
is64BitUnsigned,
|
|
14
17
|
isAny,
|
|
15
18
|
isComplex,
|
|
19
|
+
isComplex64,
|
|
16
20
|
isTypedArraySlice,
|
|
17
21
|
typedArrayConstructorForElem,
|
|
18
22
|
typeStr,
|
|
19
23
|
} from "../../typechecker/types.js";
|
|
24
|
+
import { goTypeName } from "../shared/fmt-types.js";
|
|
25
|
+
import { jsNativeCall, NATIVE_HELPERS } from "./natives.js";
|
|
20
26
|
|
|
21
27
|
// True when `node` compares the identifier `name` with nil (`l == nil`): a
|
|
22
28
|
// pointer-receiver method that does so is meant to run on a nil receiver.
|
|
@@ -37,8 +43,92 @@ export function comparesToNil(node, name) {
|
|
|
37
43
|
return someChild(node, (c) => comparesToNil(c, name));
|
|
38
44
|
}
|
|
39
45
|
|
|
46
|
+
// True when evaluating `node` reads through the receiver `name` (`r.f`,
|
|
47
|
+
// `r[i]`, `*r`), which panics on a nil receiver anyway. Calling a method of
|
|
48
|
+
// `safe` on it is not a read: that method accepts a nil receiver. A func
|
|
49
|
+
// literal's body is not evaluated where it appears, so it is not searched.
|
|
50
|
+
export function derefsReceiver(node, name, safe) {
|
|
51
|
+
if (Array.isArray(node))
|
|
52
|
+
return node.some((n) => derefsReceiver(n, name, safe));
|
|
53
|
+
if (!node || typeof node !== "object" || node.kind === "FuncLit")
|
|
54
|
+
return false;
|
|
55
|
+
const isRecv = (e) => e?.kind === "Ident" && e.name === name;
|
|
56
|
+
if (node.kind === "SelectorExpr" && isRecv(node.expr))
|
|
57
|
+
return !safe.has(node.field);
|
|
58
|
+
if (
|
|
59
|
+
(node.kind === "IndexExpr" || node.kind === "SliceExpr") &&
|
|
60
|
+
isRecv(node.expr)
|
|
61
|
+
)
|
|
62
|
+
return true;
|
|
63
|
+
if (node.kind === "UnaryExpr" && node.op === "*" && isRecv(node.operand))
|
|
64
|
+
return true;
|
|
65
|
+
return someChild(node, (c) => derefsReceiver(c, name, safe));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// True when `stmts` (a method body) reads through the receiver `name` before
|
|
69
|
+
// anything could skip the read: only the statements that always run first
|
|
70
|
+
// count, up to the first branch, loop, return or rebinding of `name`. A
|
|
71
|
+
// method that does so panics on a nil receiver either way.
|
|
72
|
+
export function derefsReceiverFirst(stmts, name, safe) {
|
|
73
|
+
return receiverPrefix(stmts, name, safe) === "deref";
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// What one statement of a method body's unconditional prefix does with the
|
|
77
|
+
// receiver: "deref" reads through it, "stop" may skip the rest (a branch,
|
|
78
|
+
// loop, return, panic or rebinding of the name), "next" runs on.
|
|
79
|
+
const binds = (lhs, name) =>
|
|
80
|
+
lhs.some((e) => e?.kind === "Ident" && e.name === name);
|
|
81
|
+
const branches = (reads, ...parts) => (parts.some(reads) ? "deref" : "stop");
|
|
82
|
+
const RECEIVER_STEP = {
|
|
83
|
+
Block: (s, _reads, name, safe) => receiverPrefix(s.stmts, name, safe),
|
|
84
|
+
IfStmt: (s, reads) => branches(reads, s.init, s.cond),
|
|
85
|
+
ForStmt: (s, reads) => branches(reads, s.init, s.cond),
|
|
86
|
+
SwitchStmt: (s, reads) => branches(reads, s.init, s.tag),
|
|
87
|
+
TypeSwitchStmt: (s, reads) => branches(reads, s.expr),
|
|
88
|
+
ReturnStmt: (s, reads) => branches(reads, s.values),
|
|
89
|
+
ExprStmt: (s, reads) => {
|
|
90
|
+
if (reads(s.expr)) return "deref";
|
|
91
|
+
const panics = s.expr?.kind === "CallExpr" && s.expr.func?.name === "panic";
|
|
92
|
+
return panics ? "stop" : "next";
|
|
93
|
+
},
|
|
94
|
+
DefineStmt: (s, reads, name) => {
|
|
95
|
+
if (reads(s.rhs) || reads(s.lhs.filter((e) => e?.kind !== "Ident")))
|
|
96
|
+
return "deref";
|
|
97
|
+
return binds(s.lhs, name) ? "stop" : "next";
|
|
98
|
+
},
|
|
99
|
+
IncDecStmt: (s, reads) => (reads(s.expr) ? "deref" : "next"),
|
|
100
|
+
VarDecl: (s, reads, name) => {
|
|
101
|
+
if (s.decls.some((d) => reads(d.value))) return "deref";
|
|
102
|
+
const named = (d) => d.names.some((n) => (n?.name ?? n) === name);
|
|
103
|
+
return s.decls.some(named) ? "stop" : "next";
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
RECEIVER_STEP.AssignStmt = RECEIVER_STEP.DefineStmt;
|
|
107
|
+
|
|
108
|
+
// "deref" when the unconditional prefix of `stmts` reads through the
|
|
109
|
+
// receiver, "stop" when something may skip the rest, else "next".
|
|
110
|
+
function receiverPrefix(stmts, name, safe) {
|
|
111
|
+
const reads = (n) => derefsReceiver(n, name, safe);
|
|
112
|
+
for (const s of stmts ?? []) {
|
|
113
|
+
const step = RECEIVER_STEP[s?.kind];
|
|
114
|
+
const r = step ? step(s, reads, name, safe) : "stop";
|
|
115
|
+
if (r !== "next") return r;
|
|
116
|
+
}
|
|
117
|
+
return "next";
|
|
118
|
+
}
|
|
119
|
+
|
|
40
120
|
/** @typedef {import('./index.js').CodeGen} CodeGen */
|
|
41
121
|
|
|
122
|
+
// True for a map, slice or array type that mentions a type parameter: one
|
|
123
|
+
// that generic code boxes into an interface is tagged by its instantiated
|
|
124
|
+
// type.
|
|
125
|
+
export function isGenericComposite(t) {
|
|
126
|
+
return (
|
|
127
|
+
(t?.kind === "map" || t?.kind === "slice" || t?.kind === "array") &&
|
|
128
|
+
mentionsTypeParam(t)
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
42
132
|
// True when type `t` mentions a type parameter anywhere.
|
|
43
133
|
export function mentionsTypeParam(t, depth = 0) {
|
|
44
134
|
if (!t || depth > 16) return false;
|
|
@@ -125,36 +215,7 @@ const STRICT_OPS = new Set([
|
|
|
125
215
|
">>",
|
|
126
216
|
]);
|
|
127
217
|
|
|
128
|
-
function collectFunctionLabels(node, labels = new Set()) {
|
|
129
|
-
if (!node || typeof node !== "object") return labels;
|
|
130
|
-
if (Array.isArray(node)) {
|
|
131
|
-
for (const item of node) collectFunctionLabels(item, labels);
|
|
132
|
-
return labels;
|
|
133
|
-
}
|
|
134
|
-
if (node.kind === "LabeledStmt" && node.label) labels.add(node.label);
|
|
135
|
-
if (node.kind === "ForStmt" && node.label) labels.add(node.label);
|
|
136
|
-
for (const k of Object.keys(node)) {
|
|
137
|
-
if (k.startsWith("_")) continue;
|
|
138
|
-
collectFunctionLabels(node[k], labels);
|
|
139
|
-
}
|
|
140
|
-
return labels;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
218
|
const NAMED_ZERO_TABLE = {
|
|
144
|
-
"time.Time": [null, "{ _d: new Date(0), _isZero: true }"],
|
|
145
|
-
"strings.Builder": [null, '{ _buf: "" }'],
|
|
146
|
-
"bytes.Buffer": [null, "{ _buf: [] }"],
|
|
147
|
-
"strings.Replacer": [null, "{ _p: [], Replace(s) { return s; } }"],
|
|
148
|
-
"sync.Mutex": ["sync", "__sync_mutex()"],
|
|
149
|
-
"sync.RWMutex": ["sync", "__sync_rwmutex()"],
|
|
150
|
-
"sync.Once": ["sync", "__sync_once()"],
|
|
151
|
-
"sync.WaitGroup": ["sync", "__sync_waitgroup()"],
|
|
152
|
-
"binary.LittleEndian": ["binary", "__binary_le"],
|
|
153
|
-
"encoding/binary.LittleEndian": ["binary", "__binary_le"],
|
|
154
|
-
"binary.BigEndian": ["binary", "__binary_be"],
|
|
155
|
-
"encoding/binary.BigEndian": ["binary", "__binary_be"],
|
|
156
|
-
"base64.Encoding": ["base64", "__base64_std"],
|
|
157
|
-
"encoding/base64.Encoding": ["base64", "__base64_std"],
|
|
158
219
|
"simd.F32x4": [null, "Object.freeze([0, 0, 0, 0])"],
|
|
159
220
|
F32x4: [null, "Object.freeze([0, 0, 0, 0])"],
|
|
160
221
|
"simd.Mask32x4": [null, "Object.freeze([false, false, false, false])"],
|
|
@@ -236,91 +297,13 @@ function splitLastAccess(js) {
|
|
|
236
297
|
|
|
237
298
|
// Namespace constants: pkg.Field → JS literal
|
|
238
299
|
const NS_CONSTANTS = {
|
|
239
|
-
math: {
|
|
240
|
-
Pi: "Math.PI",
|
|
241
|
-
E: "Math.E",
|
|
242
|
-
Phi: "1.618033988749895",
|
|
243
|
-
Sqrt2: "Math.SQRT2",
|
|
244
|
-
SqrtE: "1.6487212707001282",
|
|
245
|
-
SqrtPi: "1.7724538509055159",
|
|
246
|
-
SqrtPhi: "1.272019649514069",
|
|
247
|
-
Ln2: "Math.LN2",
|
|
248
|
-
Log2E: "Math.LOG2E",
|
|
249
|
-
Ln10: "Math.LN10",
|
|
250
|
-
Log10E: "Math.LOG10E",
|
|
251
|
-
MaxFloat32: "3.4028234663852886e38",
|
|
252
|
-
SmallestNonzeroFloat32: "1.401298464324817e-45",
|
|
253
|
-
MaxFloat64: "Number.MAX_VALUE",
|
|
254
|
-
SmallestNonzeroFloat64: "Number.MIN_VALUE",
|
|
255
|
-
MaxInt: "Number.MAX_SAFE_INTEGER",
|
|
256
|
-
MinInt: "Number.MIN_SAFE_INTEGER",
|
|
257
|
-
MaxInt8: "127",
|
|
258
|
-
MinInt8: "-128",
|
|
259
|
-
MaxInt16: "32767",
|
|
260
|
-
MinInt16: "-32768",
|
|
261
|
-
MaxInt32: "2147483647",
|
|
262
|
-
MinInt32: "-2147483648",
|
|
263
|
-
MaxInt64: "9223372036854775807",
|
|
264
|
-
MinInt64: "-9223372036854775808",
|
|
265
|
-
MaxUint8: "255",
|
|
266
|
-
MaxUint16: "65535",
|
|
267
|
-
MaxUint32: "4294967295",
|
|
268
|
-
MaxUint: "18446744073709551615",
|
|
269
|
-
MaxUint64: "18446744073709551615",
|
|
270
|
-
},
|
|
271
|
-
strconv: {
|
|
272
|
-
ErrSyntax: "__strconv_err_syntax",
|
|
273
|
-
ErrRange: "__strconv_err_range",
|
|
274
|
-
},
|
|
275
|
-
bits: {
|
|
276
|
-
UintSize: "64",
|
|
277
|
-
},
|
|
278
|
-
path: {
|
|
279
|
-
ErrBadPattern: "__path_err_bad_pattern",
|
|
280
|
-
},
|
|
281
|
-
filepath: {
|
|
282
|
-
ErrBadPattern: "__path_err_bad_pattern",
|
|
283
|
-
},
|
|
284
|
-
io: {
|
|
285
|
-
EOF: "__io_eof",
|
|
286
|
-
Discard:
|
|
287
|
-
"{ WriteString(s) { return [s.length, null]; }, Write(b) { return [b.length, null]; } }",
|
|
288
|
-
},
|
|
289
|
-
sha256: {
|
|
290
|
-
Size: "32",
|
|
291
|
-
BlockSize: "64",
|
|
292
|
-
},
|
|
293
|
-
hex: {
|
|
294
|
-
ErrLength: '__error("encoding/hex: odd length hex string")',
|
|
295
|
-
},
|
|
296
|
-
base64: {
|
|
297
|
-
StdPadding: "61",
|
|
298
|
-
NoPadding: "-1",
|
|
299
|
-
StdEncoding: "__base64_std",
|
|
300
|
-
URLEncoding: "__base64_url",
|
|
301
|
-
RawStdEncoding: "__base64_raw_std",
|
|
302
|
-
RawURLEncoding: "__base64_raw_url",
|
|
303
|
-
},
|
|
304
|
-
binary: {
|
|
305
|
-
LittleEndian: "__binary_le",
|
|
306
|
-
BigEndian: "__binary_be",
|
|
307
|
-
},
|
|
308
|
-
rand: {
|
|
309
|
-
Reader: "__crypto_rand_reader",
|
|
310
|
-
},
|
|
311
|
-
os: {
|
|
312
|
-
Args: "process.argv",
|
|
313
|
-
Stdout: "__os_stdout",
|
|
314
|
-
Stderr: "__os_stderr",
|
|
315
|
-
Stdin: "__os_stdin",
|
|
316
|
-
},
|
|
317
300
|
time: {
|
|
318
|
-
Nanosecond: "
|
|
319
|
-
Microsecond: "
|
|
320
|
-
Millisecond: "
|
|
321
|
-
Second: "
|
|
322
|
-
Minute: "
|
|
323
|
-
Hour: "
|
|
301
|
+
Nanosecond: "1n",
|
|
302
|
+
Microsecond: "1000n",
|
|
303
|
+
Millisecond: "1000000n",
|
|
304
|
+
Second: "1000000000n",
|
|
305
|
+
Minute: "60000000000n",
|
|
306
|
+
Hour: "3600000000000n",
|
|
324
307
|
RFC3339: '"2006-01-02T15:04:05Z07:00"',
|
|
325
308
|
RFC3339Nano: '"2006-01-02T15:04:05.999999999Z07:00"',
|
|
326
309
|
DateOnly: '"2006-01-02"',
|
|
@@ -348,7 +331,6 @@ const NS_CONSTANTS = {
|
|
|
348
331
|
Friday: "5",
|
|
349
332
|
Saturday: "6",
|
|
350
333
|
},
|
|
351
|
-
utf8: { RuneError: "0xFFFD", MaxRune: "0x10FFFF", UTFMax: "4" },
|
|
352
334
|
};
|
|
353
335
|
|
|
354
336
|
// JavaScript precedence of the operators emitted for Go binary expressions.
|
|
@@ -413,6 +395,7 @@ const SHARED_ZERO_FACTORIES = {
|
|
|
413
395
|
0: "__z0",
|
|
414
396
|
'""': "__zs",
|
|
415
397
|
false: "__zf",
|
|
398
|
+
"0n": "__z64",
|
|
416
399
|
};
|
|
417
400
|
|
|
418
401
|
// Zero-value JS literals for basic Go types.
|
|
@@ -422,11 +405,11 @@ const ZERO_FOR_BASIC = {
|
|
|
422
405
|
int8: "0",
|
|
423
406
|
int16: "0",
|
|
424
407
|
int32: "0",
|
|
425
|
-
int64: "
|
|
408
|
+
int64: "0n",
|
|
426
409
|
uint8: "0",
|
|
427
410
|
uint16: "0",
|
|
428
411
|
uint32: "0",
|
|
429
|
-
uint64: "
|
|
412
|
+
uint64: "0n",
|
|
430
413
|
uintptr: "0",
|
|
431
414
|
float32: "0",
|
|
432
415
|
float64: "0",
|
|
@@ -439,13 +422,18 @@ const ZERO_FOR_BASIC = {
|
|
|
439
422
|
};
|
|
440
423
|
|
|
441
424
|
// Set of all numeric type names (for type-assertion codegen).
|
|
442
|
-
const TYPEOF_NUMBER_NAMES = new Set([
|
|
425
|
+
const TYPEOF_NUMBER_NAMES = new Set([
|
|
426
|
+
...[...INT_TYPE_NAMES].filter((n) => n !== "int64" && n !== "uint64"),
|
|
427
|
+
"float32",
|
|
428
|
+
"float64",
|
|
429
|
+
]);
|
|
443
430
|
|
|
444
431
|
// Builtin function codegen — keyed by builtin name, value is (self, expr) => string.
|
|
445
432
|
const BUILTIN_GEN = {
|
|
446
433
|
append: (s, e) => s.genAppend(e),
|
|
447
434
|
len: (s, e) => s._genBuiltinLen(e),
|
|
448
435
|
cap: (s, e) => {
|
|
436
|
+
if (e._constLen != null) return s._genConstLen(e);
|
|
449
437
|
s.useHelper("slice");
|
|
450
438
|
return `__cap(${s.genExpr(e.args[0])})`;
|
|
451
439
|
},
|
|
@@ -457,13 +445,11 @@ const BUILTIN_GEN = {
|
|
|
457
445
|
copy: (s, e) => {
|
|
458
446
|
const dst = s.genExpr(e.args[0]);
|
|
459
447
|
const src = s._genSliceSource(e.args[1]);
|
|
460
|
-
// Array elements are values: the copied elements must not
|
|
448
|
+
// Array and struct elements are values: the copied elements must not
|
|
449
|
+
// alias src.
|
|
461
450
|
const t = e.args[1]._type;
|
|
462
451
|
const elem = (t?.kind === "named" ? t.underlying : t)?.elem;
|
|
463
|
-
const clone =
|
|
464
|
-
s._aggregateBase(elem)?.kind === "array"
|
|
465
|
-
? s._cloneJs(elem, "__e")
|
|
466
|
-
: "__e";
|
|
452
|
+
const clone = s._aggregateBase(elem) ? s._cloneJs(elem, "__e") : "__e";
|
|
467
453
|
const items =
|
|
468
454
|
clone === "__e"
|
|
469
455
|
? "(__cs.slice ? __cs.slice(0, n) : __cs.subarray(0, n))"
|
|
@@ -496,15 +482,21 @@ const BUILTIN_GEN = {
|
|
|
496
482
|
// `print` writes without a trailing newline (Go semantics); the wasm glue
|
|
497
483
|
// buffers the same way, so output parity holds across backends.
|
|
498
484
|
print: (s, e) =>
|
|
499
|
-
`((__s) => (typeof process !== "undefined" && process?.stdout?.write ? process.stdout.write(__s) : console.log(__s)))([${e.args.map((a) => s.genExpr(a)).join(", ")}].map(String).join("
|
|
485
|
+
`((__s) => (typeof process !== "undefined" && process?.stdout?.write ? process.stdout.write(__s) : console.log(__s)))([${e.args.map((a) => s.genExpr(a)).join(", ")}].map(String).join(""))`,
|
|
500
486
|
println: (s, e) =>
|
|
501
487
|
`console.log(${e.args.map((a) => s.genExpr(a)).join(", ")})`,
|
|
502
|
-
panic: (s, e) =>
|
|
488
|
+
panic: (s, e) => {
|
|
489
|
+
const arg = s.genExpr(e.args[0]);
|
|
490
|
+
s.useHelper("panicRt");
|
|
491
|
+
return `__gopanicv(${arg})`;
|
|
492
|
+
},
|
|
503
493
|
recover: (s) => {
|
|
504
494
|
s.useHelper("panicRt");
|
|
505
495
|
return `__recover(${s._recoverOkInScope ? "__recoverOk" : "false"})`;
|
|
506
496
|
},
|
|
507
497
|
error: (s, e) => {
|
|
498
|
+
// `error(x)` of an error (or nil) is the conversion: x itself.
|
|
499
|
+
if (!s._isStringExprType(e.args[0]?._type)) return s.genExpr(e.args[0]);
|
|
508
500
|
s.useHelper("error");
|
|
509
501
|
return `__error(${s.genExpr(e.args[0])})`;
|
|
510
502
|
},
|
|
@@ -521,13 +513,25 @@ const BUILTIN_GEN = {
|
|
|
521
513
|
// A type parameter may be a string or a number: pick the form at run time
|
|
522
514
|
// (Math.min/max keeps NaN propagation and -0 for floats).
|
|
523
515
|
function genMinMax(s, e, fn, cmp) {
|
|
524
|
-
const
|
|
516
|
+
const is64 = s._is64BitInt(e._type);
|
|
517
|
+
const args = e.args.map((a) => {
|
|
518
|
+
const js = s.genExpr(a);
|
|
519
|
+
if (
|
|
520
|
+
is64 &&
|
|
521
|
+
a._type?.kind === "untyped" &&
|
|
522
|
+
!js.endsWith("n") &&
|
|
523
|
+
/^-?\d+$/.test(js)
|
|
524
|
+
) {
|
|
525
|
+
return `${js}n`;
|
|
526
|
+
}
|
|
527
|
+
return js;
|
|
528
|
+
});
|
|
525
529
|
const fold = (xs) => `${xs}.reduce((a, b) => (b ${cmp} a ? b : a))`;
|
|
526
530
|
if (args.length === 1) return args[0];
|
|
527
531
|
if (e._type?.kind === "typeParam")
|
|
528
|
-
return `((...v) => (typeof v[0] === "string" ? ${fold("v")} : Math.${fn}(...v)))(${args.join(", ")})`;
|
|
529
|
-
if (
|
|
530
|
-
return
|
|
532
|
+
return `((...v) => (typeof v[0] === "string" || typeof v[0] === "bigint" ? ${fold("v")} : Math.${fn}(...v)))(${args.join(", ")})`;
|
|
533
|
+
if (is64 || s._isStringExprType(e._type)) return fold(`[${args.join(", ")}]`);
|
|
534
|
+
return `Math.${fn}(${args.join(", ")})`;
|
|
531
535
|
}
|
|
532
536
|
|
|
533
537
|
// Expression kinds whose folded constant value (`_const`, attached by the
|
|
@@ -588,8 +592,51 @@ function hasTypeParamType(t) {
|
|
|
588
592
|
).includes('"typeParam"');
|
|
589
593
|
}
|
|
590
594
|
|
|
595
|
+
// Complex constant arithmetic on [re, im] pairs.
|
|
596
|
+
const COMPLEX_CONST_OPS = {
|
|
597
|
+
"+": (a, b) => [a[0] + b[0], a[1] + b[1]],
|
|
598
|
+
"-": (a, b) => [a[0] - b[0], a[1] - b[1]],
|
|
599
|
+
"*": (a, b) => [a[0] * b[0] - a[1] * b[1], a[0] * b[1] + a[1] * b[0]],
|
|
600
|
+
"/": (a, b) => {
|
|
601
|
+
const d = b[0] * b[0] + b[1] * b[1];
|
|
602
|
+
if (d === 0) return null;
|
|
603
|
+
return [(a[0] * b[0] + a[1] * b[1]) / d, (a[1] * b[0] - a[0] * b[1]) / d];
|
|
604
|
+
},
|
|
605
|
+
};
|
|
606
|
+
|
|
607
|
+
// The [re, im] value of an untyped complex constant expression built from
|
|
608
|
+
// literals and named constants, or null.
|
|
609
|
+
function complexConstParts(e, depth = 0) {
|
|
610
|
+
if (!e || typeof e !== "object" || depth > 32) return null;
|
|
611
|
+
if (e.kind === "ParenExpr") return complexConstParts(e.expr, depth + 1);
|
|
612
|
+
// A named complex constant has no folded value: fold its initializer.
|
|
613
|
+
if (e._complexInit) return complexConstParts(e._complexInit, depth + 1);
|
|
614
|
+
if (e.kind === "ImagLit") return [0, Number(e.value)];
|
|
615
|
+
const val = e._const;
|
|
616
|
+
if (val && (val.k === "FLOAT" || val.k === "INT")) return [Number(val.v), 0];
|
|
617
|
+
if (e.kind === "UnaryExpr") return complexConstNeg(e, depth);
|
|
618
|
+
const op = e.kind === "BinaryExpr" ? COMPLEX_CONST_OPS[e.op] : null;
|
|
619
|
+
const a = op && complexConstParts(e.left, depth + 1);
|
|
620
|
+
const b = a && complexConstParts(e.right, depth + 1);
|
|
621
|
+
return b ? op(a, b) : null;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
function complexConstNeg(e, depth) {
|
|
625
|
+
if (e.op !== "-" && e.op !== "+") return null;
|
|
626
|
+
const c = complexConstParts(e.operand, depth + 1);
|
|
627
|
+
return c && e.op === "-" ? [-c[0], -c[1]] : c;
|
|
628
|
+
}
|
|
629
|
+
|
|
591
630
|
export const expressionGenMethods = {
|
|
592
631
|
genExpr(expr) {
|
|
632
|
+
if (expr._asComplex && !expr._complexWrapping) {
|
|
633
|
+
expr._complexWrapping = true;
|
|
634
|
+
try {
|
|
635
|
+
return `{ re: ${this.genExpr(expr)}, im: 0 }`;
|
|
636
|
+
} finally {
|
|
637
|
+
expr._complexWrapping = false;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
593
640
|
if (expr._ifaceKeyDyn && !expr._ifaceKeying) {
|
|
594
641
|
// A key typed by a type parameter: encode it for the instantiated type.
|
|
595
642
|
const f = this._typeParamZeroFactory(expr._ifaceKeyParam);
|
|
@@ -613,21 +660,96 @@ export const expressionGenMethods = {
|
|
|
613
660
|
expr._ifaceKeying = true;
|
|
614
661
|
try {
|
|
615
662
|
this.useHelper("structKey");
|
|
616
|
-
|
|
663
|
+
const tag = this._ifaceKeyTag(expr._ifaceKeyType);
|
|
664
|
+
return `__ik(${this.genExpr(expr)}${tag ? `, ${JSON.stringify(tag)}` : ""})`;
|
|
617
665
|
} finally {
|
|
618
666
|
expr._ifaceKeying = false;
|
|
619
667
|
}
|
|
620
668
|
}
|
|
621
669
|
}
|
|
670
|
+
if (expr._mapKeyEnc && !expr._mapKeying) {
|
|
671
|
+
// Array, anonymous struct and pointer keys: see HELPER_STRUCT_KEY.
|
|
672
|
+
expr._mapKeying = true;
|
|
673
|
+
try {
|
|
674
|
+
this.useHelper("structKey");
|
|
675
|
+
const enc = expr._mapKeyEnc === "pk" ? "__pk" : "__sk";
|
|
676
|
+
return `${enc}(${this.genExpr(expr)})`;
|
|
677
|
+
} finally {
|
|
678
|
+
expr._mapKeying = false;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
if (
|
|
682
|
+
expr._ifaceSrc?.kind === "pointer" &&
|
|
683
|
+
expr._ifaceSrc.base?.kind === "typeParam" &&
|
|
684
|
+
!expr._pbsWrapping
|
|
685
|
+
) {
|
|
686
|
+
// `&v` of a type parameter's variable is a box; a struct behind it
|
|
687
|
+
// is its own pointer.
|
|
688
|
+
expr._pbsWrapping = true;
|
|
689
|
+
try {
|
|
690
|
+
this.useHelper("ifaceBox");
|
|
691
|
+
return `__pbs(${this.genExpr(expr)})`;
|
|
692
|
+
} finally {
|
|
693
|
+
expr._pbsWrapping = false;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
if (
|
|
697
|
+
expr._ifaceSrc?.kind === "typeParam" &&
|
|
698
|
+
!expr._tpBoxing &&
|
|
699
|
+
!expr._ifaceKeyDyn &&
|
|
700
|
+
this._typeParamZeroFactory(expr._ifaceSrc.name)
|
|
701
|
+
) {
|
|
702
|
+
// A type parameter's value entering an interface is boxed and
|
|
703
|
+
// tagged by the instantiated type (see `__ti`).
|
|
704
|
+
expr._tpBoxing = true;
|
|
705
|
+
try {
|
|
706
|
+
const f = this._typeParamZeroFactory(expr._ifaceSrc.name);
|
|
707
|
+
return `(${f}.b ?? ${f}.fb ?? ((__v) => __v))(${this.genExpr(expr)})`;
|
|
708
|
+
} finally {
|
|
709
|
+
expr._tpBoxing = false;
|
|
710
|
+
}
|
|
711
|
+
}
|
|
622
712
|
if (this._isNamedIfaceBox(expr)) return this._genNamedIfaceBoxExpr(expr);
|
|
623
|
-
if (
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
713
|
+
if (
|
|
714
|
+
expr._ifaceSrc &&
|
|
715
|
+
!expr._tfnTagging &&
|
|
716
|
+
this._isTaggedFuncType(expr._ifaceSrc)
|
|
717
|
+
) {
|
|
718
|
+
// A value of a defined func type keeps that type in an interface.
|
|
719
|
+
expr._tfnTagging = true;
|
|
720
|
+
try {
|
|
721
|
+
this.useHelper("ifaceBox");
|
|
722
|
+
return `__tfn(${this.genExpr(expr)}, ${JSON.stringify(expr._ifaceSrc.name)})`;
|
|
723
|
+
} finally {
|
|
724
|
+
expr._tfnTagging = false;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
if (isGenericComposite(expr._ifaceSrc) && !expr._gmapTagging) {
|
|
728
|
+
const tag = this._genericMapTag(expr._ifaceSrc);
|
|
729
|
+
if (tag) {
|
|
730
|
+
this.useHelper("ifaceBox");
|
|
731
|
+
expr._gmapTagging = true;
|
|
732
|
+
try {
|
|
733
|
+
// An array is copied before it is tagged: a copy made after
|
|
734
|
+
// `__ity` (`.map`) would drop the tag.
|
|
735
|
+
return `__ity(${this.genValueExpr(expr)}, ${tag})`;
|
|
736
|
+
} finally {
|
|
737
|
+
expr._gmapTagging = false;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
{
|
|
742
|
+
const tag = this._ityTag(expr);
|
|
743
|
+
if (tag) {
|
|
744
|
+
this.useHelper("ifaceBox");
|
|
745
|
+
expr._ityTagging = true;
|
|
627
746
|
try {
|
|
628
|
-
|
|
747
|
+
const ptr = this._dynTagFlags(expr._ifaceSrc);
|
|
748
|
+
// An array is copied before it is tagged: the copy is the value
|
|
749
|
+
// the interface holds, and it must keep the tag.
|
|
750
|
+
return `__ity(${this.genValueExpr(expr)}, ${JSON.stringify(tag)}${ptr})`;
|
|
629
751
|
} finally {
|
|
630
|
-
expr.
|
|
752
|
+
expr._ityTagging = false;
|
|
631
753
|
}
|
|
632
754
|
}
|
|
633
755
|
}
|
|
@@ -635,29 +757,50 @@ export const expressionGenMethods = {
|
|
|
635
757
|
expr._ifaceBoxing = true;
|
|
636
758
|
try {
|
|
637
759
|
this.useHelper("ifaceBox");
|
|
638
|
-
|
|
760
|
+
const boxed = this._ifaceBoxWrap(
|
|
639
761
|
this.genExpr(expr),
|
|
640
762
|
expr._ifaceBox,
|
|
641
763
|
expr._ifacePtrFields,
|
|
764
|
+
expr.kind === "CompositeLit",
|
|
642
765
|
);
|
|
766
|
+
return this._typedNilWrap(boxed, expr);
|
|
643
767
|
} finally {
|
|
644
768
|
expr._ifaceBoxing = false;
|
|
645
769
|
}
|
|
646
770
|
}
|
|
771
|
+
const f32 = this._genFloat32Const(expr);
|
|
772
|
+
if (f32 !== null) return f32;
|
|
773
|
+
const c64 = this._genComplex64Const(expr);
|
|
774
|
+
if (c64 !== null) return c64;
|
|
647
775
|
const folded = this._genFoldedConst(expr);
|
|
648
776
|
if (folded !== null) return folded;
|
|
649
777
|
switch (expr.kind) {
|
|
650
778
|
case "BasicLit":
|
|
651
779
|
if (expr.litKind === "STRING") return JSON.stringify(expr.value);
|
|
780
|
+
if (
|
|
781
|
+
(this._is64BitInt(expr._type) || expr._is64Bit) &&
|
|
782
|
+
(expr.litKind === "INT" || expr._const?.k === "INT")
|
|
783
|
+
) {
|
|
784
|
+
return `${expr.value}n`;
|
|
785
|
+
}
|
|
652
786
|
if (expr._const?.k === "INT")
|
|
653
787
|
this._warnConstPrecision(expr._const, expr);
|
|
654
788
|
return expr.value; // int, float, bool, nil(null)
|
|
655
789
|
case "ImagLit":
|
|
656
790
|
return `{ re: 0, im: ${expr.value} }`;
|
|
657
|
-
case "Ident":
|
|
791
|
+
case "Ident": {
|
|
658
792
|
if (this._boxedVars.has(expr.name) && !expr._isAddressOf)
|
|
659
793
|
return `${expr.name}.value`;
|
|
660
|
-
|
|
794
|
+
// A named untyped constant used as an int64 operand: its
|
|
795
|
+
// declaration holds a Number, so emit the exact BigInt.
|
|
796
|
+
const lit64 =
|
|
797
|
+
expr._is64Bit &&
|
|
798
|
+
expr._const &&
|
|
799
|
+
(expr._type?.kind === "untyped" || this._is64BitInt(expr._type))
|
|
800
|
+
? this._gen64BitFoldedConst(expr, expr._const)
|
|
801
|
+
: null;
|
|
802
|
+
return lit64 ?? expr.name;
|
|
803
|
+
}
|
|
661
804
|
case "AwaitExpr":
|
|
662
805
|
return `await ${this.genExpr(expr.expr)}`;
|
|
663
806
|
case "InstantiationExpr":
|
|
@@ -672,6 +815,42 @@ export const expressionGenMethods = {
|
|
|
672
815
|
}
|
|
673
816
|
},
|
|
674
817
|
|
|
818
|
+
_gen64BitFoldedConst(expr, val) {
|
|
819
|
+
if (
|
|
820
|
+
(this._is64BitInt(expr._type) || expr._is64Bit) &&
|
|
821
|
+
(val.k === "INT" || expr._intConst)
|
|
822
|
+
) {
|
|
823
|
+
const n = constIntValue(val);
|
|
824
|
+
if (n !== null) return n < 0n ? `(${n}n)` : `${n}n`;
|
|
825
|
+
}
|
|
826
|
+
return null;
|
|
827
|
+
},
|
|
828
|
+
|
|
829
|
+
// An untyped constant converted to float32 (`var c float32 = 0.01`) is
|
|
830
|
+
// emitted as its float32 value, or null when that changes nothing.
|
|
831
|
+
_genFloat32Const(expr) {
|
|
832
|
+
const val = expr._const;
|
|
833
|
+
if (!expr._f32Const || !val || (val.k !== "FLOAT" && val.k !== "INT"))
|
|
834
|
+
return null;
|
|
835
|
+
const n = Number(val.v);
|
|
836
|
+
if (!Number.isFinite(n)) return null;
|
|
837
|
+
const r = Math.fround(n);
|
|
838
|
+
if (r === n) return null;
|
|
839
|
+
return CONST_LITS.FLOAT(r);
|
|
840
|
+
},
|
|
841
|
+
|
|
842
|
+
// An untyped complex constant that takes type complex64
|
|
843
|
+
// (`var c complex64 = 1 + 0.01i`) is emitted as its folded value with
|
|
844
|
+
// float32 components, or null when it cannot be folded here.
|
|
845
|
+
_genComplex64Const(expr) {
|
|
846
|
+
if (!expr._c64Const) return null;
|
|
847
|
+
const c = complexConstParts(expr);
|
|
848
|
+
if (!c) return null;
|
|
849
|
+
const re = CONST_LITS.FLOAT(Math.fround(c[0]));
|
|
850
|
+
const im = CONST_LITS.FLOAT(Math.fround(c[1]));
|
|
851
|
+
return re === null || im === null ? null : `{ re: ${re}, im: ${im} }`;
|
|
852
|
+
},
|
|
853
|
+
|
|
675
854
|
// The literal for a folded constant expression (`1 << 40` →
|
|
676
855
|
// `1099511627776`), or null. Integers outside ±2⁵³ emit the nearest
|
|
677
856
|
// Number and a precision warning.
|
|
@@ -686,6 +865,8 @@ export const expressionGenMethods = {
|
|
|
686
865
|
return null;
|
|
687
866
|
}
|
|
688
867
|
if (!FOLDED_CONST_KINDS.has(expr.kind) || isSignedLit(expr)) return null;
|
|
868
|
+
const lit64 = this._gen64BitFoldedConst(expr, val);
|
|
869
|
+
if (lit64 !== null) return lit64;
|
|
689
870
|
if (val.k === "INT" || expr._intConst) this._warnConstPrecision(val, expr);
|
|
690
871
|
return CONST_LITS[val.k]?.(val.v) ?? null;
|
|
691
872
|
},
|
|
@@ -694,6 +875,8 @@ export const expressionGenMethods = {
|
|
|
694
875
|
// per node per generate run: some forms generate the same node more than
|
|
695
876
|
// once, and cached ASTs are generated again by later compiles).
|
|
696
877
|
_warnConstPrecision(val, expr) {
|
|
878
|
+
if (this._is64BitInt(expr?._type) || expr?._is64Bit || expr?._floatConst)
|
|
879
|
+
return;
|
|
697
880
|
const i = constIntValue(val);
|
|
698
881
|
if (i === null || (i <= JS_SAFE_INT && i >= -JS_SAFE_INT)) return;
|
|
699
882
|
if (BigInt(Number(i)) !== i && !this._precisionWarned.has(expr)) {
|
|
@@ -703,7 +886,9 @@ export const expressionGenMethods = {
|
|
|
703
886
|
},
|
|
704
887
|
|
|
705
888
|
// `js` boxed as `box` (a checker _ifaceBoxMeta kind) for an interface.
|
|
706
|
-
|
|
889
|
+
// `fresh` marks a value nothing else refers to (a composite literal), which
|
|
890
|
+
// a struct box then takes over instead of copying.
|
|
891
|
+
_ifaceBoxWrap(js, box, ptrFields, fresh = false) {
|
|
707
892
|
this.useHelper("ifaceBox");
|
|
708
893
|
const fn =
|
|
709
894
|
{
|
|
@@ -713,14 +898,22 @@ export const expressionGenMethods = {
|
|
|
713
898
|
map: "__ifm",
|
|
714
899
|
slice: "__ifs",
|
|
715
900
|
}[box] ?? "__ifv";
|
|
716
|
-
const
|
|
717
|
-
|
|
901
|
+
const fields = ptrFields?.length ? JSON.stringify(ptrFields) : "";
|
|
902
|
+
if (fn === "__ifv" && fresh)
|
|
903
|
+
return `${fn}(${js}, ${fields || "null"}, true)`;
|
|
904
|
+
return `${fn}(${js}${fields ? `, ${fields}` : ""})`;
|
|
718
905
|
},
|
|
719
906
|
|
|
720
|
-
// `
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
907
|
+
// A nil `*T` entering an interface is a typed nil: it keeps T's methods
|
|
908
|
+
// (callable with a nil receiver) and differs from the nil interface.
|
|
909
|
+
_typedNilWrap(js, expr) {
|
|
910
|
+
if (expr._ifaceBox !== "ptr") return js;
|
|
911
|
+
const src = expr._ifaceSrc;
|
|
912
|
+
const base = src?.kind === "pointer" ? src.base : null;
|
|
913
|
+
if (base?.kind !== "named" || base.underlying?.kind !== "struct") return js;
|
|
914
|
+
const cls = this._dequalify(base.name).replace(/\[.*$/, "");
|
|
915
|
+
if (!this.structNames.has(cls)) return js;
|
|
916
|
+
return `__ifn(${js}, ${cls})`;
|
|
724
917
|
},
|
|
725
918
|
|
|
726
919
|
_isNamedBoxKind(expr) {
|
|
@@ -819,8 +1012,12 @@ export const expressionGenMethods = {
|
|
|
819
1012
|
// Pseudo type object for an AST type node (used where no checker type is available).
|
|
820
1013
|
_typeFromNode(node) {
|
|
821
1014
|
if (!node) return null;
|
|
822
|
-
if (node.kind === "TypeName"
|
|
823
|
-
|
|
1015
|
+
if (node.kind === "TypeName" || node.kind === "SelectorExpr") {
|
|
1016
|
+
// `pkg.S` of a bundled package names the class `S`.
|
|
1017
|
+
const name = this.getTypeName(node);
|
|
1018
|
+
if (name && this.structNames.has(name))
|
|
1019
|
+
return { kind: "named", name, underlying: { kind: "struct" } };
|
|
1020
|
+
}
|
|
824
1021
|
if (node.kind === "ArrayType")
|
|
825
1022
|
return { kind: "array", elem: this._typeFromNode(node.elem) };
|
|
826
1023
|
return null;
|
|
@@ -857,7 +1054,9 @@ export const expressionGenMethods = {
|
|
|
857
1054
|
_isAddressableExpr(e) {
|
|
858
1055
|
switch (e?.kind) {
|
|
859
1056
|
case "Ident":
|
|
860
|
-
return
|
|
1057
|
+
return (
|
|
1058
|
+
e.name !== "_" && (!e._isTypeRef || e._type?.kind === "typeParam")
|
|
1059
|
+
);
|
|
861
1060
|
case "SelectorExpr":
|
|
862
1061
|
return !e._isMethodValue && !e._isMethodExpr;
|
|
863
1062
|
case "IndexExpr":
|
|
@@ -871,10 +1070,22 @@ export const expressionGenMethods = {
|
|
|
871
1070
|
}
|
|
872
1071
|
},
|
|
873
1072
|
|
|
1073
|
+
// The type tag `expr` takes on entering an interface, or null.
|
|
1074
|
+
_ityTag(expr) {
|
|
1075
|
+
if (!expr._ifaceSrc || expr._ityTagging || this._isNamedBoxKind(expr))
|
|
1076
|
+
return null;
|
|
1077
|
+
return this._dynTypeTag(expr._ifaceSrc);
|
|
1078
|
+
},
|
|
1079
|
+
|
|
874
1080
|
// Emits `expr` as a Go value: struct/array operands that alias existing storage are copied.
|
|
875
1081
|
genValueExpr(expr) {
|
|
1082
|
+
const tagged =
|
|
1083
|
+
Boolean(this._ityTag(expr)) ||
|
|
1084
|
+
(isGenericComposite(expr._ifaceSrc) &&
|
|
1085
|
+
!expr._gmapTagging &&
|
|
1086
|
+
Boolean(this._genericMapTag(expr._ifaceSrc)));
|
|
876
1087
|
const js = this.genExpr(expr);
|
|
877
|
-
if (expr._ifaceBox) return js;
|
|
1088
|
+
if (expr._ifaceBox || tagged) return js;
|
|
878
1089
|
if (
|
|
879
1090
|
!(
|
|
880
1091
|
this._aggregateBase(expr._type) || this._typeParamClone(expr._type, js)
|
|
@@ -889,11 +1100,6 @@ export const expressionGenMethods = {
|
|
|
889
1100
|
return rootIdentName(e);
|
|
890
1101
|
},
|
|
891
1102
|
|
|
892
|
-
// True when `node` itself (not its children) writes to variable `name`.
|
|
893
|
-
_nodeWritesVar(node, name) {
|
|
894
|
-
return nodeWritesVar(node, name);
|
|
895
|
-
},
|
|
896
|
-
|
|
897
1103
|
// True when `node` may modify (or take the address of) the value held by variable `name`.
|
|
898
1104
|
_nodeMutatesVar(node, name, addrOnly = false) {
|
|
899
1105
|
return nodeMutatesVar(node, name, addrOnly);
|
|
@@ -905,12 +1111,10 @@ export const expressionGenMethods = {
|
|
|
905
1111
|
|
|
906
1112
|
_withFnCtx(body, fn) {
|
|
907
1113
|
const prev = this._fnCtx;
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
);
|
|
913
|
-
this._functionLabels = Array.from(labels);
|
|
1114
|
+
// A function starts with no JS labels in scope (see
|
|
1115
|
+
// `_emitIterTargetDispatch`).
|
|
1116
|
+
const prevLabels = this._jsLabels;
|
|
1117
|
+
this._jsLabels = [];
|
|
914
1118
|
this._fnCtx = {
|
|
915
1119
|
body,
|
|
916
1120
|
mut: new Map(),
|
|
@@ -924,7 +1128,7 @@ export const expressionGenMethods = {
|
|
|
924
1128
|
fn();
|
|
925
1129
|
} finally {
|
|
926
1130
|
this._fnCtx = prev;
|
|
927
|
-
this.
|
|
1131
|
+
this._jsLabels = prevLabels;
|
|
928
1132
|
}
|
|
929
1133
|
},
|
|
930
1134
|
|
|
@@ -1031,7 +1235,9 @@ export const expressionGenMethods = {
|
|
|
1031
1235
|
// Dereference: *p
|
|
1032
1236
|
if (op === "*") {
|
|
1033
1237
|
if (this._isStructPointerType(expr.operand._type)) {
|
|
1034
|
-
return
|
|
1238
|
+
return expr._nonNil
|
|
1239
|
+
? this.genExpr(expr.operand)
|
|
1240
|
+
: this._genNilChecked(this.genExpr(expr.operand));
|
|
1035
1241
|
}
|
|
1036
1242
|
if (expr.operand._type?.base?.kind === "typeParam") {
|
|
1037
1243
|
if (expr._derefOpTarget) return `${this.genExpr(expr.operand)}.value`;
|
|
@@ -1046,6 +1252,17 @@ export const expressionGenMethods = {
|
|
|
1046
1252
|
return inner;
|
|
1047
1253
|
}
|
|
1048
1254
|
const code = this.genExpr(expr.operand);
|
|
1255
|
+
if (this._is64BitInt(expr._type) || expr._is64Bit) {
|
|
1256
|
+
let operandCode = code;
|
|
1257
|
+
if (!operandCode.endsWith("n") && /^-?\d+$/.test(operandCode)) {
|
|
1258
|
+
operandCode = `${operandCode}n`;
|
|
1259
|
+
}
|
|
1260
|
+
if (op === "+") return operandCode;
|
|
1261
|
+
const wrap = this._is64BitUnsigned(expr._type)
|
|
1262
|
+
? "BigInt.asUintN"
|
|
1263
|
+
: "BigInt.asIntN";
|
|
1264
|
+
if (op === "-" || op === "~") return `${wrap}(64, ${op}(${operandCode}))`;
|
|
1265
|
+
}
|
|
1049
1266
|
const wrap = this.strict
|
|
1050
1267
|
? SMALL_UNSIGNED_WRAP[this._typeName(expr._type)]
|
|
1051
1268
|
: null;
|
|
@@ -1064,11 +1281,38 @@ export const expressionGenMethods = {
|
|
|
1064
1281
|
|
|
1065
1282
|
_genSelectorExpr(expr) {
|
|
1066
1283
|
// Go-source stdlib member: bundled under its mangled top-level name.
|
|
1067
|
-
if (expr._stdName)
|
|
1284
|
+
if (expr._stdName) {
|
|
1285
|
+
// An untyped constant used as an int64 operand: its declaration
|
|
1286
|
+
// holds a Number, so emit the exact BigInt (as for an Ident).
|
|
1287
|
+
const lit64 =
|
|
1288
|
+
expr._is64Bit && expr._type?.kind === "untyped" && expr._const
|
|
1289
|
+
? this._gen64BitFoldedConst(expr, expr._const)
|
|
1290
|
+
: null;
|
|
1291
|
+
return lit64 ?? expr._stdName;
|
|
1292
|
+
}
|
|
1068
1293
|
if (this._isStdlibFuncValue(expr)) return this._genStdlibFuncValue(expr);
|
|
1069
1294
|
if (expr._isMethodExpr) {
|
|
1070
1295
|
const namedExpr = this._namedMethodExprInfo(expr);
|
|
1071
1296
|
if (namedExpr) return `${namedExpr.cls}.$${expr.field}`;
|
|
1297
|
+
const rt = expr._methodExprRecv;
|
|
1298
|
+
// `(*MyInt).M` for a pointer-receiver method of a named non-struct
|
|
1299
|
+
// type: the static method takes the pointer, nil included.
|
|
1300
|
+
if (rt?.kind === "pointer") {
|
|
1301
|
+
const info = this._namedBoxInfo(rt.base);
|
|
1302
|
+
const decl = rt.base?.methods?.get(expr.field)?._methodDecl;
|
|
1303
|
+
if (info?.methods.has(expr.field) && decl?.recvPointer)
|
|
1304
|
+
return `${info.cls}.$${expr.field}`;
|
|
1305
|
+
}
|
|
1306
|
+
const protoCls = this._ptrMethodProtoClass(rt, expr.field);
|
|
1307
|
+
if (protoCls) {
|
|
1308
|
+
// A value-receiver method reads `*recv`, which panics on nil.
|
|
1309
|
+
let recv = "recv";
|
|
1310
|
+
if (rt.base.methods.get(expr.field)?._ptrRecv === false) {
|
|
1311
|
+
this.useHelper("nilCall");
|
|
1312
|
+
recv = "__nilcall(recv)";
|
|
1313
|
+
}
|
|
1314
|
+
return `((recv, ...args) => ${protoCls}.prototype.${expr.field}.call(${recv}, ...args))`;
|
|
1315
|
+
}
|
|
1072
1316
|
return `((recv, ...args) => recv.${expr.field}(...args))`;
|
|
1073
1317
|
}
|
|
1074
1318
|
const namedValue = this._genNamedMethodValue(expr);
|
|
@@ -1076,26 +1320,6 @@ export const expressionGenMethods = {
|
|
|
1076
1320
|
if (expr.expr.kind === "Ident") {
|
|
1077
1321
|
const nsConst = NS_CONSTANTS[expr.expr.name]?.[expr.field];
|
|
1078
1322
|
if (nsConst !== undefined) {
|
|
1079
|
-
if (expr.expr.name === "strconv") {
|
|
1080
|
-
this.useHelper("strconvError");
|
|
1081
|
-
} else if (expr.expr.name === "path" || expr.expr.name === "filepath") {
|
|
1082
|
-
if (expr.field === "ErrBadPattern") this.useHelper("pathError");
|
|
1083
|
-
} else if (expr.expr.name === "io" && expr.field === "EOF") {
|
|
1084
|
-
this.useHelper("ioEOF");
|
|
1085
|
-
} else if (expr.expr.name === "binary") {
|
|
1086
|
-
this.useHelper("binary");
|
|
1087
|
-
} else if (expr.expr.name === "hex") {
|
|
1088
|
-
this.useHelper("hex");
|
|
1089
|
-
this.useHelper("error");
|
|
1090
|
-
} else if (expr.expr.name === "base64") {
|
|
1091
|
-
this.useHelper("base64");
|
|
1092
|
-
} else if (expr.expr.name === "rand" && expr.field === "Reader") {
|
|
1093
|
-
this.useHelper("cryptoRand");
|
|
1094
|
-
} else if (expr.expr.name === "sha256") {
|
|
1095
|
-
this.useHelper("sha256");
|
|
1096
|
-
} else if (expr.expr.name === "os") {
|
|
1097
|
-
this.useHelper("os");
|
|
1098
|
-
}
|
|
1099
1323
|
return nsConst;
|
|
1100
1324
|
}
|
|
1101
1325
|
}
|
|
@@ -1178,7 +1402,7 @@ export const expressionGenMethods = {
|
|
|
1178
1402
|
const inner = sf.type?.kind === "pointer" ? sf.type.base : sf.type;
|
|
1179
1403
|
if (inner?.kind !== "named" || !this.structNames.has(inner.name))
|
|
1180
1404
|
continue;
|
|
1181
|
-
const name =
|
|
1405
|
+
const name = embeddedFieldName(sf);
|
|
1182
1406
|
if (name === field) return null;
|
|
1183
1407
|
const ist = inner.underlying;
|
|
1184
1408
|
if (!ist?.fields?.has(field)) continue;
|
|
@@ -1299,21 +1523,54 @@ export const expressionGenMethods = {
|
|
|
1299
1523
|
|
|
1300
1524
|
_genCallArgs(expr) {
|
|
1301
1525
|
if (expr._multiForward) return `...${this.genExpr(expr.args[0])}`;
|
|
1302
|
-
return expr.
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1526
|
+
return this._genCallArgList(expr).join(", ");
|
|
1527
|
+
},
|
|
1528
|
+
|
|
1529
|
+
_genCallArgList(expr) {
|
|
1530
|
+
return expr.args.map((a, i) =>
|
|
1531
|
+
a._spread
|
|
1532
|
+
? this._genSpreadArg(expr, a)
|
|
1533
|
+
: a._boxTypeParam
|
|
1534
|
+
? this._genTypeParamBox(a)
|
|
1306
1535
|
: this._callerCopiesArg(expr, i)
|
|
1307
1536
|
? this.genValueExpr(a)
|
|
1308
1537
|
: this.genExpr(a),
|
|
1309
|
-
|
|
1310
|
-
|
|
1538
|
+
);
|
|
1539
|
+
},
|
|
1540
|
+
|
|
1541
|
+
// A type parameter operand of a Print-family fmt call (see
|
|
1542
|
+
// src/lower/fmt-formatters.js): a JS value does not know its Go type, so
|
|
1543
|
+
// the type argument boxes it as converting it to an `any` does.
|
|
1544
|
+
_genTypeParamBox(a) {
|
|
1545
|
+
// Boxed below: skip the boxing of a value entering an interface.
|
|
1546
|
+
const wasBoxing = a._tpBoxing;
|
|
1547
|
+
a._tpBoxing = true;
|
|
1548
|
+
const js = this.genExpr(a);
|
|
1549
|
+
a._tpBoxing = wasBoxing;
|
|
1550
|
+
const tpf = this._typeParamZeroFactory(a._type.name);
|
|
1551
|
+
return tpf ? `(${tpf}.b ?? ${tpf}.fb ?? ((__v) => __v))(${js})` : js;
|
|
1552
|
+
},
|
|
1553
|
+
|
|
1554
|
+
// A direct call of a `//gofront:native` function: host code from the
|
|
1555
|
+
// registry, or null (declined, natives off, spread arguments) to call the
|
|
1556
|
+
// Go body.
|
|
1557
|
+
_genNativeCall(expr) {
|
|
1558
|
+
if (!this.natives || !expr._native) return null;
|
|
1559
|
+
if (expr._multiForward || expr.args.some((a) => a._spread)) return null;
|
|
1560
|
+
const typeArgs = expr._typeArgs ?? expr.func._typeArgs ?? [];
|
|
1561
|
+
const js = jsNativeCall(expr._native, this._genCallArgList(expr), typeArgs);
|
|
1562
|
+
if (js !== null && NATIVE_HELPERS.has(expr._native))
|
|
1563
|
+
this.useHelper(NATIVE_HELPERS.get(expr._native));
|
|
1564
|
+
return js;
|
|
1311
1565
|
},
|
|
1312
1566
|
|
|
1313
1567
|
// `f(xs...)`: a Go-compiled variadic callee receives the slice itself in a
|
|
1314
1568
|
// marker (its prologue unwraps it); other callees get a JS spread.
|
|
1315
1569
|
_genSpreadArg(call, arg) {
|
|
1316
1570
|
const js = this.genExpr(arg);
|
|
1571
|
+
// A variadic function of a Go-source package (`fmt.Println`): a nil
|
|
1572
|
+
// slice spreads to no operands.
|
|
1573
|
+
if (call.func._stdName) return `...(${js} ?? [])`;
|
|
1317
1574
|
return this._calleeIsGoVariadic(call) ? `{ __sp: ${js} }` : `...${js}`;
|
|
1318
1575
|
},
|
|
1319
1576
|
|
|
@@ -1355,7 +1612,9 @@ export const expressionGenMethods = {
|
|
|
1355
1612
|
if (this._isStructPointerType(expr.expr._type)) {
|
|
1356
1613
|
// Strict mode: a field of a nil struct pointer panics as in Go.
|
|
1357
1614
|
const recv =
|
|
1358
|
-
this.strict && !expr._isMethodValue
|
|
1615
|
+
this.strict && !expr._isMethodValue && !expr._nonNil
|
|
1616
|
+
? this._genNilChecked(base)
|
|
1617
|
+
: base;
|
|
1359
1618
|
const sel = `${recv}${this._promotedPath(expr)}.${expr.field}`;
|
|
1360
1619
|
if (expr._isMethodValue && !expr._callee) {
|
|
1361
1620
|
// `p.M` with a value receiver binds a copy of `*p` now.
|
|
@@ -1366,6 +1625,15 @@ export const expressionGenMethods = {
|
|
|
1366
1625
|
);
|
|
1367
1626
|
return `((__r) => __r.${expr.field}.bind(__r))(${copy})`;
|
|
1368
1627
|
}
|
|
1628
|
+
// A pointer receiver may be nil: bind through the prototype so
|
|
1629
|
+
// `s.M` on a nil pointer does not read `.M` off null.
|
|
1630
|
+
const protoCls = this._ptrMethodProtoClass(
|
|
1631
|
+
expr.expr._type,
|
|
1632
|
+
expr.field,
|
|
1633
|
+
expr,
|
|
1634
|
+
);
|
|
1635
|
+
if (protoCls)
|
|
1636
|
+
return `${protoCls}.prototype.${expr.field}.bind(${base})`;
|
|
1369
1637
|
return `${sel}.bind(${base})`;
|
|
1370
1638
|
}
|
|
1371
1639
|
return sel;
|
|
@@ -1382,11 +1650,12 @@ export const expressionGenMethods = {
|
|
|
1382
1650
|
const base = wrapField
|
|
1383
1651
|
? `${this.genExpr(expr.expr)}.${wrapField}`
|
|
1384
1652
|
: this.genExpr(expr.expr);
|
|
1385
|
-
|
|
1653
|
+
let idx = this.genExpr(expr.index);
|
|
1386
1654
|
if (expr._mapValueType) {
|
|
1387
1655
|
if (!expr._lvalue) return this._genMapIndexExpr(expr, base, idx);
|
|
1388
1656
|
return `${this._genMapWriteBase(base)}[${idx}]`;
|
|
1389
1657
|
}
|
|
1658
|
+
idx = this._asIndexNumber(idx, expr.index?._type);
|
|
1390
1659
|
const checked = this._genStrictIndex(expr, base, idx);
|
|
1391
1660
|
if (checked) return checked;
|
|
1392
1661
|
if (this._isStringExprType(exprType) && !expr._lvalue)
|
|
@@ -1428,8 +1697,8 @@ export const expressionGenMethods = {
|
|
|
1428
1697
|
_genMapIndexExpr(expr, base, idx) {
|
|
1429
1698
|
const zero = this.zeroValueForType(expr._mapValueType);
|
|
1430
1699
|
if (this._hasCallExpr(expr.expr) || this._hasCallExpr(expr.index))
|
|
1431
|
-
return `((__m, __k) => __m[__k] ?? ${zero})(${base}, ${idx})`;
|
|
1432
|
-
return `(${base}[${idx}] ?? ${zero})`;
|
|
1700
|
+
return `((__m, __k) => __m?.[__k] ?? ${zero})(${base}, ${idx})`;
|
|
1701
|
+
return `(${base}?.[${idx}] ?? ${zero})`;
|
|
1433
1702
|
},
|
|
1434
1703
|
|
|
1435
1704
|
_isStringExprType(exprType) {
|
|
@@ -1442,8 +1711,12 @@ export const expressionGenMethods = {
|
|
|
1442
1711
|
|
|
1443
1712
|
_genSliceExpr(expr) {
|
|
1444
1713
|
const base = this.genExpr(expr.expr);
|
|
1445
|
-
const lo = expr.low
|
|
1446
|
-
|
|
1714
|
+
const lo = expr.low
|
|
1715
|
+
? this._asIndexNumber(this.genExpr(expr.low), expr.low._type)
|
|
1716
|
+
: "0";
|
|
1717
|
+
const hi = expr.high
|
|
1718
|
+
? this._asIndexNumber(this.genExpr(expr.high), expr.high._type)
|
|
1719
|
+
: "undefined";
|
|
1447
1720
|
if (this._isStringExprType(expr.expr._type))
|
|
1448
1721
|
return expr.high ? `${base}.slice(${lo}, ${hi})` : `${base}.slice(${lo})`;
|
|
1449
1722
|
// `b[:]`, `b[0:]` and `a[:]` have the operand's length and capacity:
|
|
@@ -1456,7 +1729,9 @@ export const expressionGenMethods = {
|
|
|
1456
1729
|
// the result stays within `len(x)`; typed arrays always share.
|
|
1457
1730
|
if (expr._selfReslice && !isTypedArraySlice(expr.expr._type))
|
|
1458
1731
|
return `__reslice(${base}, ${lo}, ${hi})`;
|
|
1459
|
-
const max = expr.max
|
|
1732
|
+
const max = expr.max
|
|
1733
|
+
? `, ${this._asIndexNumber(this.genExpr(expr.max), expr.max._type)}`
|
|
1734
|
+
: "";
|
|
1460
1735
|
return `__slice(${base}, ${lo}, ${hi}${max})`;
|
|
1461
1736
|
},
|
|
1462
1737
|
|
|
@@ -1471,7 +1746,7 @@ export const expressionGenMethods = {
|
|
|
1471
1746
|
this._withFnCtx(expr.body, () =>
|
|
1472
1747
|
this.indented(() => {
|
|
1473
1748
|
this._emitParamCopies(expr.params);
|
|
1474
|
-
this._genBody(expr.body, expr);
|
|
1749
|
+
this._withNamedReturns(expr, () => this._genBody(expr.body, expr));
|
|
1475
1750
|
}),
|
|
1476
1751
|
);
|
|
1477
1752
|
const body = this.out.join("\n");
|
|
@@ -1493,18 +1768,38 @@ export const expressionGenMethods = {
|
|
|
1493
1768
|
if (!expr._commaOk) {
|
|
1494
1769
|
// plain assertion: panic if check fails (matches Go behavior)
|
|
1495
1770
|
if (check === "true") return src; // can't check at runtime — pass through
|
|
1771
|
+
this.useHelper("ifaceBox");
|
|
1496
1772
|
return wrap(
|
|
1497
|
-
`(${check} ? ${out} : (() => { throw
|
|
1773
|
+
`(${check} ? ${out} : (() => { throw ${this._typeAssertFailJs(expr, val)}; })())`,
|
|
1498
1774
|
);
|
|
1499
1775
|
}
|
|
1500
|
-
// comma-ok: emit [value-or-zero, runtimeTypeCheck]; an asserted
|
|
1501
|
-
// is a copy of the boxed value.
|
|
1776
|
+
// comma-ok: emit [value-or-zero, runtimeTypeCheck]; an asserted struct
|
|
1777
|
+
// or array is a copy of the boxed value.
|
|
1502
1778
|
const zero = this.zeroValueForTypeNode(expr.type);
|
|
1503
|
-
const
|
|
1504
|
-
const okOut =
|
|
1779
|
+
const valT = this._typeFromNode(expr.type);
|
|
1780
|
+
const okOut = this._aggregateBase(valT) ? this._cloneJs(valT, out) : out;
|
|
1505
1781
|
return wrap(`(${check} ? [${okOut}, true] : [${zero}, false])`);
|
|
1506
1782
|
},
|
|
1507
1783
|
|
|
1784
|
+
// The error a failed `x.(T)` throws: Go's `interface conversion: ...` text
|
|
1785
|
+
// naming the static interface type, the dynamic type and the target.
|
|
1786
|
+
_typeAssertFailJs(expr, val) {
|
|
1787
|
+
const target = expr._type;
|
|
1788
|
+
const u = this._underlyingOf(target);
|
|
1789
|
+
const methods =
|
|
1790
|
+
u?.kind === "interface" ? [...(u.methods?.keys() ?? [])] : [];
|
|
1791
|
+
const tName = this._assertTypeName(target) ?? goTypeName(expr.type);
|
|
1792
|
+
const src = this._assertTypeName(expr.expr?._type) ?? "interface {}";
|
|
1793
|
+
return `__tafail(${JSON.stringify(src)}, ${val}, ${JSON.stringify(tName)}, ${methods.length ? JSON.stringify(methods.sort()) : "null"})`;
|
|
1794
|
+
},
|
|
1795
|
+
|
|
1796
|
+
// Go's spelling of the static type in a failed assertion message; null for
|
|
1797
|
+
// `any` (`interface {}`).
|
|
1798
|
+
_assertTypeName(t) {
|
|
1799
|
+
if (!t || isAny(t)) return null;
|
|
1800
|
+
return t.name === "error" ? "error" : goTypeName(t);
|
|
1801
|
+
},
|
|
1802
|
+
|
|
1508
1803
|
// `csv.ParseError` names the class of the bundled Go-source package type.
|
|
1509
1804
|
_qualifiedStructName(name) {
|
|
1510
1805
|
if (!name.includes(".") || this.structNames.has(name)) return name;
|
|
@@ -1512,20 +1807,37 @@ export const expressionGenMethods = {
|
|
|
1512
1807
|
return cls && this.structNames.has(cls) ? cls : name;
|
|
1513
1808
|
},
|
|
1514
1809
|
|
|
1810
|
+
_simdCheckExpr(name, val) {
|
|
1811
|
+
if (name === "simd.F32x4" || name === "F32x4")
|
|
1812
|
+
return `(Array.isArray(${val}) && ${val}.length === 4 && typeof ${val}[0] === "number")`;
|
|
1813
|
+
if (name === "simd.Mask32x4" || name === "Mask32x4")
|
|
1814
|
+
return `(Array.isArray(${val}) && ${val}.length === 4 && typeof ${val}[0] === "boolean")`;
|
|
1815
|
+
return null;
|
|
1816
|
+
},
|
|
1817
|
+
|
|
1515
1818
|
_typeNameCheckExpr(typeName, val) {
|
|
1516
1819
|
// A type parameter: the instantiated type's test travels with its factory.
|
|
1517
1820
|
const factory = this._typeParamZeroFactory(typeName);
|
|
1518
1821
|
if (factory) return `(${factory}?.is?.(${val}) ?? true)`;
|
|
1519
1822
|
const name = this._qualifiedStructName(typeName);
|
|
1823
|
+
// int64 and uint64 share the BigInt: a value out of int64's range can
|
|
1824
|
+
// only be a uint64.
|
|
1825
|
+
if (name === "int64")
|
|
1826
|
+
return `(typeof ${val} === "bigint" && ${val} >= -9223372036854775808n && ${val} <= 9223372036854775807n)`;
|
|
1827
|
+
if (name === "uint64")
|
|
1828
|
+
return `(typeof ${val} === "bigint" && ${val} >= 0n)`;
|
|
1829
|
+
// Integer types share the JS number with the floats: only a whole
|
|
1830
|
+
// number is one, so a fraction or -0 in an `any` falls through to float64.
|
|
1831
|
+
if (INT_TYPE_NAMES.has(name))
|
|
1832
|
+
return `(Number.isSafeInteger(${val}) && !Object.is(${val}, -0))`;
|
|
1520
1833
|
if (TYPEOF_NUMBER_NAMES.has(name)) return `typeof ${val} === "number"`;
|
|
1834
|
+
if (name === "complex128" || name === "complex64")
|
|
1835
|
+
return `(typeof ${val} === "object" && ${val} !== null && typeof ${val}.re === "number" && typeof ${val}.im === "number")`;
|
|
1521
1836
|
if (name === "string") return `typeof ${val} === "string"`;
|
|
1522
1837
|
if (name === "bool") return `typeof ${val} === "boolean"`;
|
|
1523
1838
|
if (name === "nil") return `${val} === null`;
|
|
1524
|
-
|
|
1525
|
-
if (
|
|
1526
|
-
return `(Array.isArray(${val}) && ${val}.length === 4 && typeof ${val}[0] === "number")`;
|
|
1527
|
-
if (name === "simd.Mask32x4" || name === "Mask32x4")
|
|
1528
|
-
return `(Array.isArray(${val}) && ${val}.length === 4 && typeof ${val}[0] === "boolean")`;
|
|
1839
|
+
const simd = this._simdCheckExpr(name, val);
|
|
1840
|
+
if (simd) return simd;
|
|
1529
1841
|
if (name === "error")
|
|
1530
1842
|
return `(typeof ${val} === "object" && ${val} !== null && typeof ${val}.Error === "function")`;
|
|
1531
1843
|
if (this.structNames.has(name))
|
|
@@ -1534,12 +1846,20 @@ export const expressionGenMethods = {
|
|
|
1534
1846
|
const namedInfo = this._namedBoxInfo(named);
|
|
1535
1847
|
if (namedInfo) return `(${val} instanceof ${namedInfo.cls})`;
|
|
1536
1848
|
// User-declared interfaces: check the dynamic value has every method.
|
|
1537
|
-
if (
|
|
1849
|
+
if (named?.underlying?.kind === "interface")
|
|
1538
1850
|
return this._ifaceMethodCheck(
|
|
1539
1851
|
val,
|
|
1540
1852
|
[...named.underlying.methods.keys()],
|
|
1541
1853
|
this._unwrapIsMulti(named.underlying.methods.get("Unwrap")),
|
|
1542
1854
|
);
|
|
1855
|
+
if (this._isTaggedFuncType(named)) {
|
|
1856
|
+
this.useHelper("ifaceBox");
|
|
1857
|
+
return `__isty(${val}, ${JSON.stringify(name)}, "namedfunc")`;
|
|
1858
|
+
}
|
|
1859
|
+
if (this._isNamedComposite(named)) {
|
|
1860
|
+
this.useHelper("ifaceBox");
|
|
1861
|
+
return `__isty(${val}, ${JSON.stringify(name)}, "named")`;
|
|
1862
|
+
}
|
|
1543
1863
|
return "true"; // unknown type — can't check at runtime
|
|
1544
1864
|
},
|
|
1545
1865
|
|
|
@@ -1573,11 +1893,14 @@ export const expressionGenMethods = {
|
|
|
1573
1893
|
},
|
|
1574
1894
|
|
|
1575
1895
|
_ifaceMethodCheck(val, methodNames, multiUnwrap = null) {
|
|
1576
|
-
const checks = methodNames.map((m) =>
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1896
|
+
const checks = methodNames.map((m) => {
|
|
1897
|
+
const base =
|
|
1898
|
+
m === "Unwrap" && multiUnwrap !== null
|
|
1899
|
+
? `typeof ${val}.${m} === "function" && (${val}.${m}.__multi === true) === ${multiUnwrap}`
|
|
1900
|
+
: `typeof ${val}.${m} === "function"`;
|
|
1901
|
+
// A struct value's method set lacks the pointer-receiver methods.
|
|
1902
|
+
return `${base} && !(${val}.__v === true && ${val}.constructor.__pm?.includes(${JSON.stringify(m)}))`;
|
|
1903
|
+
});
|
|
1581
1904
|
return `(${[`${val} != null`, ...checks].join(" && ")})`;
|
|
1582
1905
|
},
|
|
1583
1906
|
|
|
@@ -1586,6 +1909,21 @@ export const expressionGenMethods = {
|
|
|
1586
1909
|
// for a type parameter).
|
|
1587
1910
|
_assertedValueJs(typeNode, val) {
|
|
1588
1911
|
if (this._isNamedBoxTypeNode(typeNode)) return `${val}.__gfUnbox()`;
|
|
1912
|
+
if (
|
|
1913
|
+
this._isStructPtrTypeNode(typeNode) ||
|
|
1914
|
+
(typeNode?.kind === "PointerType" && typeNode._assertType)
|
|
1915
|
+
) {
|
|
1916
|
+
this.useHelper("ifaceBox");
|
|
1917
|
+
return `__unnil(${val})`;
|
|
1918
|
+
}
|
|
1919
|
+
if (
|
|
1920
|
+
typeNode?._assertType?.kind === "slice" ||
|
|
1921
|
+
this._isNamedSliceTypeNode(typeNode)
|
|
1922
|
+
) {
|
|
1923
|
+
// A nil slice in an interface is a tagged empty array.
|
|
1924
|
+
this.useHelper("ifaceBox");
|
|
1925
|
+
return `__unnil(${val})`;
|
|
1926
|
+
}
|
|
1589
1927
|
const factory =
|
|
1590
1928
|
typeNode?.kind === "TypeName"
|
|
1591
1929
|
? this._typeParamZeroFactory(typeNode.name)
|
|
@@ -1593,6 +1931,29 @@ export const expressionGenMethods = {
|
|
|
1593
1931
|
return factory ? `(${factory}?.u ? ${val}.__gfUnbox() : ${val})` : val;
|
|
1594
1932
|
},
|
|
1595
1933
|
|
|
1934
|
+
// A type name that denotes a defined slice type (`type S []int`).
|
|
1935
|
+
_isNamedSliceTypeNode(typeNode) {
|
|
1936
|
+
if (typeNode?.kind !== "TypeName") return false;
|
|
1937
|
+
const named = this.checker?.types.get(typeNode.name);
|
|
1938
|
+
return (
|
|
1939
|
+
this._isNamedComposite(named) &&
|
|
1940
|
+
this._underlyingOf(named)?.kind === "slice"
|
|
1941
|
+
);
|
|
1942
|
+
},
|
|
1943
|
+
|
|
1944
|
+
_isStructPtrTypeNode(typeNode) {
|
|
1945
|
+
const base = typeNode?.kind === "PointerType" ? typeNode.base : null;
|
|
1946
|
+
const name =
|
|
1947
|
+
base?.kind === "TypeName"
|
|
1948
|
+
? base.name
|
|
1949
|
+
: base?.kind === "GenericTypeName"
|
|
1950
|
+
? this._dequalify(base.name)
|
|
1951
|
+
: null;
|
|
1952
|
+
return (
|
|
1953
|
+
name !== null && this.structNames.has(this._qualifiedStructName(name))
|
|
1954
|
+
);
|
|
1955
|
+
},
|
|
1956
|
+
|
|
1596
1957
|
_isNamedBoxTypeNode(typeNode) {
|
|
1597
1958
|
if (typeNode?.kind === "TypeName")
|
|
1598
1959
|
return Boolean(
|
|
@@ -1630,11 +1991,14 @@ export const expressionGenMethods = {
|
|
|
1630
1991
|
arg,
|
|
1631
1992
|
);
|
|
1632
1993
|
if (structConv) return structConv;
|
|
1633
|
-
const isRaw =
|
|
1634
|
-
targetType?.name === "json.RawMessage" ||
|
|
1635
|
-
targetType?.name === "encoding/json.RawMessage";
|
|
1636
1994
|
const base =
|
|
1637
1995
|
targetType?.kind === "named" ? targetType.underlying : targetType;
|
|
1996
|
+
if (base?.kind === "map" && this._isNilLiteral(expr.args[0]))
|
|
1997
|
+
return this._nilMapZero();
|
|
1998
|
+
if (this._isTaggedFuncType(targetType)) {
|
|
1999
|
+
this.useHelper("ifaceBox");
|
|
2000
|
+
return `__tfn(${arg}, ${JSON.stringify(targetType.name)})`;
|
|
2001
|
+
}
|
|
1638
2002
|
if (
|
|
1639
2003
|
targetType?.kind === "named" &&
|
|
1640
2004
|
base?.kind === "basic" &&
|
|
@@ -1651,19 +2015,29 @@ export const expressionGenMethods = {
|
|
|
1651
2015
|
(elem === "byte" || elem === "uint8") &&
|
|
1652
2016
|
this._isStringExprType(expr.args[0]?._type)
|
|
1653
2017
|
) {
|
|
1654
|
-
|
|
1655
|
-
return
|
|
1656
|
-
}
|
|
1657
|
-
if (isRaw) {
|
|
1658
|
-
return `((u) => { u.__raw = true; return u; })(${arg})`;
|
|
2018
|
+
this.useHelper("u8enc");
|
|
2019
|
+
return `__u8enc(${arg})`;
|
|
1659
2020
|
}
|
|
1660
2021
|
}
|
|
1661
2022
|
return arg;
|
|
1662
2023
|
},
|
|
1663
2024
|
|
|
2025
|
+
// `json.Marshal(v)` / `enc.Encode(v)` call the generated codec entry
|
|
2026
|
+
// (see src/lower/json-codecs.js); a stream method takes its receiver first.
|
|
2027
|
+
_jsonEntryCall(expr) {
|
|
2028
|
+
const sel = expr.func;
|
|
2029
|
+
const func = { kind: "Ident", name: sel._genEntry, _type: sel._type };
|
|
2030
|
+
const args = sel._jsonStream ? [sel.expr, ...expr.args] : expr.args;
|
|
2031
|
+
return { ...expr, func, args };
|
|
2032
|
+
},
|
|
2033
|
+
|
|
1664
2034
|
genCall(expr) {
|
|
2035
|
+
if (expr.func._genEntry) return this.genCall(this._jsonEntryCall(expr));
|
|
1665
2036
|
if (expr._isTypeConversion) return this._genTypeConversionCall(expr);
|
|
1666
2037
|
|
|
2038
|
+
const native = this._genNativeCall(expr);
|
|
2039
|
+
if (native !== null) return native;
|
|
2040
|
+
|
|
1667
2041
|
const funcExpr =
|
|
1668
2042
|
expr.func.kind === "InstantiationExpr" ? expr.func.expr : expr.func;
|
|
1669
2043
|
if (funcExpr.kind === "Ident") {
|
|
@@ -1671,18 +2045,30 @@ export const expressionGenMethods = {
|
|
|
1671
2045
|
if (builtin !== undefined) return builtin;
|
|
1672
2046
|
}
|
|
1673
2047
|
|
|
2048
|
+
const viaBox = this._genTypeParamMethodCall(expr);
|
|
2049
|
+
if (viaBox !== null) return viaBox;
|
|
2050
|
+
|
|
1674
2051
|
if (expr.func.kind === "SelectorExpr") {
|
|
1675
2052
|
const result = this._genSelectorCall(expr);
|
|
1676
2053
|
if (result !== undefined) return result;
|
|
1677
2054
|
}
|
|
1678
2055
|
|
|
2056
|
+
const converted = this._convertedPointerMethodCall(expr);
|
|
2057
|
+
if (converted) return converted;
|
|
2058
|
+
|
|
1679
2059
|
const nilSafe = this._nilSafeMethodClass(expr.func);
|
|
1680
2060
|
if (nilSafe) {
|
|
1681
2061
|
// The method tests its receiver for nil: call it through the
|
|
1682
2062
|
// prototype so a nil `this` reaches it instead of a TypeError.
|
|
1683
2063
|
const recv = this.genExpr(expr.func.expr);
|
|
1684
2064
|
const rest = this._genCallArgs(expr);
|
|
1685
|
-
|
|
2065
|
+
const m = expr.func.field;
|
|
2066
|
+
const viaProto = (r) =>
|
|
2067
|
+
`${nilSafe}.prototype.${m}.call(${rest ? `${r}, ${rest}` : r})`;
|
|
2068
|
+
// A non-nil variable keeps the direct (faster) call.
|
|
2069
|
+
if (/^[\w$]+$/.test(recv))
|
|
2070
|
+
return `(${recv} != null ? ${recv}.${m}(${rest}) : ${viaProto(recv)})`;
|
|
2071
|
+
return viaProto(recv);
|
|
1686
2072
|
}
|
|
1687
2073
|
|
|
1688
2074
|
expr.func._callee = true;
|
|
@@ -1694,15 +2080,91 @@ export const expressionGenMethods = {
|
|
|
1694
2080
|
return `${fn}(${args ? `${zeros}, ${args}` : zeros})`;
|
|
1695
2081
|
},
|
|
1696
2082
|
|
|
2083
|
+
// `x.M(...)` on a type-parameter value: a raw value of a named non-struct
|
|
2084
|
+
// type argument is boxed (by the type's factory) to reach its methods.
|
|
2085
|
+
_genTypeParamMethodCall(expr) {
|
|
2086
|
+
if (!this._isTypeParamMethodCall(expr)) return null;
|
|
2087
|
+
const factory = this._typeParamZeroFactory(expr.func.expr._type.name);
|
|
2088
|
+
if (!factory) return null;
|
|
2089
|
+
this.useHelper("generics");
|
|
2090
|
+
let recv = `__tbox(${factory}, ${this.genExpr(expr.func.expr)})`;
|
|
2091
|
+
// `P(&v).M()` with `P interface{ *T; M() }`: a pointer to a boxed
|
|
2092
|
+
// type-parameter variable is the box, so reach the value it holds.
|
|
2093
|
+
if (this._hasPointerTypeSet(expr.func.expr._type))
|
|
2094
|
+
recv = `((__r) => typeof __r?.${expr.func.field} === "function" ? __r : __r?.value)(${recv})`;
|
|
2095
|
+
const args = this._genCallArgs(expr);
|
|
2096
|
+
return `${recv}.${expr.func.field}(${args})`;
|
|
2097
|
+
},
|
|
2098
|
+
|
|
2099
|
+
// Whether type parameter `tp` is constrained to a single `*T` type-set term.
|
|
2100
|
+
_hasPointerTypeSet(tp) {
|
|
2101
|
+
const c = tp.constraint;
|
|
2102
|
+
const iface = c?.kind === "named" ? c.underlying : c;
|
|
2103
|
+
const terms = iface?.unionConstraint?.terms;
|
|
2104
|
+
return (
|
|
2105
|
+
terms?.length === 1 &&
|
|
2106
|
+
(terms[0].resolved?.kind === "pointer" ||
|
|
2107
|
+
terms[0].type?.kind === "PointerType")
|
|
2108
|
+
);
|
|
2109
|
+
},
|
|
2110
|
+
|
|
2111
|
+
// `(*S)(p).M()` where `p` points at another struct type: the JS object keeps
|
|
2112
|
+
// its own class, so call S's prototype method on it.
|
|
2113
|
+
_convertedPointerMethodCall(expr) {
|
|
2114
|
+
const sel = expr.func;
|
|
2115
|
+
if (sel?.kind !== "SelectorExpr" || sel._isMethodExpr) return null;
|
|
2116
|
+
let recv = sel.expr;
|
|
2117
|
+
while (recv?.kind === "ParenExpr") recv = recv.expr;
|
|
2118
|
+
if (recv?._isTypeConversion !== true) return null;
|
|
2119
|
+
const target = recv._conversionTargetType;
|
|
2120
|
+
const from = recv.args?.[0]?._type;
|
|
2121
|
+
if (target?.kind !== "pointer" || target.base?.kind !== "named")
|
|
2122
|
+
return null;
|
|
2123
|
+
if (from?.kind !== "pointer" || from.base === target.base) return null;
|
|
2124
|
+
if (target.base.underlying?.kind !== "struct") return null;
|
|
2125
|
+
const cls = target.base.name;
|
|
2126
|
+
if (!target.base.methods?.has(sel.field)) return null;
|
|
2127
|
+
const rest = this._genCallArgs(expr);
|
|
2128
|
+
const obj = this.genExpr(recv.args[0]);
|
|
2129
|
+
return `${cls}.prototype.${sel.field}.call(${rest ? `${obj}, ${rest}` : obj})`;
|
|
2130
|
+
},
|
|
2131
|
+
|
|
2132
|
+
// The class whose prototype holds pointer-receiver method `field`, for a
|
|
2133
|
+
// pointer to a struct type that declares it directly (not promoted), so a
|
|
2134
|
+
// method value or expression can reach it with a nil receiver; else null.
|
|
2135
|
+
_ptrMethodProtoClass(ptr, field, sel) {
|
|
2136
|
+
if (ptr?.kind !== "pointer" || ptr.base?.kind !== "named") return null;
|
|
2137
|
+
if (ptr.base.underlying?.kind !== "struct") return null;
|
|
2138
|
+
if (sel && this._promotedPath(sel)) return null;
|
|
2139
|
+
if (!ptr.base.methods?.has(field)) return null;
|
|
2140
|
+
return ptr.base.name;
|
|
2141
|
+
},
|
|
2142
|
+
|
|
1697
2143
|
// The class of a pointer-receiver struct method that accepts a nil receiver
|
|
1698
2144
|
// when `func` selects it on a pointer of exactly that struct, else null.
|
|
1699
2145
|
_nilSafeMethodClass(func) {
|
|
1700
2146
|
if (func?.kind !== "SelectorExpr" || func._isMethodExpr) return null;
|
|
1701
2147
|
const t = func.expr?._type;
|
|
1702
2148
|
if (t?.kind !== "pointer" || t.base?.kind !== "named") return null;
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
2149
|
+
// An instance of a generic struct (`Gen[int]`) shares its class.
|
|
2150
|
+
const cls = t.base.typeArgs?.length
|
|
2151
|
+
? this._structClassName(t.base)
|
|
2152
|
+
: t.base.name;
|
|
2153
|
+
if (!cls) return null;
|
|
2154
|
+
if (
|
|
2155
|
+
t.base.underlying?.kind === "struct" &&
|
|
2156
|
+
!this._promotedPath(func) &&
|
|
2157
|
+
this.nilSafeMethods?.has(`${cls}.${func.field}`)
|
|
2158
|
+
)
|
|
2159
|
+
return cls;
|
|
2160
|
+
// A Go-source stdlib type (`Buffer$$bytes`) is declared outside this
|
|
2161
|
+
// package: its method decl tells whether it tests the receiver for nil.
|
|
2162
|
+
const decl = cls.includes("$$")
|
|
2163
|
+
? t.base.methods?.get(func.field)?._methodDecl
|
|
2164
|
+
: null;
|
|
2165
|
+
return decl?.recvPointer && comparesToNil(decl.body, decl.recvName)
|
|
2166
|
+
? cls
|
|
2167
|
+
: null;
|
|
1706
2168
|
},
|
|
1707
2169
|
|
|
1708
2170
|
// The leading zero-value factories of a call to a generic function
|
|
@@ -1741,79 +2203,7 @@ export const expressionGenMethods = {
|
|
|
1741
2203
|
return recvType?.name ?? recvType?.base?.name;
|
|
1742
2204
|
},
|
|
1743
2205
|
|
|
1744
|
-
_genJsonStreamCall(recvName, expr) {
|
|
1745
|
-
const isEnc = this._isJsonEncoderName(recvName);
|
|
1746
|
-
if (isEnc && expr.func.field === "Encode") {
|
|
1747
|
-
this.useHelper("json");
|
|
1748
|
-
const enc = this.genExpr(expr.func.expr);
|
|
1749
|
-
this._markJsonAnyEncoders();
|
|
1750
|
-
const argType = expr.args[0]?._type;
|
|
1751
|
-
const codec = this._getJsonEncoder(argType);
|
|
1752
|
-
const arg = this.genExpr(expr.args[0]);
|
|
1753
|
-
return `__json_encoder_encode(${enc}, ${codec}, ${arg})`;
|
|
1754
|
-
}
|
|
1755
|
-
const isDec =
|
|
1756
|
-
recvName === "json.Decoder" ||
|
|
1757
|
-
recvName === "encoding/json.Decoder" ||
|
|
1758
|
-
recvName?.startsWith("Decoder$$");
|
|
1759
|
-
if (isDec && expr.func.field === "Decode") {
|
|
1760
|
-
this.useHelper("json");
|
|
1761
|
-
const dec = this.genExpr(expr.func.expr);
|
|
1762
|
-
const targetType = expr.args[0]?._type;
|
|
1763
|
-
const codec = this._getJsonDecoder(targetType);
|
|
1764
|
-
const arg = this.genExpr(expr.args[0]);
|
|
1765
|
-
return `__json_decoder_decode(${dec}, ${codec}, ${arg}, ${this._jsonTargetArgs(targetType)})`;
|
|
1766
|
-
}
|
|
1767
|
-
return undefined;
|
|
1768
|
-
},
|
|
1769
|
-
|
|
1770
2206
|
_genReceiverTypeCall(recvName, expr) {
|
|
1771
|
-
const jsonStream = this._genJsonStreamCall(recvName, expr);
|
|
1772
|
-
if (jsonStream !== undefined) return jsonStream;
|
|
1773
|
-
if (recvName === "strings.Builder" || recvName === "bytes.Buffer")
|
|
1774
|
-
return this._genBuilderCall(recvName, expr.func.field, expr);
|
|
1775
|
-
if (recvName === "regexp.Regexp")
|
|
1776
|
-
return this._genRegexpMethodCall(expr.func.field, expr);
|
|
1777
|
-
if (recvName === "time.Time")
|
|
1778
|
-
return this._genTimeMethodCall(expr.func.field, expr);
|
|
1779
|
-
if (recvName === "time.Duration")
|
|
1780
|
-
return this._genDurationMethodCall(expr.func.field, expr);
|
|
1781
|
-
if (recvName === "time.Month") {
|
|
1782
|
-
this.useHelper("duration");
|
|
1783
|
-
return `__monthString(${this.genExpr(expr.func.expr)})`;
|
|
1784
|
-
}
|
|
1785
|
-
if (recvName === "time.Weekday") {
|
|
1786
|
-
this.useHelper("duration");
|
|
1787
|
-
return `__weekdayString(${this.genExpr(expr.func.expr)})`;
|
|
1788
|
-
}
|
|
1789
|
-
if (recvName === "time.Location") {
|
|
1790
|
-
const recv = this.genExpr(expr.func.expr);
|
|
1791
|
-
return `(${recv} ? ${recv}.String() : "UTC")`;
|
|
1792
|
-
}
|
|
1793
|
-
if (recvName === "time.Timer") {
|
|
1794
|
-
const recv = this.genExpr(expr.func.expr);
|
|
1795
|
-
if (expr.func.field === "Stop")
|
|
1796
|
-
return `(${recv} ? ${recv}.Stop() : false)`;
|
|
1797
|
-
if (expr.func.field === "Reset")
|
|
1798
|
-
return `(${recv} ? ${recv}.Reset(${this.genExpr(expr.args[0])}) : false)`;
|
|
1799
|
-
}
|
|
1800
|
-
if (
|
|
1801
|
-
recvName === "testing.T" ||
|
|
1802
|
-
recvName === "testing.B" ||
|
|
1803
|
-
recvName === "testing.M" ||
|
|
1804
|
-
recvName === "testing.TB"
|
|
1805
|
-
)
|
|
1806
|
-
return this._genTestingMethodCall(expr.func.field, expr);
|
|
1807
|
-
if (recvName === "strings.Replacer") {
|
|
1808
|
-
const recv = this.genExpr(expr.func.expr);
|
|
1809
|
-
if (expr.func.field === "WriteString") {
|
|
1810
|
-
const w = this.genExpr(expr.args[0]);
|
|
1811
|
-
const s = this.genExpr(expr.args[1]);
|
|
1812
|
-
return `((r,w,s) => { s = r ? r.Replace(s) : s; const x = w?.value ?? w; if (typeof x.WriteString === "function") return x.WriteString(s); if (typeof x._buf === "string") { x._buf += s; return [s.length, null]; } if (Array.isArray(x._buf)) { const bytes = new TextEncoder().encode(s); for (let i = 0; i < bytes.length; i++) x._buf.push(bytes[i]); return [bytes.length, null]; } if (typeof x.Write === "function") return x.Write(new TextEncoder().encode(s)); return [0, "strings.Replacer.WriteString: unsupported writer"]; })(${recv}, ${w}, ${s})`;
|
|
1813
|
-
}
|
|
1814
|
-
const arg = this.genExpr(expr.args[0]);
|
|
1815
|
-
return `(${recv} ? ${recv}.Replace(${arg}) : ${arg})`;
|
|
1816
|
-
}
|
|
1817
2207
|
if (recvName?.startsWith("shared.") && expr.func.field === "Subarray") {
|
|
1818
2208
|
const args = expr.args.map((a) => this.genExpr(a)).join(", ");
|
|
1819
2209
|
return `${this.genExpr(expr.func.expr)}.subarray(${args})`;
|
|
@@ -1842,28 +2232,6 @@ export const expressionGenMethods = {
|
|
|
1842
2232
|
}
|
|
1843
2233
|
if (expr.func.field === "Wait") return "(void 0)";
|
|
1844
2234
|
}
|
|
1845
|
-
if (
|
|
1846
|
-
recvName === "binary.LittleEndian" ||
|
|
1847
|
-
recvName === "binary.BigEndian" ||
|
|
1848
|
-
recvName === "encoding/binary.LittleEndian" ||
|
|
1849
|
-
recvName === "encoding/binary.BigEndian" ||
|
|
1850
|
-
recvName === "binary.ByteOrder" ||
|
|
1851
|
-
recvName === "encoding/binary.ByteOrder"
|
|
1852
|
-
) {
|
|
1853
|
-
this.useHelper("binary");
|
|
1854
|
-
const recv = this.genExpr(expr.func.expr);
|
|
1855
|
-
const args = expr.args.map((a) => this.genExpr(a)).join(", ");
|
|
1856
|
-
return `${recv}.${expr.func.field}(${args})`;
|
|
1857
|
-
}
|
|
1858
|
-
if (
|
|
1859
|
-
recvName === "base64.Encoding" ||
|
|
1860
|
-
recvName === "encoding/base64.Encoding"
|
|
1861
|
-
) {
|
|
1862
|
-
this.useHelper("base64");
|
|
1863
|
-
const recv = this.genExpr(expr.func.expr);
|
|
1864
|
-
const args = expr.args.map((a) => this.genExpr(a)).join(", ");
|
|
1865
|
-
return `${recv}.${expr.func.field}(${args})`;
|
|
1866
|
-
}
|
|
1867
2235
|
return undefined;
|
|
1868
2236
|
},
|
|
1869
2237
|
|
|
@@ -1889,12 +2257,44 @@ export const expressionGenMethods = {
|
|
|
1889
2257
|
const ns = expr.func.expr.name;
|
|
1890
2258
|
const fn = expr.func.field;
|
|
1891
2259
|
const result = this._genStdlibCall(ns, fn, expr);
|
|
1892
|
-
if (result !== undefined)
|
|
2260
|
+
if (result !== undefined) {
|
|
2261
|
+
if (this._is64BitInt(expr._type)) {
|
|
2262
|
+
return `((__r) => typeof __r === "number" ? BigInt(__r) : __r)(${result})`;
|
|
2263
|
+
}
|
|
2264
|
+
if (
|
|
2265
|
+
expr._type?.kind === "tuple" &&
|
|
2266
|
+
expr._type.types.some((t) => this._is64BitInt(t))
|
|
2267
|
+
) {
|
|
2268
|
+
const casts = expr._type.types.map((t, i) =>
|
|
2269
|
+
this._is64BitInt(t)
|
|
2270
|
+
? `typeof __r[${i}] === "number" ? BigInt(__r[${i}]) : __r[${i}]`
|
|
2271
|
+
: `__r[${i}]`,
|
|
2272
|
+
);
|
|
2273
|
+
return `((__r) => [${casts.join(", ")}])(${result})`;
|
|
2274
|
+
}
|
|
2275
|
+
return result;
|
|
2276
|
+
}
|
|
1893
2277
|
}
|
|
1894
2278
|
const recvType = expr.func.expr._type;
|
|
1895
2279
|
const recvName = this._resolveRecvName(recvType);
|
|
1896
2280
|
const typed = this._genReceiverTypeCall(recvName, expr);
|
|
1897
|
-
if (typed !== undefined)
|
|
2281
|
+
if (typed !== undefined) {
|
|
2282
|
+
if (this._is64BitInt(expr._type)) {
|
|
2283
|
+
return `((__r) => typeof __r === "number" ? BigInt(__r) : __r)(${typed})`;
|
|
2284
|
+
}
|
|
2285
|
+
if (
|
|
2286
|
+
expr._type?.kind === "tuple" &&
|
|
2287
|
+
expr._type.types.some((t) => this._is64BitInt(t))
|
|
2288
|
+
) {
|
|
2289
|
+
const casts = expr._type.types.map((t, i) =>
|
|
2290
|
+
this._is64BitInt(t)
|
|
2291
|
+
? `typeof __r[${i}] === "number" ? BigInt(__r[${i}]) : __r[${i}]`
|
|
2292
|
+
: `__r[${i}]`,
|
|
2293
|
+
);
|
|
2294
|
+
return `((__r) => [${casts.join(", ")}])(${typed})`;
|
|
2295
|
+
}
|
|
2296
|
+
return typed;
|
|
2297
|
+
}
|
|
1898
2298
|
const namedCall = this._genNamedMethodCall(expr);
|
|
1899
2299
|
if (namedCall !== undefined) return namedCall;
|
|
1900
2300
|
if (this.strict && this._isInterfaceType(recvType)) {
|
|
@@ -1902,14 +2302,22 @@ export const expressionGenMethods = {
|
|
|
1902
2302
|
// TypeError with a different message.
|
|
1903
2303
|
this.useHelper("nilCall");
|
|
1904
2304
|
const recv = this.genExpr(expr.func.expr);
|
|
1905
|
-
const args = expr
|
|
1906
|
-
.map((a) => (a._spread ? `...${this.genExpr(a)}` : this.genExpr(a)))
|
|
1907
|
-
.join(", ");
|
|
2305
|
+
const args = this._genCallArgs(expr);
|
|
1908
2306
|
return `__nilcall(${recv}).${expr.func.field}(${args})`;
|
|
1909
2307
|
}
|
|
1910
2308
|
return undefined;
|
|
1911
2309
|
},
|
|
1912
2310
|
|
|
2311
|
+
// `len`/`cap` of an array or array pointer is a constant, but an operand
|
|
2312
|
+
// containing a call or receive is still evaluated (and a nil pointer is
|
|
2313
|
+
// not dereferenced).
|
|
2314
|
+
_genConstLen(expr) {
|
|
2315
|
+
const arg = expr.args[0];
|
|
2316
|
+
if (arg && hasCallOrReceive(arg))
|
|
2317
|
+
return `(${this.genExpr(arg)}, ${expr._constLen})`;
|
|
2318
|
+
return String(expr._constLen);
|
|
2319
|
+
},
|
|
2320
|
+
|
|
1913
2321
|
_isInterfaceType(t) {
|
|
1914
2322
|
if (!t) return false;
|
|
1915
2323
|
if (t.kind === "interface") return true;
|
|
@@ -1917,13 +2325,13 @@ export const expressionGenMethods = {
|
|
|
1917
2325
|
},
|
|
1918
2326
|
|
|
1919
2327
|
_genBuiltinLen(expr) {
|
|
1920
|
-
if (expr._constLen != null) return
|
|
2328
|
+
if (expr._constLen != null) return this._genConstLen(expr);
|
|
1921
2329
|
const arg = expr.args[0];
|
|
1922
2330
|
const t = arg?._type;
|
|
1923
2331
|
const wrapField = this._namedWrapperField(t, arg);
|
|
1924
2332
|
if (wrapField) return `${this.genExpr(arg)}.${wrapField}.length`;
|
|
1925
2333
|
const js = this.genExpr(arg);
|
|
1926
|
-
if (t?.kind === "map") return `Object.keys(${js}).length`;
|
|
2334
|
+
if (t?.kind === "map") return `Object.keys(${js} ?? {}).length`;
|
|
1927
2335
|
this.useHelper("len");
|
|
1928
2336
|
return `__len(${js})`;
|
|
1929
2337
|
},
|
|
@@ -1954,9 +2362,9 @@ export const expressionGenMethods = {
|
|
|
1954
2362
|
// contributes its UTF-8 bytes.
|
|
1955
2363
|
_genSliceSource(node) {
|
|
1956
2364
|
const js = this.genExpr(node);
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
2365
|
+
if (!this._isStringExprType(node._type)) return js;
|
|
2366
|
+
this.useHelper("u8enc");
|
|
2367
|
+
return `__u8enc(${js})`;
|
|
1960
2368
|
},
|
|
1961
2369
|
|
|
1962
2370
|
genAppend(expr) {
|
|
@@ -1981,12 +2389,23 @@ export const expressionGenMethods = {
|
|
|
1981
2389
|
const st = expr._type ?? expr.args[0]._type;
|
|
1982
2390
|
const elemT = (st?.kind === "named" ? st.underlying : st)?.elem;
|
|
1983
2391
|
const spreadOf = (a) =>
|
|
1984
|
-
this._aggregateBase(elemT)
|
|
2392
|
+
this._aggregateBase(elemT)
|
|
1985
2393
|
? `...(${this.genExpr(a)} ?? []).map((__e) => ${this._cloneJs(elemT, "__e")})`
|
|
1986
2394
|
: `...(${this._genSliceSource(a)} ?? [])`;
|
|
1987
|
-
const
|
|
1988
|
-
|
|
1989
|
-
|
|
2395
|
+
const is64Elem = this._is64BitInt(elemT);
|
|
2396
|
+
const elems = expr.args.slice(1).map((a) => {
|
|
2397
|
+
if (a._spread) return spreadOf(a);
|
|
2398
|
+
const val = this.genValueExpr(a);
|
|
2399
|
+
if (
|
|
2400
|
+
is64Elem &&
|
|
2401
|
+
a._type?.kind === "untyped" &&
|
|
2402
|
+
!val.endsWith("n") &&
|
|
2403
|
+
/^-?\d+$/.test(val)
|
|
2404
|
+
) {
|
|
2405
|
+
return `${val}n`;
|
|
2406
|
+
}
|
|
2407
|
+
return val;
|
|
2408
|
+
});
|
|
1990
2409
|
if (elems.length === 0) return slice;
|
|
1991
2410
|
this.useHelper("append");
|
|
1992
2411
|
const sliceType = expr._type ?? expr.args[0]._type;
|
|
@@ -1995,11 +2414,35 @@ export const expressionGenMethods = {
|
|
|
1995
2414
|
(sliceType.kind === "named" ? sliceType.underlying : sliceType).elem,
|
|
1996
2415
|
)
|
|
1997
2416
|
: null;
|
|
1998
|
-
if (typedCtor)
|
|
1999
|
-
|
|
2417
|
+
if (typedCtor) {
|
|
2418
|
+
const single = expr.args.length === 2 && expr.args[1]._spread;
|
|
2419
|
+
// One source slice or string: append it as it is, not spread.
|
|
2420
|
+
if (single)
|
|
2421
|
+
return `__appendv((${slice}) ?? __tn(${typedCtor}), ${this._genSliceSource(expr.args[1])} ?? [])`;
|
|
2422
|
+
return `__append((${slice}) ?? __tn(${typedCtor}), ${elems.join(", ")})`;
|
|
2423
|
+
}
|
|
2424
|
+
// A json codec grows a slice it has just made and no one else holds:
|
|
2425
|
+
// pushing in place is O(1), where __append copies the whole slice.
|
|
2426
|
+
if (
|
|
2427
|
+
this._inJsonCodecFile() &&
|
|
2428
|
+
expr.args.length === 2 &&
|
|
2429
|
+
expr.args[0].kind === "Ident" &&
|
|
2430
|
+
!expr.args[1]._spread
|
|
2431
|
+
) {
|
|
2432
|
+
this.useHelper("pushs");
|
|
2433
|
+
return `__pushs(${slice}, ${elems[0]})`;
|
|
2434
|
+
}
|
|
2000
2435
|
return `__append(${slice}, ${elems.join(", ")})`;
|
|
2001
2436
|
},
|
|
2002
2437
|
|
|
2438
|
+
// A `make` length or capacity expression wrapped in its negative-value
|
|
2439
|
+
// check (a literal needs none).
|
|
2440
|
+
_guardMakeSize(code, guard) {
|
|
2441
|
+
if (/^\d+$/.test(code)) return code;
|
|
2442
|
+
this.useHelper("mklen");
|
|
2443
|
+
return `${guard}(${code})`;
|
|
2444
|
+
},
|
|
2445
|
+
|
|
2003
2446
|
genMake(expr) {
|
|
2004
2447
|
// make([]T, n) or make([]T, n, cap) → new Array(n).fill(zero)
|
|
2005
2448
|
// make(map[K]V) → {}
|
|
@@ -2007,12 +2450,16 @@ export const expressionGenMethods = {
|
|
|
2007
2450
|
const typeNode = typeArg.kind === "TypeExpr" ? typeArg.type : typeArg;
|
|
2008
2451
|
// A defined type (`type S []int`) makes like its underlying type.
|
|
2009
2452
|
const madeType =
|
|
2010
|
-
typeArg.
|
|
2453
|
+
typeArg._coreType ??
|
|
2454
|
+
(typeArg._type?.kind === "named"
|
|
2011
2455
|
? typeArg._type.underlying
|
|
2012
|
-
: typeArg._type;
|
|
2456
|
+
: typeArg._type);
|
|
2013
2457
|
const resolvedKind = madeType?.kind;
|
|
2014
2458
|
if (typeNode.kind === "SliceType" || resolvedKind === "slice") {
|
|
2015
|
-
|
|
2459
|
+
let n = expr.args[1] ? this.genExpr(expr.args[1]) : "0";
|
|
2460
|
+
if (expr.args[1]) n = this._asIndexNumber(n, expr.args[1]._type);
|
|
2461
|
+
// A negative length or capacity panics with Go's message.
|
|
2462
|
+
n = this._guardMakeSize(n, "__mkn");
|
|
2016
2463
|
const elemNode = typeNode.kind === "SliceType" ? typeNode.elem : null;
|
|
2017
2464
|
const elemResolved = madeType?.kind === "slice" ? madeType.elem : null;
|
|
2018
2465
|
const typedCtor =
|
|
@@ -2025,8 +2472,13 @@ export const expressionGenMethods = {
|
|
|
2025
2472
|
: null);
|
|
2026
2473
|
// make([]T, n, cap) allocates cap elements and returns a view of
|
|
2027
2474
|
// the first n (`__mkslice`), so append within cap shares them.
|
|
2028
|
-
|
|
2029
|
-
if (capArg
|
|
2475
|
+
let capArg = expr.args[2] ? this.genExpr(expr.args[2]) : null;
|
|
2476
|
+
if (capArg && expr.args[2])
|
|
2477
|
+
capArg = this._asIndexNumber(capArg, expr.args[2]._type);
|
|
2478
|
+
if (capArg) {
|
|
2479
|
+
this.useHelper("slice");
|
|
2480
|
+
capArg = this._guardMakeSize(capArg, "__mkc");
|
|
2481
|
+
}
|
|
2030
2482
|
const withCap = (alloc) =>
|
|
2031
2483
|
capArg ? `__mkslice(${n}, ${alloc(capArg)})` : alloc(n);
|
|
2032
2484
|
if (typedCtor) {
|
|
@@ -2045,24 +2497,18 @@ export const expressionGenMethods = {
|
|
|
2045
2497
|
}
|
|
2046
2498
|
return withCap((k) => `new Array(${k}).fill(${zero})`);
|
|
2047
2499
|
}
|
|
2048
|
-
// map or fallback
|
|
2500
|
+
// map or fallback. The json codecs' maps hold decoded keys, among them
|
|
2501
|
+
// "__proto__": without a prototype that is an ordinary key.
|
|
2502
|
+
if (
|
|
2503
|
+
this._inJsonCodecFile() &&
|
|
2504
|
+
(resolvedKind === "map" || typeNode.kind === "MapType")
|
|
2505
|
+
)
|
|
2506
|
+
return "Object.create(null)";
|
|
2049
2507
|
return "{}";
|
|
2050
2508
|
},
|
|
2051
2509
|
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
_genStructFields(elems) {
|
|
2055
|
-
return elems
|
|
2056
|
-
.map((e) => {
|
|
2057
|
-
if (e._positionalField) {
|
|
2058
|
-
return `${e._positionalField}: ${this.genExpr(e)}`;
|
|
2059
|
-
}
|
|
2060
|
-
if (e.kind !== "KeyValueExpr") return null;
|
|
2061
|
-
if (e._isEmbedInit) return `...${this.genExpr(e.value)}`;
|
|
2062
|
-
return `${e.key.name ?? this.genExpr(e.key)}: ${this.genExpr(e.value)}`;
|
|
2063
|
-
})
|
|
2064
|
-
.filter((s) => s !== null)
|
|
2065
|
-
.join(", ");
|
|
2510
|
+
_inJsonCodecFile() {
|
|
2511
|
+
return this._jsonCodecFileIdx === (this._currentSrcFileIdx ?? 0);
|
|
2066
2512
|
},
|
|
2067
2513
|
|
|
2068
2514
|
_isSliceOrArrayType(t) {
|
|
@@ -2322,10 +2768,6 @@ export const expressionGenMethods = {
|
|
|
2322
2768
|
const t = expr.typeExpr;
|
|
2323
2769
|
if (t === null) return this._genImplicitCompositeLit(expr);
|
|
2324
2770
|
const typeName = this.getTypeName(t);
|
|
2325
|
-
if (typeName === "bytes.Buffer" || typeName === "Buffer")
|
|
2326
|
-
return "{ _buf: [] }";
|
|
2327
|
-
if (typeName === "strings.Builder" || typeName === "Builder")
|
|
2328
|
-
return '{ _buf: "" }';
|
|
2329
2771
|
if (typeName === "simd.Mask32x4" || typeName === "Mask32x4")
|
|
2330
2772
|
return "Object.freeze([false, false, false, false])";
|
|
2331
2773
|
if (typeName === "simd.F32x4" || typeName === "F32x4") {
|
|
@@ -2445,26 +2887,44 @@ export const expressionGenMethods = {
|
|
|
2445
2887
|
// ── Helpers ───────────────────────────────────────────────────
|
|
2446
2888
|
|
|
2447
2889
|
_genComplexOperand(expr) {
|
|
2448
|
-
if (isComplex(expr._type)) return this.genExpr(expr);
|
|
2890
|
+
if (isComplex(expr._type) || expr._asComplex) return this.genExpr(expr);
|
|
2449
2891
|
return `{ re: ${this.genExpr(expr)}, im: 0 }`;
|
|
2450
2892
|
},
|
|
2451
2893
|
|
|
2452
2894
|
getTypeName(typeNode) {
|
|
2453
2895
|
if (!typeNode) return null;
|
|
2454
2896
|
if (typeNode._stdName) return typeNode._stdName;
|
|
2455
|
-
if (typeNode.kind === "TypeName")
|
|
2897
|
+
if (typeNode.kind === "TypeName")
|
|
2898
|
+
return this._aliasTarget(this._dequalify(typeNode.name));
|
|
2456
2899
|
if (typeNode.kind === "GenericTypeName") return typeNode.name;
|
|
2457
|
-
if (typeNode.kind === "Ident") return typeNode.name;
|
|
2900
|
+
if (typeNode.kind === "Ident") return this._aliasTarget(typeNode.name);
|
|
2458
2901
|
if (typeNode.kind === "InstantiationExpr")
|
|
2459
2902
|
return this.getTypeName(typeNode.expr);
|
|
2460
2903
|
if (typeNode.kind === "SelectorExpr") {
|
|
2461
2904
|
const base = this.getTypeName(typeNode.expr);
|
|
2462
|
-
if (this.bundledPackages.has(base))
|
|
2905
|
+
if (this.bundledPackages.has(base)) {
|
|
2906
|
+
// `p.A` for `type A = Base` in p: no class is emitted for A.
|
|
2907
|
+
const t = this.checker?.types.get(`${base}.${typeNode.field}`);
|
|
2908
|
+
return t?.kind === "named" && t.name && !t.name.includes("$$")
|
|
2909
|
+
? t.name
|
|
2910
|
+
: typeNode.field;
|
|
2911
|
+
}
|
|
2463
2912
|
return `${base}.${typeNode.field}`;
|
|
2464
2913
|
}
|
|
2465
2914
|
return null;
|
|
2466
2915
|
},
|
|
2467
2916
|
|
|
2917
|
+
// `type A = Base`: no class is emitted for A, so it names Base's.
|
|
2918
|
+
_aliasTarget(name) {
|
|
2919
|
+
const t = this.checker?.types.get(name);
|
|
2920
|
+
return t?.kind === "named" &&
|
|
2921
|
+
t.name &&
|
|
2922
|
+
t.name !== name &&
|
|
2923
|
+
!name.includes(".")
|
|
2924
|
+
? t.name
|
|
2925
|
+
: name;
|
|
2926
|
+
},
|
|
2927
|
+
|
|
2468
2928
|
// Bundled GoFront packages are inlined, so `pkg.T` names the class `T`.
|
|
2469
2929
|
_dequalify(name) {
|
|
2470
2930
|
const dot = name.indexOf(".");
|
|
@@ -2514,6 +2974,16 @@ export const expressionGenMethods = {
|
|
|
2514
2974
|
},
|
|
2515
2975
|
|
|
2516
2976
|
// Runs `fn` with type parameters `names` bound to the factories `$z_<name>`.
|
|
2977
|
+
_withoutNatives(fn) {
|
|
2978
|
+
const saved = this.natives;
|
|
2979
|
+
this.natives = false;
|
|
2980
|
+
try {
|
|
2981
|
+
return fn();
|
|
2982
|
+
} finally {
|
|
2983
|
+
this.natives = saved;
|
|
2984
|
+
}
|
|
2985
|
+
},
|
|
2986
|
+
|
|
2517
2987
|
_withTypeParamZeros(names, fn) {
|
|
2518
2988
|
const prev = this._typeParamZeros;
|
|
2519
2989
|
this._typeParamZeros = names
|
|
@@ -2534,6 +3004,7 @@ export const expressionGenMethods = {
|
|
|
2534
3004
|
if (bound) return bound;
|
|
2535
3005
|
}
|
|
2536
3006
|
this.useHelper("generics");
|
|
3007
|
+
if (this._is64BitUnsigned(t)) return "__zu64";
|
|
2537
3008
|
const zero = this.zeroValueForType(t);
|
|
2538
3009
|
return Object.hasOwn(SHARED_ZERO_FACTORIES, zero)
|
|
2539
3010
|
? SHARED_ZERO_FACTORIES[zero]
|
|
@@ -2722,6 +3193,8 @@ export const expressionGenMethods = {
|
|
|
2722
3193
|
return this._typeNameCheckExpr(typeNode.name, val);
|
|
2723
3194
|
const generic = this._genericInstanceCheck(typeNode, val);
|
|
2724
3195
|
if (generic) return generic;
|
|
3196
|
+
const dynamic = this._dynTypeCheck(typeNode, val);
|
|
3197
|
+
if (dynamic) return dynamic;
|
|
2725
3198
|
const extended = this._namedOrIfaceCheckExpr(typeNode, val);
|
|
2726
3199
|
if (extended) return extended;
|
|
2727
3200
|
if (
|
|
@@ -2785,20 +3258,45 @@ export const expressionGenMethods = {
|
|
|
2785
3258
|
const r = this._genComplexOperand(expr.right);
|
|
2786
3259
|
switch (expr.op) {
|
|
2787
3260
|
case "+":
|
|
2788
|
-
return `{ re: ${l}.re + ${r}.re, im: ${l}.im + ${r}.im }`;
|
|
2789
3261
|
case "-":
|
|
2790
|
-
return `{ re: ${l}.re - ${r}.re, im: ${l}.im - ${r}.im }`;
|
|
2791
3262
|
case "*":
|
|
2792
|
-
this.useHelper("cmul");
|
|
2793
|
-
return `__cmul(${l}, ${r})`;
|
|
2794
3263
|
case "/":
|
|
2795
|
-
this.
|
|
2796
|
-
return `__cdiv(${l}, ${r})`;
|
|
3264
|
+
return this._genComplexArith(expr.op, l, r, isComplex64(expr._type));
|
|
2797
3265
|
case "==":
|
|
2798
|
-
return
|
|
3266
|
+
return this._genComplexEquality("===", "&&", l, r);
|
|
2799
3267
|
case "!=":
|
|
2800
|
-
return
|
|
3268
|
+
return this._genComplexEquality("!==", "||", l, r);
|
|
3269
|
+
}
|
|
3270
|
+
},
|
|
3271
|
+
|
|
3272
|
+
// `l == r` / `l != r` on complex operands: each component reads both
|
|
3273
|
+
// operands, so an operand that calls a function is bound once first.
|
|
3274
|
+
_genComplexEquality(cmp, join, l, r) {
|
|
3275
|
+
const pure = (js) => !js.includes("(");
|
|
3276
|
+
if (pure(l) && pure(r))
|
|
3277
|
+
return `(${l}.re ${cmp} ${r}.re ${join} ${l}.im ${cmp} ${r}.im)`;
|
|
3278
|
+
return `((__a, __b) => __a.re ${cmp} __b.re ${join} __a.im ${cmp} __b.im)(${l}, ${r})`;
|
|
3279
|
+
},
|
|
3280
|
+
|
|
3281
|
+
// `l op r` for complex operands. A complex64 result has float32
|
|
3282
|
+
// components, as in Go (where the arithmetic runs in float64 and the
|
|
3283
|
+
// result is narrowed).
|
|
3284
|
+
_genComplexArith(op, l, r, is64) {
|
|
3285
|
+
let re;
|
|
3286
|
+
let im;
|
|
3287
|
+
let call = null;
|
|
3288
|
+
if (op === "+" || op === "-") {
|
|
3289
|
+
re = `${l}.re ${op} ${r}.re`;
|
|
3290
|
+
im = `${l}.im ${op} ${r}.im`;
|
|
3291
|
+
} else {
|
|
3292
|
+
const helper = op === "*" ? "cmul" : "cdiv";
|
|
3293
|
+
this.useHelper(helper);
|
|
3294
|
+
call = `__${helper}(${l}, ${r})`;
|
|
2801
3295
|
}
|
|
3296
|
+
if (!is64) return call ?? `{ re: ${re}, im: ${im} }`;
|
|
3297
|
+
if (call)
|
|
3298
|
+
return `((__z) => ({ re: Math.fround(__z.re), im: Math.fround(__z.im) }))(${call})`;
|
|
3299
|
+
return `{ re: Math.fround(${re}), im: Math.fround(${im}) }`;
|
|
2802
3300
|
},
|
|
2803
3301
|
|
|
2804
3302
|
_genBinaryExpr(expr) {
|
|
@@ -2821,6 +3319,9 @@ export const expressionGenMethods = {
|
|
|
2821
3319
|
const nilMap = this._genNilMapCompare(expr);
|
|
2822
3320
|
if (nilMap) return nilMap;
|
|
2823
3321
|
|
|
3322
|
+
const anyNil = this._genAnyNilCompare(expr);
|
|
3323
|
+
if (anyNil) return anyNil;
|
|
3324
|
+
|
|
2824
3325
|
const tpEq = this._genTypeParamEquality(expr);
|
|
2825
3326
|
if (tpEq) return tpEq;
|
|
2826
3327
|
|
|
@@ -2833,12 +3334,85 @@ export const expressionGenMethods = {
|
|
|
2833
3334
|
const ptrCmp = this._genPointerEquality(expr);
|
|
2834
3335
|
if (ptrCmp !== null) return ptrCmp;
|
|
2835
3336
|
|
|
2836
|
-
if (this.
|
|
3337
|
+
if (this._is64BitInt(expr._type)) return this._gen64BitBinaryExpr(expr);
|
|
3338
|
+
|
|
3339
|
+
if (
|
|
3340
|
+
(this.strict || this._isFloat32(expr._type)) &&
|
|
3341
|
+
this._needsStrictOp(expr.op, expr._type)
|
|
3342
|
+
) {
|
|
2837
3343
|
const l = this.genExpr(expr.left);
|
|
2838
3344
|
const r = this.genExpr(expr.right);
|
|
2839
|
-
return this._genStrictBinary(expr.op, l, r, expr._type);
|
|
3345
|
+
return this._genStrictBinary(expr.op, l, r, expr._type, expr.right._type);
|
|
3346
|
+
}
|
|
3347
|
+
|
|
3348
|
+
return this._genStandardBinaryExpr(expr);
|
|
3349
|
+
},
|
|
3350
|
+
|
|
3351
|
+
_gen64BitBinaryExpr(expr) {
|
|
3352
|
+
let l = this.genExpr(expr.left);
|
|
3353
|
+
let r = this.genExpr(expr.right);
|
|
3354
|
+
// An untyped operand is a literal (`3` -> `3n`) or a named constant,
|
|
3355
|
+
// which holds a Number.
|
|
3356
|
+
const lit = (js) => (/^-?\d+$/.test(js) ? `${js}n` : `BigInt(${js})`);
|
|
3357
|
+
if (expr.left._type?.kind === "untyped" && !l.endsWith("n")) l = lit(l);
|
|
3358
|
+
else if (!this._is64BitInt(expr.left?._type)) l = `BigInt(${l})`;
|
|
3359
|
+
if (expr.right._type?.kind === "untyped" && !r.endsWith("n")) r = lit(r);
|
|
3360
|
+
else if (!this._is64BitInt(expr.right?._type)) r = `BigInt(${r})`;
|
|
3361
|
+
return this._gen64BitBinary(expr.op, l, r, expr._type);
|
|
3362
|
+
},
|
|
3363
|
+
|
|
3364
|
+
_tconvBinaryOperands(expr, l, r) {
|
|
3365
|
+
if (
|
|
3366
|
+
expr.left._type?.kind === "typeParam" &&
|
|
3367
|
+
expr.right._type?.kind === "untyped"
|
|
3368
|
+
) {
|
|
3369
|
+
const factory = this._typeParamZeroFactory(expr.left._type.name);
|
|
3370
|
+
if (factory) {
|
|
3371
|
+
this.useHelper("generics");
|
|
3372
|
+
r = `__tconv(${factory}, ${r})`;
|
|
3373
|
+
}
|
|
3374
|
+
} else if (
|
|
3375
|
+
expr.right._type?.kind === "typeParam" &&
|
|
3376
|
+
expr.left._type?.kind === "untyped"
|
|
3377
|
+
) {
|
|
3378
|
+
const factory = this._typeParamZeroFactory(expr.right._type.name);
|
|
3379
|
+
if (factory) {
|
|
3380
|
+
this.useHelper("generics");
|
|
3381
|
+
l = `__tconv(${factory}, ${l})`;
|
|
3382
|
+
}
|
|
3383
|
+
}
|
|
3384
|
+
return [l, r];
|
|
3385
|
+
},
|
|
3386
|
+
|
|
3387
|
+
_coerceUntyped64(expr, l, r, isLeft64, isRight64) {
|
|
3388
|
+
if (isLeft64 && expr.right?._type?.kind === "untyped") {
|
|
3389
|
+
if (!r.endsWith("n") && /^-?\d+$/.test(r)) return [l, `${r}n`];
|
|
3390
|
+
} else if (isRight64 && expr.left?._type?.kind === "untyped") {
|
|
3391
|
+
if (!l.endsWith("n") && /^-?\d+$/.test(l)) return [`${l}n`, r];
|
|
2840
3392
|
}
|
|
3393
|
+
return [l, r];
|
|
3394
|
+
},
|
|
3395
|
+
|
|
3396
|
+
_coerceMixed64(expr, l, r, isLeft64, isRight64) {
|
|
3397
|
+
const isShift = expr.op === "<<" || expr.op === ">>";
|
|
3398
|
+
if (isLeft64 && !isRight64 && !isShift) r = `BigInt(${r})`;
|
|
3399
|
+
else if (isRight64 && !isLeft64 && !isShift) l = `BigInt(${l})`;
|
|
3400
|
+
if (isShift && isRight64) r = `Number(${r})`;
|
|
3401
|
+
return [l, r];
|
|
3402
|
+
},
|
|
3403
|
+
|
|
3404
|
+
_wrapBinaryResult(expr, res, isLeft64, isRight64) {
|
|
3405
|
+
const isCmp =
|
|
3406
|
+
expr.op === "==" ||
|
|
3407
|
+
expr.op === "!=" ||
|
|
3408
|
+
expr.op === "<" ||
|
|
3409
|
+
expr.op === "<=" ||
|
|
3410
|
+
expr.op === ">" ||
|
|
3411
|
+
expr.op === ">=";
|
|
3412
|
+
return (isLeft64 || isRight64) && !isCmp ? `Number(${res})` : res;
|
|
3413
|
+
},
|
|
2841
3414
|
|
|
3415
|
+
_genStandardBinaryExpr(expr) {
|
|
2842
3416
|
const jsOp = this._emittedBinaryOp(expr) ?? "/";
|
|
2843
3417
|
const operand = (child, isRight) =>
|
|
2844
3418
|
wrapForJsOp(
|
|
@@ -2847,17 +3421,29 @@ export const expressionGenMethods = {
|
|
|
2847
3421
|
jsOp,
|
|
2848
3422
|
isRight,
|
|
2849
3423
|
);
|
|
2850
|
-
|
|
3424
|
+
let [l, r] = this._tconvBinaryOperands(
|
|
3425
|
+
expr,
|
|
3426
|
+
operand(expr.left, false),
|
|
3427
|
+
operand(expr.right, true),
|
|
3428
|
+
);
|
|
3429
|
+
const isLeft64 = this._is64BitInt(expr.left?._type);
|
|
3430
|
+
const isRight64 = this._is64BitInt(expr.right?._type);
|
|
3431
|
+
[l, r] = this._coerceUntyped64(expr, l, r, isLeft64, isRight64);
|
|
3432
|
+
[l, r] = this._coerceMixed64(expr, l, r, isLeft64, isRight64);
|
|
2851
3433
|
|
|
2852
3434
|
if (expr.op === "&^") {
|
|
2853
3435
|
const rCode = this.genExpr(expr.right);
|
|
2854
|
-
const
|
|
3436
|
+
const rP =
|
|
2855
3437
|
this._emittedBinaryOp(expr.right) !== null ? `(${rCode})` : rCode;
|
|
2856
|
-
return `${l} & ~${
|
|
3438
|
+
return this._wrapBinaryResult(expr, `${l} & ~${rP}`, isLeft64, isRight64);
|
|
2857
3439
|
}
|
|
2858
|
-
const r = operand(expr.right, true);
|
|
2859
3440
|
if (this._isIntDivision(expr)) return `Math.trunc(${l} / ${r})`;
|
|
2860
|
-
return
|
|
3441
|
+
return this._wrapBinaryResult(
|
|
3442
|
+
expr,
|
|
3443
|
+
`${l} ${jsOp} ${r}`,
|
|
3444
|
+
isLeft64,
|
|
3445
|
+
isRight64,
|
|
3446
|
+
);
|
|
2861
3447
|
},
|
|
2862
3448
|
|
|
2863
3449
|
// `m == nil` / `m != nil` on a map: true for a nil map (null, the strict
|
|
@@ -2872,7 +3458,7 @@ export const expressionGenMethods = {
|
|
|
2872
3458
|
if (!other) return null;
|
|
2873
3459
|
const t =
|
|
2874
3460
|
other._type?.kind === "named" ? other._type.underlying : other._type;
|
|
2875
|
-
return t?.kind === "map" ? other : null;
|
|
3461
|
+
return t?.kind === "map" || other._coreType?.kind === "map" ? other : null;
|
|
2876
3462
|
},
|
|
2877
3463
|
|
|
2878
3464
|
_genNilMapCompare(expr) {
|
|
@@ -2937,6 +3523,25 @@ export const expressionGenMethods = {
|
|
|
2937
3523
|
);
|
|
2938
3524
|
},
|
|
2939
3525
|
|
|
3526
|
+
// The `any` operand of `x == nil` / `x != nil` (JS documents a nil slice
|
|
3527
|
+
// stored in an `any` as equal to nil, though it keeps a type tag), or null.
|
|
3528
|
+
_anyNilOperand(expr) {
|
|
3529
|
+
if (expr.op !== "==" && expr.op !== "!=") return null;
|
|
3530
|
+
const operand = this._isNilLiteral(expr.right)
|
|
3531
|
+
? expr.left
|
|
3532
|
+
: this._isNilLiteral(expr.left)
|
|
3533
|
+
? expr.right
|
|
3534
|
+
: null;
|
|
3535
|
+
return operand && this._isIfaceType(operand._type) ? operand : null;
|
|
3536
|
+
},
|
|
3537
|
+
|
|
3538
|
+
_genAnyNilCompare(expr) {
|
|
3539
|
+
const operand = this._anyNilOperand(expr);
|
|
3540
|
+
if (!operand) return null;
|
|
3541
|
+
const test = `((__a) => __a == null || __a.__nils === true)(${this.genExpr(operand)})`;
|
|
3542
|
+
return expr.op === "==" ? test : `!${test}`;
|
|
3543
|
+
},
|
|
3544
|
+
|
|
2940
3545
|
_isNilLiteral(expr) {
|
|
2941
3546
|
return (
|
|
2942
3547
|
(expr.kind === "Ident" && expr.name === "nil") ||
|
|
@@ -2955,6 +3560,7 @@ export const expressionGenMethods = {
|
|
|
2955
3560
|
return null;
|
|
2956
3561
|
if (this._isStructEquality(expr) || this._isIntDivision(expr)) return null;
|
|
2957
3562
|
if (this._nilMapOperand(expr)) return null;
|
|
3563
|
+
if (this._anyNilOperand(expr)) return null;
|
|
2958
3564
|
if (this._isIfaceEquality(expr)) return null;
|
|
2959
3565
|
if (expr.op === "&^") return "&";
|
|
2960
3566
|
if (expr.op === "==" || expr.op === "!=") {
|
|
@@ -2985,6 +3591,10 @@ export const expressionGenMethods = {
|
|
|
2985
3591
|
return this._genArrayTypeConversion(expr, inner, t);
|
|
2986
3592
|
if (t?.kind === "SliceType")
|
|
2987
3593
|
return this._genSliceTypeConversion(expr, inner, t);
|
|
3594
|
+
// `map[K]V(nil)` is a nil map value, which stays distinct from the nil
|
|
3595
|
+
// interface once boxed in an `any`.
|
|
3596
|
+
if (t?.kind === "MapType" && this._isNilLiteral(expr.expr))
|
|
3597
|
+
return this._nilMapZero();
|
|
2988
3598
|
const target = t?.name;
|
|
2989
3599
|
const structConv = this._genStructClassConversion(
|
|
2990
3600
|
target,
|
|
@@ -2995,6 +3605,7 @@ export const expressionGenMethods = {
|
|
|
2995
3605
|
if (target === "error") {
|
|
2996
3606
|
// A boxed named value that implements error is already an error.
|
|
2997
3607
|
if (this._isNamedBoxKind(expr.expr)) return inner;
|
|
3608
|
+
if (!this._isStringExprType(expr.expr._type)) return inner;
|
|
2998
3609
|
this.useHelper("error");
|
|
2999
3610
|
return `__error(${inner})`;
|
|
3000
3611
|
}
|
|
@@ -3025,11 +3636,25 @@ export const expressionGenMethods = {
|
|
|
3025
3636
|
// `[]T(nil)` is the nil slice, not a copy of anything.
|
|
3026
3637
|
if (this._isNilLiteral(expr.expr)) return "null";
|
|
3027
3638
|
const elem = t.elem?.name;
|
|
3639
|
+
// Converting a nil slice yields a nil slice.
|
|
3640
|
+
const src = this._underlyingOf(expr.expr._type);
|
|
3641
|
+
if (src?.kind === "slice" && !this._sliceConvNilSafe) {
|
|
3642
|
+
this._sliceConvNilSafe = true;
|
|
3643
|
+
try {
|
|
3644
|
+
return `((__s) => __s == null ? null : ${this._genSliceTypeConversion(expr, "__s", t)})(${inner})`;
|
|
3645
|
+
} finally {
|
|
3646
|
+
this._sliceConvNilSafe = false;
|
|
3647
|
+
}
|
|
3648
|
+
}
|
|
3028
3649
|
if (elem === "byte" || elem === "uint8") {
|
|
3029
|
-
if (this._isStringExprType(expr.expr._type))
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3650
|
+
if (this._isStringExprType(expr.expr._type)) {
|
|
3651
|
+
this.useHelper("u8enc");
|
|
3652
|
+
return `__u8enc(${inner})`;
|
|
3653
|
+
}
|
|
3654
|
+
if (isAny(expr.expr._type) || !expr.expr._type) {
|
|
3655
|
+
this.useHelper("u8enc");
|
|
3656
|
+
return `(typeof ${inner} === "string" ? __u8enc(${inner}) : new Uint8Array(${inner}))`;
|
|
3657
|
+
}
|
|
3033
3658
|
return `new Uint8Array(${inner})`;
|
|
3034
3659
|
}
|
|
3035
3660
|
const typedCtor = typedArrayConstructorForElem(
|
|
@@ -3048,41 +3673,71 @@ export const expressionGenMethods = {
|
|
|
3048
3673
|
|
|
3049
3674
|
_genStringConversion(srcType, inner) {
|
|
3050
3675
|
const base = srcType?.kind === "named" ? srcType.underlying : srcType;
|
|
3051
|
-
if (base && this.isIntType(base))
|
|
3676
|
+
if (base && this.isIntType(base)) {
|
|
3677
|
+
this.useHelper("runestr");
|
|
3678
|
+
return `__rstr(${inner})`;
|
|
3679
|
+
}
|
|
3052
3680
|
if (base?.kind === "slice") {
|
|
3053
3681
|
const elem = base.elem?.name;
|
|
3054
|
-
if (elem === "byte" || elem === "uint8")
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3682
|
+
if (elem === "byte" || elem === "uint8") {
|
|
3683
|
+
this.useHelper("u8dec");
|
|
3684
|
+
return `__u8dec(${inner})`;
|
|
3685
|
+
}
|
|
3686
|
+
if (elem === "rune" || elem === "int" || elem === "int32") {
|
|
3687
|
+
this.useHelper("runestr");
|
|
3688
|
+
return `Array.from(${inner}, c => __rstr(c)).join("")`;
|
|
3689
|
+
}
|
|
3058
3690
|
}
|
|
3059
3691
|
return `String(${inner})`;
|
|
3060
3692
|
},
|
|
3061
3693
|
|
|
3062
3694
|
_genPrimitiveConversion(expr, inner, target) {
|
|
3695
|
+
const srcType = expr.expr?._type;
|
|
3696
|
+
const isSrc64 = this._is64BitInt(srcType);
|
|
3063
3697
|
switch (target) {
|
|
3064
3698
|
case "string":
|
|
3065
|
-
return this._genStringConversion(
|
|
3699
|
+
return this._genStringConversion(
|
|
3700
|
+
srcType,
|
|
3701
|
+
isSrc64 ? `Number(${inner})` : inner,
|
|
3702
|
+
);
|
|
3703
|
+
case "int64": {
|
|
3704
|
+
this.useHelper("to64");
|
|
3705
|
+
return `__to_i64(${inner})`;
|
|
3706
|
+
}
|
|
3707
|
+
case "uint64": {
|
|
3708
|
+
this.useHelper("to64");
|
|
3709
|
+
return `__to_u64(${inner})`;
|
|
3710
|
+
}
|
|
3066
3711
|
case "int":
|
|
3067
|
-
case "int64":
|
|
3068
3712
|
case "uint":
|
|
3069
|
-
case "uint64":
|
|
3070
3713
|
case "uintptr":
|
|
3071
|
-
return `Math.trunc(Number(${inner}))`;
|
|
3714
|
+
return isSrc64 ? `Number(${inner})` : `Math.trunc(Number(${inner}))`;
|
|
3072
3715
|
case "int8":
|
|
3073
|
-
return
|
|
3716
|
+
return isSrc64
|
|
3717
|
+
? `Number(BigInt.asIntN(8, ${inner}))`
|
|
3718
|
+
: `(Number(${inner}) << 24 >> 24)`;
|
|
3074
3719
|
case "int16":
|
|
3075
|
-
return
|
|
3720
|
+
return isSrc64
|
|
3721
|
+
? `Number(BigInt.asIntN(16, ${inner}))`
|
|
3722
|
+
: `(Number(${inner}) << 16 >> 16)`;
|
|
3076
3723
|
case "int32":
|
|
3077
3724
|
case "rune":
|
|
3078
|
-
return
|
|
3725
|
+
return isSrc64
|
|
3726
|
+
? `Number(BigInt.asIntN(32, ${inner}))`
|
|
3727
|
+
: `(Number(${inner}) | 0)`;
|
|
3079
3728
|
case "uint8":
|
|
3080
3729
|
case "byte":
|
|
3081
|
-
return
|
|
3730
|
+
return isSrc64
|
|
3731
|
+
? `Number(BigInt.asUintN(8, ${inner}))`
|
|
3732
|
+
: `(Number(${inner}) & 0xFF)`;
|
|
3082
3733
|
case "uint16":
|
|
3083
|
-
return
|
|
3734
|
+
return isSrc64
|
|
3735
|
+
? `Number(BigInt.asUintN(16, ${inner}))`
|
|
3736
|
+
: `(Number(${inner}) & 0xFFFF)`;
|
|
3084
3737
|
case "uint32":
|
|
3085
|
-
return
|
|
3738
|
+
return isSrc64
|
|
3739
|
+
? `Number(BigInt.asUintN(32, ${inner}))`
|
|
3740
|
+
: `(Number(${inner}) >>> 0)`;
|
|
3086
3741
|
case "float32":
|
|
3087
3742
|
return `Math.fround(Number(${inner}))`;
|
|
3088
3743
|
case "float64":
|
|
@@ -3106,9 +3761,75 @@ export const expressionGenMethods = {
|
|
|
3106
3761
|
return STRICT_NUMERIC_TYPES.has(typeName);
|
|
3107
3762
|
},
|
|
3108
3763
|
|
|
3764
|
+
_is64BitInt(t) {
|
|
3765
|
+
return is64BitInt(t);
|
|
3766
|
+
},
|
|
3767
|
+
|
|
3768
|
+
// Whether a value of type `t` can be a BigInt at run time: a 64-bit
|
|
3769
|
+
// integer, or a type parameter whose type set holds one (`~int | ~int64`).
|
|
3770
|
+
_mayBeBigInt(t) {
|
|
3771
|
+
if (t?.kind !== "typeParam") return is64BitInt(t);
|
|
3772
|
+
const terms = this.checker?._constraintTerms(t.constraint, new Set());
|
|
3773
|
+
return terms?.some(is64BitInt) === true;
|
|
3774
|
+
},
|
|
3775
|
+
|
|
3776
|
+
// `code` as a JS number for an index, slice bound or `make` size.
|
|
3777
|
+
_asIndexNumber(code, t) {
|
|
3778
|
+
return this._mayBeBigInt(t) ? `Number(${code})` : code;
|
|
3779
|
+
},
|
|
3780
|
+
|
|
3781
|
+
// float32 arithmetic rounds through Math.fround and arithmetic on a type
|
|
3782
|
+
// parameter wraps for the instantiated type (`__tar`) in every package kind.
|
|
3783
|
+
_isFloat32(t) {
|
|
3784
|
+
return (
|
|
3785
|
+
this._typeName(t) === "float32" ||
|
|
3786
|
+
(t?.kind === "typeParam" && this._typeParamZeroFactory(t.name) != null)
|
|
3787
|
+
);
|
|
3788
|
+
},
|
|
3789
|
+
|
|
3790
|
+
_is64BitUnsigned(t) {
|
|
3791
|
+
return is64BitUnsigned(t);
|
|
3792
|
+
},
|
|
3793
|
+
|
|
3794
|
+
_gen64BitBinary(op, l, r, type) {
|
|
3795
|
+
const unsigned = this._is64BitUnsigned(type);
|
|
3796
|
+
const wrap = unsigned ? "BigInt.asUintN" : "BigInt.asIntN";
|
|
3797
|
+
const divZero =
|
|
3798
|
+
'(() => { throw new Error("runtime error: integer divide by zero"); })()';
|
|
3799
|
+
if (op === "/" || op === "%") {
|
|
3800
|
+
// The divisor is read twice (zero check, division): bind both
|
|
3801
|
+
// operands once, left first, unless they are plain names or literals.
|
|
3802
|
+
const simple = /^[A-Za-z_$][\w$.]*$|^-?\d+n$/;
|
|
3803
|
+
if ((simple.test(l) && simple.test(r)) || /\b(await|yield)\b/.test(l + r))
|
|
3804
|
+
return `((${r}) === 0n ? ${divZero} : ${wrap}(64, (${l}) ${op} (${r})))`;
|
|
3805
|
+
return `((__a, __b) => __b === 0n ? ${divZero} : ${wrap}(64, __a ${op} __b))(${l}, ${r})`;
|
|
3806
|
+
}
|
|
3807
|
+
if (op === "<<" || op === ">>") {
|
|
3808
|
+
this.useHelper("shift");
|
|
3809
|
+
return op === "<<"
|
|
3810
|
+
? `__shl64(${l}, ${r}, ${unsigned})`
|
|
3811
|
+
: `__shr64(${l}, ${r}, ${unsigned})`;
|
|
3812
|
+
}
|
|
3813
|
+
if (op === "&^") return `${wrap}(64, (${l}) & ~(${r}))`;
|
|
3814
|
+
return `${wrap}(64, (${l}) ${op} (${r}))`;
|
|
3815
|
+
},
|
|
3816
|
+
|
|
3817
|
+
// `uint` / `uintptr`: 64-bit unsigned values held as JS numbers.
|
|
3818
|
+
_isUintWord(type) {
|
|
3819
|
+
const name = this._typeName(type);
|
|
3820
|
+
return name === "uint" || name === "uintptr";
|
|
3821
|
+
},
|
|
3822
|
+
|
|
3109
3823
|
_needsStrictOp(op, type) {
|
|
3824
|
+
if (type?.kind === "typeParam")
|
|
3825
|
+
return (
|
|
3826
|
+
STRICT_OPS.has(op) && this._typeParamZeroFactory(type.name) != null
|
|
3827
|
+
);
|
|
3828
|
+
if (this._is64BitInt(type))
|
|
3829
|
+
return STRICT_OPS.has(op) || op === "<<" || op === ">>" || op === "&^";
|
|
3110
3830
|
if (!STRICT_OPS.has(op)) return false;
|
|
3111
3831
|
if (this._isStrictAtomicOp(op, this._typeName(type))) return true;
|
|
3832
|
+
if (op === "-" && this._isUintWord(type)) return true;
|
|
3112
3833
|
if (op === "<<" || op === ">>")
|
|
3113
3834
|
return WIDE_INT_NAMES.has(this._typeName(type));
|
|
3114
3835
|
return this.isIntType(type) && (op === "/" || op === "%");
|
|
@@ -3116,7 +3837,14 @@ export const expressionGenMethods = {
|
|
|
3116
3837
|
|
|
3117
3838
|
// Emits `lhs = strict(lhs op rhs)` for any lvalue shape; returns false when not applicable.
|
|
3118
3839
|
// Indexed / nested targets are split into object + key temps so sub-expressions run once.
|
|
3119
|
-
_genStrictCompound(
|
|
3840
|
+
_genStrictCompound(
|
|
3841
|
+
lhsNode,
|
|
3842
|
+
op,
|
|
3843
|
+
rhsJs,
|
|
3844
|
+
srcLine = null,
|
|
3845
|
+
lhsJs = null,
|
|
3846
|
+
countType = null,
|
|
3847
|
+
) {
|
|
3120
3848
|
const type = lhsNode._type;
|
|
3121
3849
|
if (!this._needsStrictOp(op, type)) return false;
|
|
3122
3850
|
if (lhsNode.kind === "IndexExpr" && lhsNode.expr?._type?.kind === "map")
|
|
@@ -3124,7 +3852,7 @@ export const expressionGenMethods = {
|
|
|
3124
3852
|
const l = lhsJs ?? this._genAssignLhsExpr(lhsNode);
|
|
3125
3853
|
if (/^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*)*$/.test(l)) {
|
|
3126
3854
|
this.line(
|
|
3127
|
-
`${l} = ${this._genStrictBinary(op, l, rhsJs, type)};`,
|
|
3855
|
+
`${l} = ${this._genStrictBinary(op, l, rhsJs, type, countType)};`,
|
|
3128
3856
|
srcLine,
|
|
3129
3857
|
);
|
|
3130
3858
|
return true;
|
|
@@ -3137,21 +3865,40 @@ export const expressionGenMethods = {
|
|
|
3137
3865
|
const k = `__k${this._tmpCounter}`;
|
|
3138
3866
|
const target = `${o}[${k}]`;
|
|
3139
3867
|
this.line(
|
|
3140
|
-
`{ const ${o} = ${split.obj}, ${k} = ${split.key}; ${target} = ${this._genStrictBinary(op, target, rhsJs, type)}; }`,
|
|
3868
|
+
`{ const ${o} = ${split.obj}, ${k} = ${split.key}; ${target} = ${this._genStrictBinary(op, target, rhsJs, type, countType)}; }`,
|
|
3141
3869
|
srcLine,
|
|
3142
3870
|
);
|
|
3143
3871
|
} else {
|
|
3144
3872
|
const target = `${o}.${split.prop}`;
|
|
3145
3873
|
this.line(
|
|
3146
|
-
`{ const ${o} = ${split.obj}; ${target} = ${this._genStrictBinary(op, target, rhsJs, type)}; }`,
|
|
3874
|
+
`{ const ${o} = ${split.obj}; ${target} = ${this._genStrictBinary(op, target, rhsJs, type, countType)}; }`,
|
|
3147
3875
|
srcLine,
|
|
3148
3876
|
);
|
|
3149
3877
|
}
|
|
3150
3878
|
return true;
|
|
3151
3879
|
},
|
|
3152
3880
|
|
|
3153
|
-
|
|
3881
|
+
// A non-constant signed shift count on a narrow integer is checked for a
|
|
3882
|
+
// negative value (Go panics); 64-bit counts arrive as BigInt.
|
|
3883
|
+
_guardShiftCount(r, countType) {
|
|
3884
|
+
if (!countType || countType.kind === "untyped") return r;
|
|
3885
|
+
const name = this._typeName(countType);
|
|
3886
|
+
if (!name || name.startsWith("u")) return r;
|
|
3887
|
+
this.useHelper("shift");
|
|
3888
|
+
return `__shc(${this._is64BitInt(countType) ? `Number(${r})` : r})`;
|
|
3889
|
+
},
|
|
3890
|
+
|
|
3891
|
+
_genStrictBinary(op, l, r, type, countType = null) {
|
|
3892
|
+
if (type?.kind === "typeParam") {
|
|
3893
|
+
this.useHelper("generics");
|
|
3894
|
+
return `__tar(${this._typeParamZeroFactory(type.name)}, "${op}", ${l}, ${r})`;
|
|
3895
|
+
}
|
|
3896
|
+
if (this._is64BitInt(type)) {
|
|
3897
|
+
return this._gen64BitBinary(op, l, r, type);
|
|
3898
|
+
}
|
|
3154
3899
|
const tName = this._typeName(type);
|
|
3900
|
+
const isShift = op === "<<" || op === ">>";
|
|
3901
|
+
const rc = isShift ? this._guardShiftCount(r, countType) : r;
|
|
3155
3902
|
if (tName === "float32") {
|
|
3156
3903
|
return `Math.fround(${l} ${op} ${r})`;
|
|
3157
3904
|
}
|
|
@@ -3171,7 +3918,7 @@ export const expressionGenMethods = {
|
|
|
3171
3918
|
if (op === "<<" || op === ">>") {
|
|
3172
3919
|
const ovf = signed && op === ">>" ? `((${l}) < 0 ? -1 : 0)` : "0";
|
|
3173
3920
|
const jsOp = !signed && op === ">>" ? ">>>" : op;
|
|
3174
|
-
return `(((${
|
|
3921
|
+
return `(((${rc}) >= 32 ? ${ovf} : ((${l}) ${jsOp} (${r}))) ${cast})`;
|
|
3175
3922
|
}
|
|
3176
3923
|
if (op === "&^") return `(((${l}) & ~(${r})) ${cast})`;
|
|
3177
3924
|
return `((${l} ${op} ${r}) ${cast})`;
|
|
@@ -3184,7 +3931,7 @@ export const expressionGenMethods = {
|
|
|
3184
3931
|
if (op === "%")
|
|
3185
3932
|
return `((${r}) === 0 ? ${divZero} : (((((${l}) % (${r})) << ${sh}) >> ${sh})))`;
|
|
3186
3933
|
if (op === "<<" || op === ">>") {
|
|
3187
|
-
return `((${
|
|
3934
|
+
return `((${rc}) >= ${bits} ? (${op === ">>" ? `((${l}) < 0 ? -1 : 0)` : "0"}) : (((${l}) ${op} (${r})) << ${sh} >> ${sh}))`;
|
|
3188
3935
|
}
|
|
3189
3936
|
if (op === "&^") return `((((${l}) & ~(${r})) << ${sh}) >> ${sh})`;
|
|
3190
3937
|
return `((((${l} ${op} ${r})) << ${sh}) >> ${sh})`;
|
|
@@ -3198,11 +3945,13 @@ export const expressionGenMethods = {
|
|
|
3198
3945
|
return `((${r}) === 0 ? ${divZero} : (((${l}) % (${r})) & ${mask}))`;
|
|
3199
3946
|
if (op === "<<" || op === ">>") {
|
|
3200
3947
|
const jsOp = op === ">>" ? ">>>" : "<<";
|
|
3201
|
-
return `((${
|
|
3948
|
+
return `((${rc}) >= ${bits} ? 0 : (((${l}) ${jsOp} (${r})) & ${mask}))`;
|
|
3202
3949
|
}
|
|
3203
3950
|
if (op === "&^") return `(((${l}) & ~(${r})) & ${mask})`;
|
|
3204
3951
|
return `((${l} ${op} ${r}) & ${mask})`;
|
|
3205
3952
|
}
|
|
3953
|
+
if (op === "-" && this._isUintWord(type))
|
|
3954
|
+
return `((__d) => __d < 0 ? __d + 2 ** 64 : __d)((${l}) - (${r}))`;
|
|
3206
3955
|
if (op === "<<" || op === ">>") {
|
|
3207
3956
|
// 64-bit semantics: operand first, then count (Go order); a negative
|
|
3208
3957
|
// count panics and the result wraps to 64 bits.
|