gofront 1.7.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -14
- package/package.json +10 -38
- package/src/backend/js/expressions.js +2060 -389
- package/src/backend/js/index.js +586 -174
- package/src/backend/js/natives.js +383 -0
- package/src/backend/js/runtime.js +936 -1644
- package/src/backend/js/statements.js +1349 -132
- package/src/backend/js/stdlib/index.js +51 -94
- package/src/backend/js/templ.js +67 -118
- package/src/backend/js/type-info.js +436 -0
- package/src/backend/shared/fixed-float.js +20 -0
- package/src/backend/shared/fmt-types.js +80 -0
- package/src/backend/shared/host-unicode.js +36 -0
- package/src/backend/wasm/boundary.js +753 -872
- package/src/backend/wasm/emit-builtins.js +914 -237
- package/src/backend/wasm/emit-exprs.js +1456 -195
- package/src/backend/wasm/emit-maps.js +78 -380
- package/src/backend/wasm/emit-stmts.js +1659 -214
- package/src/backend/wasm/emit.js +614 -88
- package/src/backend/wasm/encode.js +43 -13
- package/src/backend/wasm/globals.js +491 -0
- package/src/backend/wasm/glue.js +2 -4
- package/src/backend/wasm/index.js +406 -3195
- package/src/backend/wasm/method-expr.js +392 -0
- package/src/backend/wasm/module-emitter.js +1615 -0
- package/src/backend/wasm/monomorph.js +164 -9
- package/src/backend/wasm/natives.js +596 -0
- package/src/backend/wasm/prescan.js +249 -0
- package/src/backend/wasm/prune-funcs.js +89 -0
- package/src/backend/wasm/stdlib/index.js +2 -37
- package/src/backend/wasm/stdlib/simd.js +293 -28
- package/src/backend/wasm/types.js +260 -27
- package/src/backend/wasm/variadic.js +93 -0
- package/src/backend/wasm/wat.js +47 -20
- package/src/{cli-core.js → cli/cli-core.js} +241 -11
- package/src/{test-runner.js → cli/test-runner.js} +188 -122
- package/src/{vendor.js → cli/vendor.js} +3 -4
- package/src/compiler.js +202 -386
- package/src/index.js +15 -169
- package/src/{lexer.js → lexer/lexer.js} +203 -62
- package/src/{templ-lexer.js → lexer/templ-lexer.js} +61 -18
- package/src/{tokens.js → lexer/tokens.js} +2 -0
- package/src/link/sourcemap.js +97 -0
- package/src/link/stdlib.js +737 -0
- package/src/link/type-names.js +148 -0
- package/src/link/wasm.js +318 -0
- package/src/lower/bounds.js +547 -0
- package/src/lower/boxing.js +1 -1
- package/src/lower/captures.js +267 -64
- package/src/lower/embedding.js +32 -3
- package/src/lower/errors-as.js +196 -0
- package/src/lower/fmt-formatters.js +905 -0
- package/src/lower/functions.js +2 -0
- package/src/lower/gen-types.js +274 -0
- package/src/lower/index.js +33 -19
- package/src/lower/json-codecs.js +1379 -0
- package/src/lower/ownership.js +47 -8
- package/src/lower/range.js +86 -1
- package/src/lower/reachability.js +150 -0
- package/src/lower/walk.js +12 -0
- package/src/parser/declarations.js +83 -15
- package/src/parser/expressions.js +110 -18
- package/src/parser/index.js +2 -1
- package/src/parser/statements.js +38 -22
- package/src/{templ-parser.js → parser/templ.js} +179 -17
- package/src/parser/types.js +70 -9
- package/src/resolver.js +21 -1
- package/src/typechecker/assignability.js +291 -19
- package/src/typechecker/const-fold.js +399 -0
- package/src/typechecker/constants.js +282 -0
- package/src/typechecker/expressions.js +1220 -183
- package/src/typechecker/index.js +659 -92
- package/src/typechecker/init-order.js +426 -0
- package/src/typechecker/operand-text.js +40 -0
- package/src/typechecker/resolve.js +613 -62
- package/src/typechecker/statements.js +212 -20
- package/src/typechecker/stdlib/excluded.js +902 -0
- package/src/typechecker/stdlib/index.js +38 -0
- package/src/typechecker/stdlib/simd.js +65 -15
- package/src/typechecker/termination.js +11 -2
- package/src/typechecker/types.js +268 -514
- package/stdlib/bytes/buffer.go +479 -0
- package/stdlib/bytes/bytes.go +1010 -0
- package/stdlib/bytes/reader.go +161 -0
- package/stdlib/cmp/cmp.go +65 -0
- package/stdlib/context/context.go +115 -0
- package/stdlib/crypto/rand/rand.go +34 -0
- package/stdlib/crypto/sha256/sha256.go +249 -0
- package/stdlib/encoding/base64/base64.go +331 -0
- package/stdlib/encoding/binary/binary.go +332 -0
- package/stdlib/encoding/hex/hex.go +140 -0
- package/stdlib/encoding/json/errors.go +31 -0
- package/stdlib/encoding/json/indent.go +71 -0
- package/stdlib/encoding/json/json.go +62 -0
- package/stdlib/encoding/json/number.go +12 -0
- package/stdlib/encoding/json/stream.go +173 -0
- package/stdlib/errors/errors.go +174 -0
- package/stdlib/fmt/errors.go +110 -0
- package/stdlib/fmt/format.go +732 -0
- package/stdlib/fmt/print.go +1232 -0
- package/stdlib/gom/gom.go +293 -0
- package/stdlib/hash/hash.go +53 -0
- package/stdlib/html/html.go +251 -0
- package/stdlib/internal/fmtrt/fmtrt.go +163 -0
- package/stdlib/internal/jsonrt/dec.go +871 -0
- package/stdlib/internal/jsonrt/enc.go +516 -0
- package/stdlib/internal/jsonrt/indent.go +135 -0
- package/stdlib/internal/jsonrt/number.go +27 -0
- package/stdlib/internal/jsonrt/reader.go +73 -0
- package/stdlib/internal/jsonrt/scanner.go +658 -0
- package/stdlib/io/io.go +502 -0
- package/stdlib/iter/iter.go +18 -0
- package/stdlib/maps/maps.go +131 -0
- package/stdlib/math/bits/bits.go +507 -0
- package/stdlib/math/extra.go +272 -0
- package/stdlib/math/math.go +439 -0
- package/stdlib/math/rand/rand.go +297 -0
- package/stdlib/os/os.go +93 -0
- package/stdlib/path/filepath/filepath.go +47 -0
- package/stdlib/path/path.go +394 -0
- package/stdlib/regexp/regexp.go +231 -0
- package/stdlib/slices/slices.go +526 -0
- package/stdlib/slices/sort.go +209 -0
- package/stdlib/slices/zsort_cmp.go +480 -0
- package/stdlib/slices/zsort_ordered.go +482 -0
- package/stdlib/sort/sort.go +500 -0
- package/stdlib/sort/zsort_func.go +480 -0
- package/stdlib/sort/zsort_iface.go +480 -0
- package/stdlib/strconv/float.go +1699 -0
- package/stdlib/strconv/isprint.go +752 -0
- package/stdlib/strconv/quote.go +526 -0
- package/stdlib/strconv/strconv.go +616 -0
- package/stdlib/strings/builder.go +95 -0
- package/stdlib/strings/reader.go +163 -0
- package/stdlib/strings/replacer.go +102 -0
- package/stdlib/strings/strings.go +807 -0
- package/stdlib/sync/sync.go +268 -0
- package/stdlib/testing/async_js.go +61 -0
- package/stdlib/testing/benchmark.go +297 -0
- package/stdlib/testing/testing.go +523 -0
- package/stdlib/time/duration.go +233 -0
- package/stdlib/time/format.go +745 -0
- package/stdlib/time/parse.go +790 -0
- package/stdlib/time/sleep_js.go +63 -0
- package/stdlib/time/time.go +834 -0
- package/stdlib/unicode/unicode.go +250 -0
- package/stdlib/unicode/utf8/utf8.go +464 -0
- package/src/backend/js/stdlib/base64.js +0 -12
- package/src/backend/js/stdlib/binary.js +0 -102
- package/src/backend/js/stdlib/bits.js +0 -46
- package/src/backend/js/stdlib/builder.js +0 -63
- package/src/backend/js/stdlib/bytes.js +0 -122
- package/src/backend/js/stdlib/errors.js +0 -44
- package/src/backend/js/stdlib/fmt.js +0 -178
- package/src/backend/js/stdlib/gom.js +0 -155
- package/src/backend/js/stdlib/hex.js +0 -17
- package/src/backend/js/stdlib/html.js +0 -20
- package/src/backend/js/stdlib/io.js +0 -137
- package/src/backend/js/stdlib/json.js +0 -972
- package/src/backend/js/stdlib/maps.js +0 -45
- package/src/backend/js/stdlib/math.js +0 -69
- package/src/backend/js/stdlib/os.js +0 -13
- package/src/backend/js/stdlib/path.js +0 -48
- package/src/backend/js/stdlib/rand.js +0 -32
- package/src/backend/js/stdlib/regexp.js +0 -54
- package/src/backend/js/stdlib/sha256.js +0 -12
- package/src/backend/js/stdlib/shared.js +0 -17
- package/src/backend/js/stdlib/simd.js +0 -24
- package/src/backend/js/stdlib/slices.js +0 -77
- package/src/backend/js/stdlib/sort.js +0 -30
- package/src/backend/js/stdlib/strconv.js +0 -74
- package/src/backend/js/stdlib/strings.js +0 -222
- package/src/backend/js/stdlib/sync.js +0 -13
- package/src/backend/js/stdlib/testing.js +0 -34
- package/src/backend/js/stdlib/time.js +0 -128
- package/src/backend/js/stdlib/unicode.js +0 -25
- package/src/backend/js/stdlib/utf8.js +0 -26
- package/src/backend/wasm/const-fold.js +0 -205
- package/src/backend/wasm/emit-stdlib.js +0 -13
- package/src/backend/wasm/stdlib/bytes.js +0 -5039
- package/src/backend/wasm/stdlib/core.js +0 -8903
- package/src/backend/wasm/stdlib/io.js +0 -2161
- package/src/backend/wasm/stdlib/packages.js +0 -2400
- package/src/backend/wasm/stdlib/pure.js +0 -720
- package/src/backend/wasm/stdlib/time.js +0 -1200
- package/src/typechecker/stdlib/core.js +0 -801
- package/src/typechecker/stdlib/extended.js +0 -914
- package/src/typechecker/stdlib.js +0 -22
- /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
- /package/src/{colors.js → cli/colors.js} +0 -0
- /package/src/{dev-server.js → cli/dev-server.js} +0 -0
- /package/src/{minifier.js → cli/minifier.js} +0 -0
- /package/src/{project-config.js → cli/project-config.js} +0 -0
- /package/src/{pwa.js → cli/pwa.js} +0 -0
|
@@ -2,19 +2,89 @@ import {
|
|
|
2
2
|
closureMutates,
|
|
3
3
|
fnMutates,
|
|
4
4
|
nodeMutatesVar,
|
|
5
|
-
nodeWritesVar,
|
|
6
5
|
rootIdentName,
|
|
6
|
+
someChild,
|
|
7
7
|
} from "../../lower/index.js";
|
|
8
|
+
import { hasCallOrReceive } from "../../lower/walk.js";
|
|
9
|
+
import { constIntValue } from "../../typechecker/const-fold.js";
|
|
8
10
|
import {
|
|
11
|
+
BROWSER_GLOBALS,
|
|
12
|
+
compositeLitIndices,
|
|
9
13
|
ERROR,
|
|
14
|
+
embeddedFieldName,
|
|
15
|
+
is64BitInt,
|
|
16
|
+
is64BitUnsigned,
|
|
10
17
|
isAny,
|
|
11
18
|
isComplex,
|
|
12
19
|
isTypedArraySlice,
|
|
13
20
|
typedArrayConstructorForElem,
|
|
21
|
+
typeStr,
|
|
14
22
|
} from "../../typechecker/types.js";
|
|
23
|
+
import { goTypeName } from "../shared/fmt-types.js";
|
|
24
|
+
import { jsNativeCall, NATIVE_HELPERS } from "./natives.js";
|
|
25
|
+
|
|
26
|
+
// True when `node` compares the identifier `name` with nil (`l == nil`): a
|
|
27
|
+
// pointer-receiver method that does so is meant to run on a nil receiver.
|
|
28
|
+
export function comparesToNil(node, name) {
|
|
29
|
+
if (Array.isArray(node)) return node.some((n) => comparesToNil(n, name));
|
|
30
|
+
if (!node || typeof node !== "object") return false;
|
|
31
|
+
if (node.kind === "BinaryExpr" && (node.op === "==" || node.op === "!=")) {
|
|
32
|
+
const isRecv = (e) => e?.kind === "Ident" && e.name === name;
|
|
33
|
+
const isNil = (e) =>
|
|
34
|
+
(e?.kind === "Ident" && e.name === "nil") ||
|
|
35
|
+
(e?.kind === "BasicLit" && e.litKind === "NIL");
|
|
36
|
+
if (
|
|
37
|
+
(isRecv(node.left) && isNil(node.right)) ||
|
|
38
|
+
(isRecv(node.right) && isNil(node.left))
|
|
39
|
+
)
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
return someChild(node, (c) => comparesToNil(c, name));
|
|
43
|
+
}
|
|
15
44
|
|
|
16
45
|
/** @typedef {import('./index.js').CodeGen} CodeGen */
|
|
17
46
|
|
|
47
|
+
// True when type `t` mentions a type parameter anywhere.
|
|
48
|
+
export function mentionsTypeParam(t, depth = 0) {
|
|
49
|
+
if (!t || depth > 16) return false;
|
|
50
|
+
switch (t.kind) {
|
|
51
|
+
case "typeParam":
|
|
52
|
+
return true;
|
|
53
|
+
case "slice":
|
|
54
|
+
case "array":
|
|
55
|
+
return mentionsTypeParam(t.elem, depth + 1);
|
|
56
|
+
case "pointer":
|
|
57
|
+
return mentionsTypeParam(t.base, depth + 1);
|
|
58
|
+
case "map":
|
|
59
|
+
return (
|
|
60
|
+
mentionsTypeParam(t.key, depth + 1) ||
|
|
61
|
+
mentionsTypeParam(t.value, depth + 1)
|
|
62
|
+
);
|
|
63
|
+
case "func":
|
|
64
|
+
return [...t.params, ...t.returns].some((x) =>
|
|
65
|
+
mentionsTypeParam(x, depth + 1),
|
|
66
|
+
);
|
|
67
|
+
case "named":
|
|
68
|
+
return (t.typeArgs ?? []).some((x) => mentionsTypeParam(x, depth + 1));
|
|
69
|
+
default:
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// How an instance of a generic struct is identified at run time: the type
|
|
75
|
+
// arguments' printed form, kept after the zero-value factories in the
|
|
76
|
+
// instance's `__tz` array. Null when an argument is a type parameter (the
|
|
77
|
+
// instantiation is not known inside erased generic code).
|
|
78
|
+
export function instanceId(typeArgs) {
|
|
79
|
+
if (!typeArgs?.length || typeArgs.some((t) => mentionsTypeParam(t)))
|
|
80
|
+
return null;
|
|
81
|
+
return typeArgs.map(typeStr).join(",");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Array literals longer than this that leave most slots zero are emitted as
|
|
85
|
+
// the zero array plus assignments (_genSparseArrayLit).
|
|
86
|
+
const SPARSE_ARRAY_MIN = 32;
|
|
87
|
+
|
|
18
88
|
const STRICT_NUMERIC_TYPES = new Set([
|
|
19
89
|
"float32",
|
|
20
90
|
"int8",
|
|
@@ -27,6 +97,25 @@ const STRICT_NUMERIC_TYPES = new Set([
|
|
|
27
97
|
"uint32",
|
|
28
98
|
]);
|
|
29
99
|
|
|
100
|
+
// Unsigned types narrower than 64 bits: `-x` and `^x` wrap back into range.
|
|
101
|
+
const SMALL_UNSIGNED_WRAP = {
|
|
102
|
+
uint8: "& 0xFF",
|
|
103
|
+
byte: "& 0xFF",
|
|
104
|
+
uint16: "& 0xFFFF",
|
|
105
|
+
uint32: ">>> 0",
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// Signed types narrower than 64 bits: `-x` wraps back into range.
|
|
109
|
+
const SIGNED_NEG_WRAP = {
|
|
110
|
+
int8: "<< 24 >> 24",
|
|
111
|
+
int16: "<< 16 >> 16",
|
|
112
|
+
int32: "| 0",
|
|
113
|
+
rune: "| 0",
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// Integer types whose JS shifts must follow Go's 64-bit semantics.
|
|
117
|
+
const WIDE_INT_NAMES = new Set(["int", "int64", "uint", "uint64", "uintptr"]);
|
|
118
|
+
|
|
30
119
|
const STRICT_OPS = new Set([
|
|
31
120
|
"+",
|
|
32
121
|
"-",
|
|
@@ -41,23 +130,66 @@ const STRICT_OPS = new Set([
|
|
|
41
130
|
">>",
|
|
42
131
|
]);
|
|
43
132
|
|
|
133
|
+
function collectFunctionLabels(node, labels = new Set()) {
|
|
134
|
+
if (!node || typeof node !== "object") return labels;
|
|
135
|
+
if (Array.isArray(node)) {
|
|
136
|
+
for (const item of node) collectFunctionLabels(item, labels);
|
|
137
|
+
return labels;
|
|
138
|
+
}
|
|
139
|
+
if (node.kind === "LabeledStmt" && node.label) labels.add(node.label);
|
|
140
|
+
if (node.kind === "ForStmt" && node.label) labels.add(node.label);
|
|
141
|
+
for (const k of Object.keys(node)) {
|
|
142
|
+
if (k.startsWith("_")) continue;
|
|
143
|
+
collectFunctionLabels(node[k], labels);
|
|
144
|
+
}
|
|
145
|
+
return labels;
|
|
146
|
+
}
|
|
147
|
+
|
|
44
148
|
const NAMED_ZERO_TABLE = {
|
|
45
|
-
"time.Time": [null, "{ _d: new Date(0), _isZero: true }"],
|
|
46
|
-
"strings.Builder": [null, '{ _buf: "" }'],
|
|
47
|
-
"bytes.Buffer": [null, "{ _buf: [] }"],
|
|
48
|
-
"strings.Replacer": [null, "{ _p: [], Replace(s) { return s; } }"],
|
|
49
|
-
"sync.Mutex": ["sync", "__sync_mutex()"],
|
|
50
|
-
"sync.RWMutex": ["sync", "__sync_rwmutex()"],
|
|
51
|
-
"sync.Once": ["sync", "__sync_once()"],
|
|
52
|
-
"sync.WaitGroup": ["sync", "__sync_waitgroup()"],
|
|
53
|
-
"binary.LittleEndian": ["binary", "__binary_le"],
|
|
54
|
-
"encoding/binary.LittleEndian": ["binary", "__binary_le"],
|
|
55
|
-
"binary.BigEndian": ["binary", "__binary_be"],
|
|
56
|
-
"encoding/binary.BigEndian": ["binary", "__binary_be"],
|
|
57
|
-
"base64.Encoding": ["base64", "__base64_std"],
|
|
58
|
-
"encoding/base64.Encoding": ["base64", "__base64_std"],
|
|
59
149
|
"simd.F32x4": [null, "Object.freeze([0, 0, 0, 0])"],
|
|
60
150
|
F32x4: [null, "Object.freeze([0, 0, 0, 0])"],
|
|
151
|
+
"simd.Mask32x4": [null, "Object.freeze([false, false, false, false])"],
|
|
152
|
+
Mask32x4: [null, "Object.freeze([false, false, false, false])"],
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// gofront/simd receiver methods -> runtime helper; the receiver is passed first.
|
|
156
|
+
const SIMD_VECTOR_HELPERS = {
|
|
157
|
+
Add: "__simd_add",
|
|
158
|
+
Sub: "__simd_sub",
|
|
159
|
+
Mul: "__simd_mul",
|
|
160
|
+
Div: "__simd_div",
|
|
161
|
+
Min: "__simd_min",
|
|
162
|
+
Max: "__simd_max",
|
|
163
|
+
MulAdd: "__simd_muladd",
|
|
164
|
+
Neg: "__simd_neg",
|
|
165
|
+
Abs: "__simd_abs",
|
|
166
|
+
Sqrt: "__simd_sqrt",
|
|
167
|
+
Floor: "__simd_floor",
|
|
168
|
+
Ceil: "__simd_ceil",
|
|
169
|
+
Trunc: "__simd_trunc",
|
|
170
|
+
RoundToEven: "__simd_nearest",
|
|
171
|
+
Dot3: "__simd_dot3",
|
|
172
|
+
Dot4: "__simd_dot4",
|
|
173
|
+
Sum: "__simd_sum",
|
|
174
|
+
WithLane: "__simd_with_lane",
|
|
175
|
+
Swizzle: "__simd_swizzle",
|
|
176
|
+
Equal: "__simd_eq",
|
|
177
|
+
NotEqual: "__simd_ne",
|
|
178
|
+
Less: "__simd_lt",
|
|
179
|
+
LessEqual: "__simd_le",
|
|
180
|
+
Greater: "__simd_gt",
|
|
181
|
+
GreaterEqual: "__simd_ge",
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const SIMD_MASK_HELPERS = {
|
|
185
|
+
And: "__simd_mask_and",
|
|
186
|
+
Or: "__simd_mask_or",
|
|
187
|
+
Xor: "__simd_mask_xor",
|
|
188
|
+
AndNot: "__simd_mask_andnot",
|
|
189
|
+
Not: "__simd_mask_not",
|
|
190
|
+
Any: "__simd_mask_any",
|
|
191
|
+
All: "__simd_mask_all",
|
|
192
|
+
Bits: "__simd_mask_bits",
|
|
61
193
|
};
|
|
62
194
|
|
|
63
195
|
// Splits a generated JS lvalue at its outermost trailing access:
|
|
@@ -95,83 +227,13 @@ function splitLastAccess(js) {
|
|
|
95
227
|
|
|
96
228
|
// Namespace constants: pkg.Field → JS literal
|
|
97
229
|
const NS_CONSTANTS = {
|
|
98
|
-
math: {
|
|
99
|
-
Pi: "Math.PI",
|
|
100
|
-
E: "Math.E",
|
|
101
|
-
Phi: "1.618033988749895",
|
|
102
|
-
Sqrt2: "Math.SQRT2",
|
|
103
|
-
SqrtE: "1.6487212707001282",
|
|
104
|
-
SqrtPi: "1.7724538509055159",
|
|
105
|
-
SqrtPhi: "1.272019649514069",
|
|
106
|
-
Ln2: "Math.LN2",
|
|
107
|
-
Log2E: "Math.LOG2E",
|
|
108
|
-
Ln10: "Math.LN10",
|
|
109
|
-
Log10E: "Math.LOG10E",
|
|
110
|
-
MaxFloat32: "3.4028234663852886e38",
|
|
111
|
-
SmallestNonzeroFloat32: "1.401298464324817e-45",
|
|
112
|
-
MaxFloat64: "Number.MAX_VALUE",
|
|
113
|
-
SmallestNonzeroFloat64: "Number.MIN_VALUE",
|
|
114
|
-
MaxInt: "Number.MAX_SAFE_INTEGER",
|
|
115
|
-
MinInt: "Number.MIN_SAFE_INTEGER",
|
|
116
|
-
MaxInt8: "127",
|
|
117
|
-
MinInt8: "-128",
|
|
118
|
-
MaxInt16: "32767",
|
|
119
|
-
MinInt16: "-32768",
|
|
120
|
-
MaxInt32: "2147483647",
|
|
121
|
-
MinInt32: "-2147483648",
|
|
122
|
-
MaxInt64: "9223372036854775807",
|
|
123
|
-
MinInt64: "-9223372036854775808",
|
|
124
|
-
MaxUint8: "255",
|
|
125
|
-
MaxUint16: "65535",
|
|
126
|
-
MaxUint32: "4294967295",
|
|
127
|
-
MaxUint: "18446744073709551615",
|
|
128
|
-
MaxUint64: "18446744073709551615",
|
|
129
|
-
},
|
|
130
|
-
strconv: {
|
|
131
|
-
ErrSyntax: "__strconv_err_syntax",
|
|
132
|
-
ErrRange: "__strconv_err_range",
|
|
133
|
-
},
|
|
134
|
-
path: {
|
|
135
|
-
ErrBadPattern: "__path_err_bad_pattern",
|
|
136
|
-
},
|
|
137
|
-
filepath: {
|
|
138
|
-
ErrBadPattern: "__path_err_bad_pattern",
|
|
139
|
-
},
|
|
140
|
-
io: {
|
|
141
|
-
EOF: "__io_eof",
|
|
142
|
-
Discard:
|
|
143
|
-
"{ WriteString(s) { return [s.length, null]; }, Write(b) { return [b.length, null]; } }",
|
|
144
|
-
},
|
|
145
|
-
sha256: {
|
|
146
|
-
Size: "32",
|
|
147
|
-
BlockSize: "64",
|
|
148
|
-
},
|
|
149
|
-
hex: {
|
|
150
|
-
ErrLength: '__error("encoding/hex: odd length hex string")',
|
|
151
|
-
},
|
|
152
|
-
base64: {
|
|
153
|
-
StdPadding: "61",
|
|
154
|
-
NoPadding: "-1",
|
|
155
|
-
StdEncoding: "__base64_std",
|
|
156
|
-
URLEncoding: "__base64_url",
|
|
157
|
-
RawStdEncoding: "__base64_raw_std",
|
|
158
|
-
RawURLEncoding: "__base64_raw_url",
|
|
159
|
-
},
|
|
160
|
-
binary: {
|
|
161
|
-
LittleEndian: "__binary_le",
|
|
162
|
-
BigEndian: "__binary_be",
|
|
163
|
-
},
|
|
164
|
-
rand: {
|
|
165
|
-
Reader: "__crypto_rand_reader",
|
|
166
|
-
},
|
|
167
|
-
os: { Args: "process.argv" },
|
|
168
230
|
time: {
|
|
169
|
-
Nanosecond: "
|
|
170
|
-
Microsecond: "
|
|
171
|
-
Millisecond: "
|
|
172
|
-
Second: "
|
|
173
|
-
Minute: "
|
|
174
|
-
Hour: "
|
|
231
|
+
Nanosecond: "1n",
|
|
232
|
+
Microsecond: "1000n",
|
|
233
|
+
Millisecond: "1000000n",
|
|
234
|
+
Second: "1000000000n",
|
|
235
|
+
Minute: "60000000000n",
|
|
236
|
+
Hour: "3600000000000n",
|
|
175
237
|
RFC3339: '"2006-01-02T15:04:05Z07:00"',
|
|
176
238
|
RFC3339Nano: '"2006-01-02T15:04:05.999999999Z07:00"',
|
|
177
239
|
DateOnly: '"2006-01-02"',
|
|
@@ -199,7 +261,6 @@ const NS_CONSTANTS = {
|
|
|
199
261
|
Friday: "5",
|
|
200
262
|
Saturday: "6",
|
|
201
263
|
},
|
|
202
|
-
utf8: { RuneError: "0xFFFD", MaxRune: "0x10FFFF", UTFMax: "4" },
|
|
203
264
|
};
|
|
204
265
|
|
|
205
266
|
// JavaScript precedence of the operators emitted for Go binary expressions.
|
|
@@ -258,6 +319,15 @@ const INT_TYPE_NAMES = new Set([
|
|
|
258
319
|
"rune",
|
|
259
320
|
]);
|
|
260
321
|
|
|
322
|
+
// Zero values with a shared factory in HELPER_GENERICS.
|
|
323
|
+
const SHARED_ZERO_FACTORIES = {
|
|
324
|
+
null: "__zn",
|
|
325
|
+
0: "__z0",
|
|
326
|
+
'""': "__zs",
|
|
327
|
+
false: "__zf",
|
|
328
|
+
"0n": "__z64",
|
|
329
|
+
};
|
|
330
|
+
|
|
261
331
|
// Zero-value JS literals for basic Go types.
|
|
262
332
|
const ZERO_FOR_BASIC = {
|
|
263
333
|
int: "0",
|
|
@@ -265,11 +335,11 @@ const ZERO_FOR_BASIC = {
|
|
|
265
335
|
int8: "0",
|
|
266
336
|
int16: "0",
|
|
267
337
|
int32: "0",
|
|
268
|
-
int64: "
|
|
338
|
+
int64: "0n",
|
|
269
339
|
uint8: "0",
|
|
270
340
|
uint16: "0",
|
|
271
341
|
uint32: "0",
|
|
272
|
-
uint64: "
|
|
342
|
+
uint64: "0n",
|
|
273
343
|
uintptr: "0",
|
|
274
344
|
float32: "0",
|
|
275
345
|
float64: "0",
|
|
@@ -282,24 +352,47 @@ const ZERO_FOR_BASIC = {
|
|
|
282
352
|
};
|
|
283
353
|
|
|
284
354
|
// Set of all numeric type names (for type-assertion codegen).
|
|
285
|
-
const TYPEOF_NUMBER_NAMES = new Set([
|
|
355
|
+
const TYPEOF_NUMBER_NAMES = new Set([
|
|
356
|
+
...[...INT_TYPE_NAMES].filter((n) => n !== "int64" && n !== "uint64"),
|
|
357
|
+
"float32",
|
|
358
|
+
"float64",
|
|
359
|
+
]);
|
|
286
360
|
|
|
287
361
|
// Builtin function codegen — keyed by builtin name, value is (self, expr) => string.
|
|
288
362
|
const BUILTIN_GEN = {
|
|
289
363
|
append: (s, e) => s.genAppend(e),
|
|
290
364
|
len: (s, e) => s._genBuiltinLen(e),
|
|
291
|
-
cap: (s, e) =>
|
|
365
|
+
cap: (s, e) => {
|
|
366
|
+
if (e._constLen != null) return s._genConstLen(e);
|
|
367
|
+
s.useHelper("slice");
|
|
368
|
+
return `__cap(${s.genExpr(e.args[0])})`;
|
|
369
|
+
},
|
|
292
370
|
make: (s, e) => s.genMake(e),
|
|
293
371
|
delete: (s, e) => {
|
|
294
372
|
const [m, k] = e.args.map((a) => s.genExpr(a));
|
|
295
373
|
return `(delete ${m}[${k}])`;
|
|
296
374
|
},
|
|
297
375
|
copy: (s, e) => {
|
|
298
|
-
const
|
|
299
|
-
|
|
376
|
+
const dst = s.genExpr(e.args[0]);
|
|
377
|
+
const src = s._genSliceSource(e.args[1]);
|
|
378
|
+
// Array and struct elements are values: the copied elements must not
|
|
379
|
+
// alias src.
|
|
380
|
+
const t = e.args[1]._type;
|
|
381
|
+
const elem = (t?.kind === "named" ? t.underlying : t)?.elem;
|
|
382
|
+
const clone = s._aggregateBase(elem) ? s._cloneJs(elem, "__e") : "__e";
|
|
383
|
+
const items =
|
|
384
|
+
clone === "__e"
|
|
385
|
+
? "(__cs.slice ? __cs.slice(0, n) : __cs.subarray(0, n))"
|
|
386
|
+
: `__cs.slice(0, n).map((__e) => ${clone})`;
|
|
387
|
+
return `((__cd, __cs) => { if (!__cd || !__cs) return 0; const n = Math.min(__cd.length, __cs.length); if (__cd.set) { __cd.set(__cs.length === n ? __cs : (__cs.subarray ? __cs.subarray(0, n) : __cs.slice(0, n))); } else { __cd.splice(0, n, ...${items}); } return n; })(${dst}, ${src})`;
|
|
300
388
|
},
|
|
301
389
|
new: (s, e) => {
|
|
302
390
|
const arg = e.args[0];
|
|
391
|
+
const base = e._type?.kind === "pointer" ? e._type.base : null;
|
|
392
|
+
if (base?.kind === "named" && base.typeArgs?.length) {
|
|
393
|
+
const inst = s._genericStructNew(base.name, base.typeArgs);
|
|
394
|
+
if (inst) return inst;
|
|
395
|
+
}
|
|
303
396
|
const typeName = arg.name ?? s.getTypeName(arg);
|
|
304
397
|
if (typeName && s.structNames.has(typeName)) {
|
|
305
398
|
return `new ${typeName}()`;
|
|
@@ -309,25 +402,36 @@ const BUILTIN_GEN = {
|
|
|
309
402
|
const name = t.name ?? (t.kind === "named" ? t.name : null);
|
|
310
403
|
if (name) return `new ${name}()`;
|
|
311
404
|
}
|
|
312
|
-
|
|
405
|
+
// `new([4]byte)`: the type argument is wrapped in a TypeExpr node.
|
|
406
|
+
const zero = s.zeroValueForExpr(arg.kind === "TypeExpr" ? arg.type : arg);
|
|
407
|
+
// Erased type parameter: a struct instantiation is its own pointer.
|
|
408
|
+
if (base?.kind === "typeParam")
|
|
409
|
+
return `((__v) => (${s._typeParamIsStructJs(base.name)} ? __v : { value: __v }))(${zero})`;
|
|
410
|
+
return `{ value: ${zero} }`;
|
|
313
411
|
},
|
|
314
412
|
// `print` writes without a trailing newline (Go semantics); the wasm glue
|
|
315
413
|
// buffers the same way, so output parity holds across backends.
|
|
316
414
|
print: (s, e) =>
|
|
317
|
-
`((__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("
|
|
415
|
+
`((__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(""))`,
|
|
318
416
|
println: (s, e) =>
|
|
319
417
|
`console.log(${e.args.map((a) => s.genExpr(a)).join(", ")})`,
|
|
320
|
-
panic: (s, e) =>
|
|
418
|
+
panic: (s, e) => {
|
|
419
|
+
const arg = s.genExpr(e.args[0]);
|
|
420
|
+
s.useHelper("panicRt");
|
|
421
|
+
return `__gopanicv(${arg})`;
|
|
422
|
+
},
|
|
321
423
|
recover: (s) => {
|
|
322
424
|
s.useHelper("panicRt");
|
|
323
425
|
return `__recover(${s._recoverOkInScope ? "__recoverOk" : "false"})`;
|
|
324
426
|
},
|
|
325
427
|
error: (s, e) => {
|
|
428
|
+
// `error(x)` of an error (or nil) is the conversion: x itself.
|
|
429
|
+
if (!s._isStringExprType(e.args[0]?._type)) return s.genExpr(e.args[0]);
|
|
326
430
|
s.useHelper("error");
|
|
327
431
|
return `__error(${s.genExpr(e.args[0])})`;
|
|
328
432
|
},
|
|
329
|
-
min: (s, e) =>
|
|
330
|
-
max: (s, e) =>
|
|
433
|
+
min: (s, e) => genMinMax(s, e, "min", "<"),
|
|
434
|
+
max: (s, e) => genMinMax(s, e, "max", ">"),
|
|
331
435
|
clear: (s, e) => s._genBuiltinClear(e),
|
|
332
436
|
complex: (s, e) =>
|
|
333
437
|
`{ re: ${s.genExpr(e.args[0])}, im: ${s.genExpr(e.args[1])} }`,
|
|
@@ -335,6 +439,65 @@ const BUILTIN_GEN = {
|
|
|
335
439
|
imag: (s, e) => `(${s.genExpr(e.args[0])}).im`,
|
|
336
440
|
};
|
|
337
441
|
|
|
442
|
+
// Math.min/max coerce strings to NaN, so string operands fold with `<`/`>`.
|
|
443
|
+
// A type parameter may be a string or a number: pick the form at run time
|
|
444
|
+
// (Math.min/max keeps NaN propagation and -0 for floats).
|
|
445
|
+
function genMinMax(s, e, fn, cmp) {
|
|
446
|
+
const is64 = s._is64BitInt(e._type);
|
|
447
|
+
const args = e.args.map((a) => {
|
|
448
|
+
const js = s.genExpr(a);
|
|
449
|
+
if (
|
|
450
|
+
is64 &&
|
|
451
|
+
a._type?.kind === "untyped" &&
|
|
452
|
+
!js.endsWith("n") &&
|
|
453
|
+
/^-?\d+$/.test(js)
|
|
454
|
+
) {
|
|
455
|
+
return `${js}n`;
|
|
456
|
+
}
|
|
457
|
+
return js;
|
|
458
|
+
});
|
|
459
|
+
const fold = (xs) => `${xs}.reduce((a, b) => (b ${cmp} a ? b : a))`;
|
|
460
|
+
if (args.length === 1) return args[0];
|
|
461
|
+
if (e._type?.kind === "typeParam")
|
|
462
|
+
return `((...v) => (typeof v[0] === "string" || typeof v[0] === "bigint" ? ${fold("v")} : Math.${fn}(...v)))(${args.join(", ")})`;
|
|
463
|
+
if (is64 || s._isStringExprType(e._type)) return fold(`[${args.join(", ")}]`);
|
|
464
|
+
return `Math.${fn}(${args.join(", ")})`;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Expression kinds whose folded constant value (`_const`, attached by the
|
|
468
|
+
// checker — see typechecker/const-fold.js) is emitted as a literal. Literals
|
|
469
|
+
// and constant names already emit their value.
|
|
470
|
+
const FOLDED_CONST_KINDS = new Set([
|
|
471
|
+
"UnaryExpr",
|
|
472
|
+
"BinaryExpr",
|
|
473
|
+
"ParenExpr",
|
|
474
|
+
"CallExpr",
|
|
475
|
+
"TypeConversion",
|
|
476
|
+
]);
|
|
477
|
+
|
|
478
|
+
const JS_SAFE_INT = 2n ** 53n;
|
|
479
|
+
|
|
480
|
+
// `-1.0` already emits its exact value; keep it as written.
|
|
481
|
+
const isSignedLit = (e) =>
|
|
482
|
+
e.kind === "UnaryExpr" &&
|
|
483
|
+
(e.op === "-" || e.op === "+") &&
|
|
484
|
+
e.operand?.kind === "BasicLit";
|
|
485
|
+
|
|
486
|
+
// Negative literals are parenthesised so they splice safely into operators.
|
|
487
|
+
const signed = (n, text) => (n < 0 || Object.is(n, -0) ? `(${text})` : text);
|
|
488
|
+
|
|
489
|
+
// JS literal per folded constant kind; null when JS cannot represent it.
|
|
490
|
+
const CONST_LITS = {
|
|
491
|
+
STRING: (v) => JSON.stringify(v),
|
|
492
|
+
BOOL: (v) => (v ? "true" : "false"),
|
|
493
|
+
FLOAT: (v) =>
|
|
494
|
+
Number.isFinite(v) ? signed(v, Object.is(v, -0) ? "-0" : String(v)) : null,
|
|
495
|
+
INT: (v) => {
|
|
496
|
+
const n = Number(v);
|
|
497
|
+
return Number.isFinite(n) ? signed(n, BigInt(n).toString()) : null;
|
|
498
|
+
},
|
|
499
|
+
};
|
|
500
|
+
|
|
338
501
|
// Method-name dispatch for genExpr — string values add no static call edges.
|
|
339
502
|
const EXPR_GEN_DELEGATE = {
|
|
340
503
|
UnaryExpr: "_genUnaryExpr",
|
|
@@ -350,46 +513,159 @@ const EXPR_GEN_DELEGATE = {
|
|
|
350
513
|
};
|
|
351
514
|
|
|
352
515
|
/** @type {ThisType<CodeGen>} */
|
|
516
|
+
const stdIdent = (name, type) => ({ kind: "Ident", name, _type: type });
|
|
517
|
+
|
|
518
|
+
// True when a func type mentions a type parameter (a generic stdlib function).
|
|
519
|
+
function hasTypeParamType(t) {
|
|
520
|
+
return JSON.stringify([t.params, t.returns], (k, v) =>
|
|
521
|
+
k.startsWith("_") || k === "underlying" ? undefined : v,
|
|
522
|
+
).includes('"typeParam"');
|
|
523
|
+
}
|
|
524
|
+
|
|
353
525
|
export const expressionGenMethods = {
|
|
354
526
|
genExpr(expr) {
|
|
527
|
+
if (expr._asComplex && !expr._complexWrapping) {
|
|
528
|
+
expr._complexWrapping = true;
|
|
529
|
+
try {
|
|
530
|
+
return `{ re: ${this.genExpr(expr)}, im: 0 }`;
|
|
531
|
+
} finally {
|
|
532
|
+
expr._complexWrapping = false;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
if (expr._ifaceKeyDyn && !expr._ifaceKeying) {
|
|
536
|
+
// A key typed by a type parameter: encode it for the instantiated type.
|
|
537
|
+
const f = this._typeParamZeroFactory(expr._ifaceKeyParam);
|
|
538
|
+
if (f) {
|
|
539
|
+
expr._ifaceKeying = true;
|
|
540
|
+
try {
|
|
541
|
+
this.useHelper("structKey");
|
|
542
|
+
const box = expr._ifaceKeyDyn === "box" ? ", true" : "";
|
|
543
|
+
return `__ikt(${f}, ${this.genExpr(expr)}${box})`;
|
|
544
|
+
} finally {
|
|
545
|
+
expr._ifaceKeying = false;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
if (expr._ifaceKey && !expr._ifaceKeying) {
|
|
550
|
+
// A string key is its own property key; any other dynamic value is
|
|
551
|
+
// encoded with its type (see HELPER_STRUCT_KEY).
|
|
552
|
+
const plain =
|
|
553
|
+
!this._isNamedBoxKind(expr) && this._isStringExprType(expr._type);
|
|
554
|
+
if (!plain) {
|
|
555
|
+
expr._ifaceKeying = true;
|
|
556
|
+
try {
|
|
557
|
+
this.useHelper("structKey");
|
|
558
|
+
const tag = this._ifaceKeyTag(expr._ifaceKeyType);
|
|
559
|
+
return `__ik(${this.genExpr(expr)}${tag ? `, ${JSON.stringify(tag)}` : ""})`;
|
|
560
|
+
} finally {
|
|
561
|
+
expr._ifaceKeying = false;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
if (expr._mapKeyEnc && !expr._mapKeying) {
|
|
566
|
+
// Array, anonymous struct and pointer keys: see HELPER_STRUCT_KEY.
|
|
567
|
+
expr._mapKeying = true;
|
|
568
|
+
try {
|
|
569
|
+
this.useHelper("structKey");
|
|
570
|
+
const enc = expr._mapKeyEnc === "pk" ? "__pk" : "__sk";
|
|
571
|
+
return `${enc}(${this.genExpr(expr)})`;
|
|
572
|
+
} finally {
|
|
573
|
+
expr._mapKeying = false;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
if (
|
|
577
|
+
expr._ifaceSrc?.kind === "pointer" &&
|
|
578
|
+
expr._ifaceSrc.base?.kind === "typeParam" &&
|
|
579
|
+
!expr._pbsWrapping
|
|
580
|
+
) {
|
|
581
|
+
// `&v` of a type parameter's variable is a box; a struct behind it
|
|
582
|
+
// is its own pointer.
|
|
583
|
+
expr._pbsWrapping = true;
|
|
584
|
+
try {
|
|
585
|
+
this.useHelper("ifaceBox");
|
|
586
|
+
return `__pbs(${this.genExpr(expr)})`;
|
|
587
|
+
} finally {
|
|
588
|
+
expr._pbsWrapping = false;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
if (
|
|
592
|
+
expr._ifaceSrc?.kind === "typeParam" &&
|
|
593
|
+
!expr._tpBoxing &&
|
|
594
|
+
!expr._ifaceKeyDyn &&
|
|
595
|
+
this._typeParamZeroFactory(expr._ifaceSrc.name)
|
|
596
|
+
) {
|
|
597
|
+
// A type parameter's value entering an interface is boxed and
|
|
598
|
+
// tagged by the instantiated type (see `__ti`).
|
|
599
|
+
expr._tpBoxing = true;
|
|
600
|
+
try {
|
|
601
|
+
const f = this._typeParamZeroFactory(expr._ifaceSrc.name);
|
|
602
|
+
return `(${f}.b ?? ${f}.fb ?? ((__v) => __v))(${this.genExpr(expr)})`;
|
|
603
|
+
} finally {
|
|
604
|
+
expr._tpBoxing = false;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
355
607
|
if (this._isNamedIfaceBox(expr)) return this._genNamedIfaceBoxExpr(expr);
|
|
608
|
+
if (expr._ifaceSrc && !expr._ityTagging && !this._isNamedBoxKind(expr)) {
|
|
609
|
+
const tag = this._dynTypeTag(expr._ifaceSrc);
|
|
610
|
+
if (tag) {
|
|
611
|
+
this.useHelper("ifaceBox");
|
|
612
|
+
expr._ityTagging = true;
|
|
613
|
+
try {
|
|
614
|
+
const ptr = expr._ifaceSrc.kind === "pointer" ? ", true" : "";
|
|
615
|
+
return `__ity(${this.genExpr(expr)}, ${JSON.stringify(tag)}${ptr})`;
|
|
616
|
+
} finally {
|
|
617
|
+
expr._ityTagging = false;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
356
621
|
if (expr._ifaceBox && !expr._ifaceBoxing && !this._isNamedBoxKind(expr)) {
|
|
357
622
|
expr._ifaceBoxing = true;
|
|
358
623
|
try {
|
|
359
624
|
this.useHelper("ifaceBox");
|
|
360
|
-
const
|
|
361
|
-
expr
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
: expr._ifaceBox === "map"
|
|
368
|
-
? "__ifm"
|
|
369
|
-
: expr._ifaceBox === "slice"
|
|
370
|
-
? "__ifs"
|
|
371
|
-
: "__ifv";
|
|
372
|
-
const extra = expr._ifacePtrFields?.length
|
|
373
|
-
? `, ${JSON.stringify(expr._ifacePtrFields)}`
|
|
374
|
-
: "";
|
|
375
|
-
return `${fn}(${this.genExpr(expr)}${extra})`;
|
|
625
|
+
const boxed = this._ifaceBoxWrap(
|
|
626
|
+
this.genExpr(expr),
|
|
627
|
+
expr._ifaceBox,
|
|
628
|
+
expr._ifacePtrFields,
|
|
629
|
+
expr.kind === "CompositeLit",
|
|
630
|
+
);
|
|
631
|
+
return this._typedNilWrap(boxed, expr);
|
|
376
632
|
} finally {
|
|
377
633
|
expr._ifaceBoxing = false;
|
|
378
634
|
}
|
|
379
635
|
}
|
|
636
|
+
const folded = this._genFoldedConst(expr);
|
|
637
|
+
if (folded !== null) return folded;
|
|
380
638
|
switch (expr.kind) {
|
|
381
639
|
case "BasicLit":
|
|
382
640
|
if (expr.litKind === "STRING") return JSON.stringify(expr.value);
|
|
641
|
+
if (
|
|
642
|
+
(this._is64BitInt(expr._type) || expr._is64Bit) &&
|
|
643
|
+
(expr.litKind === "INT" || expr._const?.k === "INT")
|
|
644
|
+
) {
|
|
645
|
+
return `${expr.value}n`;
|
|
646
|
+
}
|
|
647
|
+
if (expr._const?.k === "INT")
|
|
648
|
+
this._warnConstPrecision(expr._const, expr);
|
|
383
649
|
return expr.value; // int, float, bool, nil(null)
|
|
384
650
|
case "ImagLit":
|
|
385
651
|
return `{ re: 0, im: ${expr.value} }`;
|
|
386
|
-
case "Ident":
|
|
652
|
+
case "Ident": {
|
|
387
653
|
if (this._boxedVars.has(expr.name) && !expr._isAddressOf)
|
|
388
654
|
return `${expr.name}.value`;
|
|
389
|
-
|
|
655
|
+
// A named untyped constant used as an int64 operand: its
|
|
656
|
+
// declaration holds a Number, so emit the exact BigInt.
|
|
657
|
+
const lit64 =
|
|
658
|
+
expr._is64Bit &&
|
|
659
|
+
expr._const &&
|
|
660
|
+
(expr._type?.kind === "untyped" || this._is64BitInt(expr._type))
|
|
661
|
+
? this._gen64BitFoldedConst(expr, expr._const)
|
|
662
|
+
: null;
|
|
663
|
+
return lit64 ?? expr.name;
|
|
664
|
+
}
|
|
390
665
|
case "AwaitExpr":
|
|
391
666
|
return `await ${this.genExpr(expr.expr)}`;
|
|
392
667
|
case "InstantiationExpr":
|
|
668
|
+
return this._genInstantiationExpr(expr);
|
|
393
669
|
case "RangeExpr":
|
|
394
670
|
return this.genExpr(expr.expr);
|
|
395
671
|
default: {
|
|
@@ -400,6 +676,82 @@ export const expressionGenMethods = {
|
|
|
400
676
|
}
|
|
401
677
|
},
|
|
402
678
|
|
|
679
|
+
_gen64BitFoldedConst(expr, val) {
|
|
680
|
+
if (
|
|
681
|
+
(this._is64BitInt(expr._type) || expr._is64Bit) &&
|
|
682
|
+
(val.k === "INT" || expr._intConst)
|
|
683
|
+
) {
|
|
684
|
+
const n = constIntValue(val);
|
|
685
|
+
if (n !== null) return n < 0n ? `(${n}n)` : `${n}n`;
|
|
686
|
+
}
|
|
687
|
+
return null;
|
|
688
|
+
},
|
|
689
|
+
|
|
690
|
+
// The literal for a folded constant expression (`1 << 40` →
|
|
691
|
+
// `1099511627776`), or null. Integers outside ±2⁵³ emit the nearest
|
|
692
|
+
// Number and a precision warning.
|
|
693
|
+
_genFoldedConst(expr) {
|
|
694
|
+
const val = expr._const;
|
|
695
|
+
if (!val) return null;
|
|
696
|
+
// Constant names emit themselves, but an integer use of one still warns
|
|
697
|
+
// (an integer constant declared in this package warned at its declaration).
|
|
698
|
+
if (expr.kind === "Ident" || expr.kind === "SelectorExpr") {
|
|
699
|
+
const declared = expr.kind === "Ident" && val.k === "INT";
|
|
700
|
+
if (expr._intConst && !declared) this._warnConstPrecision(val, expr);
|
|
701
|
+
return null;
|
|
702
|
+
}
|
|
703
|
+
if (!FOLDED_CONST_KINDS.has(expr.kind) || isSignedLit(expr)) return null;
|
|
704
|
+
const lit64 = this._gen64BitFoldedConst(expr, val);
|
|
705
|
+
if (lit64 !== null) return lit64;
|
|
706
|
+
if (val.k === "INT" || expr._intConst) this._warnConstPrecision(val, expr);
|
|
707
|
+
return CONST_LITS[val.k]?.(val.v) ?? null;
|
|
708
|
+
},
|
|
709
|
+
|
|
710
|
+
// Warns when an integer constant outside ±2⁵³ has no exact Number (once
|
|
711
|
+
// per node per generate run: some forms generate the same node more than
|
|
712
|
+
// once, and cached ASTs are generated again by later compiles).
|
|
713
|
+
_warnConstPrecision(val, expr) {
|
|
714
|
+
if (this._is64BitInt(expr?._type) || expr?._is64Bit || expr?._floatConst)
|
|
715
|
+
return;
|
|
716
|
+
const i = constIntValue(val);
|
|
717
|
+
if (i === null || (i <= JS_SAFE_INT && i >= -JS_SAFE_INT)) return;
|
|
718
|
+
if (BigInt(Number(i)) !== i && !this._precisionWarned.has(expr)) {
|
|
719
|
+
this._precisionWarned.add(expr);
|
|
720
|
+
this._warn(`constant ${i} exceeds JS Number precision`, expr);
|
|
721
|
+
}
|
|
722
|
+
},
|
|
723
|
+
|
|
724
|
+
// `js` boxed as `box` (a checker _ifaceBoxMeta kind) for an interface.
|
|
725
|
+
// `fresh` marks a value nothing else refers to (a composite literal), which
|
|
726
|
+
// a struct box then takes over instead of copying.
|
|
727
|
+
_ifaceBoxWrap(js, box, ptrFields, fresh = false) {
|
|
728
|
+
this.useHelper("ifaceBox");
|
|
729
|
+
const fn =
|
|
730
|
+
{
|
|
731
|
+
ptr: "__ifp",
|
|
732
|
+
ptr_slice: "__ifps",
|
|
733
|
+
ptr_map: "__ifpm",
|
|
734
|
+
map: "__ifm",
|
|
735
|
+
slice: "__ifs",
|
|
736
|
+
}[box] ?? "__ifv";
|
|
737
|
+
const fields = ptrFields?.length ? JSON.stringify(ptrFields) : "";
|
|
738
|
+
if (fn === "__ifv" && fresh)
|
|
739
|
+
return `${fn}(${js}, ${fields || "null"}, true)`;
|
|
740
|
+
return `${fn}(${js}${fields ? `, ${fields}` : ""})`;
|
|
741
|
+
},
|
|
742
|
+
|
|
743
|
+
// A nil `*T` entering an interface is a typed nil: it keeps T's methods
|
|
744
|
+
// (callable with a nil receiver) and differs from the nil interface.
|
|
745
|
+
_typedNilWrap(js, expr) {
|
|
746
|
+
if (expr._ifaceBox !== "ptr") return js;
|
|
747
|
+
const src = expr._ifaceSrc;
|
|
748
|
+
const base = src?.kind === "pointer" ? src.base : null;
|
|
749
|
+
if (base?.kind !== "named" || base.underlying?.kind !== "struct") return js;
|
|
750
|
+
const cls = this._dequalify(base.name).replace(/\[.*$/, "");
|
|
751
|
+
if (!this.structNames.has(cls)) return js;
|
|
752
|
+
return `__ifn(${js}, ${cls})`;
|
|
753
|
+
},
|
|
754
|
+
|
|
403
755
|
_isNamedBoxKind(expr) {
|
|
404
756
|
return expr._ifaceBox === "named" || expr._ifaceBox === "named_ptr";
|
|
405
757
|
},
|
|
@@ -444,6 +796,29 @@ export const expressionGenMethods = {
|
|
|
444
796
|
return this._isStructType(pt.base);
|
|
445
797
|
},
|
|
446
798
|
|
|
799
|
+
_isScalarPointerType(t) {
|
|
800
|
+
if (!t) return false;
|
|
801
|
+
const pt =
|
|
802
|
+
t.kind === "named"
|
|
803
|
+
? (t.underlying ?? this.checker?.types.get(t.name)?.underlying)
|
|
804
|
+
: t;
|
|
805
|
+
if (pt?.kind !== "pointer") return false;
|
|
806
|
+
return !this._isStructType(pt.base);
|
|
807
|
+
},
|
|
808
|
+
|
|
809
|
+
_genPointerEquality(expr) {
|
|
810
|
+
if (expr.op !== "==" && expr.op !== "!=") return null;
|
|
811
|
+
if (
|
|
812
|
+
!this._isScalarPointerType(expr.left._type) &&
|
|
813
|
+
!this._isScalarPointerType(expr.right._type)
|
|
814
|
+
) {
|
|
815
|
+
return null;
|
|
816
|
+
}
|
|
817
|
+
this.useHelper("ptreq");
|
|
818
|
+
const cmp = `__ptreq(${this.genExpr(expr.left)}, ${this.genExpr(expr.right)})`;
|
|
819
|
+
return expr.op === "==" ? cmp : `!${cmp}`;
|
|
820
|
+
},
|
|
821
|
+
|
|
447
822
|
_isStructExpr(expr) {
|
|
448
823
|
if (!expr) return false;
|
|
449
824
|
if (expr.kind === "CompositeLit") {
|
|
@@ -473,14 +848,31 @@ export const expressionGenMethods = {
|
|
|
473
848
|
// Pseudo type object for an AST type node (used where no checker type is available).
|
|
474
849
|
_typeFromNode(node) {
|
|
475
850
|
if (!node) return null;
|
|
476
|
-
if (node.kind === "TypeName"
|
|
477
|
-
|
|
851
|
+
if (node.kind === "TypeName" || node.kind === "SelectorExpr") {
|
|
852
|
+
// `pkg.S` of a bundled package names the class `S`.
|
|
853
|
+
const name = this.getTypeName(node);
|
|
854
|
+
if (name && this.structNames.has(name))
|
|
855
|
+
return { kind: "named", name, underlying: { kind: "struct" } };
|
|
856
|
+
}
|
|
478
857
|
if (node.kind === "ArrayType")
|
|
479
858
|
return { kind: "array", elem: this._typeFromNode(node.elem) };
|
|
480
859
|
return null;
|
|
481
860
|
},
|
|
482
861
|
|
|
862
|
+
// A value of type-parameter type is copied at run time when its type
|
|
863
|
+
// argument is a struct or array (decision: runtime check on the zero-value
|
|
864
|
+
// factory, so one erased function body serves every instantiation).
|
|
865
|
+
_typeParamClone(t, js) {
|
|
866
|
+
if (t?.kind !== "typeParam") return null;
|
|
867
|
+
const factory = this._typeParamZeroFactory(t.name);
|
|
868
|
+
if (!factory) return null;
|
|
869
|
+
this.useHelper("vclone");
|
|
870
|
+
return `__vclone(${factory}, ${js})`;
|
|
871
|
+
},
|
|
872
|
+
|
|
483
873
|
_cloneJs(t, js) {
|
|
874
|
+
const tp = this._typeParamClone(t, js);
|
|
875
|
+
if (tp) return tp;
|
|
484
876
|
const base = this._aggregateBase(t);
|
|
485
877
|
if (!base) return js;
|
|
486
878
|
if (base.kind === "struct") {
|
|
@@ -498,7 +890,9 @@ export const expressionGenMethods = {
|
|
|
498
890
|
_isAddressableExpr(e) {
|
|
499
891
|
switch (e?.kind) {
|
|
500
892
|
case "Ident":
|
|
501
|
-
return
|
|
893
|
+
return (
|
|
894
|
+
e.name !== "_" && (!e._isTypeRef || e._type?.kind === "typeParam")
|
|
895
|
+
);
|
|
502
896
|
case "SelectorExpr":
|
|
503
897
|
return !e._isMethodValue && !e._isMethodExpr;
|
|
504
898
|
case "IndexExpr":
|
|
@@ -516,7 +910,12 @@ export const expressionGenMethods = {
|
|
|
516
910
|
genValueExpr(expr) {
|
|
517
911
|
const js = this.genExpr(expr);
|
|
518
912
|
if (expr._ifaceBox) return js;
|
|
519
|
-
if (
|
|
913
|
+
if (
|
|
914
|
+
!(
|
|
915
|
+
this._aggregateBase(expr._type) || this._typeParamClone(expr._type, js)
|
|
916
|
+
) ||
|
|
917
|
+
!this._isAddressableExpr(expr)
|
|
918
|
+
)
|
|
520
919
|
return js;
|
|
521
920
|
return this._cloneJs(expr._type, js);
|
|
522
921
|
},
|
|
@@ -525,11 +924,6 @@ export const expressionGenMethods = {
|
|
|
525
924
|
return rootIdentName(e);
|
|
526
925
|
},
|
|
527
926
|
|
|
528
|
-
// True when `node` itself (not its children) writes to variable `name`.
|
|
529
|
-
_nodeWritesVar(node, name) {
|
|
530
|
-
return nodeWritesVar(node, name);
|
|
531
|
-
},
|
|
532
|
-
|
|
533
927
|
// True when `node` may modify (or take the address of) the value held by variable `name`.
|
|
534
928
|
_nodeMutatesVar(node, name, addrOnly = false) {
|
|
535
929
|
return nodeMutatesVar(node, name, addrOnly);
|
|
@@ -541,16 +935,26 @@ export const expressionGenMethods = {
|
|
|
541
935
|
|
|
542
936
|
_withFnCtx(body, fn) {
|
|
543
937
|
const prev = this._fnCtx;
|
|
938
|
+
const prevLabels = this._functionLabels;
|
|
939
|
+
const labels = collectFunctionLabels(
|
|
940
|
+
body,
|
|
941
|
+
new Set(this._functionLabels ?? []),
|
|
942
|
+
);
|
|
943
|
+
this._functionLabels = Array.from(labels);
|
|
544
944
|
this._fnCtx = {
|
|
545
945
|
body,
|
|
546
946
|
mut: new Map(),
|
|
547
947
|
owned: new Set(),
|
|
548
948
|
borrowed: new Set(),
|
|
949
|
+
// Parameters, receivers and named results, including the enclosing
|
|
950
|
+
// functions' (closures): `x = x[lo:hi]` on them must not copy.
|
|
951
|
+
params: new Set(prev?.params),
|
|
549
952
|
};
|
|
550
953
|
try {
|
|
551
954
|
fn();
|
|
552
955
|
} finally {
|
|
553
956
|
this._fnCtx = prev;
|
|
957
|
+
this._functionLabels = prevLabels;
|
|
554
958
|
}
|
|
555
959
|
},
|
|
556
960
|
|
|
@@ -568,6 +972,15 @@ export const expressionGenMethods = {
|
|
|
568
972
|
// Copies aggregate params/receivers that the function body mutates.
|
|
569
973
|
_emitParamCopies(params) {
|
|
570
974
|
for (const p of params) {
|
|
975
|
+
if (p.name) this._fnCtx?.params.add(p.name);
|
|
976
|
+
if (p.variadic && p.name && p.name !== "_") {
|
|
977
|
+
this.useHelper("va");
|
|
978
|
+
// Only a function that tests the parameter against nil needs an
|
|
979
|
+
// empty call to be nil; others keep an empty slice so stdlib
|
|
980
|
+
// helpers and printing see `[]`.
|
|
981
|
+
const nilTested = comparesToNil(this._fnCtx?.body, p.name);
|
|
982
|
+
this.line(`${p.name} = __va(${p.name}${nilTested ? ", true" : ""});`);
|
|
983
|
+
}
|
|
571
984
|
if (p.variadic || !p.name || p.name === "_") continue;
|
|
572
985
|
if (this._boxedVars.has(p.name)) {
|
|
573
986
|
// Address-taken parameter: rebind it to a { value } box.
|
|
@@ -621,12 +1034,26 @@ export const expressionGenMethods = {
|
|
|
621
1034
|
) {
|
|
622
1035
|
return this.genExpr(expr.operand);
|
|
623
1036
|
}
|
|
624
|
-
if (
|
|
625
|
-
expr.operand.
|
|
626
|
-
expr.operand.
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
1037
|
+
if (expr.operand.kind === "IndexExpr") {
|
|
1038
|
+
const obj = this.genExpr(expr.operand.expr);
|
|
1039
|
+
const idx = this.genExpr(expr.operand.index);
|
|
1040
|
+
const check = this._genStrictIndex(
|
|
1041
|
+
{ ...expr.operand, _lvalue: false },
|
|
1042
|
+
"__o",
|
|
1043
|
+
"__i",
|
|
1044
|
+
);
|
|
1045
|
+
// Erased generic element: a struct instantiation is its own
|
|
1046
|
+
// pointer, anything else gets the get/set wrapper.
|
|
1047
|
+
if (expr.operand._type?.kind === "typeParam")
|
|
1048
|
+
return `((__o, __i) => { ${check ? `${check}; ` : ""}if (${this._typeParamIsStructJs(expr.operand._type.name)}) return __o[__i]; return Object.defineProperties({ get value() { return __o[__i]; }, set value(v) { __o[__i] = v; } }, { __target: { value: __o }, __index: { value: __i } }); })(${obj}, ${idx})`;
|
|
1049
|
+
if (check)
|
|
1050
|
+
return `((__o, __i) => (${check}, Object.defineProperties({ get value() { return __o[__i]; }, set value(v) { __o[__i] = v; } }, { __target: { value: __o }, __index: { value: __i } })))(${obj}, ${idx})`;
|
|
1051
|
+
return `((__o, __i) => Object.defineProperties({ get value() { return __o[__i]; }, set value(v) { __o[__i] = v; } }, { __target: { value: __o }, __index: { value: __i } }))(${obj}, ${idx})`;
|
|
1052
|
+
}
|
|
1053
|
+
if (expr.operand.kind === "SelectorExpr") {
|
|
1054
|
+
const obj = this.genExpr(expr.operand.expr);
|
|
1055
|
+
const field = expr.operand.field;
|
|
1056
|
+
return `((__o) => Object.defineProperties({ get value() { return __o.${field}; }, set value(v) { __o.${field} = v; } }, { __target: { value: __o }, __index: { value: "${field}" } }))(${obj})`;
|
|
630
1057
|
}
|
|
631
1058
|
// For non-boxed vars (primitives, etc.), wrap in { value: x }
|
|
632
1059
|
return `{ value: ${this.genExpr(expr.operand)} }`;
|
|
@@ -634,7 +1061,13 @@ export const expressionGenMethods = {
|
|
|
634
1061
|
// Dereference: *p
|
|
635
1062
|
if (op === "*") {
|
|
636
1063
|
if (this._isStructPointerType(expr.operand._type)) {
|
|
637
|
-
return
|
|
1064
|
+
return expr._nonNil
|
|
1065
|
+
? this.genExpr(expr.operand)
|
|
1066
|
+
: this._genNilChecked(this.genExpr(expr.operand));
|
|
1067
|
+
}
|
|
1068
|
+
if (expr.operand._type?.base?.kind === "typeParam") {
|
|
1069
|
+
if (expr._derefOpTarget) return `${this.genExpr(expr.operand)}.value`;
|
|
1070
|
+
return `((__p) => (${this._typeParamIsStructJs(expr.operand._type.base.name)} ? __p : __p.value))(${this.genExpr(expr.operand)})`;
|
|
638
1071
|
}
|
|
639
1072
|
return `${this.genExpr(expr.operand)}.value`;
|
|
640
1073
|
}
|
|
@@ -645,6 +1078,27 @@ export const expressionGenMethods = {
|
|
|
645
1078
|
return inner;
|
|
646
1079
|
}
|
|
647
1080
|
const code = this.genExpr(expr.operand);
|
|
1081
|
+
if (this._is64BitInt(expr._type) || expr._is64Bit) {
|
|
1082
|
+
let operandCode = code;
|
|
1083
|
+
if (!operandCode.endsWith("n") && /^-?\d+$/.test(operandCode)) {
|
|
1084
|
+
operandCode = `${operandCode}n`;
|
|
1085
|
+
}
|
|
1086
|
+
if (op === "+") return operandCode;
|
|
1087
|
+
const wrap = this._is64BitUnsigned(expr._type)
|
|
1088
|
+
? "BigInt.asUintN"
|
|
1089
|
+
: "BigInt.asIntN";
|
|
1090
|
+
if (op === "-" || op === "~") return `${wrap}(64, ${op}(${operandCode}))`;
|
|
1091
|
+
}
|
|
1092
|
+
const wrap = this.strict
|
|
1093
|
+
? SMALL_UNSIGNED_WRAP[this._typeName(expr._type)]
|
|
1094
|
+
: null;
|
|
1095
|
+
if (wrap && (op === "-" || op === "~")) return `(${op}(${code}) ${wrap})`;
|
|
1096
|
+
// -MinInt wraps back to itself for signed narrow integers.
|
|
1097
|
+
const signedWrap =
|
|
1098
|
+
this.strict && op === "-"
|
|
1099
|
+
? SIGNED_NEG_WRAP[this._typeName(expr._type)]
|
|
1100
|
+
: null;
|
|
1101
|
+
if (signedWrap) return `(-(${code}) ${signedWrap})`;
|
|
648
1102
|
const needsParens =
|
|
649
1103
|
this._emittedBinaryOp(expr.operand) !== null ||
|
|
650
1104
|
((op === "-" || op === "+") && /^[-+]/.test(code));
|
|
@@ -652,9 +1106,24 @@ export const expressionGenMethods = {
|
|
|
652
1106
|
},
|
|
653
1107
|
|
|
654
1108
|
_genSelectorExpr(expr) {
|
|
1109
|
+
// Go-source stdlib member: bundled under its mangled top-level name.
|
|
1110
|
+
if (expr._stdName) {
|
|
1111
|
+
// An untyped constant used as an int64 operand: its declaration
|
|
1112
|
+
// holds a Number, so emit the exact BigInt (as for an Ident).
|
|
1113
|
+
const lit64 =
|
|
1114
|
+
expr._is64Bit && expr._type?.kind === "untyped" && expr._const
|
|
1115
|
+
? this._gen64BitFoldedConst(expr, expr._const)
|
|
1116
|
+
: null;
|
|
1117
|
+
return lit64 ?? expr._stdName;
|
|
1118
|
+
}
|
|
1119
|
+
if (this._isStdlibFuncValue(expr)) return this._genStdlibFuncValue(expr);
|
|
655
1120
|
if (expr._isMethodExpr) {
|
|
656
1121
|
const namedExpr = this._namedMethodExprInfo(expr);
|
|
657
1122
|
if (namedExpr) return `${namedExpr.cls}.$${expr.field}`;
|
|
1123
|
+
const rt = expr._methodExprRecv;
|
|
1124
|
+
const protoCls = this._ptrMethodProtoClass(rt, expr.field);
|
|
1125
|
+
if (protoCls)
|
|
1126
|
+
return `((recv, ...args) => ${protoCls}.prototype.${expr.field}.call(recv, ...args))`;
|
|
658
1127
|
return `((recv, ...args) => recv.${expr.field}(...args))`;
|
|
659
1128
|
}
|
|
660
1129
|
const namedValue = this._genNamedMethodValue(expr);
|
|
@@ -662,36 +1131,101 @@ export const expressionGenMethods = {
|
|
|
662
1131
|
if (expr.expr.kind === "Ident") {
|
|
663
1132
|
const nsConst = NS_CONSTANTS[expr.expr.name]?.[expr.field];
|
|
664
1133
|
if (nsConst !== undefined) {
|
|
665
|
-
if (expr.expr.name === "strconv") {
|
|
666
|
-
this.useHelper("strconvError");
|
|
667
|
-
} else if (expr.expr.name === "path" || expr.expr.name === "filepath") {
|
|
668
|
-
if (expr.field === "ErrBadPattern") this.useHelper("pathError");
|
|
669
|
-
} else if (expr.expr.name === "io" && expr.field === "EOF") {
|
|
670
|
-
this.useHelper("ioEOF");
|
|
671
|
-
} else if (expr.expr.name === "binary") {
|
|
672
|
-
this.useHelper("binary");
|
|
673
|
-
} else if (expr.expr.name === "hex") {
|
|
674
|
-
this.useHelper("hex");
|
|
675
|
-
this.useHelper("error");
|
|
676
|
-
} else if (expr.expr.name === "base64") {
|
|
677
|
-
this.useHelper("base64");
|
|
678
|
-
} else if (expr.expr.name === "rand" && expr.field === "Reader") {
|
|
679
|
-
this.useHelper("cryptoRand");
|
|
680
|
-
} else if (expr.expr.name === "sha256") {
|
|
681
|
-
this.useHelper("sha256");
|
|
682
|
-
}
|
|
683
1134
|
return nsConst;
|
|
684
1135
|
}
|
|
685
1136
|
}
|
|
686
1137
|
const base = this.genExpr(expr.expr);
|
|
687
|
-
|
|
1138
|
+
// Bundled GoFront package member; browser-global names carry the `$`
|
|
1139
|
+
// suffix of their declaration (see jsBindingName).
|
|
1140
|
+
if (this.bundledPackages.has(base))
|
|
1141
|
+
return BROWSER_GLOBALS.has(expr.field) ? `${expr.field}$` : expr.field;
|
|
688
1142
|
if (expr.expr._type?.kind === "pointer" && expr.field !== "value")
|
|
689
1143
|
return this._genPointerSelectorField(expr, base);
|
|
690
|
-
const sel = `${base}.${this._jsMemberName(expr)}`;
|
|
691
|
-
if (expr._isMethodValue && !expr._callee)
|
|
1144
|
+
const sel = `${base}${this._promotedPath(expr)}.${this._jsMemberName(expr)}`;
|
|
1145
|
+
if (expr._isMethodValue && !expr._callee) {
|
|
1146
|
+
// A value receiver is copied when the method value is evaluated.
|
|
1147
|
+
const t = expr.expr._type;
|
|
1148
|
+
if (!expr._type?._ptrRecv && this._aggregateBase(t))
|
|
1149
|
+
return `((__r) => __r.${this._jsMemberName(expr)}.bind(__r))(${this._cloneJs(t, base)})`;
|
|
1150
|
+
return `${sel}.bind(${base})`;
|
|
1151
|
+
}
|
|
692
1152
|
return sel;
|
|
693
1153
|
},
|
|
694
1154
|
|
|
1155
|
+
// `pkg.Fn` used as a value (not called): a stdlib function is inlined at
|
|
1156
|
+
// its call sites, so a value is an arrow function that makes that call.
|
|
1157
|
+
_isStdlibFuncValue(expr) {
|
|
1158
|
+
const t = expr._type;
|
|
1159
|
+
if (expr._callee || t?.kind !== "func" || expr._isMethodExpr) return false;
|
|
1160
|
+
const ns = expr.expr;
|
|
1161
|
+
if (ns?.kind !== "Ident" || ns._type?.kind !== "namespace") return false;
|
|
1162
|
+
return !this.bundledPackages.has(ns.name) && !hasTypeParamType(t);
|
|
1163
|
+
},
|
|
1164
|
+
|
|
1165
|
+
_genStdlibFuncValue(expr) {
|
|
1166
|
+
const t = expr._type;
|
|
1167
|
+
const ps = t.params ?? [];
|
|
1168
|
+
const nFixed = t.variadic && ps.length > 0 ? ps.length - 1 : ps.length;
|
|
1169
|
+
const fixed = ps.slice(0, nFixed).map((p, i) => stdIdent(`__a${i}`, p));
|
|
1170
|
+
const names = fixed.map((a) => a.name);
|
|
1171
|
+
const callJs = (args) =>
|
|
1172
|
+
this.genExpr({
|
|
1173
|
+
kind: "CallExpr",
|
|
1174
|
+
func: { ...expr, _callee: true },
|
|
1175
|
+
args,
|
|
1176
|
+
_type: t.returns?.[0] ?? { kind: "basic", name: "void" },
|
|
1177
|
+
});
|
|
1178
|
+
if (nFixed === ps.length)
|
|
1179
|
+
return `((${names.join(", ")}) => ${callJs(fixed)})`;
|
|
1180
|
+
// Stdlib calls are inlined per operand count, so dispatch on it.
|
|
1181
|
+
const rest = `__a${nFixed}`;
|
|
1182
|
+
let body = `(() => { throw new Error("too many arguments in call to ${expr.expr.name}.${expr.field}"); })()`;
|
|
1183
|
+
for (let k = 8; k >= 0; k--) {
|
|
1184
|
+
const extra = Array.from({ length: k }, (_, i) =>
|
|
1185
|
+
stdIdent(`${rest}[${i}]`, ps[nFixed]),
|
|
1186
|
+
);
|
|
1187
|
+
body = `${rest}.length === ${k} ? ${callJs([...fixed, ...extra])} : ${body}`;
|
|
1188
|
+
}
|
|
1189
|
+
return `((${[...names, `...${rest}`].join(", ")}) => ${body})`;
|
|
1190
|
+
},
|
|
1191
|
+
|
|
1192
|
+
// A promoted field (`o.A` with embedded `In`) lives on the embedded value:
|
|
1193
|
+
// the class also carries a flattened copy, so reads and writes go through
|
|
1194
|
+
// the embedded path (`.In`) to stay in sync. Returns "" for direct fields.
|
|
1195
|
+
_promotedPath(expr) {
|
|
1196
|
+
if (expr._isMethodValue || expr._isMethodExpr) return "";
|
|
1197
|
+
let t = expr.expr._type;
|
|
1198
|
+
if (t?.kind === "pointer") t = t.base;
|
|
1199
|
+
const path = this._embedPath(t, expr.field, 0);
|
|
1200
|
+
return path ? path.map((n) => `.${n}`).join("") : "";
|
|
1201
|
+
},
|
|
1202
|
+
|
|
1203
|
+
_embedPath(t, field, depth) {
|
|
1204
|
+
const st = t?.kind === "named" ? t.underlying : t;
|
|
1205
|
+
if (st?.kind !== "struct" || !st.fields?.has(field) || depth > 8)
|
|
1206
|
+
return null;
|
|
1207
|
+
const direct = (st._structFields ?? []).some(
|
|
1208
|
+
(sf) => !sf.embedded && sf.names?.includes(field),
|
|
1209
|
+
);
|
|
1210
|
+
if (direct) return null;
|
|
1211
|
+
for (const sf of st._structFields ?? []) {
|
|
1212
|
+
if (!sf.embedded) continue;
|
|
1213
|
+
const inner = sf.type?.kind === "pointer" ? sf.type.base : sf.type;
|
|
1214
|
+
if (inner?.kind !== "named" || !this.structNames.has(inner.name))
|
|
1215
|
+
continue;
|
|
1216
|
+
const name = embeddedFieldName(sf);
|
|
1217
|
+
if (name === field) return null;
|
|
1218
|
+
const ist = inner.underlying;
|
|
1219
|
+
if (!ist?.fields?.has(field)) continue;
|
|
1220
|
+
const sub = this._embedPath(inner, field, depth + 1);
|
|
1221
|
+
const idirect = (ist._structFields ?? []).some(
|
|
1222
|
+
(x) => !x.embedded && x.names?.includes(field),
|
|
1223
|
+
);
|
|
1224
|
+
return idirect ? [name] : sub ? [name, ...sub] : null;
|
|
1225
|
+
}
|
|
1226
|
+
return null;
|
|
1227
|
+
},
|
|
1228
|
+
|
|
695
1229
|
// PascalCase aliases of Web API methods (gl.ClearColor) map to the real camelCase member.
|
|
696
1230
|
_jsMemberName(expr) {
|
|
697
1231
|
const f = expr.field;
|
|
@@ -733,7 +1267,12 @@ export const expressionGenMethods = {
|
|
|
733
1267
|
? `${this.genExpr(sel.expr)}.value`
|
|
734
1268
|
: this.genExpr(sel.expr);
|
|
735
1269
|
}
|
|
736
|
-
return {
|
|
1270
|
+
return {
|
|
1271
|
+
cls: info.cls,
|
|
1272
|
+
recv,
|
|
1273
|
+
byValue: !method._ptrRecv,
|
|
1274
|
+
recvType: viaPtr ? recvType.base : recvType,
|
|
1275
|
+
};
|
|
737
1276
|
},
|
|
738
1277
|
|
|
739
1278
|
// `x.M` used as a value: binds the receiver now, as Go does.
|
|
@@ -741,7 +1280,11 @@ export const expressionGenMethods = {
|
|
|
741
1280
|
if (!expr._isMethodValue || expr._callee) return undefined;
|
|
742
1281
|
const target = this._namedMethodTarget(expr);
|
|
743
1282
|
if (!target) return undefined;
|
|
744
|
-
|
|
1283
|
+
// A value receiver (a named array) is copied when the value is evaluated.
|
|
1284
|
+
const recv = target.byValue
|
|
1285
|
+
? this._cloneJs(target.recvType, target.recv)
|
|
1286
|
+
: target.recv;
|
|
1287
|
+
return `((__r) => (...__a) => ${target.cls}.$${expr.field}(__r, ...__a))(${recv})`;
|
|
745
1288
|
},
|
|
746
1289
|
|
|
747
1290
|
_genAddressOfExpr(operand) {
|
|
@@ -776,17 +1319,134 @@ export const expressionGenMethods = {
|
|
|
776
1319
|
return `${target.cls}.$${sel.field}(${args ? `${target.recv}, ${args}` : target.recv})`;
|
|
777
1320
|
},
|
|
778
1321
|
|
|
1322
|
+
// `F[int]` as a value is a function that passes F its zero-value factories.
|
|
1323
|
+
_genInstantiationExpr(expr) {
|
|
1324
|
+
const fn = this.genExpr(expr.expr);
|
|
1325
|
+
if (!expr._genericFnType || expr._callee) return fn;
|
|
1326
|
+
const { typeParams } = expr._genericFnType;
|
|
1327
|
+
const zeros = this._typeArgFactories(
|
|
1328
|
+
typeParams,
|
|
1329
|
+
expr._typeArgs,
|
|
1330
|
+
this._genericFnNeedsTypeInfo(expr._genericFnType),
|
|
1331
|
+
);
|
|
1332
|
+
return `((...a) => ${fn}(${zeros.join(", ")}, ...a))`;
|
|
1333
|
+
},
|
|
1334
|
+
|
|
779
1335
|
_genCallArgs(expr) {
|
|
780
1336
|
if (expr._multiForward) return `...${this.genExpr(expr.args[0])}`;
|
|
781
|
-
return expr.
|
|
782
|
-
|
|
783
|
-
|
|
1337
|
+
return this._genCallArgList(expr).join(", ");
|
|
1338
|
+
},
|
|
1339
|
+
|
|
1340
|
+
_genCallArgList(expr) {
|
|
1341
|
+
return expr.args.map((a, i) =>
|
|
1342
|
+
a._spread
|
|
1343
|
+
? this._genSpreadArg(expr, a)
|
|
1344
|
+
: a._boxTypeParam
|
|
1345
|
+
? this._genTypeParamBox(a)
|
|
1346
|
+
: this._callerCopiesArg(expr, i)
|
|
1347
|
+
? this.genValueExpr(a)
|
|
1348
|
+
: this.genExpr(a),
|
|
1349
|
+
);
|
|
1350
|
+
},
|
|
1351
|
+
|
|
1352
|
+
// A type parameter operand of a Print-family fmt call (see
|
|
1353
|
+
// src/lower/fmt-formatters.js): a JS value does not know its Go type, so
|
|
1354
|
+
// the type argument boxes it as converting it to an `any` does.
|
|
1355
|
+
_genTypeParamBox(a) {
|
|
1356
|
+
// Boxed below: skip the boxing of a value entering an interface.
|
|
1357
|
+
const wasBoxing = a._tpBoxing;
|
|
1358
|
+
a._tpBoxing = true;
|
|
1359
|
+
const js = this.genExpr(a);
|
|
1360
|
+
a._tpBoxing = wasBoxing;
|
|
1361
|
+
const tpf = this._typeParamZeroFactory(a._type.name);
|
|
1362
|
+
return tpf ? `(${tpf}.b ?? ${tpf}.fb ?? ((__v) => __v))(${js})` : js;
|
|
1363
|
+
},
|
|
1364
|
+
|
|
1365
|
+
// A direct call of a `//gofront:native` function: host code from the
|
|
1366
|
+
// registry, or null (declined, natives off, spread arguments) to call the
|
|
1367
|
+
// Go body.
|
|
1368
|
+
_genNativeCall(expr) {
|
|
1369
|
+
if (!this.natives || !expr._native) return null;
|
|
1370
|
+
if (expr._multiForward || expr.args.some((a) => a._spread)) return null;
|
|
1371
|
+
const typeArgs = expr._typeArgs ?? expr.func._typeArgs ?? [];
|
|
1372
|
+
const js = jsNativeCall(expr._native, this._genCallArgList(expr), typeArgs);
|
|
1373
|
+
if (js !== null && NATIVE_HELPERS.has(expr._native))
|
|
1374
|
+
this.useHelper(NATIVE_HELPERS.get(expr._native));
|
|
1375
|
+
return js;
|
|
1376
|
+
},
|
|
1377
|
+
|
|
1378
|
+
// `f(xs...)`: a Go-compiled variadic callee receives the slice itself in a
|
|
1379
|
+
// marker (its prologue unwraps it); other callees get a JS spread.
|
|
1380
|
+
_genSpreadArg(call, arg) {
|
|
1381
|
+
const js = this.genExpr(arg);
|
|
1382
|
+
// A variadic function of a Go-source package (`fmt.Println`): a nil
|
|
1383
|
+
// slice spreads to no operands.
|
|
1384
|
+
if (call.func._stdName) return `...(${js} ?? [])`;
|
|
1385
|
+
return this._calleeIsGoVariadic(call) ? `{ __sp: ${js} }` : `...${js}`;
|
|
1386
|
+
},
|
|
1387
|
+
|
|
1388
|
+
_calleeIsGoVariadic(call) {
|
|
1389
|
+
const f = call.func;
|
|
1390
|
+
if (f.kind === "Ident") {
|
|
1391
|
+
if (this.variadicFns?.has(f.name)) return true;
|
|
1392
|
+
// A local func value (closure or parameter) compiled from Go source.
|
|
1393
|
+
const imported =
|
|
1394
|
+
this.checker?._externalNames?.has(f.name) ||
|
|
1395
|
+
BROWSER_GLOBALS.has(f.name) ||
|
|
1396
|
+
[...(this.jsImports?.values() ?? [])].some((names) =>
|
|
1397
|
+
names.includes(f.name),
|
|
1398
|
+
);
|
|
1399
|
+
return !imported && f._type?.kind === "func" && !!f._type.variadic;
|
|
1400
|
+
}
|
|
1401
|
+
if (f.kind !== "SelectorExpr" || f._isMethodExpr) return false;
|
|
1402
|
+
const recv = this._resolveRecvName(f.expr._type);
|
|
1403
|
+
return !!recv && !!this.variadicFns?.has(`${recv}.${f.field}`);
|
|
1404
|
+
},
|
|
1405
|
+
|
|
1406
|
+
// A callee copies its mutated array and struct parameters on entry, except
|
|
1407
|
+
// when the parameter is a type parameter (generics are erased in JS) or
|
|
1408
|
+
// part of a variadic rest array. For those the caller passes aggregate
|
|
1409
|
+
// (array and struct) arguments as copies.
|
|
1410
|
+
_callerCopiesArg(expr, i) {
|
|
1411
|
+
const argT = expr.args[i]._type;
|
|
1412
|
+
if (!this._aggregateBase(argT) && !this._typeParamClone(argT, ""))
|
|
1413
|
+
return false;
|
|
1414
|
+
let ft = expr.func?._type ?? expr.func?.expr?._type;
|
|
1415
|
+
if (ft?.kind === "generic") ft = ft.underlying;
|
|
1416
|
+
if (ft?.kind !== "func") return false;
|
|
1417
|
+
const params = ft.params ?? [];
|
|
1418
|
+
if (ft.variadic && i >= params.length - 1) return true;
|
|
1419
|
+
return params[i]?.kind === "typeParam";
|
|
784
1420
|
},
|
|
785
1421
|
|
|
786
1422
|
_genPointerSelectorField(expr, base) {
|
|
787
1423
|
if (this._isStructPointerType(expr.expr._type)) {
|
|
788
|
-
|
|
789
|
-
|
|
1424
|
+
// Strict mode: a field of a nil struct pointer panics as in Go.
|
|
1425
|
+
const recv =
|
|
1426
|
+
this.strict && !expr._isMethodValue && !expr._nonNil
|
|
1427
|
+
? this._genNilChecked(base)
|
|
1428
|
+
: base;
|
|
1429
|
+
const sel = `${recv}${this._promotedPath(expr)}.${expr.field}`;
|
|
1430
|
+
if (expr._isMethodValue && !expr._callee) {
|
|
1431
|
+
// `p.M` with a value receiver binds a copy of `*p` now.
|
|
1432
|
+
if (expr._derefRecv) {
|
|
1433
|
+
const copy = this._cloneJs(
|
|
1434
|
+
expr.expr._type.base,
|
|
1435
|
+
this.strict ? this._genNilChecked(base) : base,
|
|
1436
|
+
);
|
|
1437
|
+
return `((__r) => __r.${expr.field}.bind(__r))(${copy})`;
|
|
1438
|
+
}
|
|
1439
|
+
// A pointer receiver may be nil: bind through the prototype so
|
|
1440
|
+
// `s.M` on a nil pointer does not read `.M` off null.
|
|
1441
|
+
const protoCls = this._ptrMethodProtoClass(
|
|
1442
|
+
expr.expr._type,
|
|
1443
|
+
expr.field,
|
|
1444
|
+
expr,
|
|
1445
|
+
);
|
|
1446
|
+
if (protoCls)
|
|
1447
|
+
return `${protoCls}.prototype.${expr.field}.bind(${base})`;
|
|
1448
|
+
return `${sel}.bind(${base})`;
|
|
1449
|
+
}
|
|
790
1450
|
return sel;
|
|
791
1451
|
}
|
|
792
1452
|
const sel = `${base}.value.${expr.field}`;
|
|
@@ -801,19 +1461,55 @@ export const expressionGenMethods = {
|
|
|
801
1461
|
const base = wrapField
|
|
802
1462
|
? `${this.genExpr(expr.expr)}.${wrapField}`
|
|
803
1463
|
: this.genExpr(expr.expr);
|
|
804
|
-
|
|
805
|
-
if (expr._mapValueType
|
|
806
|
-
return this._genMapIndexExpr(expr, base, idx);
|
|
1464
|
+
let idx = this.genExpr(expr.index);
|
|
1465
|
+
if (expr._mapValueType) {
|
|
1466
|
+
if (!expr._lvalue) return this._genMapIndexExpr(expr, base, idx);
|
|
1467
|
+
return `${this._genMapWriteBase(base)}[${idx}]`;
|
|
1468
|
+
}
|
|
1469
|
+
if (this._is64BitInt(expr.index?._type)) idx = `Number(${idx})`;
|
|
1470
|
+
const checked = this._genStrictIndex(expr, base, idx);
|
|
1471
|
+
if (checked) return checked;
|
|
807
1472
|
if (this._isStringExprType(exprType) && !expr._lvalue)
|
|
808
1473
|
return `${base}.charCodeAt(${idx})`;
|
|
809
1474
|
return `${base}[${idx}]`;
|
|
810
1475
|
},
|
|
811
1476
|
|
|
1477
|
+
// `p` for a field access `p.F`, panicking with Go's message when nil.
|
|
1478
|
+
_genNilChecked(base) {
|
|
1479
|
+
this.useHelper("nilCall");
|
|
1480
|
+
return `__nilcall(${base})`;
|
|
1481
|
+
},
|
|
1482
|
+
|
|
1483
|
+
// The map of a write `m[k] = v`: strict mode panics on a nil map.
|
|
1484
|
+
_genMapWriteBase(base) {
|
|
1485
|
+
if (!this.strict) return base;
|
|
1486
|
+
this.useHelper("nilMap");
|
|
1487
|
+
return `__mw(${base})`;
|
|
1488
|
+
},
|
|
1489
|
+
|
|
1490
|
+
// Strict mode: `xs[i]` on a slice, array or string with Go's bounds check
|
|
1491
|
+
// (null when no check applies). Reads of slices and arrays use `__at`;
|
|
1492
|
+
// lvalues and string reads use `base[__ix(base, i)]`, binding `base` to
|
|
1493
|
+
// `__lv` first when evaluating it twice could repeat a call.
|
|
1494
|
+
_genStrictIndex(expr, base, idx) {
|
|
1495
|
+
if (!this.strict || expr._inBounds) return null;
|
|
1496
|
+
const t = expr.expr._type;
|
|
1497
|
+
const u = t?.kind === "named" ? t.underlying : t;
|
|
1498
|
+
const isStr = this._isStringExprType(t);
|
|
1499
|
+
if (!isStr && u?.kind !== "slice" && u?.kind !== "array") return null;
|
|
1500
|
+
this.useHelper("bounds");
|
|
1501
|
+
if (!expr._lvalue && !isStr) return `__at(${base}, ${idx})`;
|
|
1502
|
+
const once = this._hasCallExpr(expr.expr);
|
|
1503
|
+
const obj = once ? `(__lv = ${base})` : base;
|
|
1504
|
+
const at = `__ix(${once ? "__lv" : base}, ${idx})`;
|
|
1505
|
+
return expr._lvalue ? `${obj}[${at}]` : `${obj}.charCodeAt(${at})`;
|
|
1506
|
+
},
|
|
1507
|
+
|
|
812
1508
|
_genMapIndexExpr(expr, base, idx) {
|
|
813
1509
|
const zero = this.zeroValueForType(expr._mapValueType);
|
|
814
1510
|
if (this._hasCallExpr(expr.expr) || this._hasCallExpr(expr.index))
|
|
815
|
-
return `((__m, __k) => __m[__k] ?? ${zero})(${base}, ${idx})`;
|
|
816
|
-
return `(${base}[${idx}] ?? ${zero})`;
|
|
1511
|
+
return `((__m, __k) => __m?.[__k] ?? ${zero})(${base}, ${idx})`;
|
|
1512
|
+
return `(${base}?.[${idx}] ?? ${zero})`;
|
|
817
1513
|
},
|
|
818
1514
|
|
|
819
1515
|
_isStringExprType(exprType) {
|
|
@@ -826,17 +1522,36 @@ export const expressionGenMethods = {
|
|
|
826
1522
|
|
|
827
1523
|
_genSliceExpr(expr) {
|
|
828
1524
|
const base = this.genExpr(expr.expr);
|
|
829
|
-
const lo = expr.low
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
1525
|
+
const lo = expr.low
|
|
1526
|
+
? this._is64BitInt(expr.low._type)
|
|
1527
|
+
? `Number(${this.genExpr(expr.low)})`
|
|
1528
|
+
: this.genExpr(expr.low)
|
|
1529
|
+
: "0";
|
|
1530
|
+
const hi = expr.high
|
|
1531
|
+
? this._is64BitInt(expr.high._type)
|
|
1532
|
+
? `Number(${this.genExpr(expr.high)})`
|
|
1533
|
+
: this.genExpr(expr.high)
|
|
1534
|
+
: "undefined";
|
|
1535
|
+
if (this._isStringExprType(expr.expr._type))
|
|
1536
|
+
return expr.high ? `${base}.slice(${lo}, ${hi})` : `${base}.slice(${lo})`;
|
|
1537
|
+
// `b[:]`, `b[0:]` and `a[:]` have the operand's length and capacity:
|
|
1538
|
+
// the result is the operand itself. Other bounds make a view sharing
|
|
1539
|
+
// the backing store (`__slice`, which checks the bounds against cap).
|
|
1540
|
+
const zeroLow = !expr.low || Number(expr.low._const?.v) === 0;
|
|
1541
|
+
if (zeroLow && !expr.high && !expr.max) return base;
|
|
1542
|
+
this.useHelper("slice");
|
|
1543
|
+
// `x = x[lo:hi]` (see `_markSelfReslice`) copies a plain array when
|
|
1544
|
+
// the result stays within `len(x)`; typed arrays always share.
|
|
1545
|
+
if (expr._selfReslice && !isTypedArraySlice(expr.expr._type))
|
|
1546
|
+
return `__reslice(${base}, ${lo}, ${hi})`;
|
|
1547
|
+
const max = expr.max
|
|
1548
|
+
? `, ${this._is64BitInt(expr.max._type) ? `Number(${this.genExpr(expr.max)})` : this.genExpr(expr.max)}`
|
|
1549
|
+
: "";
|
|
1550
|
+
return `__slice(${base}, ${lo}, ${hi}${max})`;
|
|
836
1551
|
},
|
|
837
1552
|
|
|
838
1553
|
_genFuncLit(expr) {
|
|
839
|
-
const params = expr.params
|
|
1554
|
+
const params = this._paramNames(expr.params).join(", ");
|
|
840
1555
|
const asyncPrefix = expr.async ? "async " : "";
|
|
841
1556
|
const saved = this.out;
|
|
842
1557
|
const prevBoxed = this._boxedVars;
|
|
@@ -846,7 +1561,7 @@ export const expressionGenMethods = {
|
|
|
846
1561
|
this._withFnCtx(expr.body, () =>
|
|
847
1562
|
this.indented(() => {
|
|
848
1563
|
this._emitParamCopies(expr.params);
|
|
849
|
-
this._genBody(expr.body, expr);
|
|
1564
|
+
this._withNamedReturns(expr, () => this._genBody(expr.body, expr));
|
|
850
1565
|
}),
|
|
851
1566
|
);
|
|
852
1567
|
const body = this.out.join("\n");
|
|
@@ -864,26 +1579,80 @@ export const expressionGenMethods = {
|
|
|
864
1579
|
const check = this._typeCheckExpr(expr.type, val);
|
|
865
1580
|
const wrap = (body) => (hoist ? `((__v) => ${body})(${src})` : body);
|
|
866
1581
|
// Named non-struct values sit in interfaces as boxes; the asserted value is raw.
|
|
867
|
-
const out = this.
|
|
868
|
-
? `${val}.__gfUnbox()`
|
|
869
|
-
: val;
|
|
1582
|
+
const out = this._assertedValueJs(expr.type, val);
|
|
870
1583
|
if (!expr._commaOk) {
|
|
871
1584
|
// plain assertion: panic if check fails (matches Go behavior)
|
|
872
1585
|
if (check === "true") return src; // can't check at runtime — pass through
|
|
1586
|
+
this.useHelper("ifaceBox");
|
|
873
1587
|
return wrap(
|
|
874
|
-
`(${check} ? ${out} : (() => { throw
|
|
1588
|
+
`(${check} ? ${out} : (() => { throw ${this._typeAssertFailJs(expr, val)}; })())`,
|
|
875
1589
|
);
|
|
876
1590
|
}
|
|
877
|
-
// comma-ok: emit [value-or-zero, runtimeTypeCheck]
|
|
1591
|
+
// comma-ok: emit [value-or-zero, runtimeTypeCheck]; an asserted struct
|
|
1592
|
+
// or array is a copy of the boxed value.
|
|
878
1593
|
const zero = this.zeroValueForTypeNode(expr.type);
|
|
879
|
-
|
|
1594
|
+
const valT = this._typeFromNode(expr.type);
|
|
1595
|
+
const okOut = this._aggregateBase(valT) ? this._cloneJs(valT, out) : out;
|
|
1596
|
+
return wrap(`(${check} ? [${okOut}, true] : [${zero}, false])`);
|
|
1597
|
+
},
|
|
1598
|
+
|
|
1599
|
+
// The error a failed `x.(T)` throws: Go's `interface conversion: ...` text
|
|
1600
|
+
// naming the static interface type, the dynamic type and the target.
|
|
1601
|
+
_typeAssertFailJs(expr, val) {
|
|
1602
|
+
const target = expr._type;
|
|
1603
|
+
const u = this._underlyingOf(target);
|
|
1604
|
+
const methods =
|
|
1605
|
+
u?.kind === "interface" ? [...(u.methods?.keys() ?? [])] : [];
|
|
1606
|
+
const tName = this._assertTypeName(target) ?? goTypeName(expr.type);
|
|
1607
|
+
const src = this._assertTypeName(expr.expr?._type) ?? "interface {}";
|
|
1608
|
+
return `__tafail(${JSON.stringify(src)}, ${val}, ${JSON.stringify(tName)}, ${methods.length ? JSON.stringify(methods.sort()) : "null"})`;
|
|
1609
|
+
},
|
|
1610
|
+
|
|
1611
|
+
// Go's spelling of the static type in a failed assertion message; null for
|
|
1612
|
+
// `any` (`interface {}`).
|
|
1613
|
+
_assertTypeName(t) {
|
|
1614
|
+
if (!t || isAny(t)) return null;
|
|
1615
|
+
return t.name === "error" ? "error" : goTypeName(t);
|
|
1616
|
+
},
|
|
1617
|
+
|
|
1618
|
+
// `csv.ParseError` names the class of the bundled Go-source package type.
|
|
1619
|
+
_qualifiedStructName(name) {
|
|
1620
|
+
if (!name.includes(".") || this.structNames.has(name)) return name;
|
|
1621
|
+
const cls = this.checker?.types.get(name)?.name;
|
|
1622
|
+
return cls && this.structNames.has(cls) ? cls : name;
|
|
1623
|
+
},
|
|
1624
|
+
|
|
1625
|
+
_simdCheckExpr(name, val) {
|
|
1626
|
+
if (name === "simd.F32x4" || name === "F32x4")
|
|
1627
|
+
return `(Array.isArray(${val}) && ${val}.length === 4 && typeof ${val}[0] === "number")`;
|
|
1628
|
+
if (name === "simd.Mask32x4" || name === "Mask32x4")
|
|
1629
|
+
return `(Array.isArray(${val}) && ${val}.length === 4 && typeof ${val}[0] === "boolean")`;
|
|
1630
|
+
return null;
|
|
880
1631
|
},
|
|
881
1632
|
|
|
882
|
-
_typeNameCheckExpr(
|
|
1633
|
+
_typeNameCheckExpr(typeName, val) {
|
|
1634
|
+
// A type parameter: the instantiated type's test travels with its factory.
|
|
1635
|
+
const factory = this._typeParamZeroFactory(typeName);
|
|
1636
|
+
if (factory) return `(${factory}?.is?.(${val}) ?? true)`;
|
|
1637
|
+
const name = this._qualifiedStructName(typeName);
|
|
1638
|
+
// int64 and uint64 share the BigInt: a value out of int64's range can
|
|
1639
|
+
// only be a uint64.
|
|
1640
|
+
if (name === "int64")
|
|
1641
|
+
return `(typeof ${val} === "bigint" && ${val} >= -9223372036854775808n && ${val} <= 9223372036854775807n)`;
|
|
1642
|
+
if (name === "uint64")
|
|
1643
|
+
return `(typeof ${val} === "bigint" && ${val} >= 0n)`;
|
|
1644
|
+
// Integer types share the JS number with the floats: only a whole
|
|
1645
|
+
// number is one, so a fraction or -0 in an `any` falls through to float64.
|
|
1646
|
+
if (INT_TYPE_NAMES.has(name))
|
|
1647
|
+
return `(Number.isSafeInteger(${val}) && !Object.is(${val}, -0))`;
|
|
883
1648
|
if (TYPEOF_NUMBER_NAMES.has(name)) return `typeof ${val} === "number"`;
|
|
1649
|
+
if (name === "complex128" || name === "complex64")
|
|
1650
|
+
return `(typeof ${val} === "object" && ${val} !== null && typeof ${val}.re === "number" && typeof ${val}.im === "number")`;
|
|
884
1651
|
if (name === "string") return `typeof ${val} === "string"`;
|
|
885
1652
|
if (name === "bool") return `typeof ${val} === "boolean"`;
|
|
886
1653
|
if (name === "nil") return `${val} === null`;
|
|
1654
|
+
const simd = this._simdCheckExpr(name, val);
|
|
1655
|
+
if (simd) return simd;
|
|
887
1656
|
if (name === "error")
|
|
888
1657
|
return `(typeof ${val} === "object" && ${val} !== null && typeof ${val}.Error === "function")`;
|
|
889
1658
|
if (this.structNames.has(name))
|
|
@@ -892,8 +1661,16 @@ export const expressionGenMethods = {
|
|
|
892
1661
|
const namedInfo = this._namedBoxInfo(named);
|
|
893
1662
|
if (namedInfo) return `(${val} instanceof ${namedInfo.cls})`;
|
|
894
1663
|
// User-declared interfaces: check the dynamic value has every method.
|
|
895
|
-
if (
|
|
896
|
-
return this._ifaceMethodCheck(
|
|
1664
|
+
if (named?.underlying?.kind === "interface")
|
|
1665
|
+
return this._ifaceMethodCheck(
|
|
1666
|
+
val,
|
|
1667
|
+
[...named.underlying.methods.keys()],
|
|
1668
|
+
this._unwrapIsMulti(named.underlying.methods.get("Unwrap")),
|
|
1669
|
+
);
|
|
1670
|
+
if (this._isNamedComposite(named)) {
|
|
1671
|
+
this.useHelper("ifaceBox");
|
|
1672
|
+
return `__isty(${val}, ${JSON.stringify(name)}, "named")`;
|
|
1673
|
+
}
|
|
897
1674
|
return "true"; // unknown type — can't check at runtime
|
|
898
1675
|
},
|
|
899
1676
|
|
|
@@ -903,6 +1680,9 @@ export const expressionGenMethods = {
|
|
|
903
1680
|
return this._ifaceMethodCheck(
|
|
904
1681
|
val,
|
|
905
1682
|
(typeNode.methods ?? []).map((m) => m.name),
|
|
1683
|
+
this._unwrapIsMulti(
|
|
1684
|
+
(typeNode.methods ?? []).find((m) => m.name === "Unwrap"),
|
|
1685
|
+
),
|
|
906
1686
|
);
|
|
907
1687
|
if (typeNode.kind === "PointerType" && typeNode.base?.kind === "TypeName") {
|
|
908
1688
|
const info = this._namedBoxInfo(
|
|
@@ -913,11 +1693,65 @@ export const expressionGenMethods = {
|
|
|
913
1693
|
return null;
|
|
914
1694
|
},
|
|
915
1695
|
|
|
916
|
-
|
|
917
|
-
|
|
1696
|
+
// `Unwrap() []error` (errors.Join) and `Unwrap() error` share a name: the
|
|
1697
|
+
// JS function of a multi-error `Unwrap` is tagged `__multi`. `method` is
|
|
1698
|
+
// a func type or an interface method AST; null when there is no `Unwrap`.
|
|
1699
|
+
_unwrapIsMulti(method) {
|
|
1700
|
+
if (!method) return null;
|
|
1701
|
+
const ret = method.returnType ?? method.returns?.[0];
|
|
1702
|
+
const u = ret?.kind === "named" ? ret.underlying : ret;
|
|
1703
|
+
return u?.kind === "slice" || u?.kind === "SliceType";
|
|
1704
|
+
},
|
|
1705
|
+
|
|
1706
|
+
_ifaceMethodCheck(val, methodNames, multiUnwrap = null) {
|
|
1707
|
+
const checks = methodNames.map((m) => {
|
|
1708
|
+
const base =
|
|
1709
|
+
m === "Unwrap" && multiUnwrap !== null
|
|
1710
|
+
? `typeof ${val}.${m} === "function" && (${val}.${m}.__multi === true) === ${multiUnwrap}`
|
|
1711
|
+
: `typeof ${val}.${m} === "function"`;
|
|
1712
|
+
// A struct value's method set lacks the pointer-receiver methods.
|
|
1713
|
+
return `${base} && !(${val}.__v === true && ${val}.constructor.__pm?.includes(${JSON.stringify(m)}))`;
|
|
1714
|
+
});
|
|
918
1715
|
return `(${[`${val} != null`, ...checks].join(" && ")})`;
|
|
919
1716
|
},
|
|
920
1717
|
|
|
1718
|
+
// The value an assertion to `typeNode` yields: raw, unboxed from the
|
|
1719
|
+
// interface's box for a named non-struct type (known only at run time
|
|
1720
|
+
// for a type parameter).
|
|
1721
|
+
_assertedValueJs(typeNode, val) {
|
|
1722
|
+
if (this._isNamedBoxTypeNode(typeNode)) return `${val}.__gfUnbox()`;
|
|
1723
|
+
if (
|
|
1724
|
+
this._isStructPtrTypeNode(typeNode) ||
|
|
1725
|
+
(typeNode?.kind === "PointerType" && typeNode._assertType)
|
|
1726
|
+
) {
|
|
1727
|
+
this.useHelper("ifaceBox");
|
|
1728
|
+
return `__unnil(${val})`;
|
|
1729
|
+
}
|
|
1730
|
+
if (typeNode?._assertType?.kind === "slice") {
|
|
1731
|
+
// A nil slice in an interface is a tagged empty array.
|
|
1732
|
+
this.useHelper("ifaceBox");
|
|
1733
|
+
return `__unnil(${val})`;
|
|
1734
|
+
}
|
|
1735
|
+
const factory =
|
|
1736
|
+
typeNode?.kind === "TypeName"
|
|
1737
|
+
? this._typeParamZeroFactory(typeNode.name)
|
|
1738
|
+
: null;
|
|
1739
|
+
return factory ? `(${factory}?.u ? ${val}.__gfUnbox() : ${val})` : val;
|
|
1740
|
+
},
|
|
1741
|
+
|
|
1742
|
+
_isStructPtrTypeNode(typeNode) {
|
|
1743
|
+
const base = typeNode?.kind === "PointerType" ? typeNode.base : null;
|
|
1744
|
+
const name =
|
|
1745
|
+
base?.kind === "TypeName"
|
|
1746
|
+
? base.name
|
|
1747
|
+
: base?.kind === "GenericTypeName"
|
|
1748
|
+
? this._dequalify(base.name)
|
|
1749
|
+
: null;
|
|
1750
|
+
return (
|
|
1751
|
+
name !== null && this.structNames.has(this._qualifiedStructName(name))
|
|
1752
|
+
);
|
|
1753
|
+
},
|
|
1754
|
+
|
|
921
1755
|
_isNamedBoxTypeNode(typeNode) {
|
|
922
1756
|
if (typeNode?.kind === "TypeName")
|
|
923
1757
|
return Boolean(
|
|
@@ -930,14 +1764,35 @@ export const expressionGenMethods = {
|
|
|
930
1764
|
return false;
|
|
931
1765
|
},
|
|
932
1766
|
|
|
1767
|
+
// Converting a struct to a different defined struct type builds a copy in
|
|
1768
|
+
// the target's class (so its methods exist); null when not applicable.
|
|
1769
|
+
_genStructClassConversion(targetName, srcType, inner) {
|
|
1770
|
+
if (!targetName || targetName.includes("[")) return null;
|
|
1771
|
+
const target = this.checker?.types.get(targetName);
|
|
1772
|
+
if (target?.kind !== "named" || target.underlying?.kind !== "struct")
|
|
1773
|
+
return null;
|
|
1774
|
+
if (srcType?.name === targetName) return null;
|
|
1775
|
+
const srcU = srcType?.kind === "named" ? srcType.underlying : srcType;
|
|
1776
|
+
if (srcU?.kind !== "struct") return null;
|
|
1777
|
+
const cls = this._dequalify(targetName);
|
|
1778
|
+
return `Object.assign(new ${cls}(), ${this._cloneJs(srcType, inner)})`;
|
|
1779
|
+
},
|
|
1780
|
+
|
|
933
1781
|
_genTypeConversionCall(expr) {
|
|
934
1782
|
const targetType = expr._conversionTargetType;
|
|
935
1783
|
const arg = this.genExpr(expr.args[0]);
|
|
936
|
-
const
|
|
937
|
-
|
|
938
|
-
|
|
1784
|
+
const viaParam = this._genTypeParamConversion(targetType?.name, arg);
|
|
1785
|
+
if (targetType?.kind === "typeParam" && viaParam) return viaParam;
|
|
1786
|
+
const structConv = this._genStructClassConversion(
|
|
1787
|
+
targetType?.kind === "named" ? targetType.name : undefined,
|
|
1788
|
+
expr.args[0]?._type,
|
|
1789
|
+
arg,
|
|
1790
|
+
);
|
|
1791
|
+
if (structConv) return structConv;
|
|
939
1792
|
const base =
|
|
940
1793
|
targetType?.kind === "named" ? targetType.underlying : targetType;
|
|
1794
|
+
if (base?.kind === "map" && this._isNilLiteral(expr.args[0]))
|
|
1795
|
+
return this._nilMapZero();
|
|
941
1796
|
if (
|
|
942
1797
|
targetType?.kind === "named" &&
|
|
943
1798
|
base?.kind === "basic" &&
|
|
@@ -954,19 +1809,29 @@ export const expressionGenMethods = {
|
|
|
954
1809
|
(elem === "byte" || elem === "uint8") &&
|
|
955
1810
|
this._isStringExprType(expr.args[0]?._type)
|
|
956
1811
|
) {
|
|
957
|
-
|
|
958
|
-
return
|
|
959
|
-
}
|
|
960
|
-
if (isRaw) {
|
|
961
|
-
return `((u) => { u.__raw = true; return u; })(${arg})`;
|
|
1812
|
+
this.useHelper("u8enc");
|
|
1813
|
+
return `__u8enc(${arg})`;
|
|
962
1814
|
}
|
|
963
1815
|
}
|
|
964
1816
|
return arg;
|
|
965
1817
|
},
|
|
966
1818
|
|
|
1819
|
+
// `json.Marshal(v)` / `enc.Encode(v)` call the generated codec entry
|
|
1820
|
+
// (see src/lower/json-codecs.js); a stream method takes its receiver first.
|
|
1821
|
+
_jsonEntryCall(expr) {
|
|
1822
|
+
const sel = expr.func;
|
|
1823
|
+
const func = { kind: "Ident", name: sel._genEntry, _type: sel._type };
|
|
1824
|
+
const args = sel._jsonStream ? [sel.expr, ...expr.args] : expr.args;
|
|
1825
|
+
return { ...expr, func, args };
|
|
1826
|
+
},
|
|
1827
|
+
|
|
967
1828
|
genCall(expr) {
|
|
1829
|
+
if (expr.func._genEntry) return this.genCall(this._jsonEntryCall(expr));
|
|
968
1830
|
if (expr._isTypeConversion) return this._genTypeConversionCall(expr);
|
|
969
1831
|
|
|
1832
|
+
const native = this._genNativeCall(expr);
|
|
1833
|
+
if (native !== null) return native;
|
|
1834
|
+
|
|
970
1835
|
const funcExpr =
|
|
971
1836
|
expr.func.kind === "InstantiationExpr" ? expr.func.expr : expr.func;
|
|
972
1837
|
if (funcExpr.kind === "Ident") {
|
|
@@ -974,19 +1839,113 @@ export const expressionGenMethods = {
|
|
|
974
1839
|
if (builtin !== undefined) return builtin;
|
|
975
1840
|
}
|
|
976
1841
|
|
|
1842
|
+
const viaBox = this._genTypeParamMethodCall(expr);
|
|
1843
|
+
if (viaBox !== null) return viaBox;
|
|
1844
|
+
|
|
977
1845
|
if (expr.func.kind === "SelectorExpr") {
|
|
978
1846
|
const result = this._genSelectorCall(expr);
|
|
979
1847
|
if (result !== undefined) return result;
|
|
980
1848
|
}
|
|
981
1849
|
|
|
1850
|
+
const converted = this._convertedPointerMethodCall(expr);
|
|
1851
|
+
if (converted) return converted;
|
|
1852
|
+
|
|
1853
|
+
const nilSafe = this._nilSafeMethodClass(expr.func);
|
|
1854
|
+
if (nilSafe) {
|
|
1855
|
+
// The method tests its receiver for nil: call it through the
|
|
1856
|
+
// prototype so a nil `this` reaches it instead of a TypeError.
|
|
1857
|
+
const recv = this.genExpr(expr.func.expr);
|
|
1858
|
+
const rest = this._genCallArgs(expr);
|
|
1859
|
+
return `${nilSafe}.prototype.${expr.func.field}.call(${rest ? `${recv}, ${rest}` : recv})`;
|
|
1860
|
+
}
|
|
1861
|
+
|
|
982
1862
|
expr.func._callee = true;
|
|
983
1863
|
const rawFn = this.genExpr(expr.func);
|
|
984
1864
|
const fn = expr.func.kind === "FuncLit" ? `(${rawFn})` : rawFn;
|
|
985
|
-
|
|
986
|
-
const
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
1865
|
+
const args = this._genCallArgs(expr);
|
|
1866
|
+
const zeros = this._genericCallZeros(expr);
|
|
1867
|
+
if (!zeros) return `${fn}(${args})`;
|
|
1868
|
+
return `${fn}(${args ? `${zeros}, ${args}` : zeros})`;
|
|
1869
|
+
},
|
|
1870
|
+
|
|
1871
|
+
// `x.M(...)` on a type-parameter value: a raw value of a named non-struct
|
|
1872
|
+
// type argument is boxed (by the type's factory) to reach its methods.
|
|
1873
|
+
_genTypeParamMethodCall(expr) {
|
|
1874
|
+
if (!this._isTypeParamMethodCall(expr)) return null;
|
|
1875
|
+
const factory = this._typeParamZeroFactory(expr.func.expr._type.name);
|
|
1876
|
+
if (!factory) return null;
|
|
1877
|
+
this.useHelper("generics");
|
|
1878
|
+
const recv = `__tbox(${factory}, ${this.genExpr(expr.func.expr)})`;
|
|
1879
|
+
const args = this._genCallArgs(expr);
|
|
1880
|
+
return `${recv}.${expr.func.field}(${args})`;
|
|
1881
|
+
},
|
|
1882
|
+
|
|
1883
|
+
// `(*S)(p).M()` where `p` points at another struct type: the JS object keeps
|
|
1884
|
+
// its own class, so call S's prototype method on it.
|
|
1885
|
+
_convertedPointerMethodCall(expr) {
|
|
1886
|
+
const sel = expr.func;
|
|
1887
|
+
if (sel?.kind !== "SelectorExpr" || sel._isMethodExpr) return null;
|
|
1888
|
+
let recv = sel.expr;
|
|
1889
|
+
while (recv?.kind === "ParenExpr") recv = recv.expr;
|
|
1890
|
+
if (recv?._isTypeConversion !== true) return null;
|
|
1891
|
+
const target = recv._conversionTargetType;
|
|
1892
|
+
const from = recv.args?.[0]?._type;
|
|
1893
|
+
if (target?.kind !== "pointer" || target.base?.kind !== "named")
|
|
1894
|
+
return null;
|
|
1895
|
+
if (from?.kind !== "pointer" || from.base === target.base) return null;
|
|
1896
|
+
if (target.base.underlying?.kind !== "struct") return null;
|
|
1897
|
+
const cls = target.base.name;
|
|
1898
|
+
if (!target.base.methods?.has(sel.field)) return null;
|
|
1899
|
+
const rest = this._genCallArgs(expr);
|
|
1900
|
+
const obj = this.genExpr(recv.args[0]);
|
|
1901
|
+
return `${cls}.prototype.${sel.field}.call(${rest ? `${obj}, ${rest}` : obj})`;
|
|
1902
|
+
},
|
|
1903
|
+
|
|
1904
|
+
// The class whose prototype holds pointer-receiver method `field`, for a
|
|
1905
|
+
// pointer to a struct type that declares it directly (not promoted), so a
|
|
1906
|
+
// method value or expression can reach it with a nil receiver; else null.
|
|
1907
|
+
_ptrMethodProtoClass(ptr, field, sel) {
|
|
1908
|
+
if (ptr?.kind !== "pointer" || ptr.base?.kind !== "named") return null;
|
|
1909
|
+
if (ptr.base.underlying?.kind !== "struct") return null;
|
|
1910
|
+
if (sel && this._promotedPath(sel)) return null;
|
|
1911
|
+
if (!ptr.base.methods?.has(field)) return null;
|
|
1912
|
+
return ptr.base.name;
|
|
1913
|
+
},
|
|
1914
|
+
|
|
1915
|
+
// The class of a pointer-receiver struct method that accepts a nil receiver
|
|
1916
|
+
// when `func` selects it on a pointer of exactly that struct, else null.
|
|
1917
|
+
_nilSafeMethodClass(func) {
|
|
1918
|
+
if (func?.kind !== "SelectorExpr" || func._isMethodExpr) return null;
|
|
1919
|
+
const t = func.expr?._type;
|
|
1920
|
+
if (t?.kind !== "pointer" || t.base?.kind !== "named") return null;
|
|
1921
|
+
const cls = t.base.name;
|
|
1922
|
+
if (t.base.typeArgs?.length) return null;
|
|
1923
|
+
if (this.nilSafeMethods?.has(`${cls}.${func.field}`)) return cls;
|
|
1924
|
+
// A Go-source stdlib type (`Buffer$$bytes`) is declared outside this
|
|
1925
|
+
// package: its method decl tells whether it tests the receiver for nil.
|
|
1926
|
+
const decl = cls.includes("$$")
|
|
1927
|
+
? t.base.methods?.get(func.field)?._methodDecl
|
|
1928
|
+
: null;
|
|
1929
|
+
return decl?.recvPointer && comparesToNil(decl.body, decl.recvName)
|
|
1930
|
+
? cls
|
|
1931
|
+
: null;
|
|
1932
|
+
},
|
|
1933
|
+
|
|
1934
|
+
// The leading zero-value factories of a call to a generic function
|
|
1935
|
+
// (`F(x)` with inferred, `F[int](x)` with explicit type arguments), or null.
|
|
1936
|
+
_genericCallZeros(expr) {
|
|
1937
|
+
const site = expr._genericFnType
|
|
1938
|
+
? expr
|
|
1939
|
+
: expr.func.kind === "InstantiationExpr" && expr.func._genericFnType
|
|
1940
|
+
? expr.func
|
|
1941
|
+
: null;
|
|
1942
|
+
if (!site) return null;
|
|
1943
|
+
const { typeParams } = site._genericFnType;
|
|
1944
|
+
return this._typeArgFactories(
|
|
1945
|
+
typeParams,
|
|
1946
|
+
site._typeArgs,
|
|
1947
|
+
this._genericFnNeedsTypeInfo(site._genericFnType),
|
|
1948
|
+
).join(", ");
|
|
990
1949
|
},
|
|
991
1950
|
|
|
992
1951
|
_isErrorInterfaceType(t) {
|
|
@@ -1009,31 +1968,17 @@ export const expressionGenMethods = {
|
|
|
1009
1968
|
},
|
|
1010
1969
|
|
|
1011
1970
|
_genReceiverTypeCall(recvName, expr) {
|
|
1012
|
-
if (recvName === "strings.Builder" || recvName === "bytes.Buffer")
|
|
1013
|
-
return this._genBuilderCall(recvName, expr.func.field, expr);
|
|
1014
|
-
if (recvName === "regexp.Regexp")
|
|
1015
|
-
return this._genRegexpMethodCall(expr.func.field, expr);
|
|
1016
|
-
if (recvName === "time.Time")
|
|
1017
|
-
return this._genTimeMethodCall(expr.func.field, expr);
|
|
1018
|
-
if (recvName === "time.Duration")
|
|
1019
|
-
return this._genDurationMethodCall(expr.func.field, expr);
|
|
1020
|
-
if (recvName === "testing.T")
|
|
1021
|
-
return this._genTestingMethodCall(expr.func.field, expr);
|
|
1022
|
-
if (recvName === "strings.Replacer") {
|
|
1023
|
-
const recv = this.genExpr(expr.func.expr);
|
|
1024
|
-
const arg = this.genExpr(expr.args[0]);
|
|
1025
|
-
return `(${recv} ? ${recv}.Replace(${arg}) : ${arg})`;
|
|
1026
|
-
}
|
|
1027
1971
|
if (recvName?.startsWith("shared.") && expr.func.field === "Subarray") {
|
|
1028
1972
|
const args = expr.args.map((a) => this.genExpr(a)).join(", ");
|
|
1029
1973
|
return `${this.genExpr(expr.func.expr)}.subarray(${args})`;
|
|
1030
1974
|
}
|
|
1031
|
-
if (
|
|
1032
|
-
return this._genSimdReceiverCall(expr);
|
|
1975
|
+
if (/^(simd\.)?(F32x4|Mask32x4)$/.test(recvName ?? "")) {
|
|
1976
|
+
return this._genSimdReceiverCall(expr, recvName.endsWith("Mask32x4"));
|
|
1033
1977
|
}
|
|
1034
1978
|
if (recvName === "sync.Mutex" || recvName === "sync.RWMutex") {
|
|
1035
1979
|
if (expr.func.field === "TryLock" || expr.func.field === "TryRLock")
|
|
1036
1980
|
return "true";
|
|
1981
|
+
if (expr.func.field === "RLocker") return "({ Lock() {}, Unlock() {} })";
|
|
1037
1982
|
return "(void 0)";
|
|
1038
1983
|
}
|
|
1039
1984
|
if (recvName === "sync.Once" && expr.func.field === "Do") {
|
|
@@ -1051,58 +1996,18 @@ export const expressionGenMethods = {
|
|
|
1051
1996
|
}
|
|
1052
1997
|
if (expr.func.field === "Wait") return "(void 0)";
|
|
1053
1998
|
}
|
|
1054
|
-
if (
|
|
1055
|
-
recvName === "binary.LittleEndian" ||
|
|
1056
|
-
recvName === "binary.BigEndian" ||
|
|
1057
|
-
recvName === "encoding/binary.LittleEndian" ||
|
|
1058
|
-
recvName === "encoding/binary.BigEndian" ||
|
|
1059
|
-
recvName === "binary.ByteOrder" ||
|
|
1060
|
-
recvName === "encoding/binary.ByteOrder"
|
|
1061
|
-
) {
|
|
1062
|
-
this.useHelper("binary");
|
|
1063
|
-
const recv = this.genExpr(expr.func.expr);
|
|
1064
|
-
const args = expr.args.map((a) => this.genExpr(a)).join(", ");
|
|
1065
|
-
return `${recv}.${expr.func.field}(${args})`;
|
|
1066
|
-
}
|
|
1067
|
-
if (
|
|
1068
|
-
recvName === "base64.Encoding" ||
|
|
1069
|
-
recvName === "encoding/base64.Encoding"
|
|
1070
|
-
) {
|
|
1071
|
-
this.useHelper("base64");
|
|
1072
|
-
const recv = this.genExpr(expr.func.expr);
|
|
1073
|
-
const args = expr.args.map((a) => this.genExpr(a)).join(", ");
|
|
1074
|
-
return `${recv}.${expr.func.field}(${args})`;
|
|
1075
|
-
}
|
|
1076
1999
|
return undefined;
|
|
1077
2000
|
},
|
|
1078
2001
|
|
|
1079
|
-
_genSimdReceiverCall(expr) {
|
|
2002
|
+
_genSimdReceiverCall(expr, isMask = false) {
|
|
1080
2003
|
this.useHelper("simd");
|
|
1081
2004
|
const method = expr.func.field;
|
|
1082
2005
|
const recv = this.genExpr(expr.func.expr);
|
|
1083
|
-
const
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
Min: "__simd_min",
|
|
1089
|
-
Max: "__simd_max",
|
|
1090
|
-
Dot3: "__simd_dot3",
|
|
1091
|
-
Dot4: "__simd_dot4",
|
|
1092
|
-
};
|
|
1093
|
-
if (helpers[method]) {
|
|
1094
|
-
return `${helpers[method]}(${recv}, ${this.genExpr(expr.args[0])})`;
|
|
1095
|
-
}
|
|
1096
|
-
if (method === "Lane") {
|
|
1097
|
-
return `${recv}[${this.genExpr(expr.args[0])}]`;
|
|
1098
|
-
}
|
|
1099
|
-
if (method === "WithLane") {
|
|
1100
|
-
return `__simd_with_lane(${recv}, ${this.genExpr(expr.args[0])}, ${this.genExpr(expr.args[1])})`;
|
|
1101
|
-
}
|
|
1102
|
-
if (method === "Splat") {
|
|
1103
|
-
return `__simd_splat(${this.genExpr(expr.args[0])})`;
|
|
1104
|
-
}
|
|
1105
|
-
return undefined;
|
|
2006
|
+
const args = expr.args.map((a) => this.genExpr(a));
|
|
2007
|
+
if (method === "Lane") return `${recv}[${args[0]}]`;
|
|
2008
|
+
if (method === "Splat" && !isMask) return `__simd_splat(${args[0]})`;
|
|
2009
|
+
const helper = (isMask ? SIMD_MASK_HELPERS : SIMD_VECTOR_HELPERS)[method];
|
|
2010
|
+
return helper ? `${helper}(${[recv, ...args].join(", ")})` : undefined;
|
|
1106
2011
|
},
|
|
1107
2012
|
|
|
1108
2013
|
_genSelectorCall(expr) {
|
|
@@ -1116,12 +2021,44 @@ export const expressionGenMethods = {
|
|
|
1116
2021
|
const ns = expr.func.expr.name;
|
|
1117
2022
|
const fn = expr.func.field;
|
|
1118
2023
|
const result = this._genStdlibCall(ns, fn, expr);
|
|
1119
|
-
if (result !== undefined)
|
|
2024
|
+
if (result !== undefined) {
|
|
2025
|
+
if (this._is64BitInt(expr._type)) {
|
|
2026
|
+
return `((__r) => typeof __r === "number" ? BigInt(__r) : __r)(${result})`;
|
|
2027
|
+
}
|
|
2028
|
+
if (
|
|
2029
|
+
expr._type?.kind === "tuple" &&
|
|
2030
|
+
expr._type.types.some((t) => this._is64BitInt(t))
|
|
2031
|
+
) {
|
|
2032
|
+
const casts = expr._type.types.map((t, i) =>
|
|
2033
|
+
this._is64BitInt(t)
|
|
2034
|
+
? `typeof __r[${i}] === "number" ? BigInt(__r[${i}]) : __r[${i}]`
|
|
2035
|
+
: `__r[${i}]`,
|
|
2036
|
+
);
|
|
2037
|
+
return `((__r) => [${casts.join(", ")}])(${result})`;
|
|
2038
|
+
}
|
|
2039
|
+
return result;
|
|
2040
|
+
}
|
|
1120
2041
|
}
|
|
1121
2042
|
const recvType = expr.func.expr._type;
|
|
1122
2043
|
const recvName = this._resolveRecvName(recvType);
|
|
1123
2044
|
const typed = this._genReceiverTypeCall(recvName, expr);
|
|
1124
|
-
if (typed !== undefined)
|
|
2045
|
+
if (typed !== undefined) {
|
|
2046
|
+
if (this._is64BitInt(expr._type)) {
|
|
2047
|
+
return `((__r) => typeof __r === "number" ? BigInt(__r) : __r)(${typed})`;
|
|
2048
|
+
}
|
|
2049
|
+
if (
|
|
2050
|
+
expr._type?.kind === "tuple" &&
|
|
2051
|
+
expr._type.types.some((t) => this._is64BitInt(t))
|
|
2052
|
+
) {
|
|
2053
|
+
const casts = expr._type.types.map((t, i) =>
|
|
2054
|
+
this._is64BitInt(t)
|
|
2055
|
+
? `typeof __r[${i}] === "number" ? BigInt(__r[${i}]) : __r[${i}]`
|
|
2056
|
+
: `__r[${i}]`,
|
|
2057
|
+
);
|
|
2058
|
+
return `((__r) => [${casts.join(", ")}])(${typed})`;
|
|
2059
|
+
}
|
|
2060
|
+
return typed;
|
|
2061
|
+
}
|
|
1125
2062
|
const namedCall = this._genNamedMethodCall(expr);
|
|
1126
2063
|
if (namedCall !== undefined) return namedCall;
|
|
1127
2064
|
if (this.strict && this._isInterfaceType(recvType)) {
|
|
@@ -1137,6 +2074,16 @@ export const expressionGenMethods = {
|
|
|
1137
2074
|
return undefined;
|
|
1138
2075
|
},
|
|
1139
2076
|
|
|
2077
|
+
// `len`/`cap` of an array or array pointer is a constant, but an operand
|
|
2078
|
+
// containing a call or receive is still evaluated (and a nil pointer is
|
|
2079
|
+
// not dereferenced).
|
|
2080
|
+
_genConstLen(expr) {
|
|
2081
|
+
const arg = expr.args[0];
|
|
2082
|
+
if (arg && hasCallOrReceive(arg))
|
|
2083
|
+
return `(${this.genExpr(arg)}, ${expr._constLen})`;
|
|
2084
|
+
return String(expr._constLen);
|
|
2085
|
+
},
|
|
2086
|
+
|
|
1140
2087
|
_isInterfaceType(t) {
|
|
1141
2088
|
if (!t) return false;
|
|
1142
2089
|
if (t.kind === "interface") return true;
|
|
@@ -1144,13 +2091,13 @@ export const expressionGenMethods = {
|
|
|
1144
2091
|
},
|
|
1145
2092
|
|
|
1146
2093
|
_genBuiltinLen(expr) {
|
|
1147
|
-
if (expr._constLen != null) return
|
|
2094
|
+
if (expr._constLen != null) return this._genConstLen(expr);
|
|
1148
2095
|
const arg = expr.args[0];
|
|
1149
2096
|
const t = arg?._type;
|
|
1150
2097
|
const wrapField = this._namedWrapperField(t, arg);
|
|
1151
2098
|
if (wrapField) return `${this.genExpr(arg)}.${wrapField}.length`;
|
|
1152
2099
|
const js = this.genExpr(arg);
|
|
1153
|
-
if (t?.kind === "map") return `Object.keys(${js}).length`;
|
|
2100
|
+
if (t?.kind === "map") return `Object.keys(${js} ?? {}).length`;
|
|
1154
2101
|
this.useHelper("len");
|
|
1155
2102
|
return `__len(${js})`;
|
|
1156
2103
|
},
|
|
@@ -1164,7 +2111,12 @@ export const expressionGenMethods = {
|
|
|
1164
2111
|
(t?.kind === "named" && t.underlying?.kind === "map")
|
|
1165
2112
|
)
|
|
1166
2113
|
return `((__m) => { for (const __k in __m) delete __m[__k]; })(${js})`;
|
|
1167
|
-
|
|
2114
|
+
// A slice: every element goes back to its zero value (the length stays).
|
|
2115
|
+
const st = t?.kind === "named" ? t.underlying : t;
|
|
2116
|
+
const zero = this.zeroValueForType(st?.elem);
|
|
2117
|
+
if (/^(new |\{|\[|Array|__nm\()|\$z_/.test(zero))
|
|
2118
|
+
return `((__s) => { if (__s) for (let __i = 0; __i < __s.length; __i++) __s[__i] = ${zero}; })(${js})`;
|
|
2119
|
+
return `(${js})?.fill(${zero})`;
|
|
1168
2120
|
},
|
|
1169
2121
|
|
|
1170
2122
|
_genBuiltinIdent(name, expr) {
|
|
@@ -1172,6 +2124,15 @@ export const expressionGenMethods = {
|
|
|
1172
2124
|
return gen ? gen(this, expr) : undefined;
|
|
1173
2125
|
},
|
|
1174
2126
|
|
|
2127
|
+
// The source operand of `append(b, x...)` / `copy(b, x)`: a string
|
|
2128
|
+
// contributes its UTF-8 bytes.
|
|
2129
|
+
_genSliceSource(node) {
|
|
2130
|
+
const js = this.genExpr(node);
|
|
2131
|
+
if (!this._isStringExprType(node._type)) return js;
|
|
2132
|
+
this.useHelper("u8enc");
|
|
2133
|
+
return `__u8enc(${js})`;
|
|
2134
|
+
},
|
|
2135
|
+
|
|
1175
2136
|
genAppend(expr) {
|
|
1176
2137
|
const retType = expr._type;
|
|
1177
2138
|
const wrapField = this._namedWrapperField(retType, expr.args[0]);
|
|
@@ -1181,16 +2142,36 @@ export const expressionGenMethods = {
|
|
|
1181
2142
|
const elems = expr.args
|
|
1182
2143
|
.slice(1)
|
|
1183
2144
|
.map((a) =>
|
|
1184
|
-
a._spread
|
|
2145
|
+
a._spread
|
|
2146
|
+
? `...(${this._genSliceSource(a)} ?? [])`
|
|
2147
|
+
: this.genValueExpr(a),
|
|
1185
2148
|
);
|
|
1186
2149
|
if (elems.length === 0) return this.genExpr(expr.args[0]);
|
|
1187
2150
|
this.useHelper("append");
|
|
1188
2151
|
return `new ${retType.name}(__append(${sliceJS}, ${elems.join(", ")}))`;
|
|
1189
2152
|
}
|
|
1190
2153
|
const slice = this.genExpr(expr.args[0]);
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
2154
|
+
// Spread elements of array type are values: append copies them.
|
|
2155
|
+
const st = expr._type ?? expr.args[0]._type;
|
|
2156
|
+
const elemT = (st?.kind === "named" ? st.underlying : st)?.elem;
|
|
2157
|
+
const spreadOf = (a) =>
|
|
2158
|
+
this._aggregateBase(elemT)
|
|
2159
|
+
? `...(${this.genExpr(a)} ?? []).map((__e) => ${this._cloneJs(elemT, "__e")})`
|
|
2160
|
+
: `...(${this._genSliceSource(a)} ?? [])`;
|
|
2161
|
+
const is64Elem = this._is64BitInt(elemT);
|
|
2162
|
+
const elems = expr.args.slice(1).map((a) => {
|
|
2163
|
+
if (a._spread) return spreadOf(a);
|
|
2164
|
+
const val = this.genValueExpr(a);
|
|
2165
|
+
if (
|
|
2166
|
+
is64Elem &&
|
|
2167
|
+
a._type?.kind === "untyped" &&
|
|
2168
|
+
!val.endsWith("n") &&
|
|
2169
|
+
/^-?\d+$/.test(val)
|
|
2170
|
+
) {
|
|
2171
|
+
return `${val}n`;
|
|
2172
|
+
}
|
|
2173
|
+
return val;
|
|
2174
|
+
});
|
|
1194
2175
|
if (elems.length === 0) return slice;
|
|
1195
2176
|
this.useHelper("append");
|
|
1196
2177
|
const sliceType = expr._type ?? expr.args[0]._type;
|
|
@@ -1199,22 +2180,55 @@ export const expressionGenMethods = {
|
|
|
1199
2180
|
(sliceType.kind === "named" ? sliceType.underlying : sliceType).elem,
|
|
1200
2181
|
)
|
|
1201
2182
|
: null;
|
|
1202
|
-
if (typedCtor)
|
|
1203
|
-
|
|
2183
|
+
if (typedCtor) {
|
|
2184
|
+
const single = expr.args.length === 2 && expr.args[1]._spread;
|
|
2185
|
+
// One source slice or string: append it as it is, not spread.
|
|
2186
|
+
if (single)
|
|
2187
|
+
return `__appendv((${slice}) ?? __tn(${typedCtor}), ${this._genSliceSource(expr.args[1])} ?? [])`;
|
|
2188
|
+
return `__append((${slice}) ?? __tn(${typedCtor}), ${elems.join(", ")})`;
|
|
2189
|
+
}
|
|
2190
|
+
// A json codec grows a slice it has just made and no one else holds:
|
|
2191
|
+
// pushing in place is O(1), where __append copies the whole slice.
|
|
2192
|
+
if (
|
|
2193
|
+
this._inJsonCodecFile() &&
|
|
2194
|
+
expr.args.length === 2 &&
|
|
2195
|
+
expr.args[0].kind === "Ident" &&
|
|
2196
|
+
!expr.args[1]._spread
|
|
2197
|
+
) {
|
|
2198
|
+
this.useHelper("pushs");
|
|
2199
|
+
return `__pushs(${slice}, ${elems[0]})`;
|
|
2200
|
+
}
|
|
1204
2201
|
return `__append(${slice}, ${elems.join(", ")})`;
|
|
1205
2202
|
},
|
|
1206
2203
|
|
|
2204
|
+
// A `make` length or capacity expression wrapped in its negative-value
|
|
2205
|
+
// check (a literal needs none).
|
|
2206
|
+
_guardMakeSize(code, guard) {
|
|
2207
|
+
if (/^\d+$/.test(code)) return code;
|
|
2208
|
+
this.useHelper("mklen");
|
|
2209
|
+
return `${guard}(${code})`;
|
|
2210
|
+
},
|
|
2211
|
+
|
|
1207
2212
|
genMake(expr) {
|
|
1208
2213
|
// make([]T, n) or make([]T, n, cap) → new Array(n).fill(zero)
|
|
1209
2214
|
// make(map[K]V) → {}
|
|
1210
2215
|
const typeArg = expr.args[0];
|
|
1211
2216
|
const typeNode = typeArg.kind === "TypeExpr" ? typeArg.type : typeArg;
|
|
1212
|
-
|
|
2217
|
+
// A defined type (`type S []int`) makes like its underlying type.
|
|
2218
|
+
const madeType =
|
|
2219
|
+
typeArg._coreType ??
|
|
2220
|
+
(typeArg._type?.kind === "named"
|
|
2221
|
+
? typeArg._type.underlying
|
|
2222
|
+
: typeArg._type);
|
|
2223
|
+
const resolvedKind = madeType?.kind;
|
|
1213
2224
|
if (typeNode.kind === "SliceType" || resolvedKind === "slice") {
|
|
1214
|
-
|
|
2225
|
+
let n = expr.args[1] ? this.genExpr(expr.args[1]) : "0";
|
|
2226
|
+
if (expr.args[1] && this._is64BitInt(expr.args[1]._type))
|
|
2227
|
+
n = `Number(${n})`;
|
|
2228
|
+
// A negative length or capacity panics with Go's message.
|
|
2229
|
+
n = this._guardMakeSize(n, "__mkn");
|
|
1215
2230
|
const elemNode = typeNode.kind === "SliceType" ? typeNode.elem : null;
|
|
1216
|
-
const elemResolved =
|
|
1217
|
-
typeArg._type?.kind === "slice" ? typeArg._type.elem : null;
|
|
2231
|
+
const elemResolved = madeType?.kind === "slice" ? madeType.elem : null;
|
|
1218
2232
|
const typedCtor =
|
|
1219
2233
|
typedArrayConstructorForElem(elemResolved) ??
|
|
1220
2234
|
(elemNode?.name
|
|
@@ -1223,8 +2237,19 @@ export const expressionGenMethods = {
|
|
|
1223
2237
|
name: elemNode.name,
|
|
1224
2238
|
})
|
|
1225
2239
|
: null);
|
|
2240
|
+
// make([]T, n, cap) allocates cap elements and returns a view of
|
|
2241
|
+
// the first n (`__mkslice`), so append within cap shares them.
|
|
2242
|
+
let capArg = expr.args[2] ? this.genExpr(expr.args[2]) : null;
|
|
2243
|
+
if (capArg && expr.args[2] && this._is64BitInt(expr.args[2]._type))
|
|
2244
|
+
capArg = `Number(${capArg})`;
|
|
2245
|
+
if (capArg) {
|
|
2246
|
+
this.useHelper("slice");
|
|
2247
|
+
capArg = this._guardMakeSize(capArg, "__mkc");
|
|
2248
|
+
}
|
|
2249
|
+
const withCap = (alloc) =>
|
|
2250
|
+
capArg ? `__mkslice(${n}, ${alloc(capArg)})` : alloc(n);
|
|
1226
2251
|
if (typedCtor) {
|
|
1227
|
-
return `new ${typedCtor}(${
|
|
2252
|
+
return withCap((k) => `new ${typedCtor}(${k})`);
|
|
1228
2253
|
}
|
|
1229
2254
|
// Use the proper zero value for the element type (e.g. new Point() not 0)
|
|
1230
2255
|
let zero = "null";
|
|
@@ -1234,29 +2259,23 @@ export const expressionGenMethods = {
|
|
|
1234
2259
|
zero = this.zeroValueForTypeNode(elemNode);
|
|
1235
2260
|
}
|
|
1236
2261
|
// If zero value is a constructor call, use a factory function so each element is distinct
|
|
1237
|
-
if (zero.startsWith("new ")) {
|
|
1238
|
-
return `Array.from({length: ${
|
|
2262
|
+
if (zero.startsWith("new ") || zero.includes("$z_")) {
|
|
2263
|
+
return withCap((k) => `Array.from({length: ${k}}, () => ${zero})`);
|
|
1239
2264
|
}
|
|
1240
|
-
return `new Array(${
|
|
2265
|
+
return withCap((k) => `new Array(${k}).fill(${zero})`);
|
|
1241
2266
|
}
|
|
1242
|
-
// map or fallback
|
|
2267
|
+
// map or fallback. The json codecs' maps hold decoded keys, among them
|
|
2268
|
+
// "__proto__": without a prototype that is an ordinary key.
|
|
2269
|
+
if (
|
|
2270
|
+
this._inJsonCodecFile() &&
|
|
2271
|
+
(resolvedKind === "map" || typeNode.kind === "MapType")
|
|
2272
|
+
)
|
|
2273
|
+
return "Object.create(null)";
|
|
1243
2274
|
return "{}";
|
|
1244
2275
|
},
|
|
1245
2276
|
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
_genStructFields(elems) {
|
|
1249
|
-
return elems
|
|
1250
|
-
.map((e) => {
|
|
1251
|
-
if (e._positionalField) {
|
|
1252
|
-
return `${e._positionalField}: ${this.genExpr(e)}`;
|
|
1253
|
-
}
|
|
1254
|
-
if (e.kind !== "KeyValueExpr") return null;
|
|
1255
|
-
if (e._isEmbedInit) return `...${this.genExpr(e.value)}`;
|
|
1256
|
-
return `${e.key.name ?? this.genExpr(e.key)}: ${this.genExpr(e.value)}`;
|
|
1257
|
-
})
|
|
1258
|
-
.filter((s) => s !== null)
|
|
1259
|
-
.join(", ");
|
|
2277
|
+
_inJsonCodecFile() {
|
|
2278
|
+
return this._jsonCodecFileIdx === (this._currentSrcFileIdx ?? 0);
|
|
1260
2279
|
},
|
|
1261
2280
|
|
|
1262
2281
|
_isSliceOrArrayType(t) {
|
|
@@ -1267,22 +2286,42 @@ export const expressionGenMethods = {
|
|
|
1267
2286
|
return t?.kind === "MapType";
|
|
1268
2287
|
},
|
|
1269
2288
|
|
|
2289
|
+
// The one place a map literal key becomes a JS property name: a plain
|
|
2290
|
+
// string literal stays as is, any other key is computed so that the key
|
|
2291
|
+
// encoding of `genExpr` (`__ik`, struct keys) applies.
|
|
2292
|
+
_genMapLitKey(key) {
|
|
2293
|
+
return key.litKind === "STRING"
|
|
2294
|
+
? JSON.stringify(key.value)
|
|
2295
|
+
: `[${this.genExpr(key)}]`;
|
|
2296
|
+
},
|
|
2297
|
+
|
|
1270
2298
|
_genMapLitEntries(elems) {
|
|
1271
2299
|
return elems
|
|
1272
|
-
.map((e) =>
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
: `[${this.genExpr(e.key)}]`;
|
|
1278
|
-
return `${k}: ${this.genValueExpr(e.value)}`;
|
|
1279
|
-
}
|
|
1280
|
-
return this.genValueExpr(e);
|
|
1281
|
-
})
|
|
2300
|
+
.map((e) =>
|
|
2301
|
+
e.kind === "KeyValueExpr"
|
|
2302
|
+
? `${this._genMapLitKey(e.key)}: ${this.genValueExpr(e.value)}`
|
|
2303
|
+
: this.genValueExpr(e),
|
|
2304
|
+
)
|
|
1282
2305
|
.join(", ");
|
|
1283
2306
|
},
|
|
1284
2307
|
|
|
2308
|
+
// A literal whose (possibly elided or named) type is a map type.
|
|
2309
|
+
_isMapLit(expr) {
|
|
2310
|
+
let t = expr._type;
|
|
2311
|
+
for (let i = 0; t?.kind === "named" && i < 32; i++) t = t.underlying;
|
|
2312
|
+
return t?.kind === "map";
|
|
2313
|
+
},
|
|
2314
|
+
|
|
1285
2315
|
_genFallbackCompositeLit(expr) {
|
|
2316
|
+
if (this._isMapLit(expr))
|
|
2317
|
+
return `{ ${this._genMapLitEntries(expr.elems)} }`;
|
|
2318
|
+
const at = this._litArrayType(expr);
|
|
2319
|
+
if (at) {
|
|
2320
|
+
// A named array type without methods (`type V [3]int; V{1}`).
|
|
2321
|
+
const elems = this._genArrayLitElems(at, expr);
|
|
2322
|
+
const ctor = typedArrayConstructorForElem(at.elem);
|
|
2323
|
+
return ctor ? `new ${ctor}([${elems}])` : `[${elems}]`;
|
|
2324
|
+
}
|
|
1286
2325
|
if (expr.elems.length > 0 && expr.elems[0]?.kind === "KeyValueExpr") {
|
|
1287
2326
|
const fields = expr.elems
|
|
1288
2327
|
.map(
|
|
@@ -1323,7 +2362,7 @@ export const expressionGenMethods = {
|
|
|
1323
2362
|
(ce) => (ce._positionalField ?? ce.key?.name) === ef,
|
|
1324
2363
|
);
|
|
1325
2364
|
values[idx] = match
|
|
1326
|
-
? this.genValueExpr(match.value
|
|
2365
|
+
? this.genValueExpr(match.kind === "KeyValueExpr" ? match.value : match)
|
|
1327
2366
|
: fields[idx].zero;
|
|
1328
2367
|
} else {
|
|
1329
2368
|
const valExpr = this.genExpr(e.value);
|
|
@@ -1364,6 +2403,8 @@ export const expressionGenMethods = {
|
|
|
1364
2403
|
|
|
1365
2404
|
_genPositionalStructLit(typeName, expr) {
|
|
1366
2405
|
const fields = this.getStructFields(typeName);
|
|
2406
|
+
const generic = this._genGenericStructLit(fields, expr);
|
|
2407
|
+
if (generic) return generic;
|
|
1367
2408
|
if (!fields || fields.length === 0 || expr.elems.length === 0) {
|
|
1368
2409
|
return `new ${typeName}()`;
|
|
1369
2410
|
}
|
|
@@ -1381,10 +2422,95 @@ export const expressionGenMethods = {
|
|
|
1381
2422
|
return inst;
|
|
1382
2423
|
},
|
|
1383
2424
|
|
|
1384
|
-
|
|
1385
|
-
|
|
2425
|
+
// `Box[int]{…}`: every field is passed (unset ones as undefined, so the
|
|
2426
|
+
// constructor starts them at their zero value) followed by the factories.
|
|
2427
|
+
_genGenericStructLit(fields, expr) {
|
|
2428
|
+
const t = expr._type;
|
|
2429
|
+
if (t?.kind !== "named" || !t.typeArgs?.length) return null;
|
|
2430
|
+
const values = fields.map(() => undefined);
|
|
2431
|
+
if (expr.elems.length > 0) {
|
|
2432
|
+
const hasPositional = expr.elems.some(
|
|
2433
|
+
(e) => e._positionalField || e.kind !== "KeyValueExpr",
|
|
2434
|
+
);
|
|
2435
|
+
if (hasPositional) this._fillPositionalStructElems(fields, expr, values);
|
|
2436
|
+
else this._fillKeyedStructElems(fields, expr, values);
|
|
2437
|
+
}
|
|
2438
|
+
return this._genericStructNew(t.name, t.typeArgs, values);
|
|
2439
|
+
},
|
|
2440
|
+
|
|
2441
|
+
// The array type of a literal (through named types), or null.
|
|
2442
|
+
_litArrayType(expr) {
|
|
2443
|
+
let t = expr._type;
|
|
2444
|
+
while (t?.kind === "named") t = t.underlying;
|
|
2445
|
+
return t?.kind === "array" && typeof t.size === "number" ? t : null;
|
|
2446
|
+
},
|
|
2447
|
+
|
|
2448
|
+
// Elements of a literal of array type `at` (or `{ size, elem }` for a
|
|
2449
|
+
// slice), placed at their (keyed) indices, with every other slot the
|
|
2450
|
+
// element zero value: `[3]int{1}` is `[1, 0, 0]` and `[5]int{2: 7}` is
|
|
2451
|
+
// `[0, 0, 7, 0, 0]`, as in Go. Keys out of ascending order would
|
|
2452
|
+
// evaluate the elements out of source order, so the values are then
|
|
2453
|
+
// computed first and spread into place.
|
|
2454
|
+
_genArrayLitElems(at, expr) {
|
|
2455
|
+
const values = new Array(at.size).fill(null);
|
|
2456
|
+
const indices = compositeLitIndices(expr.elems);
|
|
2457
|
+
const src = expr.elems.map((e) =>
|
|
2458
|
+
this.genValueExpr(e.kind === "KeyValueExpr" ? e.value : e),
|
|
2459
|
+
);
|
|
2460
|
+
const ascending = indices.every((v, i) => i === 0 || v > indices[i - 1]);
|
|
2461
|
+
indices.forEach((idx, i) => {
|
|
2462
|
+
values[idx] = ascending ? src[i] : `v$[${i}]`;
|
|
2463
|
+
});
|
|
2464
|
+
for (let i = 0; i < values.length; i++)
|
|
2465
|
+
values[i] ??= this.zeroValueForType(at.elem);
|
|
2466
|
+
if (ascending) return values.join(", ");
|
|
2467
|
+
return `...((v$) => [${values.join(", ")}])([${src.join(", ")}])`;
|
|
2468
|
+
},
|
|
2469
|
+
|
|
2470
|
+
// `{ size, elem }` for a slice or array literal (through named types).
|
|
2471
|
+
_litIndexedShape(expr) {
|
|
2472
|
+
const at = this._litArrayType(expr);
|
|
2473
|
+
if (at) return at;
|
|
2474
|
+
let t = expr._type;
|
|
2475
|
+
while (t?.kind === "named") t = t.underlying;
|
|
2476
|
+
if (t?.kind !== "slice") return null;
|
|
2477
|
+
// A loop, not a spread: huge literals would overflow the call stack.
|
|
2478
|
+
let max = -1;
|
|
2479
|
+
for (const i of compositeLitIndices(expr.elems)) if (i > max) max = i;
|
|
2480
|
+
const size = max + 1;
|
|
2481
|
+
return { size, elem: t.elem };
|
|
2482
|
+
},
|
|
2483
|
+
|
|
2484
|
+
// A large array literal that leaves most slots zero (`[100000]byte{}`,
|
|
2485
|
+
// `[65536]int{1: 2}`) starts from the zero array, as `var a [N]T` does,
|
|
2486
|
+
// and assigns only the given elements instead of spelling out every
|
|
2487
|
+
// slot. Small or mostly-filled literals stay inline (null).
|
|
2488
|
+
_genSparseArrayLit(expr) {
|
|
2489
|
+
const at = this._litArrayType(expr);
|
|
2490
|
+
if (!at || at.size <= SPARSE_ARRAY_MIN) return null;
|
|
2491
|
+
if (expr.elems.length * 2 >= at.size) return null;
|
|
2492
|
+
const zero = this.zeroValueForType(at);
|
|
2493
|
+
if (expr.elems.length === 0) return zero;
|
|
2494
|
+
const indices = compositeLitIndices(expr.elems);
|
|
2495
|
+
const sets = expr.elems.map((e, i) => {
|
|
2496
|
+
const v = this.genValueExpr(e.kind === "KeyValueExpr" ? e.value : e);
|
|
2497
|
+
return `a$[${indices[i]}] = (${v})`;
|
|
2498
|
+
});
|
|
2499
|
+
return `((a$) => (${sets.join(", ")}, a$))(${zero})`;
|
|
2500
|
+
},
|
|
2501
|
+
|
|
2502
|
+
_genLitElems(expr) {
|
|
2503
|
+
const at = this._litIndexedShape(expr);
|
|
2504
|
+
if (at) return this._genArrayLitElems(at, expr);
|
|
2505
|
+
return expr.elems
|
|
1386
2506
|
.map((e) => this.genValueExpr(e.kind === "KeyValueExpr" ? e.value : e))
|
|
1387
2507
|
.join(", ");
|
|
2508
|
+
},
|
|
2509
|
+
|
|
2510
|
+
_genSliceOrArrayLit(t, expr) {
|
|
2511
|
+
const sparse = this._genSparseArrayLit(expr);
|
|
2512
|
+
if (sparse) return sparse;
|
|
2513
|
+
const elems = this._genLitElems(expr);
|
|
1388
2514
|
const elemNode =
|
|
1389
2515
|
t.kind === "SliceType" || t.kind === "ArrayType" ? t.elem : null;
|
|
1390
2516
|
const elemResolved =
|
|
@@ -1409,10 +2535,8 @@ export const expressionGenMethods = {
|
|
|
1409
2535
|
const t = expr.typeExpr;
|
|
1410
2536
|
if (t === null) return this._genImplicitCompositeLit(expr);
|
|
1411
2537
|
const typeName = this.getTypeName(t);
|
|
1412
|
-
if (typeName === "
|
|
1413
|
-
return "
|
|
1414
|
-
if (typeName === "strings.Builder" || typeName === "Builder")
|
|
1415
|
-
return '{ _buf: "" }';
|
|
2538
|
+
if (typeName === "simd.Mask32x4" || typeName === "Mask32x4")
|
|
2539
|
+
return "Object.freeze([false, false, false, false])";
|
|
1416
2540
|
if (typeName === "simd.F32x4" || typeName === "F32x4") {
|
|
1417
2541
|
this.useHelper("simd");
|
|
1418
2542
|
const a = expr.elems[0] ? this.genExpr(expr.elems[0]) : "0";
|
|
@@ -1421,6 +2545,8 @@ export const expressionGenMethods = {
|
|
|
1421
2545
|
const d = expr.elems[3] ? this.genExpr(expr.elems[3]) : "0";
|
|
1422
2546
|
return `__simd_f32x4(${a}, ${b}, ${c}, ${d})`;
|
|
1423
2547
|
}
|
|
2548
|
+
const aliasLit = this._genAliasLit(t, typeName, expr);
|
|
2549
|
+
if (aliasLit !== null) return aliasLit;
|
|
1424
2550
|
if (typeName && this.structNames.has(typeName))
|
|
1425
2551
|
return this._genPositionalStructLit(typeName, expr);
|
|
1426
2552
|
if (typeName && this.namedWrapperNames.has(typeName))
|
|
@@ -1434,14 +2560,68 @@ export const expressionGenMethods = {
|
|
|
1434
2560
|
return this._genFallbackCompositeLit(expr);
|
|
1435
2561
|
},
|
|
1436
2562
|
|
|
2563
|
+
// Literals whose type expression is not the class to construct: a generic
|
|
2564
|
+
// alias `A[X]{…}`, an anonymous `struct{…}{…}`, or `P{…}` for
|
|
2565
|
+
// `type P = struct{…}`. Null for every other literal.
|
|
2566
|
+
_genAliasLit(t, typeName, expr) {
|
|
2567
|
+
if (t.kind === "GenericTypeName" || t.kind === "InstantiationExpr")
|
|
2568
|
+
return this._genGenericAliasLit(typeName, expr);
|
|
2569
|
+
const anon =
|
|
2570
|
+
t.kind === "StructType" ||
|
|
2571
|
+
((t.kind === "TypeName" || t.kind === "Ident") &&
|
|
2572
|
+
this.checker?.types.get(t.name)?.kind === "struct");
|
|
2573
|
+
return anon ? this._genAnonStructLit(expr) : null;
|
|
2574
|
+
},
|
|
2575
|
+
|
|
2576
|
+
// `A[X]{…}` for a generic alias `type A[P any] = T`: the literal has the
|
|
2577
|
+
// aliased type (expr._type), so dispatch on that instead of on `A`.
|
|
2578
|
+
_genGenericAliasLit(typeName, expr) {
|
|
2579
|
+
if (!this.checker?.types.get(typeName)?._generic?.isAlias) return null;
|
|
2580
|
+
const rt = expr._type;
|
|
2581
|
+
const base = rt?.kind === "named" ? rt.underlying : rt;
|
|
2582
|
+
if (rt?.kind === "named" && this.structNames.has(rt.name))
|
|
2583
|
+
return this._genPositionalStructLit(rt.name, expr);
|
|
2584
|
+
if (base?.kind === "map")
|
|
2585
|
+
return `{ ${this._genMapLitEntries(expr.elems)} }`;
|
|
2586
|
+
if (base?.kind === "struct") return this._genAnonStructLit(expr);
|
|
2587
|
+
return this._genImplicitCompositeLit(expr);
|
|
2588
|
+
},
|
|
2589
|
+
|
|
2590
|
+
// A literal of an anonymous struct type: a plain object with every field,
|
|
2591
|
+
// the omitted ones set to their zero value.
|
|
2592
|
+
_genAnonStructLit(expr) {
|
|
2593
|
+
const st =
|
|
2594
|
+
expr._type?.kind === "named" ? expr._type.underlying : expr._type;
|
|
2595
|
+
if (st?.kind !== "struct" || !(st.fields instanceof Map))
|
|
2596
|
+
return this._genFallbackCompositeLit(expr);
|
|
2597
|
+
const values = new Map();
|
|
2598
|
+
for (const e of expr.elems) {
|
|
2599
|
+
if (e._positionalField)
|
|
2600
|
+
values.set(e._positionalField, this.genValueExpr(e));
|
|
2601
|
+
else if (e.kind === "KeyValueExpr")
|
|
2602
|
+
values.set(e.key.name, this.genValueExpr(e.value));
|
|
2603
|
+
}
|
|
2604
|
+
const fields = [...st.fields].map(
|
|
2605
|
+
([name, ft]) =>
|
|
2606
|
+
`${name}: ${values.get(name) ?? this.zeroValueForType(ft)}`,
|
|
2607
|
+
);
|
|
2608
|
+
return `{ ${fields.join(", ")} }`;
|
|
2609
|
+
},
|
|
2610
|
+
|
|
1437
2611
|
_genImplicitCompositeLit(expr) {
|
|
1438
|
-
|
|
2612
|
+
// An elided `&T` (`[]*T{{1}}`) builds the struct; a struct pointer is the object.
|
|
2613
|
+
const lit = expr._type?.kind === "pointer" ? expr._type.base : expr._type;
|
|
2614
|
+
const typeName = lit?.name ?? lit?.underlying?.name;
|
|
1439
2615
|
if (typeName && this.structNames.has(typeName)) {
|
|
1440
2616
|
return this._genPositionalStructLit(typeName, expr);
|
|
1441
2617
|
}
|
|
2618
|
+
if (this._isMapLit(expr))
|
|
2619
|
+
return `{ ${this._genMapLitEntries(expr.elems)} }`;
|
|
1442
2620
|
const hasPositional = expr.elems.some((e) => e._positionalField);
|
|
1443
2621
|
const hasKeyed = expr.elems.some((e) => e.kind === "KeyValueExpr");
|
|
1444
|
-
|
|
2622
|
+
// `{2: 5}` inside `[2][3]int{…}` is an array, not an object.
|
|
2623
|
+
const indexed = this._litIndexedShape(expr) !== null;
|
|
2624
|
+
if (hasPositional || (hasKeyed && !indexed)) {
|
|
1445
2625
|
const fields = expr.elems
|
|
1446
2626
|
.map((e) => {
|
|
1447
2627
|
if (e._positionalField)
|
|
@@ -1451,7 +2631,9 @@ export const expressionGenMethods = {
|
|
|
1451
2631
|
.join(", ");
|
|
1452
2632
|
return `{ ${fields} }`;
|
|
1453
2633
|
}
|
|
1454
|
-
const
|
|
2634
|
+
const sparse = this._genSparseArrayLit(expr);
|
|
2635
|
+
if (sparse) return sparse;
|
|
2636
|
+
const elems = this._genLitElems(expr);
|
|
1455
2637
|
if (isTypedArraySlice(expr._type)) {
|
|
1456
2638
|
const typedCtor = typedArrayConstructorForElem(expr._type.elem);
|
|
1457
2639
|
if (typedCtor) return `new ${typedCtor}([${elems}])`;
|
|
@@ -1465,35 +2647,51 @@ export const expressionGenMethods = {
|
|
|
1465
2647
|
if (u?.kind === "map") {
|
|
1466
2648
|
return `{ ${this._genMapLitEntries(expr.elems)} }`;
|
|
1467
2649
|
}
|
|
1468
|
-
// Default: slice
|
|
1469
|
-
|
|
1470
|
-
.map((e) => this.genValueExpr(e.kind === "KeyValueExpr" ? e.value : e))
|
|
1471
|
-
.join(", ");
|
|
1472
|
-
return `[${elems}]`;
|
|
2650
|
+
// Default: slice or array
|
|
2651
|
+
return `[${this._genLitElems(expr)}]`;
|
|
1473
2652
|
},
|
|
1474
2653
|
|
|
1475
2654
|
// ── Helpers ───────────────────────────────────────────────────
|
|
1476
2655
|
|
|
1477
2656
|
_genComplexOperand(expr) {
|
|
1478
|
-
if (isComplex(expr._type)) return this.genExpr(expr);
|
|
2657
|
+
if (isComplex(expr._type) || expr._asComplex) return this.genExpr(expr);
|
|
1479
2658
|
return `{ re: ${this.genExpr(expr)}, im: 0 }`;
|
|
1480
2659
|
},
|
|
1481
2660
|
|
|
1482
2661
|
getTypeName(typeNode) {
|
|
1483
2662
|
if (!typeNode) return null;
|
|
1484
|
-
if (typeNode.
|
|
2663
|
+
if (typeNode._stdName) return typeNode._stdName;
|
|
2664
|
+
if (typeNode.kind === "TypeName")
|
|
2665
|
+
return this._aliasTarget(this._dequalify(typeNode.name));
|
|
1485
2666
|
if (typeNode.kind === "GenericTypeName") return typeNode.name;
|
|
1486
|
-
if (typeNode.kind === "Ident") return typeNode.name;
|
|
2667
|
+
if (typeNode.kind === "Ident") return this._aliasTarget(typeNode.name);
|
|
1487
2668
|
if (typeNode.kind === "InstantiationExpr")
|
|
1488
2669
|
return this.getTypeName(typeNode.expr);
|
|
1489
2670
|
if (typeNode.kind === "SelectorExpr") {
|
|
1490
2671
|
const base = this.getTypeName(typeNode.expr);
|
|
1491
|
-
if (this.bundledPackages.has(base))
|
|
2672
|
+
if (this.bundledPackages.has(base)) {
|
|
2673
|
+
// `p.A` for `type A = Base` in p: no class is emitted for A.
|
|
2674
|
+
const t = this.checker?.types.get(`${base}.${typeNode.field}`);
|
|
2675
|
+
return t?.kind === "named" && t.name && !t.name.includes("$$")
|
|
2676
|
+
? t.name
|
|
2677
|
+
: typeNode.field;
|
|
2678
|
+
}
|
|
1492
2679
|
return `${base}.${typeNode.field}`;
|
|
1493
2680
|
}
|
|
1494
2681
|
return null;
|
|
1495
2682
|
},
|
|
1496
2683
|
|
|
2684
|
+
// `type A = Base`: no class is emitted for A, so it names Base's.
|
|
2685
|
+
_aliasTarget(name) {
|
|
2686
|
+
const t = this.checker?.types.get(name);
|
|
2687
|
+
return t?.kind === "named" &&
|
|
2688
|
+
t.name &&
|
|
2689
|
+
t.name !== name &&
|
|
2690
|
+
!name.includes(".")
|
|
2691
|
+
? t.name
|
|
2692
|
+
: name;
|
|
2693
|
+
},
|
|
2694
|
+
|
|
1497
2695
|
// Bundled GoFront packages are inlined, so `pkg.T` names the class `T`.
|
|
1498
2696
|
_dequalify(name) {
|
|
1499
2697
|
const dot = name.indexOf(".");
|
|
@@ -1523,6 +2721,104 @@ export const expressionGenMethods = {
|
|
|
1523
2721
|
return false;
|
|
1524
2722
|
},
|
|
1525
2723
|
|
|
2724
|
+
// ── Generic zero values (see HELPER_GENERICS) ─────────────────
|
|
2725
|
+
|
|
2726
|
+
// JS condition: type parameter `name` was instantiated with a struct (its
|
|
2727
|
+
// zero value is a struct object, which is its own pointer when erased; a
|
|
2728
|
+
// map's zero is a bare `{}`, so only a class instance or a populated
|
|
2729
|
+
// object counts).
|
|
2730
|
+
_typeParamIsStructJs(name) {
|
|
2731
|
+
const f = this._typeParamZeroFactory(name);
|
|
2732
|
+
return f
|
|
2733
|
+
? `((__z) => __z !== null && typeof __z === "object" && !Array.isArray(__z) && !ArrayBuffer.isView(__z) && (Object.getPrototypeOf(Object.getPrototypeOf(__z)) !== null || Object.keys(__z).length > 0))(${f}())`
|
|
2734
|
+
: "false";
|
|
2735
|
+
},
|
|
2736
|
+
|
|
2737
|
+
// The zero-value factory bound to type parameter `name` in the code being
|
|
2738
|
+
// emitted (`$z_T`), or null outside a generic body.
|
|
2739
|
+
_typeParamZeroFactory(name) {
|
|
2740
|
+
return this._typeParamZeros?.get(name) ?? null;
|
|
2741
|
+
},
|
|
2742
|
+
|
|
2743
|
+
// Runs `fn` with type parameters `names` bound to the factories `$z_<name>`.
|
|
2744
|
+
_withoutNatives(fn) {
|
|
2745
|
+
const saved = this.natives;
|
|
2746
|
+
this.natives = false;
|
|
2747
|
+
try {
|
|
2748
|
+
return fn();
|
|
2749
|
+
} finally {
|
|
2750
|
+
this.natives = saved;
|
|
2751
|
+
}
|
|
2752
|
+
},
|
|
2753
|
+
|
|
2754
|
+
_withTypeParamZeros(names, fn) {
|
|
2755
|
+
const prev = this._typeParamZeros;
|
|
2756
|
+
this._typeParamZeros = names
|
|
2757
|
+
? new Map(names.map((n) => [n, `$z_${n}`]))
|
|
2758
|
+
: null;
|
|
2759
|
+
try {
|
|
2760
|
+
return fn();
|
|
2761
|
+
} finally {
|
|
2762
|
+
this._typeParamZeros = prev;
|
|
2763
|
+
}
|
|
2764
|
+
},
|
|
2765
|
+
|
|
2766
|
+
// A factory `() => zero` for type `t`: what a generic callee receives for
|
|
2767
|
+
// one of its type parameters.
|
|
2768
|
+
zeroFactoryFor(t) {
|
|
2769
|
+
if (t?.kind === "typeParam") {
|
|
2770
|
+
const bound = this._typeParamZeroFactory(t.name);
|
|
2771
|
+
if (bound) return bound;
|
|
2772
|
+
}
|
|
2773
|
+
this.useHelper("generics");
|
|
2774
|
+
if (this._is64BitUnsigned(t)) return "__zu64";
|
|
2775
|
+
const zero = this.zeroValueForType(t);
|
|
2776
|
+
return Object.hasOwn(SHARED_ZERO_FACTORIES, zero)
|
|
2777
|
+
? SHARED_ZERO_FACTORIES[zero]
|
|
2778
|
+
: `() => (${zero})`;
|
|
2779
|
+
},
|
|
2780
|
+
|
|
2781
|
+
// One factory per type parameter of generic function or type `generic`.
|
|
2782
|
+
// `withInfo`: the callee needs the instantiated types (see type-info.js).
|
|
2783
|
+
_typeArgFactories(typeParams, typeArgs, withInfo = false) {
|
|
2784
|
+
return typeParams.map((_, i) =>
|
|
2785
|
+
withInfo
|
|
2786
|
+
? this._typeInfoFactory(typeArgs?.[i])
|
|
2787
|
+
: this.zeroFactoryFor(typeArgs?.[i]),
|
|
2788
|
+
);
|
|
2789
|
+
},
|
|
2790
|
+
|
|
2791
|
+
// `new Box(args…, [factories])` for an instance of generic struct `name`
|
|
2792
|
+
// with `typeArgs`; fields without an argument start at their zero value.
|
|
2793
|
+
// Null when `name` is not a generic struct.
|
|
2794
|
+
_genericStructNew(name, typeArgs, args = []) {
|
|
2795
|
+
const generic = this.checker?.types.get(name);
|
|
2796
|
+
const st = generic?.underlying;
|
|
2797
|
+
const cls = this._dequalify(name);
|
|
2798
|
+
if (!generic?._generic || generic._generic.isAlias) return null;
|
|
2799
|
+
if (st?.kind !== "struct") return null;
|
|
2800
|
+
if (!this.structNames.has(cls)) return null;
|
|
2801
|
+
const tps = generic._generic.typeParams;
|
|
2802
|
+
const vals = Array.from(
|
|
2803
|
+
{ length: st.fields.size },
|
|
2804
|
+
(_, i) => args[i] ?? "undefined",
|
|
2805
|
+
);
|
|
2806
|
+
const tz = this._typeArgFactories(
|
|
2807
|
+
tps,
|
|
2808
|
+
typeArgs,
|
|
2809
|
+
this._genericTypeNeedsTypeInfo(generic) ||
|
|
2810
|
+
typeArgs?.some((t) => mentionsTypeParam(t)),
|
|
2811
|
+
);
|
|
2812
|
+
const id = instanceId(typeArgs);
|
|
2813
|
+
if (id !== null) tz.push(JSON.stringify(id));
|
|
2814
|
+
else {
|
|
2815
|
+
const runtimeId = this._runtimeInstanceId(typeArgs);
|
|
2816
|
+
if (runtimeId) tz.push(runtimeId);
|
|
2817
|
+
}
|
|
2818
|
+
vals.push(`[${tz.join(", ")}]`);
|
|
2819
|
+
return `new ${cls}(${vals.join(", ")})`;
|
|
2820
|
+
},
|
|
2821
|
+
|
|
1526
2822
|
// Returns the JS zero-value literal for a basic type name, or null if not a basic type.
|
|
1527
2823
|
_zeroForBasicName(name) {
|
|
1528
2824
|
return Object.hasOwn(ZERO_FOR_BASIC, name) ? ZERO_FOR_BASIC[name] : null;
|
|
@@ -1543,6 +2839,10 @@ export const expressionGenMethods = {
|
|
|
1543
2839
|
u.kind !== "named"
|
|
1544
2840
|
)
|
|
1545
2841
|
return this.zeroValueForType(u);
|
|
2842
|
+
if (named?.kind === "named" && u?.kind === "struct") {
|
|
2843
|
+
const clsName = named.name ?? this._dequalify(name);
|
|
2844
|
+
if (this.structNames.has(clsName)) return `new ${clsName}()`;
|
|
2845
|
+
}
|
|
1546
2846
|
name = this._dequalify(name);
|
|
1547
2847
|
if (this.structNames.has(name)) return `new ${name}()`;
|
|
1548
2848
|
return "null";
|
|
@@ -1551,7 +2851,10 @@ export const expressionGenMethods = {
|
|
|
1551
2851
|
zeroValueForTypeNode(typeNode) {
|
|
1552
2852
|
if (!typeNode) return "null";
|
|
1553
2853
|
switch (typeNode.kind) {
|
|
2854
|
+
case "Ident":
|
|
1554
2855
|
case "TypeName": {
|
|
2856
|
+
const factory = this._typeParamZeroFactory(typeNode.name);
|
|
2857
|
+
if (factory) return `${factory}()`;
|
|
1555
2858
|
const basic = this._zeroForBasicName(typeNode.name);
|
|
1556
2859
|
if (basic !== null) return basic;
|
|
1557
2860
|
return this._zeroForNamedType(typeNode.name);
|
|
@@ -1573,7 +2876,7 @@ export const expressionGenMethods = {
|
|
|
1573
2876
|
);
|
|
1574
2877
|
}
|
|
1575
2878
|
case "MapType":
|
|
1576
|
-
return
|
|
2879
|
+
return this._nilMapZero();
|
|
1577
2880
|
case "PointerType":
|
|
1578
2881
|
return "null";
|
|
1579
2882
|
case "StructType": {
|
|
@@ -1586,16 +2889,31 @@ export const expressionGenMethods = {
|
|
|
1586
2889
|
.join(", ");
|
|
1587
2890
|
return `{ ${fields} }`;
|
|
1588
2891
|
}
|
|
2892
|
+
case "GenericTypeName":
|
|
2893
|
+
case "InstantiationExpr":
|
|
2894
|
+
return (
|
|
2895
|
+
this._genericStructNew(
|
|
2896
|
+
this.getTypeName(typeNode),
|
|
2897
|
+
typeNode._typeArgs,
|
|
2898
|
+
) ?? "null"
|
|
2899
|
+
);
|
|
1589
2900
|
default:
|
|
1590
2901
|
return "null";
|
|
1591
2902
|
}
|
|
1592
2903
|
},
|
|
1593
2904
|
|
|
2905
|
+
// A nil map: a fresh tagged `{}` in js packages (`m == nil` holds while it
|
|
2906
|
+
// stays empty); in strict mode the frozen `__nilmap`, which panics on write.
|
|
2907
|
+
_nilMapZero() {
|
|
2908
|
+
this.useHelper(this.strict ? "nilMap" : "nm");
|
|
2909
|
+
return this.strict ? "__nilmap" : "__nm()";
|
|
2910
|
+
},
|
|
2911
|
+
|
|
1594
2912
|
_arrayZero(n, ctor, elemZero) {
|
|
1595
2913
|
if (ctor) return `new ${ctor}(${n})`;
|
|
1596
2914
|
if (n === 0) return "[]";
|
|
1597
|
-
if (/^(new |\{|\[|Array)/.test(elemZero))
|
|
1598
|
-
return `Array.from({ length: ${n} }, () => ${elemZero})`;
|
|
2915
|
+
if (/^(new |\{|\[|Array|__nm\()|\$z_/.test(elemZero))
|
|
2916
|
+
return `Array.from({ length: ${n} }, () => (${elemZero}))`;
|
|
1599
2917
|
return `new Array(${n}).fill(${elemZero})`;
|
|
1600
2918
|
},
|
|
1601
2919
|
|
|
@@ -1614,7 +2932,7 @@ export const expressionGenMethods = {
|
|
|
1614
2932
|
this.zeroValueForType(t.elem),
|
|
1615
2933
|
);
|
|
1616
2934
|
case "map":
|
|
1617
|
-
return
|
|
2935
|
+
return this._nilMapZero();
|
|
1618
2936
|
case "struct": {
|
|
1619
2937
|
const fields = [...t.fields.entries()]
|
|
1620
2938
|
.map(([name, ft]) => `${name}: ${this.zeroValueForType(ft)}`)
|
|
@@ -1622,7 +2940,14 @@ export const expressionGenMethods = {
|
|
|
1622
2940
|
return `{ ${fields} }`;
|
|
1623
2941
|
}
|
|
1624
2942
|
case "named":
|
|
1625
|
-
return
|
|
2943
|
+
return (
|
|
2944
|
+
this._genericStructNew(t.name, t.typeArgs) ??
|
|
2945
|
+
this._zeroForNamedType(t.name)
|
|
2946
|
+
);
|
|
2947
|
+
case "typeParam": {
|
|
2948
|
+
const factory = this._typeParamZeroFactory(t.name);
|
|
2949
|
+
return factory ? `${factory}()` : "null";
|
|
2950
|
+
}
|
|
1626
2951
|
default:
|
|
1627
2952
|
return "null";
|
|
1628
2953
|
}
|
|
@@ -1633,35 +2958,59 @@ export const expressionGenMethods = {
|
|
|
1633
2958
|
if (!typeNode) return "true";
|
|
1634
2959
|
if (typeNode.kind === "TypeName")
|
|
1635
2960
|
return this._typeNameCheckExpr(typeNode.name, val);
|
|
2961
|
+
const generic = this._genericInstanceCheck(typeNode, val);
|
|
2962
|
+
if (generic) return generic;
|
|
2963
|
+
const dynamic = this._dynTypeCheck(typeNode, val);
|
|
2964
|
+
if (dynamic) return dynamic;
|
|
1636
2965
|
const extended = this._namedOrIfaceCheckExpr(typeNode, val);
|
|
1637
2966
|
if (extended) return extended;
|
|
1638
2967
|
if (
|
|
1639
2968
|
typeNode.kind === "PointerType" &&
|
|
1640
2969
|
typeNode.base?.kind === "TypeName" &&
|
|
1641
|
-
this.structNames.has(typeNode.base.name)
|
|
2970
|
+
this.structNames.has(this._qualifiedStructName(typeNode.base.name))
|
|
1642
2971
|
) {
|
|
1643
|
-
const name = typeNode.base.name;
|
|
2972
|
+
const name = this._qualifiedStructName(typeNode.base.name);
|
|
1644
2973
|
return `(${val} instanceof ${name} && ${val}.__v !== true)`;
|
|
1645
2974
|
}
|
|
1646
2975
|
return "true";
|
|
1647
2976
|
},
|
|
1648
2977
|
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
2978
|
+
// `Stack[int]` / `*Stack[int]`: the class test plus the instantiation
|
|
2979
|
+
// recorded on the instance (an instance without one matches any).
|
|
2980
|
+
_genericInstanceCheck(typeNode, val) {
|
|
2981
|
+
const isPtr = typeNode.kind === "PointerType";
|
|
2982
|
+
const base = isPtr ? typeNode.base : typeNode;
|
|
2983
|
+
if (base?.kind !== "GenericTypeName" || !base._typeArgs) return null;
|
|
2984
|
+
const cls = this._dequalify(base.name);
|
|
2985
|
+
if (!this.structNames.has(cls)) return null;
|
|
2986
|
+
const generic = this.checker?.types.get(base.name)?._generic;
|
|
2987
|
+
const id = instanceId(base._typeArgs);
|
|
2988
|
+
let check = isPtr
|
|
2989
|
+
? `${val} instanceof ${cls} && ${val}.__v !== true`
|
|
2990
|
+
: `${val} instanceof ${cls} && ${val}.__p !== true`;
|
|
2991
|
+
if (id !== null && generic) {
|
|
2992
|
+
this.useHelper("generics");
|
|
2993
|
+
const at = generic.typeParams.length;
|
|
2994
|
+
check += ` && (${val}[__tz]?.[${at}] ?? ${JSON.stringify(id)}) === ${JSON.stringify(id)}`;
|
|
1663
2995
|
}
|
|
1664
|
-
return
|
|
2996
|
+
return `(${check})`;
|
|
2997
|
+
},
|
|
2998
|
+
|
|
2999
|
+
zeroValueForExpr(expr) {
|
|
3000
|
+
return this.zeroValueForTypeNode(expr);
|
|
3001
|
+
},
|
|
3002
|
+
|
|
3003
|
+
_genSimdEquality(expr) {
|
|
3004
|
+
if (expr.op !== "==" && expr.op !== "!=") return null;
|
|
3005
|
+
if (!this._isSimdValueType(expr.left._type)) return null;
|
|
3006
|
+
this.useHelper("simd");
|
|
3007
|
+
const cmp = `__simd_equal(${this.genExpr(expr.left)}, ${this.genExpr(expr.right)})`;
|
|
3008
|
+
return expr.op === "==" ? cmp : `!${cmp}`;
|
|
3009
|
+
},
|
|
3010
|
+
|
|
3011
|
+
_isSimdValueType(t) {
|
|
3012
|
+
const base = t?.kind === "named" ? t.underlying : t;
|
|
3013
|
+
return base?.kind === "simd";
|
|
1665
3014
|
},
|
|
1666
3015
|
|
|
1667
3016
|
// Returns true if the type is a struct or array (uses value comparison with __equal).
|
|
@@ -1700,24 +3049,112 @@ export const expressionGenMethods = {
|
|
|
1700
3049
|
)
|
|
1701
3050
|
return this._genComplexBinary(expr);
|
|
1702
3051
|
|
|
3052
|
+
const simdEq = this._genSimdEquality(expr);
|
|
3053
|
+
if (simdEq !== null) return simdEq;
|
|
3054
|
+
|
|
1703
3055
|
if (this._isStructEquality(expr)) {
|
|
1704
3056
|
this.useHelper("equal");
|
|
1705
3057
|
const cmp = `__equal(${this.genExpr(expr.left)}, ${this.genExpr(expr.right)})`;
|
|
1706
3058
|
return expr.op === "==" ? cmp : `!${cmp}`;
|
|
1707
3059
|
}
|
|
1708
3060
|
|
|
3061
|
+
const nilMap = this._genNilMapCompare(expr);
|
|
3062
|
+
if (nilMap) return nilMap;
|
|
3063
|
+
|
|
3064
|
+
const anyNil = this._genAnyNilCompare(expr);
|
|
3065
|
+
if (anyNil) return anyNil;
|
|
3066
|
+
|
|
3067
|
+
const tpEq = this._genTypeParamEquality(expr);
|
|
3068
|
+
if (tpEq) return tpEq;
|
|
3069
|
+
|
|
1709
3070
|
if (this._isIfaceEquality(expr)) {
|
|
1710
3071
|
this.useHelper("ifeq");
|
|
1711
3072
|
const cmp = `__ifeq(${this._genIfaceOperand(expr.left)}, ${this._genIfaceOperand(expr.right)})`;
|
|
1712
3073
|
return expr.op === "==" ? cmp : `!${cmp}`;
|
|
1713
3074
|
}
|
|
1714
3075
|
|
|
1715
|
-
|
|
3076
|
+
const ptrCmp = this._genPointerEquality(expr);
|
|
3077
|
+
if (ptrCmp !== null) return ptrCmp;
|
|
3078
|
+
|
|
3079
|
+
if (this._is64BitInt(expr._type)) return this._gen64BitBinaryExpr(expr);
|
|
3080
|
+
|
|
3081
|
+
if (
|
|
3082
|
+
(this.strict || this._isFloat32(expr._type)) &&
|
|
3083
|
+
this._needsStrictOp(expr.op, expr._type)
|
|
3084
|
+
) {
|
|
1716
3085
|
const l = this.genExpr(expr.left);
|
|
1717
3086
|
const r = this.genExpr(expr.right);
|
|
1718
|
-
return this._genStrictBinary(expr.op, l, r, expr._type);
|
|
3087
|
+
return this._genStrictBinary(expr.op, l, r, expr._type, expr.right._type);
|
|
3088
|
+
}
|
|
3089
|
+
|
|
3090
|
+
return this._genStandardBinaryExpr(expr);
|
|
3091
|
+
},
|
|
3092
|
+
|
|
3093
|
+
_gen64BitBinaryExpr(expr) {
|
|
3094
|
+
let l = this.genExpr(expr.left);
|
|
3095
|
+
let r = this.genExpr(expr.right);
|
|
3096
|
+
// An untyped operand is a literal (`3` -> `3n`) or a named constant,
|
|
3097
|
+
// which holds a Number.
|
|
3098
|
+
const lit = (js) => (/^-?\d+$/.test(js) ? `${js}n` : `BigInt(${js})`);
|
|
3099
|
+
if (expr.left._type?.kind === "untyped" && !l.endsWith("n")) l = lit(l);
|
|
3100
|
+
else if (!this._is64BitInt(expr.left?._type)) l = `BigInt(${l})`;
|
|
3101
|
+
if (expr.right._type?.kind === "untyped" && !r.endsWith("n")) r = lit(r);
|
|
3102
|
+
else if (!this._is64BitInt(expr.right?._type)) r = `BigInt(${r})`;
|
|
3103
|
+
return this._gen64BitBinary(expr.op, l, r, expr._type);
|
|
3104
|
+
},
|
|
3105
|
+
|
|
3106
|
+
_tconvBinaryOperands(expr, l, r) {
|
|
3107
|
+
if (
|
|
3108
|
+
expr.left._type?.kind === "typeParam" &&
|
|
3109
|
+
expr.right._type?.kind === "untyped"
|
|
3110
|
+
) {
|
|
3111
|
+
const factory = this._typeParamZeroFactory(expr.left._type.name);
|
|
3112
|
+
if (factory) {
|
|
3113
|
+
this.useHelper("generics");
|
|
3114
|
+
r = `__tconv(${factory}, ${r})`;
|
|
3115
|
+
}
|
|
3116
|
+
} else if (
|
|
3117
|
+
expr.right._type?.kind === "typeParam" &&
|
|
3118
|
+
expr.left._type?.kind === "untyped"
|
|
3119
|
+
) {
|
|
3120
|
+
const factory = this._typeParamZeroFactory(expr.right._type.name);
|
|
3121
|
+
if (factory) {
|
|
3122
|
+
this.useHelper("generics");
|
|
3123
|
+
l = `__tconv(${factory}, ${l})`;
|
|
3124
|
+
}
|
|
1719
3125
|
}
|
|
3126
|
+
return [l, r];
|
|
3127
|
+
},
|
|
3128
|
+
|
|
3129
|
+
_coerceUntyped64(expr, l, r, isLeft64, isRight64) {
|
|
3130
|
+
if (isLeft64 && expr.right?._type?.kind === "untyped") {
|
|
3131
|
+
if (!r.endsWith("n") && /^-?\d+$/.test(r)) return [l, `${r}n`];
|
|
3132
|
+
} else if (isRight64 && expr.left?._type?.kind === "untyped") {
|
|
3133
|
+
if (!l.endsWith("n") && /^-?\d+$/.test(l)) return [`${l}n`, r];
|
|
3134
|
+
}
|
|
3135
|
+
return [l, r];
|
|
3136
|
+
},
|
|
3137
|
+
|
|
3138
|
+
_coerceMixed64(expr, l, r, isLeft64, isRight64) {
|
|
3139
|
+
const isShift = expr.op === "<<" || expr.op === ">>";
|
|
3140
|
+
if (isLeft64 && !isRight64 && !isShift) r = `BigInt(${r})`;
|
|
3141
|
+
else if (isRight64 && !isLeft64 && !isShift) l = `BigInt(${l})`;
|
|
3142
|
+
if (isShift && isRight64) r = `Number(${r})`;
|
|
3143
|
+
return [l, r];
|
|
3144
|
+
},
|
|
3145
|
+
|
|
3146
|
+
_wrapBinaryResult(expr, res, isLeft64, isRight64) {
|
|
3147
|
+
const isCmp =
|
|
3148
|
+
expr.op === "==" ||
|
|
3149
|
+
expr.op === "!=" ||
|
|
3150
|
+
expr.op === "<" ||
|
|
3151
|
+
expr.op === "<=" ||
|
|
3152
|
+
expr.op === ">" ||
|
|
3153
|
+
expr.op === ">=";
|
|
3154
|
+
return (isLeft64 || isRight64) && !isCmp ? `Number(${res})` : res;
|
|
3155
|
+
},
|
|
1720
3156
|
|
|
3157
|
+
_genStandardBinaryExpr(expr) {
|
|
1721
3158
|
const jsOp = this._emittedBinaryOp(expr) ?? "/";
|
|
1722
3159
|
const operand = (child, isRight) =>
|
|
1723
3160
|
wrapForJsOp(
|
|
@@ -1726,17 +3163,66 @@ export const expressionGenMethods = {
|
|
|
1726
3163
|
jsOp,
|
|
1727
3164
|
isRight,
|
|
1728
3165
|
);
|
|
1729
|
-
|
|
3166
|
+
let [l, r] = this._tconvBinaryOperands(
|
|
3167
|
+
expr,
|
|
3168
|
+
operand(expr.left, false),
|
|
3169
|
+
operand(expr.right, true),
|
|
3170
|
+
);
|
|
3171
|
+
const isLeft64 = this._is64BitInt(expr.left?._type);
|
|
3172
|
+
const isRight64 = this._is64BitInt(expr.right?._type);
|
|
3173
|
+
[l, r] = this._coerceUntyped64(expr, l, r, isLeft64, isRight64);
|
|
3174
|
+
[l, r] = this._coerceMixed64(expr, l, r, isLeft64, isRight64);
|
|
1730
3175
|
|
|
1731
3176
|
if (expr.op === "&^") {
|
|
1732
3177
|
const rCode = this.genExpr(expr.right);
|
|
1733
|
-
const
|
|
3178
|
+
const rP =
|
|
1734
3179
|
this._emittedBinaryOp(expr.right) !== null ? `(${rCode})` : rCode;
|
|
1735
|
-
return `${l} & ~${
|
|
3180
|
+
return this._wrapBinaryResult(expr, `${l} & ~${rP}`, isLeft64, isRight64);
|
|
1736
3181
|
}
|
|
1737
|
-
const r = operand(expr.right, true);
|
|
1738
3182
|
if (this._isIntDivision(expr)) return `Math.trunc(${l} / ${r})`;
|
|
1739
|
-
return
|
|
3183
|
+
return this._wrapBinaryResult(
|
|
3184
|
+
expr,
|
|
3185
|
+
`${l} ${jsOp} ${r}`,
|
|
3186
|
+
isLeft64,
|
|
3187
|
+
isRight64,
|
|
3188
|
+
);
|
|
3189
|
+
},
|
|
3190
|
+
|
|
3191
|
+
// `m == nil` / `m != nil` on a map: true for a nil map (null, the strict
|
|
3192
|
+
// sentinel, or an untouched js-package zero map), false for `map[K]V{}`.
|
|
3193
|
+
_nilMapOperand(expr) {
|
|
3194
|
+
if (expr.op !== "==" && expr.op !== "!=") return null;
|
|
3195
|
+
const other = this._isNilLiteral(expr.right)
|
|
3196
|
+
? expr.left
|
|
3197
|
+
: this._isNilLiteral(expr.left)
|
|
3198
|
+
? expr.right
|
|
3199
|
+
: null;
|
|
3200
|
+
if (!other) return null;
|
|
3201
|
+
const t =
|
|
3202
|
+
other._type?.kind === "named" ? other._type.underlying : other._type;
|
|
3203
|
+
return t?.kind === "map" || other._coreType?.kind === "map" ? other : null;
|
|
3204
|
+
},
|
|
3205
|
+
|
|
3206
|
+
_genNilMapCompare(expr) {
|
|
3207
|
+
const operand = this._nilMapOperand(expr);
|
|
3208
|
+
if (!operand) return null;
|
|
3209
|
+
this.useHelper(this.strict ? "nilMap" : "nm");
|
|
3210
|
+
const cmp = `__isnm(${this.genExpr(operand)})`;
|
|
3211
|
+
return expr.op === "==" ? cmp : `!${cmp}`;
|
|
3212
|
+
},
|
|
3213
|
+
|
|
3214
|
+
// `a == b` on type-parameter operands: __geq compares structs and arrays by value.
|
|
3215
|
+
_genTypeParamEquality(expr) {
|
|
3216
|
+
if (expr.op !== "==" && expr.op !== "!=") return null;
|
|
3217
|
+
const t = expr.left._type;
|
|
3218
|
+
if (t?.kind !== "typeParam" || expr.right._type?.kind !== "typeParam")
|
|
3219
|
+
return null;
|
|
3220
|
+
const factory = this._typeParamZeroFactory(t.name);
|
|
3221
|
+
if (!factory) return null;
|
|
3222
|
+
this.useHelper("equal");
|
|
3223
|
+
this.useHelper("geq");
|
|
3224
|
+
const cmp = `__geq(${factory}, ${this.genExpr(expr.left)}, ${this.genExpr(expr.right)})`;
|
|
3225
|
+
return expr.op === "==" ? cmp : `!${cmp}`;
|
|
1740
3226
|
},
|
|
1741
3227
|
|
|
1742
3228
|
_isStructEquality(expr) {
|
|
@@ -1779,10 +3265,29 @@ export const expressionGenMethods = {
|
|
|
1779
3265
|
);
|
|
1780
3266
|
},
|
|
1781
3267
|
|
|
3268
|
+
// The `any` operand of `x == nil` / `x != nil` (JS documents a nil slice
|
|
3269
|
+
// stored in an `any` as equal to nil, though it keeps a type tag), or null.
|
|
3270
|
+
_anyNilOperand(expr) {
|
|
3271
|
+
if (expr.op !== "==" && expr.op !== "!=") return null;
|
|
3272
|
+
const operand = this._isNilLiteral(expr.right)
|
|
3273
|
+
? expr.left
|
|
3274
|
+
: this._isNilLiteral(expr.left)
|
|
3275
|
+
? expr.right
|
|
3276
|
+
: null;
|
|
3277
|
+
return operand && this._isIfaceType(operand._type) ? operand : null;
|
|
3278
|
+
},
|
|
3279
|
+
|
|
3280
|
+
_genAnyNilCompare(expr) {
|
|
3281
|
+
const operand = this._anyNilOperand(expr);
|
|
3282
|
+
if (!operand) return null;
|
|
3283
|
+
const test = `((__a) => __a == null || __a.__nils === true)(${this.genExpr(operand)})`;
|
|
3284
|
+
return expr.op === "==" ? test : `!${test}`;
|
|
3285
|
+
},
|
|
3286
|
+
|
|
1782
3287
|
_isNilLiteral(expr) {
|
|
1783
3288
|
return (
|
|
1784
3289
|
(expr.kind === "Ident" && expr.name === "nil") ||
|
|
1785
|
-
(expr.kind === "BasicLit" && expr.
|
|
3290
|
+
(expr.kind === "BasicLit" && expr.litKind === "NIL")
|
|
1786
3291
|
);
|
|
1787
3292
|
},
|
|
1788
3293
|
|
|
@@ -1796,6 +3301,8 @@ export const expressionGenMethods = {
|
|
|
1796
3301
|
)
|
|
1797
3302
|
return null;
|
|
1798
3303
|
if (this._isStructEquality(expr) || this._isIntDivision(expr)) return null;
|
|
3304
|
+
if (this._nilMapOperand(expr)) return null;
|
|
3305
|
+
if (this._anyNilOperand(expr)) return null;
|
|
1799
3306
|
if (this._isIfaceEquality(expr)) return null;
|
|
1800
3307
|
if (expr.op === "&^") return "&";
|
|
1801
3308
|
if (expr.op === "==" || expr.op === "!=") {
|
|
@@ -1806,9 +3313,19 @@ export const expressionGenMethods = {
|
|
|
1806
3313
|
return expr.op;
|
|
1807
3314
|
},
|
|
1808
3315
|
|
|
3316
|
+
// `T(x)` with T a type parameter: converts by the instantiated type.
|
|
3317
|
+
_genTypeParamConversion(name, inner) {
|
|
3318
|
+
const factory = name ? this._typeParamZeroFactory(name) : null;
|
|
3319
|
+
if (!factory) return null;
|
|
3320
|
+
this.useHelper("generics");
|
|
3321
|
+
return `__tconv(${factory}, ${inner})`;
|
|
3322
|
+
},
|
|
3323
|
+
|
|
1809
3324
|
_genTypeConversion(expr) {
|
|
1810
3325
|
const inner = this.genExpr(expr.expr);
|
|
1811
3326
|
const t = expr.targetType;
|
|
3327
|
+
const viaParam = this._genTypeParamConversion(t?.name, inner);
|
|
3328
|
+
if (viaParam) return viaParam;
|
|
1812
3329
|
if (t?.name === "complex128" || t?.name === "complex64") {
|
|
1813
3330
|
return isComplex(expr.expr._type) ? inner : `{ re: ${inner}, im: 0 }`;
|
|
1814
3331
|
}
|
|
@@ -1816,8 +3333,21 @@ export const expressionGenMethods = {
|
|
|
1816
3333
|
return this._genArrayTypeConversion(expr, inner, t);
|
|
1817
3334
|
if (t?.kind === "SliceType")
|
|
1818
3335
|
return this._genSliceTypeConversion(expr, inner, t);
|
|
3336
|
+
// `map[K]V(nil)` is a nil map value, which stays distinct from the nil
|
|
3337
|
+
// interface once boxed in an `any`.
|
|
3338
|
+
if (t?.kind === "MapType" && this._isNilLiteral(expr.expr))
|
|
3339
|
+
return this._nilMapZero();
|
|
1819
3340
|
const target = t?.name;
|
|
3341
|
+
const structConv = this._genStructClassConversion(
|
|
3342
|
+
target,
|
|
3343
|
+
expr.expr._type,
|
|
3344
|
+
inner,
|
|
3345
|
+
);
|
|
3346
|
+
if (structConv) return structConv;
|
|
1820
3347
|
if (target === "error") {
|
|
3348
|
+
// A boxed named value that implements error is already an error.
|
|
3349
|
+
if (this._isNamedBoxKind(expr.expr)) return inner;
|
|
3350
|
+
if (!this._isStringExprType(expr.expr._type)) return inner;
|
|
1821
3351
|
this.useHelper("error");
|
|
1822
3352
|
return `__error(${inner})`;
|
|
1823
3353
|
}
|
|
@@ -1840,16 +3370,33 @@ export const expressionGenMethods = {
|
|
|
1840
3370
|
const size = t.size?.value !== undefined ? Number(t.size.value) : t.size;
|
|
1841
3371
|
return `${inner}.slice(0, ${size})`;
|
|
1842
3372
|
}
|
|
1843
|
-
|
|
3373
|
+
// Array-to-array conversion yields a new array value.
|
|
3374
|
+
return this._cloneJs(expr.expr._type, inner);
|
|
1844
3375
|
},
|
|
1845
3376
|
|
|
1846
3377
|
_genSliceTypeConversion(expr, inner, t) {
|
|
3378
|
+
// `[]T(nil)` is the nil slice, not a copy of anything.
|
|
3379
|
+
if (this._isNilLiteral(expr.expr)) return "null";
|
|
1847
3380
|
const elem = t.elem?.name;
|
|
3381
|
+
// Converting a nil slice yields a nil slice.
|
|
3382
|
+
const src = this._underlyingOf(expr.expr._type);
|
|
3383
|
+
if (src?.kind === "slice" && !this._sliceConvNilSafe) {
|
|
3384
|
+
this._sliceConvNilSafe = true;
|
|
3385
|
+
try {
|
|
3386
|
+
return `((__s) => __s == null ? null : ${this._genSliceTypeConversion(expr, "__s", t)})(${inner})`;
|
|
3387
|
+
} finally {
|
|
3388
|
+
this._sliceConvNilSafe = false;
|
|
3389
|
+
}
|
|
3390
|
+
}
|
|
1848
3391
|
if (elem === "byte" || elem === "uint8") {
|
|
1849
|
-
if (this._isStringExprType(expr.expr._type))
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
3392
|
+
if (this._isStringExprType(expr.expr._type)) {
|
|
3393
|
+
this.useHelper("u8enc");
|
|
3394
|
+
return `__u8enc(${inner})`;
|
|
3395
|
+
}
|
|
3396
|
+
if (isAny(expr.expr._type) || !expr.expr._type) {
|
|
3397
|
+
this.useHelper("u8enc");
|
|
3398
|
+
return `(typeof ${inner} === "string" ? __u8enc(${inner}) : new Uint8Array(${inner}))`;
|
|
3399
|
+
}
|
|
1853
3400
|
return `new Uint8Array(${inner})`;
|
|
1854
3401
|
}
|
|
1855
3402
|
const typedCtor = typedArrayConstructorForElem(
|
|
@@ -1868,41 +3415,71 @@ export const expressionGenMethods = {
|
|
|
1868
3415
|
|
|
1869
3416
|
_genStringConversion(srcType, inner) {
|
|
1870
3417
|
const base = srcType?.kind === "named" ? srcType.underlying : srcType;
|
|
1871
|
-
if (base && this.isIntType(base))
|
|
3418
|
+
if (base && this.isIntType(base)) {
|
|
3419
|
+
this.useHelper("runestr");
|
|
3420
|
+
return `__rstr(${inner})`;
|
|
3421
|
+
}
|
|
1872
3422
|
if (base?.kind === "slice") {
|
|
1873
3423
|
const elem = base.elem?.name;
|
|
1874
|
-
if (elem === "byte" || elem === "uint8")
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
3424
|
+
if (elem === "byte" || elem === "uint8") {
|
|
3425
|
+
this.useHelper("u8dec");
|
|
3426
|
+
return `__u8dec(${inner})`;
|
|
3427
|
+
}
|
|
3428
|
+
if (elem === "rune" || elem === "int" || elem === "int32") {
|
|
3429
|
+
this.useHelper("runestr");
|
|
3430
|
+
return `Array.from(${inner}, c => __rstr(c)).join("")`;
|
|
3431
|
+
}
|
|
1878
3432
|
}
|
|
1879
3433
|
return `String(${inner})`;
|
|
1880
3434
|
},
|
|
1881
3435
|
|
|
1882
3436
|
_genPrimitiveConversion(expr, inner, target) {
|
|
3437
|
+
const srcType = expr.expr?._type;
|
|
3438
|
+
const isSrc64 = this._is64BitInt(srcType);
|
|
1883
3439
|
switch (target) {
|
|
1884
3440
|
case "string":
|
|
1885
|
-
return this._genStringConversion(
|
|
3441
|
+
return this._genStringConversion(
|
|
3442
|
+
srcType,
|
|
3443
|
+
isSrc64 ? `Number(${inner})` : inner,
|
|
3444
|
+
);
|
|
3445
|
+
case "int64": {
|
|
3446
|
+
this.useHelper("to64");
|
|
3447
|
+
return `__to_i64(${inner})`;
|
|
3448
|
+
}
|
|
3449
|
+
case "uint64": {
|
|
3450
|
+
this.useHelper("to64");
|
|
3451
|
+
return `__to_u64(${inner})`;
|
|
3452
|
+
}
|
|
1886
3453
|
case "int":
|
|
1887
|
-
case "int64":
|
|
1888
3454
|
case "uint":
|
|
1889
|
-
case "uint64":
|
|
1890
3455
|
case "uintptr":
|
|
1891
|
-
return `Math.trunc(Number(${inner}))`;
|
|
3456
|
+
return isSrc64 ? `Number(${inner})` : `Math.trunc(Number(${inner}))`;
|
|
1892
3457
|
case "int8":
|
|
1893
|
-
return
|
|
3458
|
+
return isSrc64
|
|
3459
|
+
? `Number(BigInt.asIntN(8, ${inner}))`
|
|
3460
|
+
: `(Number(${inner}) << 24 >> 24)`;
|
|
1894
3461
|
case "int16":
|
|
1895
|
-
return
|
|
3462
|
+
return isSrc64
|
|
3463
|
+
? `Number(BigInt.asIntN(16, ${inner}))`
|
|
3464
|
+
: `(Number(${inner}) << 16 >> 16)`;
|
|
1896
3465
|
case "int32":
|
|
1897
3466
|
case "rune":
|
|
1898
|
-
return
|
|
3467
|
+
return isSrc64
|
|
3468
|
+
? `Number(BigInt.asIntN(32, ${inner}))`
|
|
3469
|
+
: `(Number(${inner}) | 0)`;
|
|
1899
3470
|
case "uint8":
|
|
1900
3471
|
case "byte":
|
|
1901
|
-
return
|
|
3472
|
+
return isSrc64
|
|
3473
|
+
? `Number(BigInt.asUintN(8, ${inner}))`
|
|
3474
|
+
: `(Number(${inner}) & 0xFF)`;
|
|
1902
3475
|
case "uint16":
|
|
1903
|
-
return
|
|
3476
|
+
return isSrc64
|
|
3477
|
+
? `Number(BigInt.asUintN(16, ${inner}))`
|
|
3478
|
+
: `(Number(${inner}) & 0xFFFF)`;
|
|
1904
3479
|
case "uint32":
|
|
1905
|
-
return
|
|
3480
|
+
return isSrc64
|
|
3481
|
+
? `Number(BigInt.asUintN(32, ${inner}))`
|
|
3482
|
+
: `(Number(${inner}) >>> 0)`;
|
|
1906
3483
|
case "float32":
|
|
1907
3484
|
return `Math.fround(Number(${inner}))`;
|
|
1908
3485
|
case "float64":
|
|
@@ -1926,15 +3503,77 @@ export const expressionGenMethods = {
|
|
|
1926
3503
|
return STRICT_NUMERIC_TYPES.has(typeName);
|
|
1927
3504
|
},
|
|
1928
3505
|
|
|
3506
|
+
_is64BitInt(t) {
|
|
3507
|
+
return is64BitInt(t);
|
|
3508
|
+
},
|
|
3509
|
+
|
|
3510
|
+
// float32 arithmetic rounds through Math.fround and arithmetic on a type
|
|
3511
|
+
// parameter wraps for the instantiated type (`__tar`) in every package kind.
|
|
3512
|
+
_isFloat32(t) {
|
|
3513
|
+
return (
|
|
3514
|
+
this._typeName(t) === "float32" ||
|
|
3515
|
+
(t?.kind === "typeParam" && this._typeParamZeroFactory(t.name) != null)
|
|
3516
|
+
);
|
|
3517
|
+
},
|
|
3518
|
+
|
|
3519
|
+
_is64BitUnsigned(t) {
|
|
3520
|
+
return is64BitUnsigned(t);
|
|
3521
|
+
},
|
|
3522
|
+
|
|
3523
|
+
_gen64BitBinary(op, l, r, type) {
|
|
3524
|
+
const unsigned = this._is64BitUnsigned(type);
|
|
3525
|
+
const wrap = unsigned ? "BigInt.asUintN" : "BigInt.asIntN";
|
|
3526
|
+
const divZero =
|
|
3527
|
+
'(() => { throw new Error("runtime error: integer divide by zero"); })()';
|
|
3528
|
+
if (op === "/" || op === "%") {
|
|
3529
|
+
// The divisor is read twice (zero check, division): bind both
|
|
3530
|
+
// operands once, left first, unless they are plain names or literals.
|
|
3531
|
+
const simple = /^[A-Za-z_$][\w$.]*$|^-?\d+n$/;
|
|
3532
|
+
if ((simple.test(l) && simple.test(r)) || /\b(await|yield)\b/.test(l + r))
|
|
3533
|
+
return `((${r}) === 0n ? ${divZero} : ${wrap}(64, (${l}) ${op} (${r})))`;
|
|
3534
|
+
return `((__a, __b) => __b === 0n ? ${divZero} : ${wrap}(64, __a ${op} __b))(${l}, ${r})`;
|
|
3535
|
+
}
|
|
3536
|
+
if (op === "<<" || op === ">>") {
|
|
3537
|
+
this.useHelper("shift");
|
|
3538
|
+
return op === "<<"
|
|
3539
|
+
? `__shl64(${l}, ${r}, ${unsigned})`
|
|
3540
|
+
: `__shr64(${l}, ${r}, ${unsigned})`;
|
|
3541
|
+
}
|
|
3542
|
+
if (op === "&^") return `${wrap}(64, (${l}) & ~(${r}))`;
|
|
3543
|
+
return `${wrap}(64, (${l}) ${op} (${r}))`;
|
|
3544
|
+
},
|
|
3545
|
+
|
|
3546
|
+
// `uint` / `uintptr`: 64-bit unsigned values held as JS numbers.
|
|
3547
|
+
_isUintWord(type) {
|
|
3548
|
+
const name = this._typeName(type);
|
|
3549
|
+
return name === "uint" || name === "uintptr";
|
|
3550
|
+
},
|
|
3551
|
+
|
|
1929
3552
|
_needsStrictOp(op, type) {
|
|
3553
|
+
if (type?.kind === "typeParam")
|
|
3554
|
+
return (
|
|
3555
|
+
STRICT_OPS.has(op) && this._typeParamZeroFactory(type.name) != null
|
|
3556
|
+
);
|
|
3557
|
+
if (this._is64BitInt(type))
|
|
3558
|
+
return STRICT_OPS.has(op) || op === "<<" || op === ">>" || op === "&^";
|
|
1930
3559
|
if (!STRICT_OPS.has(op)) return false;
|
|
1931
3560
|
if (this._isStrictAtomicOp(op, this._typeName(type))) return true;
|
|
3561
|
+
if (op === "-" && this._isUintWord(type)) return true;
|
|
3562
|
+
if (op === "<<" || op === ">>")
|
|
3563
|
+
return WIDE_INT_NAMES.has(this._typeName(type));
|
|
1932
3564
|
return this.isIntType(type) && (op === "/" || op === "%");
|
|
1933
3565
|
},
|
|
1934
3566
|
|
|
1935
3567
|
// Emits `lhs = strict(lhs op rhs)` for any lvalue shape; returns false when not applicable.
|
|
1936
3568
|
// Indexed / nested targets are split into object + key temps so sub-expressions run once.
|
|
1937
|
-
_genStrictCompound(
|
|
3569
|
+
_genStrictCompound(
|
|
3570
|
+
lhsNode,
|
|
3571
|
+
op,
|
|
3572
|
+
rhsJs,
|
|
3573
|
+
srcLine = null,
|
|
3574
|
+
lhsJs = null,
|
|
3575
|
+
countType = null,
|
|
3576
|
+
) {
|
|
1938
3577
|
const type = lhsNode._type;
|
|
1939
3578
|
if (!this._needsStrictOp(op, type)) return false;
|
|
1940
3579
|
if (lhsNode.kind === "IndexExpr" && lhsNode.expr?._type?.kind === "map")
|
|
@@ -1942,7 +3581,7 @@ export const expressionGenMethods = {
|
|
|
1942
3581
|
const l = lhsJs ?? this._genAssignLhsExpr(lhsNode);
|
|
1943
3582
|
if (/^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*)*$/.test(l)) {
|
|
1944
3583
|
this.line(
|
|
1945
|
-
`${l} = ${this._genStrictBinary(op, l, rhsJs, type)};`,
|
|
3584
|
+
`${l} = ${this._genStrictBinary(op, l, rhsJs, type, countType)};`,
|
|
1946
3585
|
srcLine,
|
|
1947
3586
|
);
|
|
1948
3587
|
return true;
|
|
@@ -1955,21 +3594,40 @@ export const expressionGenMethods = {
|
|
|
1955
3594
|
const k = `__k${this._tmpCounter}`;
|
|
1956
3595
|
const target = `${o}[${k}]`;
|
|
1957
3596
|
this.line(
|
|
1958
|
-
`{ const ${o} = ${split.obj}, ${k} = ${split.key}; ${target} = ${this._genStrictBinary(op, target, rhsJs, type)}; }`,
|
|
3597
|
+
`{ const ${o} = ${split.obj}, ${k} = ${split.key}; ${target} = ${this._genStrictBinary(op, target, rhsJs, type, countType)}; }`,
|
|
1959
3598
|
srcLine,
|
|
1960
3599
|
);
|
|
1961
3600
|
} else {
|
|
1962
3601
|
const target = `${o}.${split.prop}`;
|
|
1963
3602
|
this.line(
|
|
1964
|
-
`{ const ${o} = ${split.obj}; ${target} = ${this._genStrictBinary(op, target, rhsJs, type)}; }`,
|
|
3603
|
+
`{ const ${o} = ${split.obj}; ${target} = ${this._genStrictBinary(op, target, rhsJs, type, countType)}; }`,
|
|
1965
3604
|
srcLine,
|
|
1966
3605
|
);
|
|
1967
3606
|
}
|
|
1968
3607
|
return true;
|
|
1969
3608
|
},
|
|
1970
3609
|
|
|
1971
|
-
|
|
3610
|
+
// A non-constant signed shift count on a narrow integer is checked for a
|
|
3611
|
+
// negative value (Go panics); 64-bit counts arrive as BigInt.
|
|
3612
|
+
_guardShiftCount(r, countType) {
|
|
3613
|
+
if (!countType || countType.kind === "untyped") return r;
|
|
3614
|
+
const name = this._typeName(countType);
|
|
3615
|
+
if (!name || name.startsWith("u")) return r;
|
|
3616
|
+
this.useHelper("shift");
|
|
3617
|
+
return `__shc(${this._is64BitInt(countType) ? `Number(${r})` : r})`;
|
|
3618
|
+
},
|
|
3619
|
+
|
|
3620
|
+
_genStrictBinary(op, l, r, type, countType = null) {
|
|
3621
|
+
if (type?.kind === "typeParam") {
|
|
3622
|
+
this.useHelper("generics");
|
|
3623
|
+
return `__tar(${this._typeParamZeroFactory(type.name)}, "${op}", ${l}, ${r})`;
|
|
3624
|
+
}
|
|
3625
|
+
if (this._is64BitInt(type)) {
|
|
3626
|
+
return this._gen64BitBinary(op, l, r, type);
|
|
3627
|
+
}
|
|
1972
3628
|
const tName = this._typeName(type);
|
|
3629
|
+
const isShift = op === "<<" || op === ">>";
|
|
3630
|
+
const rc = isShift ? this._guardShiftCount(r, countType) : r;
|
|
1973
3631
|
if (tName === "float32") {
|
|
1974
3632
|
return `Math.fround(${l} ${op} ${r})`;
|
|
1975
3633
|
}
|
|
@@ -1978,7 +3636,10 @@ export const expressionGenMethods = {
|
|
|
1978
3636
|
if (tName === "int32" || tName === "rune" || tName === "uint32") {
|
|
1979
3637
|
const signed = tName !== "uint32";
|
|
1980
3638
|
const cast = signed ? "| 0" : ">>> 0";
|
|
1981
|
-
if (
|
|
3639
|
+
if (op === "*")
|
|
3640
|
+
return signed
|
|
3641
|
+
? `Math.imul(${l}, ${r})`
|
|
3642
|
+
: `(Math.imul(${l}, ${r}) >>> 0)`;
|
|
1982
3643
|
if (op === "/")
|
|
1983
3644
|
return `((${r}) === 0 ? ${divZero} : (Math.trunc((${l}) / (${r})) ${cast}))`;
|
|
1984
3645
|
if (op === "%")
|
|
@@ -1986,9 +3647,9 @@ export const expressionGenMethods = {
|
|
|
1986
3647
|
if (op === "<<" || op === ">>") {
|
|
1987
3648
|
const ovf = signed && op === ">>" ? `((${l}) < 0 ? -1 : 0)` : "0";
|
|
1988
3649
|
const jsOp = !signed && op === ">>" ? ">>>" : op;
|
|
1989
|
-
return `((${
|
|
3650
|
+
return `(((${rc}) >= 32 ? ${ovf} : ((${l}) ${jsOp} (${r}))) ${cast})`;
|
|
1990
3651
|
}
|
|
1991
|
-
if (op === "&^") return `((${l}) & ~(${r})) ${cast}`;
|
|
3652
|
+
if (op === "&^") return `(((${l}) & ~(${r})) ${cast})`;
|
|
1992
3653
|
return `((${l} ${op} ${r}) ${cast})`;
|
|
1993
3654
|
}
|
|
1994
3655
|
if (tName === "int8" || tName === "int16") {
|
|
@@ -1999,7 +3660,7 @@ export const expressionGenMethods = {
|
|
|
1999
3660
|
if (op === "%")
|
|
2000
3661
|
return `((${r}) === 0 ? ${divZero} : (((((${l}) % (${r})) << ${sh}) >> ${sh})))`;
|
|
2001
3662
|
if (op === "<<" || op === ">>") {
|
|
2002
|
-
return `((${
|
|
3663
|
+
return `((${rc}) >= ${bits} ? (${op === ">>" ? `((${l}) < 0 ? -1 : 0)` : "0"}) : (((${l}) ${op} (${r})) << ${sh} >> ${sh}))`;
|
|
2003
3664
|
}
|
|
2004
3665
|
if (op === "&^") return `((((${l}) & ~(${r})) << ${sh}) >> ${sh})`;
|
|
2005
3666
|
return `((((${l} ${op} ${r})) << ${sh}) >> ${sh})`;
|
|
@@ -2013,11 +3674,21 @@ export const expressionGenMethods = {
|
|
|
2013
3674
|
return `((${r}) === 0 ? ${divZero} : (((${l}) % (${r})) & ${mask}))`;
|
|
2014
3675
|
if (op === "<<" || op === ">>") {
|
|
2015
3676
|
const jsOp = op === ">>" ? ">>>" : "<<";
|
|
2016
|
-
return `((${
|
|
3677
|
+
return `((${rc}) >= ${bits} ? 0 : (((${l}) ${jsOp} (${r})) & ${mask}))`;
|
|
2017
3678
|
}
|
|
2018
3679
|
if (op === "&^") return `(((${l}) & ~(${r})) & ${mask})`;
|
|
2019
3680
|
return `((${l} ${op} ${r}) & ${mask})`;
|
|
2020
3681
|
}
|
|
3682
|
+
if (op === "-" && this._isUintWord(type))
|
|
3683
|
+
return `((__d) => __d < 0 ? __d + 2 ** 64 : __d)((${l}) - (${r}))`;
|
|
3684
|
+
if (op === "<<" || op === ">>") {
|
|
3685
|
+
// 64-bit semantics: operand first, then count (Go order); a negative
|
|
3686
|
+
// count panics and the result wraps to 64 bits.
|
|
3687
|
+
this.useHelper("shift");
|
|
3688
|
+
const unsigned = this._typeName(type).startsWith("u");
|
|
3689
|
+
if (op === "<<") return `__shl64(${l}, ${r}, ${unsigned})`;
|
|
3690
|
+
return `__shr64(${l}, ${r}, ${unsigned})`;
|
|
3691
|
+
}
|
|
2021
3692
|
if (this.isIntType(type)) {
|
|
2022
3693
|
if (op === "/")
|
|
2023
3694
|
return `((${r}) === 0 ? ${divZero} : Math.trunc((${l}) / (${r})))`;
|