gofront 1.8.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 +3 -3
- package/package.json +5 -4
- package/src/backend/js/expressions.js +849 -371
- package/src/backend/js/index.js +241 -147
- package/src/backend/js/natives.js +383 -0
- package/src/backend/js/runtime.js +512 -2547
- package/src/backend/js/statements.js +506 -103
- package/src/backend/js/stdlib/index.js +45 -93
- package/src/backend/js/templ.js +32 -45
- package/src/backend/js/type-info.js +177 -13
- package/src/backend/shared/fixed-float.js +20 -0
- package/src/backend/shared/fmt-types.js +63 -171
- package/src/backend/shared/host-unicode.js +36 -0
- package/src/backend/wasm/boundary.js +490 -965
- package/src/backend/wasm/emit-builtins.js +643 -256
- package/src/backend/wasm/emit-exprs.js +124 -57
- package/src/backend/wasm/emit-maps.js +17 -367
- package/src/backend/wasm/emit-stmts.js +325 -75
- package/src/backend/wasm/emit.js +104 -13
- package/src/backend/wasm/globals.js +47 -8
- package/src/backend/wasm/glue.js +2 -4
- package/src/backend/wasm/index.js +36 -530
- package/src/backend/wasm/method-expr.js +12 -1
- package/src/backend/wasm/module-emitter.js +92 -717
- package/src/backend/wasm/monomorph.js +103 -6
- package/src/backend/wasm/natives.js +596 -0
- package/src/backend/wasm/prescan.js +5 -153
- package/src/backend/wasm/prune-funcs.js +89 -0
- package/src/backend/wasm/stdlib/index.js +2 -32
- package/src/backend/wasm/types.js +68 -20
- package/src/backend/wasm/variadic.js +3 -3
- package/src/cli/cli-core.js +5 -1
- package/src/cli/test-runner.js +115 -342
- package/src/compiler.js +108 -130
- package/src/lexer/lexer.js +105 -11
- package/src/lexer/templ-lexer.js +61 -18
- package/src/lexer/tokens.js +1 -0
- package/src/link/stdlib.js +354 -132
- package/src/link/type-names.js +148 -0
- package/src/link/wasm.js +30 -6
- package/src/lower/bounds.js +153 -0
- package/src/lower/captures.js +92 -33
- package/src/lower/embedding.js +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 +7 -1
- package/src/lower/json-codecs.js +228 -247
- package/src/lower/ownership.js +25 -7
- package/src/lower/reachability.js +69 -51
- package/src/lower/walk.js +12 -0
- package/src/parser/declarations.js +28 -5
- package/src/parser/expressions.js +86 -16
- package/src/parser/statements.js +1 -0
- package/src/parser/templ.js +44 -5
- package/src/parser/types.js +1 -0
- package/src/resolver.js +1 -2
- package/src/typechecker/assignability.js +124 -11
- package/src/typechecker/const-fold.js +70 -27
- package/src/typechecker/constants.js +32 -6
- package/src/typechecker/expressions.js +326 -98
- package/src/typechecker/index.js +208 -33
- package/src/typechecker/init-order.js +136 -40
- package/src/typechecker/operand-text.js +40 -0
- package/src/typechecker/resolve.js +192 -36
- package/src/typechecker/statements.js +122 -11
- package/src/typechecker/stdlib/excluded.js +9 -5
- package/src/typechecker/stdlib/index.js +25 -9
- package/src/typechecker/termination.js +10 -1
- package/src/typechecker/types.js +203 -573
- package/stdlib/bytes/buffer.go +479 -0
- package/stdlib/bytes/bytes.go +1010 -0
- package/stdlib/bytes/reader.go +161 -0
- package/stdlib/context/context.go +115 -0
- package/stdlib/crypto/rand/rand.go +34 -0
- package/stdlib/crypto/sha256/sha256.go +249 -0
- package/stdlib/encoding/base64/base64.go +331 -0
- package/stdlib/encoding/binary/binary.go +332 -0
- package/stdlib/encoding/hex/hex.go +140 -0
- package/stdlib/encoding/json/json.go +62 -0
- package/stdlib/errors/errors.go +174 -0
- package/stdlib/fmt/errors.go +110 -0
- package/stdlib/fmt/format.go +732 -0
- package/stdlib/fmt/print.go +1232 -0
- package/stdlib/gom/gom.go +293 -0
- package/stdlib/html/html.go +251 -0
- package/stdlib/internal/fmtrt/fmtrt.go +163 -0
- package/stdlib/internal/jsonrt/dec.go +171 -38
- package/stdlib/internal/jsonrt/enc.go +186 -36
- package/stdlib/internal/jsonrt/scanner.go +17 -8
- package/stdlib/io/io.go +157 -1
- package/stdlib/maps/maps.go +131 -0
- package/stdlib/math/bits/bits.go +507 -0
- package/stdlib/math/extra.go +16 -14
- package/stdlib/math/math.go +439 -0
- package/stdlib/math/rand/rand.go +297 -0
- package/stdlib/os/os.go +93 -0
- package/stdlib/path/filepath/filepath.go +47 -0
- package/stdlib/path/path.go +394 -0
- package/stdlib/regexp/regexp.go +231 -0
- package/stdlib/slices/slices.go +526 -0
- package/stdlib/slices/sort.go +209 -0
- package/stdlib/slices/zsort_cmp.go +480 -0
- package/stdlib/slices/zsort_ordered.go +482 -0
- package/stdlib/sort/sort.go +391 -452
- package/stdlib/sort/zsort_func.go +480 -0
- package/stdlib/sort/zsort_iface.go +480 -0
- package/stdlib/strconv/float.go +1699 -0
- package/stdlib/strconv/isprint.go +752 -0
- package/stdlib/strconv/quote.go +235 -50
- package/stdlib/strconv/strconv.go +616 -0
- package/stdlib/strings/builder.go +95 -0
- package/stdlib/strings/reader.go +163 -0
- package/stdlib/strings/replacer.go +102 -0
- package/stdlib/strings/strings.go +807 -0
- package/stdlib/sync/sync.go +268 -0
- package/stdlib/testing/async_js.go +61 -0
- package/stdlib/testing/benchmark.go +297 -0
- package/stdlib/testing/testing.go +523 -0
- package/stdlib/time/duration.go +233 -0
- package/stdlib/time/format.go +745 -0
- package/stdlib/time/parse.go +790 -0
- package/stdlib/time/sleep_js.go +63 -0
- package/stdlib/time/time.go +834 -0
- package/stdlib/unicode/unicode.go +141 -3
- package/stdlib/unicode/utf8/utf8.go +464 -0
- package/src/backend/js/fmt-runtime.js +0 -573
- package/src/backend/js/stdlib/base64.js +0 -12
- package/src/backend/js/stdlib/binary.js +0 -102
- package/src/backend/js/stdlib/bits.js +0 -119
- package/src/backend/js/stdlib/builder.js +0 -98
- package/src/backend/js/stdlib/bytes.js +0 -368
- package/src/backend/js/stdlib/errors.js +0 -45
- package/src/backend/js/stdlib/fmt.js +0 -517
- package/src/backend/js/stdlib/gom.js +0 -155
- package/src/backend/js/stdlib/hex.js +0 -17
- package/src/backend/js/stdlib/html.js +0 -20
- package/src/backend/js/stdlib/io.js +0 -137
- package/src/backend/js/stdlib/json.js +0 -1167
- package/src/backend/js/stdlib/maps.js +0 -37
- package/src/backend/js/stdlib/math.js +0 -85
- package/src/backend/js/stdlib/os.js +0 -13
- package/src/backend/js/stdlib/path.js +0 -48
- package/src/backend/js/stdlib/rand.js +0 -32
- package/src/backend/js/stdlib/regexp.js +0 -54
- package/src/backend/js/stdlib/sha256.js +0 -12
- package/src/backend/js/stdlib/shared.js +0 -17
- package/src/backend/js/stdlib/simd.js +0 -34
- package/src/backend/js/stdlib/slices.js +0 -107
- package/src/backend/js/stdlib/sort.js +0 -30
- package/src/backend/js/stdlib/strconv.js +0 -79
- package/src/backend/js/stdlib/strings.js +0 -301
- package/src/backend/js/stdlib/sync.js +0 -13
- package/src/backend/js/stdlib/testing.js +0 -41
- package/src/backend/js/stdlib/time.js +0 -152
- package/src/backend/js/stdlib/unicode.js +0 -25
- package/src/backend/js/stdlib/utf8.js +0 -26
- package/src/backend/wasm/stdlib/bytes.js +0 -6716
- package/src/backend/wasm/stdlib/core.js +0 -10278
- package/src/backend/wasm/stdlib/io.js +0 -3578
- package/src/backend/wasm/stdlib/packages.js +0 -2402
- package/src/backend/wasm/stdlib/pure.js +0 -720
- package/src/backend/wasm/stdlib/time.js +0 -2229
- package/src/typechecker/stdlib/core.js +0 -1079
- package/src/typechecker/stdlib/extended.js +0 -1210
- package/stdlib/bytes/iter.go +0 -192
- package/stdlib/bytes/search.go +0 -240
- package/stdlib/bytes/split.go +0 -218
- package/stdlib/errors/overlay.go +0 -12
- package/stdlib/maps/iter.go +0 -65
- package/stdlib/slices/extra.go +0 -73
- package/stdlib/slices/iter.go +0 -113
- package/stdlib/strings/extra.go +0 -35
- package/stdlib/strings/iter.go +0 -122
- package/stdlib/sync/extra.go +0 -105
- package/stdlib/unicode/utf8/bytes.go +0 -239
|
@@ -2,21 +2,26 @@ import {
|
|
|
2
2
|
closureMutates,
|
|
3
3
|
fnMutates,
|
|
4
4
|
nodeMutatesVar,
|
|
5
|
-
nodeWritesVar,
|
|
6
5
|
rootIdentName,
|
|
7
6
|
someChild,
|
|
8
7
|
} from "../../lower/index.js";
|
|
8
|
+
import { hasCallOrReceive } from "../../lower/walk.js";
|
|
9
9
|
import { constIntValue } from "../../typechecker/const-fold.js";
|
|
10
10
|
import {
|
|
11
11
|
BROWSER_GLOBALS,
|
|
12
12
|
compositeLitIndices,
|
|
13
13
|
ERROR,
|
|
14
|
+
embeddedFieldName,
|
|
15
|
+
is64BitInt,
|
|
16
|
+
is64BitUnsigned,
|
|
14
17
|
isAny,
|
|
15
18
|
isComplex,
|
|
16
19
|
isTypedArraySlice,
|
|
17
20
|
typedArrayConstructorForElem,
|
|
18
21
|
typeStr,
|
|
19
22
|
} from "../../typechecker/types.js";
|
|
23
|
+
import { goTypeName } from "../shared/fmt-types.js";
|
|
24
|
+
import { jsNativeCall, NATIVE_HELPERS } from "./natives.js";
|
|
20
25
|
|
|
21
26
|
// True when `node` compares the identifier `name` with nil (`l == nil`): a
|
|
22
27
|
// pointer-receiver method that does so is meant to run on a nil receiver.
|
|
@@ -141,20 +146,6 @@ function collectFunctionLabels(node, labels = new Set()) {
|
|
|
141
146
|
}
|
|
142
147
|
|
|
143
148
|
const NAMED_ZERO_TABLE = {
|
|
144
|
-
"time.Time": [null, "{ _d: new Date(0), _isZero: true }"],
|
|
145
|
-
"strings.Builder": [null, '{ _buf: "" }'],
|
|
146
|
-
"bytes.Buffer": [null, "{ _buf: [] }"],
|
|
147
|
-
"strings.Replacer": [null, "{ _p: [], Replace(s) { return s; } }"],
|
|
148
|
-
"sync.Mutex": ["sync", "__sync_mutex()"],
|
|
149
|
-
"sync.RWMutex": ["sync", "__sync_rwmutex()"],
|
|
150
|
-
"sync.Once": ["sync", "__sync_once()"],
|
|
151
|
-
"sync.WaitGroup": ["sync", "__sync_waitgroup()"],
|
|
152
|
-
"binary.LittleEndian": ["binary", "__binary_le"],
|
|
153
|
-
"encoding/binary.LittleEndian": ["binary", "__binary_le"],
|
|
154
|
-
"binary.BigEndian": ["binary", "__binary_be"],
|
|
155
|
-
"encoding/binary.BigEndian": ["binary", "__binary_be"],
|
|
156
|
-
"base64.Encoding": ["base64", "__base64_std"],
|
|
157
|
-
"encoding/base64.Encoding": ["base64", "__base64_std"],
|
|
158
149
|
"simd.F32x4": [null, "Object.freeze([0, 0, 0, 0])"],
|
|
159
150
|
F32x4: [null, "Object.freeze([0, 0, 0, 0])"],
|
|
160
151
|
"simd.Mask32x4": [null, "Object.freeze([false, false, false, false])"],
|
|
@@ -236,91 +227,13 @@ function splitLastAccess(js) {
|
|
|
236
227
|
|
|
237
228
|
// Namespace constants: pkg.Field → JS literal
|
|
238
229
|
const NS_CONSTANTS = {
|
|
239
|
-
math: {
|
|
240
|
-
Pi: "Math.PI",
|
|
241
|
-
E: "Math.E",
|
|
242
|
-
Phi: "1.618033988749895",
|
|
243
|
-
Sqrt2: "Math.SQRT2",
|
|
244
|
-
SqrtE: "1.6487212707001282",
|
|
245
|
-
SqrtPi: "1.7724538509055159",
|
|
246
|
-
SqrtPhi: "1.272019649514069",
|
|
247
|
-
Ln2: "Math.LN2",
|
|
248
|
-
Log2E: "Math.LOG2E",
|
|
249
|
-
Ln10: "Math.LN10",
|
|
250
|
-
Log10E: "Math.LOG10E",
|
|
251
|
-
MaxFloat32: "3.4028234663852886e38",
|
|
252
|
-
SmallestNonzeroFloat32: "1.401298464324817e-45",
|
|
253
|
-
MaxFloat64: "Number.MAX_VALUE",
|
|
254
|
-
SmallestNonzeroFloat64: "Number.MIN_VALUE",
|
|
255
|
-
MaxInt: "Number.MAX_SAFE_INTEGER",
|
|
256
|
-
MinInt: "Number.MIN_SAFE_INTEGER",
|
|
257
|
-
MaxInt8: "127",
|
|
258
|
-
MinInt8: "-128",
|
|
259
|
-
MaxInt16: "32767",
|
|
260
|
-
MinInt16: "-32768",
|
|
261
|
-
MaxInt32: "2147483647",
|
|
262
|
-
MinInt32: "-2147483648",
|
|
263
|
-
MaxInt64: "9223372036854775807",
|
|
264
|
-
MinInt64: "-9223372036854775808",
|
|
265
|
-
MaxUint8: "255",
|
|
266
|
-
MaxUint16: "65535",
|
|
267
|
-
MaxUint32: "4294967295",
|
|
268
|
-
MaxUint: "18446744073709551615",
|
|
269
|
-
MaxUint64: "18446744073709551615",
|
|
270
|
-
},
|
|
271
|
-
strconv: {
|
|
272
|
-
ErrSyntax: "__strconv_err_syntax",
|
|
273
|
-
ErrRange: "__strconv_err_range",
|
|
274
|
-
},
|
|
275
|
-
bits: {
|
|
276
|
-
UintSize: "64",
|
|
277
|
-
},
|
|
278
|
-
path: {
|
|
279
|
-
ErrBadPattern: "__path_err_bad_pattern",
|
|
280
|
-
},
|
|
281
|
-
filepath: {
|
|
282
|
-
ErrBadPattern: "__path_err_bad_pattern",
|
|
283
|
-
},
|
|
284
|
-
io: {
|
|
285
|
-
EOF: "__io_eof",
|
|
286
|
-
Discard:
|
|
287
|
-
"{ WriteString(s) { return [s.length, null]; }, Write(b) { return [b.length, null]; } }",
|
|
288
|
-
},
|
|
289
|
-
sha256: {
|
|
290
|
-
Size: "32",
|
|
291
|
-
BlockSize: "64",
|
|
292
|
-
},
|
|
293
|
-
hex: {
|
|
294
|
-
ErrLength: '__error("encoding/hex: odd length hex string")',
|
|
295
|
-
},
|
|
296
|
-
base64: {
|
|
297
|
-
StdPadding: "61",
|
|
298
|
-
NoPadding: "-1",
|
|
299
|
-
StdEncoding: "__base64_std",
|
|
300
|
-
URLEncoding: "__base64_url",
|
|
301
|
-
RawStdEncoding: "__base64_raw_std",
|
|
302
|
-
RawURLEncoding: "__base64_raw_url",
|
|
303
|
-
},
|
|
304
|
-
binary: {
|
|
305
|
-
LittleEndian: "__binary_le",
|
|
306
|
-
BigEndian: "__binary_be",
|
|
307
|
-
},
|
|
308
|
-
rand: {
|
|
309
|
-
Reader: "__crypto_rand_reader",
|
|
310
|
-
},
|
|
311
|
-
os: {
|
|
312
|
-
Args: "process.argv",
|
|
313
|
-
Stdout: "__os_stdout",
|
|
314
|
-
Stderr: "__os_stderr",
|
|
315
|
-
Stdin: "__os_stdin",
|
|
316
|
-
},
|
|
317
230
|
time: {
|
|
318
|
-
Nanosecond: "
|
|
319
|
-
Microsecond: "
|
|
320
|
-
Millisecond: "
|
|
321
|
-
Second: "
|
|
322
|
-
Minute: "
|
|
323
|
-
Hour: "
|
|
231
|
+
Nanosecond: "1n",
|
|
232
|
+
Microsecond: "1000n",
|
|
233
|
+
Millisecond: "1000000n",
|
|
234
|
+
Second: "1000000000n",
|
|
235
|
+
Minute: "60000000000n",
|
|
236
|
+
Hour: "3600000000000n",
|
|
324
237
|
RFC3339: '"2006-01-02T15:04:05Z07:00"',
|
|
325
238
|
RFC3339Nano: '"2006-01-02T15:04:05.999999999Z07:00"',
|
|
326
239
|
DateOnly: '"2006-01-02"',
|
|
@@ -348,7 +261,6 @@ const NS_CONSTANTS = {
|
|
|
348
261
|
Friday: "5",
|
|
349
262
|
Saturday: "6",
|
|
350
263
|
},
|
|
351
|
-
utf8: { RuneError: "0xFFFD", MaxRune: "0x10FFFF", UTFMax: "4" },
|
|
352
264
|
};
|
|
353
265
|
|
|
354
266
|
// JavaScript precedence of the operators emitted for Go binary expressions.
|
|
@@ -413,6 +325,7 @@ const SHARED_ZERO_FACTORIES = {
|
|
|
413
325
|
0: "__z0",
|
|
414
326
|
'""': "__zs",
|
|
415
327
|
false: "__zf",
|
|
328
|
+
"0n": "__z64",
|
|
416
329
|
};
|
|
417
330
|
|
|
418
331
|
// Zero-value JS literals for basic Go types.
|
|
@@ -422,11 +335,11 @@ const ZERO_FOR_BASIC = {
|
|
|
422
335
|
int8: "0",
|
|
423
336
|
int16: "0",
|
|
424
337
|
int32: "0",
|
|
425
|
-
int64: "
|
|
338
|
+
int64: "0n",
|
|
426
339
|
uint8: "0",
|
|
427
340
|
uint16: "0",
|
|
428
341
|
uint32: "0",
|
|
429
|
-
uint64: "
|
|
342
|
+
uint64: "0n",
|
|
430
343
|
uintptr: "0",
|
|
431
344
|
float32: "0",
|
|
432
345
|
float64: "0",
|
|
@@ -439,13 +352,18 @@ const ZERO_FOR_BASIC = {
|
|
|
439
352
|
};
|
|
440
353
|
|
|
441
354
|
// Set of all numeric type names (for type-assertion codegen).
|
|
442
|
-
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
|
+
]);
|
|
443
360
|
|
|
444
361
|
// Builtin function codegen — keyed by builtin name, value is (self, expr) => string.
|
|
445
362
|
const BUILTIN_GEN = {
|
|
446
363
|
append: (s, e) => s.genAppend(e),
|
|
447
364
|
len: (s, e) => s._genBuiltinLen(e),
|
|
448
365
|
cap: (s, e) => {
|
|
366
|
+
if (e._constLen != null) return s._genConstLen(e);
|
|
449
367
|
s.useHelper("slice");
|
|
450
368
|
return `__cap(${s.genExpr(e.args[0])})`;
|
|
451
369
|
},
|
|
@@ -457,13 +375,11 @@ const BUILTIN_GEN = {
|
|
|
457
375
|
copy: (s, e) => {
|
|
458
376
|
const dst = s.genExpr(e.args[0]);
|
|
459
377
|
const src = s._genSliceSource(e.args[1]);
|
|
460
|
-
// Array elements are values: the copied elements must not
|
|
378
|
+
// Array and struct elements are values: the copied elements must not
|
|
379
|
+
// alias src.
|
|
461
380
|
const t = e.args[1]._type;
|
|
462
381
|
const elem = (t?.kind === "named" ? t.underlying : t)?.elem;
|
|
463
|
-
const clone =
|
|
464
|
-
s._aggregateBase(elem)?.kind === "array"
|
|
465
|
-
? s._cloneJs(elem, "__e")
|
|
466
|
-
: "__e";
|
|
382
|
+
const clone = s._aggregateBase(elem) ? s._cloneJs(elem, "__e") : "__e";
|
|
467
383
|
const items =
|
|
468
384
|
clone === "__e"
|
|
469
385
|
? "(__cs.slice ? __cs.slice(0, n) : __cs.subarray(0, n))"
|
|
@@ -496,15 +412,21 @@ const BUILTIN_GEN = {
|
|
|
496
412
|
// `print` writes without a trailing newline (Go semantics); the wasm glue
|
|
497
413
|
// buffers the same way, so output parity holds across backends.
|
|
498
414
|
print: (s, e) =>
|
|
499
|
-
`((__s) => (typeof process !== "undefined" && process?.stdout?.write ? process.stdout.write(__s) : console.log(__s)))([${e.args.map((a) => s.genExpr(a)).join(", ")}].map(String).join("
|
|
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(""))`,
|
|
500
416
|
println: (s, e) =>
|
|
501
417
|
`console.log(${e.args.map((a) => s.genExpr(a)).join(", ")})`,
|
|
502
|
-
panic: (s, e) =>
|
|
418
|
+
panic: (s, e) => {
|
|
419
|
+
const arg = s.genExpr(e.args[0]);
|
|
420
|
+
s.useHelper("panicRt");
|
|
421
|
+
return `__gopanicv(${arg})`;
|
|
422
|
+
},
|
|
503
423
|
recover: (s) => {
|
|
504
424
|
s.useHelper("panicRt");
|
|
505
425
|
return `__recover(${s._recoverOkInScope ? "__recoverOk" : "false"})`;
|
|
506
426
|
},
|
|
507
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]);
|
|
508
430
|
s.useHelper("error");
|
|
509
431
|
return `__error(${s.genExpr(e.args[0])})`;
|
|
510
432
|
},
|
|
@@ -521,13 +443,25 @@ const BUILTIN_GEN = {
|
|
|
521
443
|
// A type parameter may be a string or a number: pick the form at run time
|
|
522
444
|
// (Math.min/max keeps NaN propagation and -0 for floats).
|
|
523
445
|
function genMinMax(s, e, fn, cmp) {
|
|
524
|
-
const
|
|
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
|
+
});
|
|
525
459
|
const fold = (xs) => `${xs}.reduce((a, b) => (b ${cmp} a ? b : a))`;
|
|
526
460
|
if (args.length === 1) return args[0];
|
|
527
461
|
if (e._type?.kind === "typeParam")
|
|
528
|
-
return `((...v) => (typeof v[0] === "string" ? ${fold("v")} : Math.${fn}(...v)))(${args.join(", ")})`;
|
|
529
|
-
if (
|
|
530
|
-
return
|
|
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(", ")})`;
|
|
531
465
|
}
|
|
532
466
|
|
|
533
467
|
// Expression kinds whose folded constant value (`_const`, attached by the
|
|
@@ -590,6 +524,14 @@ function hasTypeParamType(t) {
|
|
|
590
524
|
|
|
591
525
|
export const expressionGenMethods = {
|
|
592
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
|
+
}
|
|
593
535
|
if (expr._ifaceKeyDyn && !expr._ifaceKeying) {
|
|
594
536
|
// A key typed by a type parameter: encode it for the instantiated type.
|
|
595
537
|
const f = this._typeParamZeroFactory(expr._ifaceKeyParam);
|
|
@@ -613,21 +555,66 @@ export const expressionGenMethods = {
|
|
|
613
555
|
expr._ifaceKeying = true;
|
|
614
556
|
try {
|
|
615
557
|
this.useHelper("structKey");
|
|
616
|
-
|
|
558
|
+
const tag = this._ifaceKeyTag(expr._ifaceKeyType);
|
|
559
|
+
return `__ik(${this.genExpr(expr)}${tag ? `, ${JSON.stringify(tag)}` : ""})`;
|
|
617
560
|
} finally {
|
|
618
561
|
expr._ifaceKeying = false;
|
|
619
562
|
}
|
|
620
563
|
}
|
|
621
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
|
+
}
|
|
622
607
|
if (this._isNamedIfaceBox(expr)) return this._genNamedIfaceBoxExpr(expr);
|
|
623
|
-
if (expr._ifaceSrc && !expr.
|
|
624
|
-
const
|
|
625
|
-
if (
|
|
626
|
-
|
|
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;
|
|
627
613
|
try {
|
|
628
|
-
|
|
614
|
+
const ptr = expr._ifaceSrc.kind === "pointer" ? ", true" : "";
|
|
615
|
+
return `__ity(${this.genExpr(expr)}, ${JSON.stringify(tag)}${ptr})`;
|
|
629
616
|
} finally {
|
|
630
|
-
expr.
|
|
617
|
+
expr._ityTagging = false;
|
|
631
618
|
}
|
|
632
619
|
}
|
|
633
620
|
}
|
|
@@ -635,11 +622,13 @@ export const expressionGenMethods = {
|
|
|
635
622
|
expr._ifaceBoxing = true;
|
|
636
623
|
try {
|
|
637
624
|
this.useHelper("ifaceBox");
|
|
638
|
-
|
|
625
|
+
const boxed = this._ifaceBoxWrap(
|
|
639
626
|
this.genExpr(expr),
|
|
640
627
|
expr._ifaceBox,
|
|
641
628
|
expr._ifacePtrFields,
|
|
629
|
+
expr.kind === "CompositeLit",
|
|
642
630
|
);
|
|
631
|
+
return this._typedNilWrap(boxed, expr);
|
|
643
632
|
} finally {
|
|
644
633
|
expr._ifaceBoxing = false;
|
|
645
634
|
}
|
|
@@ -649,15 +638,30 @@ export const expressionGenMethods = {
|
|
|
649
638
|
switch (expr.kind) {
|
|
650
639
|
case "BasicLit":
|
|
651
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
|
+
}
|
|
652
647
|
if (expr._const?.k === "INT")
|
|
653
648
|
this._warnConstPrecision(expr._const, expr);
|
|
654
649
|
return expr.value; // int, float, bool, nil(null)
|
|
655
650
|
case "ImagLit":
|
|
656
651
|
return `{ re: 0, im: ${expr.value} }`;
|
|
657
|
-
case "Ident":
|
|
652
|
+
case "Ident": {
|
|
658
653
|
if (this._boxedVars.has(expr.name) && !expr._isAddressOf)
|
|
659
654
|
return `${expr.name}.value`;
|
|
660
|
-
|
|
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
|
+
}
|
|
661
665
|
case "AwaitExpr":
|
|
662
666
|
return `await ${this.genExpr(expr.expr)}`;
|
|
663
667
|
case "InstantiationExpr":
|
|
@@ -672,6 +676,17 @@ export const expressionGenMethods = {
|
|
|
672
676
|
}
|
|
673
677
|
},
|
|
674
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
|
+
|
|
675
690
|
// The literal for a folded constant expression (`1 << 40` →
|
|
676
691
|
// `1099511627776`), or null. Integers outside ±2⁵³ emit the nearest
|
|
677
692
|
// Number and a precision warning.
|
|
@@ -686,6 +701,8 @@ export const expressionGenMethods = {
|
|
|
686
701
|
return null;
|
|
687
702
|
}
|
|
688
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;
|
|
689
706
|
if (val.k === "INT" || expr._intConst) this._warnConstPrecision(val, expr);
|
|
690
707
|
return CONST_LITS[val.k]?.(val.v) ?? null;
|
|
691
708
|
},
|
|
@@ -694,6 +711,8 @@ export const expressionGenMethods = {
|
|
|
694
711
|
// per node per generate run: some forms generate the same node more than
|
|
695
712
|
// once, and cached ASTs are generated again by later compiles).
|
|
696
713
|
_warnConstPrecision(val, expr) {
|
|
714
|
+
if (this._is64BitInt(expr?._type) || expr?._is64Bit || expr?._floatConst)
|
|
715
|
+
return;
|
|
697
716
|
const i = constIntValue(val);
|
|
698
717
|
if (i === null || (i <= JS_SAFE_INT && i >= -JS_SAFE_INT)) return;
|
|
699
718
|
if (BigInt(Number(i)) !== i && !this._precisionWarned.has(expr)) {
|
|
@@ -703,7 +722,9 @@ export const expressionGenMethods = {
|
|
|
703
722
|
},
|
|
704
723
|
|
|
705
724
|
// `js` boxed as `box` (a checker _ifaceBoxMeta kind) for an interface.
|
|
706
|
-
|
|
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) {
|
|
707
728
|
this.useHelper("ifaceBox");
|
|
708
729
|
const fn =
|
|
709
730
|
{
|
|
@@ -713,14 +734,22 @@ export const expressionGenMethods = {
|
|
|
713
734
|
map: "__ifm",
|
|
714
735
|
slice: "__ifs",
|
|
715
736
|
}[box] ?? "__ifv";
|
|
716
|
-
const
|
|
717
|
-
|
|
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}` : ""})`;
|
|
718
741
|
},
|
|
719
742
|
|
|
720
|
-
// `
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
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})`;
|
|
724
753
|
},
|
|
725
754
|
|
|
726
755
|
_isNamedBoxKind(expr) {
|
|
@@ -819,8 +848,12 @@ export const expressionGenMethods = {
|
|
|
819
848
|
// Pseudo type object for an AST type node (used where no checker type is available).
|
|
820
849
|
_typeFromNode(node) {
|
|
821
850
|
if (!node) return null;
|
|
822
|
-
if (node.kind === "TypeName"
|
|
823
|
-
|
|
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
|
+
}
|
|
824
857
|
if (node.kind === "ArrayType")
|
|
825
858
|
return { kind: "array", elem: this._typeFromNode(node.elem) };
|
|
826
859
|
return null;
|
|
@@ -857,7 +890,9 @@ export const expressionGenMethods = {
|
|
|
857
890
|
_isAddressableExpr(e) {
|
|
858
891
|
switch (e?.kind) {
|
|
859
892
|
case "Ident":
|
|
860
|
-
return
|
|
893
|
+
return (
|
|
894
|
+
e.name !== "_" && (!e._isTypeRef || e._type?.kind === "typeParam")
|
|
895
|
+
);
|
|
861
896
|
case "SelectorExpr":
|
|
862
897
|
return !e._isMethodValue && !e._isMethodExpr;
|
|
863
898
|
case "IndexExpr":
|
|
@@ -889,11 +924,6 @@ export const expressionGenMethods = {
|
|
|
889
924
|
return rootIdentName(e);
|
|
890
925
|
},
|
|
891
926
|
|
|
892
|
-
// True when `node` itself (not its children) writes to variable `name`.
|
|
893
|
-
_nodeWritesVar(node, name) {
|
|
894
|
-
return nodeWritesVar(node, name);
|
|
895
|
-
},
|
|
896
|
-
|
|
897
927
|
// True when `node` may modify (or take the address of) the value held by variable `name`.
|
|
898
928
|
_nodeMutatesVar(node, name, addrOnly = false) {
|
|
899
929
|
return nodeMutatesVar(node, name, addrOnly);
|
|
@@ -1031,7 +1061,9 @@ export const expressionGenMethods = {
|
|
|
1031
1061
|
// Dereference: *p
|
|
1032
1062
|
if (op === "*") {
|
|
1033
1063
|
if (this._isStructPointerType(expr.operand._type)) {
|
|
1034
|
-
return
|
|
1064
|
+
return expr._nonNil
|
|
1065
|
+
? this.genExpr(expr.operand)
|
|
1066
|
+
: this._genNilChecked(this.genExpr(expr.operand));
|
|
1035
1067
|
}
|
|
1036
1068
|
if (expr.operand._type?.base?.kind === "typeParam") {
|
|
1037
1069
|
if (expr._derefOpTarget) return `${this.genExpr(expr.operand)}.value`;
|
|
@@ -1046,6 +1078,17 @@ export const expressionGenMethods = {
|
|
|
1046
1078
|
return inner;
|
|
1047
1079
|
}
|
|
1048
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
|
+
}
|
|
1049
1092
|
const wrap = this.strict
|
|
1050
1093
|
? SMALL_UNSIGNED_WRAP[this._typeName(expr._type)]
|
|
1051
1094
|
: null;
|
|
@@ -1064,11 +1107,23 @@ export const expressionGenMethods = {
|
|
|
1064
1107
|
|
|
1065
1108
|
_genSelectorExpr(expr) {
|
|
1066
1109
|
// Go-source stdlib member: bundled under its mangled top-level name.
|
|
1067
|
-
if (expr._stdName)
|
|
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
|
+
}
|
|
1068
1119
|
if (this._isStdlibFuncValue(expr)) return this._genStdlibFuncValue(expr);
|
|
1069
1120
|
if (expr._isMethodExpr) {
|
|
1070
1121
|
const namedExpr = this._namedMethodExprInfo(expr);
|
|
1071
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))`;
|
|
1072
1127
|
return `((recv, ...args) => recv.${expr.field}(...args))`;
|
|
1073
1128
|
}
|
|
1074
1129
|
const namedValue = this._genNamedMethodValue(expr);
|
|
@@ -1076,26 +1131,6 @@ export const expressionGenMethods = {
|
|
|
1076
1131
|
if (expr.expr.kind === "Ident") {
|
|
1077
1132
|
const nsConst = NS_CONSTANTS[expr.expr.name]?.[expr.field];
|
|
1078
1133
|
if (nsConst !== undefined) {
|
|
1079
|
-
if (expr.expr.name === "strconv") {
|
|
1080
|
-
this.useHelper("strconvError");
|
|
1081
|
-
} else if (expr.expr.name === "path" || expr.expr.name === "filepath") {
|
|
1082
|
-
if (expr.field === "ErrBadPattern") this.useHelper("pathError");
|
|
1083
|
-
} else if (expr.expr.name === "io" && expr.field === "EOF") {
|
|
1084
|
-
this.useHelper("ioEOF");
|
|
1085
|
-
} else if (expr.expr.name === "binary") {
|
|
1086
|
-
this.useHelper("binary");
|
|
1087
|
-
} else if (expr.expr.name === "hex") {
|
|
1088
|
-
this.useHelper("hex");
|
|
1089
|
-
this.useHelper("error");
|
|
1090
|
-
} else if (expr.expr.name === "base64") {
|
|
1091
|
-
this.useHelper("base64");
|
|
1092
|
-
} else if (expr.expr.name === "rand" && expr.field === "Reader") {
|
|
1093
|
-
this.useHelper("cryptoRand");
|
|
1094
|
-
} else if (expr.expr.name === "sha256") {
|
|
1095
|
-
this.useHelper("sha256");
|
|
1096
|
-
} else if (expr.expr.name === "os") {
|
|
1097
|
-
this.useHelper("os");
|
|
1098
|
-
}
|
|
1099
1134
|
return nsConst;
|
|
1100
1135
|
}
|
|
1101
1136
|
}
|
|
@@ -1178,7 +1213,7 @@ export const expressionGenMethods = {
|
|
|
1178
1213
|
const inner = sf.type?.kind === "pointer" ? sf.type.base : sf.type;
|
|
1179
1214
|
if (inner?.kind !== "named" || !this.structNames.has(inner.name))
|
|
1180
1215
|
continue;
|
|
1181
|
-
const name =
|
|
1216
|
+
const name = embeddedFieldName(sf);
|
|
1182
1217
|
if (name === field) return null;
|
|
1183
1218
|
const ist = inner.underlying;
|
|
1184
1219
|
if (!ist?.fields?.has(field)) continue;
|
|
@@ -1299,21 +1334,54 @@ export const expressionGenMethods = {
|
|
|
1299
1334
|
|
|
1300
1335
|
_genCallArgs(expr) {
|
|
1301
1336
|
if (expr._multiForward) return `...${this.genExpr(expr.args[0])}`;
|
|
1302
|
-
return expr.
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
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)
|
|
1306
1346
|
: this._callerCopiesArg(expr, i)
|
|
1307
1347
|
? this.genValueExpr(a)
|
|
1308
1348
|
: this.genExpr(a),
|
|
1309
|
-
|
|
1310
|
-
|
|
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;
|
|
1311
1376
|
},
|
|
1312
1377
|
|
|
1313
1378
|
// `f(xs...)`: a Go-compiled variadic callee receives the slice itself in a
|
|
1314
1379
|
// marker (its prologue unwraps it); other callees get a JS spread.
|
|
1315
1380
|
_genSpreadArg(call, arg) {
|
|
1316
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} ?? [])`;
|
|
1317
1385
|
return this._calleeIsGoVariadic(call) ? `{ __sp: ${js} }` : `...${js}`;
|
|
1318
1386
|
},
|
|
1319
1387
|
|
|
@@ -1355,7 +1423,9 @@ export const expressionGenMethods = {
|
|
|
1355
1423
|
if (this._isStructPointerType(expr.expr._type)) {
|
|
1356
1424
|
// Strict mode: a field of a nil struct pointer panics as in Go.
|
|
1357
1425
|
const recv =
|
|
1358
|
-
this.strict && !expr._isMethodValue
|
|
1426
|
+
this.strict && !expr._isMethodValue && !expr._nonNil
|
|
1427
|
+
? this._genNilChecked(base)
|
|
1428
|
+
: base;
|
|
1359
1429
|
const sel = `${recv}${this._promotedPath(expr)}.${expr.field}`;
|
|
1360
1430
|
if (expr._isMethodValue && !expr._callee) {
|
|
1361
1431
|
// `p.M` with a value receiver binds a copy of `*p` now.
|
|
@@ -1366,6 +1436,15 @@ export const expressionGenMethods = {
|
|
|
1366
1436
|
);
|
|
1367
1437
|
return `((__r) => __r.${expr.field}.bind(__r))(${copy})`;
|
|
1368
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})`;
|
|
1369
1448
|
return `${sel}.bind(${base})`;
|
|
1370
1449
|
}
|
|
1371
1450
|
return sel;
|
|
@@ -1382,11 +1461,12 @@ export const expressionGenMethods = {
|
|
|
1382
1461
|
const base = wrapField
|
|
1383
1462
|
? `${this.genExpr(expr.expr)}.${wrapField}`
|
|
1384
1463
|
: this.genExpr(expr.expr);
|
|
1385
|
-
|
|
1464
|
+
let idx = this.genExpr(expr.index);
|
|
1386
1465
|
if (expr._mapValueType) {
|
|
1387
1466
|
if (!expr._lvalue) return this._genMapIndexExpr(expr, base, idx);
|
|
1388
1467
|
return `${this._genMapWriteBase(base)}[${idx}]`;
|
|
1389
1468
|
}
|
|
1469
|
+
if (this._is64BitInt(expr.index?._type)) idx = `Number(${idx})`;
|
|
1390
1470
|
const checked = this._genStrictIndex(expr, base, idx);
|
|
1391
1471
|
if (checked) return checked;
|
|
1392
1472
|
if (this._isStringExprType(exprType) && !expr._lvalue)
|
|
@@ -1428,8 +1508,8 @@ export const expressionGenMethods = {
|
|
|
1428
1508
|
_genMapIndexExpr(expr, base, idx) {
|
|
1429
1509
|
const zero = this.zeroValueForType(expr._mapValueType);
|
|
1430
1510
|
if (this._hasCallExpr(expr.expr) || this._hasCallExpr(expr.index))
|
|
1431
|
-
return `((__m, __k) => __m[__k] ?? ${zero})(${base}, ${idx})`;
|
|
1432
|
-
return `(${base}[${idx}] ?? ${zero})`;
|
|
1511
|
+
return `((__m, __k) => __m?.[__k] ?? ${zero})(${base}, ${idx})`;
|
|
1512
|
+
return `(${base}?.[${idx}] ?? ${zero})`;
|
|
1433
1513
|
},
|
|
1434
1514
|
|
|
1435
1515
|
_isStringExprType(exprType) {
|
|
@@ -1442,8 +1522,16 @@ export const expressionGenMethods = {
|
|
|
1442
1522
|
|
|
1443
1523
|
_genSliceExpr(expr) {
|
|
1444
1524
|
const base = this.genExpr(expr.expr);
|
|
1445
|
-
const lo = expr.low
|
|
1446
|
-
|
|
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";
|
|
1447
1535
|
if (this._isStringExprType(expr.expr._type))
|
|
1448
1536
|
return expr.high ? `${base}.slice(${lo}, ${hi})` : `${base}.slice(${lo})`;
|
|
1449
1537
|
// `b[:]`, `b[0:]` and `a[:]` have the operand's length and capacity:
|
|
@@ -1456,7 +1544,9 @@ export const expressionGenMethods = {
|
|
|
1456
1544
|
// the result stays within `len(x)`; typed arrays always share.
|
|
1457
1545
|
if (expr._selfReslice && !isTypedArraySlice(expr.expr._type))
|
|
1458
1546
|
return `__reslice(${base}, ${lo}, ${hi})`;
|
|
1459
|
-
const max = expr.max
|
|
1547
|
+
const max = expr.max
|
|
1548
|
+
? `, ${this._is64BitInt(expr.max._type) ? `Number(${this.genExpr(expr.max)})` : this.genExpr(expr.max)}`
|
|
1549
|
+
: "";
|
|
1460
1550
|
return `__slice(${base}, ${lo}, ${hi}${max})`;
|
|
1461
1551
|
},
|
|
1462
1552
|
|
|
@@ -1471,7 +1561,7 @@ export const expressionGenMethods = {
|
|
|
1471
1561
|
this._withFnCtx(expr.body, () =>
|
|
1472
1562
|
this.indented(() => {
|
|
1473
1563
|
this._emitParamCopies(expr.params);
|
|
1474
|
-
this._genBody(expr.body, expr);
|
|
1564
|
+
this._withNamedReturns(expr, () => this._genBody(expr.body, expr));
|
|
1475
1565
|
}),
|
|
1476
1566
|
);
|
|
1477
1567
|
const body = this.out.join("\n");
|
|
@@ -1493,18 +1583,38 @@ export const expressionGenMethods = {
|
|
|
1493
1583
|
if (!expr._commaOk) {
|
|
1494
1584
|
// plain assertion: panic if check fails (matches Go behavior)
|
|
1495
1585
|
if (check === "true") return src; // can't check at runtime — pass through
|
|
1586
|
+
this.useHelper("ifaceBox");
|
|
1496
1587
|
return wrap(
|
|
1497
|
-
`(${check} ? ${out} : (() => { throw
|
|
1588
|
+
`(${check} ? ${out} : (() => { throw ${this._typeAssertFailJs(expr, val)}; })())`,
|
|
1498
1589
|
);
|
|
1499
1590
|
}
|
|
1500
|
-
// comma-ok: emit [value-or-zero, runtimeTypeCheck]; an asserted
|
|
1501
|
-
// is a copy of the boxed value.
|
|
1591
|
+
// comma-ok: emit [value-or-zero, runtimeTypeCheck]; an asserted struct
|
|
1592
|
+
// or array is a copy of the boxed value.
|
|
1502
1593
|
const zero = this.zeroValueForTypeNode(expr.type);
|
|
1503
|
-
const
|
|
1504
|
-
const okOut =
|
|
1594
|
+
const valT = this._typeFromNode(expr.type);
|
|
1595
|
+
const okOut = this._aggregateBase(valT) ? this._cloneJs(valT, out) : out;
|
|
1505
1596
|
return wrap(`(${check} ? [${okOut}, true] : [${zero}, false])`);
|
|
1506
1597
|
},
|
|
1507
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
|
+
|
|
1508
1618
|
// `csv.ParseError` names the class of the bundled Go-source package type.
|
|
1509
1619
|
_qualifiedStructName(name) {
|
|
1510
1620
|
if (!name.includes(".") || this.structNames.has(name)) return name;
|
|
@@ -1512,20 +1622,37 @@ export const expressionGenMethods = {
|
|
|
1512
1622
|
return cls && this.structNames.has(cls) ? cls : name;
|
|
1513
1623
|
},
|
|
1514
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;
|
|
1631
|
+
},
|
|
1632
|
+
|
|
1515
1633
|
_typeNameCheckExpr(typeName, val) {
|
|
1516
1634
|
// A type parameter: the instantiated type's test travels with its factory.
|
|
1517
1635
|
const factory = this._typeParamZeroFactory(typeName);
|
|
1518
1636
|
if (factory) return `(${factory}?.is?.(${val}) ?? true)`;
|
|
1519
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))`;
|
|
1520
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")`;
|
|
1521
1651
|
if (name === "string") return `typeof ${val} === "string"`;
|
|
1522
1652
|
if (name === "bool") return `typeof ${val} === "boolean"`;
|
|
1523
1653
|
if (name === "nil") return `${val} === null`;
|
|
1524
|
-
|
|
1525
|
-
if (
|
|
1526
|
-
return `(Array.isArray(${val}) && ${val}.length === 4 && typeof ${val}[0] === "number")`;
|
|
1527
|
-
if (name === "simd.Mask32x4" || name === "Mask32x4")
|
|
1528
|
-
return `(Array.isArray(${val}) && ${val}.length === 4 && typeof ${val}[0] === "boolean")`;
|
|
1654
|
+
const simd = this._simdCheckExpr(name, val);
|
|
1655
|
+
if (simd) return simd;
|
|
1529
1656
|
if (name === "error")
|
|
1530
1657
|
return `(typeof ${val} === "object" && ${val} !== null && typeof ${val}.Error === "function")`;
|
|
1531
1658
|
if (this.structNames.has(name))
|
|
@@ -1534,12 +1661,16 @@ export const expressionGenMethods = {
|
|
|
1534
1661
|
const namedInfo = this._namedBoxInfo(named);
|
|
1535
1662
|
if (namedInfo) return `(${val} instanceof ${namedInfo.cls})`;
|
|
1536
1663
|
// User-declared interfaces: check the dynamic value has every method.
|
|
1537
|
-
if (
|
|
1664
|
+
if (named?.underlying?.kind === "interface")
|
|
1538
1665
|
return this._ifaceMethodCheck(
|
|
1539
1666
|
val,
|
|
1540
1667
|
[...named.underlying.methods.keys()],
|
|
1541
1668
|
this._unwrapIsMulti(named.underlying.methods.get("Unwrap")),
|
|
1542
1669
|
);
|
|
1670
|
+
if (this._isNamedComposite(named)) {
|
|
1671
|
+
this.useHelper("ifaceBox");
|
|
1672
|
+
return `__isty(${val}, ${JSON.stringify(name)}, "named")`;
|
|
1673
|
+
}
|
|
1543
1674
|
return "true"; // unknown type — can't check at runtime
|
|
1544
1675
|
},
|
|
1545
1676
|
|
|
@@ -1573,11 +1704,14 @@ export const expressionGenMethods = {
|
|
|
1573
1704
|
},
|
|
1574
1705
|
|
|
1575
1706
|
_ifaceMethodCheck(val, methodNames, multiUnwrap = null) {
|
|
1576
|
-
const checks = methodNames.map((m) =>
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
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
|
+
});
|
|
1581
1715
|
return `(${[`${val} != null`, ...checks].join(" && ")})`;
|
|
1582
1716
|
},
|
|
1583
1717
|
|
|
@@ -1586,6 +1720,18 @@ export const expressionGenMethods = {
|
|
|
1586
1720
|
// for a type parameter).
|
|
1587
1721
|
_assertedValueJs(typeNode, val) {
|
|
1588
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
|
+
}
|
|
1589
1735
|
const factory =
|
|
1590
1736
|
typeNode?.kind === "TypeName"
|
|
1591
1737
|
? this._typeParamZeroFactory(typeNode.name)
|
|
@@ -1593,6 +1739,19 @@ export const expressionGenMethods = {
|
|
|
1593
1739
|
return factory ? `(${factory}?.u ? ${val}.__gfUnbox() : ${val})` : val;
|
|
1594
1740
|
},
|
|
1595
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
|
+
|
|
1596
1755
|
_isNamedBoxTypeNode(typeNode) {
|
|
1597
1756
|
if (typeNode?.kind === "TypeName")
|
|
1598
1757
|
return Boolean(
|
|
@@ -1630,11 +1789,10 @@ export const expressionGenMethods = {
|
|
|
1630
1789
|
arg,
|
|
1631
1790
|
);
|
|
1632
1791
|
if (structConv) return structConv;
|
|
1633
|
-
const isRaw =
|
|
1634
|
-
targetType?.name === "json.RawMessage" ||
|
|
1635
|
-
targetType?.name === "encoding/json.RawMessage";
|
|
1636
1792
|
const base =
|
|
1637
1793
|
targetType?.kind === "named" ? targetType.underlying : targetType;
|
|
1794
|
+
if (base?.kind === "map" && this._isNilLiteral(expr.args[0]))
|
|
1795
|
+
return this._nilMapZero();
|
|
1638
1796
|
if (
|
|
1639
1797
|
targetType?.kind === "named" &&
|
|
1640
1798
|
base?.kind === "basic" &&
|
|
@@ -1651,19 +1809,29 @@ export const expressionGenMethods = {
|
|
|
1651
1809
|
(elem === "byte" || elem === "uint8") &&
|
|
1652
1810
|
this._isStringExprType(expr.args[0]?._type)
|
|
1653
1811
|
) {
|
|
1654
|
-
|
|
1655
|
-
return
|
|
1656
|
-
}
|
|
1657
|
-
if (isRaw) {
|
|
1658
|
-
return `((u) => { u.__raw = true; return u; })(${arg})`;
|
|
1812
|
+
this.useHelper("u8enc");
|
|
1813
|
+
return `__u8enc(${arg})`;
|
|
1659
1814
|
}
|
|
1660
1815
|
}
|
|
1661
1816
|
return arg;
|
|
1662
1817
|
},
|
|
1663
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
|
+
|
|
1664
1828
|
genCall(expr) {
|
|
1829
|
+
if (expr.func._genEntry) return this.genCall(this._jsonEntryCall(expr));
|
|
1665
1830
|
if (expr._isTypeConversion) return this._genTypeConversionCall(expr);
|
|
1666
1831
|
|
|
1832
|
+
const native = this._genNativeCall(expr);
|
|
1833
|
+
if (native !== null) return native;
|
|
1834
|
+
|
|
1667
1835
|
const funcExpr =
|
|
1668
1836
|
expr.func.kind === "InstantiationExpr" ? expr.func.expr : expr.func;
|
|
1669
1837
|
if (funcExpr.kind === "Ident") {
|
|
@@ -1671,11 +1839,17 @@ export const expressionGenMethods = {
|
|
|
1671
1839
|
if (builtin !== undefined) return builtin;
|
|
1672
1840
|
}
|
|
1673
1841
|
|
|
1842
|
+
const viaBox = this._genTypeParamMethodCall(expr);
|
|
1843
|
+
if (viaBox !== null) return viaBox;
|
|
1844
|
+
|
|
1674
1845
|
if (expr.func.kind === "SelectorExpr") {
|
|
1675
1846
|
const result = this._genSelectorCall(expr);
|
|
1676
1847
|
if (result !== undefined) return result;
|
|
1677
1848
|
}
|
|
1678
1849
|
|
|
1850
|
+
const converted = this._convertedPointerMethodCall(expr);
|
|
1851
|
+
if (converted) return converted;
|
|
1852
|
+
|
|
1679
1853
|
const nilSafe = this._nilSafeMethodClass(expr.func);
|
|
1680
1854
|
if (nilSafe) {
|
|
1681
1855
|
// The method tests its receiver for nil: call it through the
|
|
@@ -1694,6 +1868,50 @@ export const expressionGenMethods = {
|
|
|
1694
1868
|
return `${fn}(${args ? `${zeros}, ${args}` : zeros})`;
|
|
1695
1869
|
},
|
|
1696
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
|
+
|
|
1697
1915
|
// The class of a pointer-receiver struct method that accepts a nil receiver
|
|
1698
1916
|
// when `func` selects it on a pointer of exactly that struct, else null.
|
|
1699
1917
|
_nilSafeMethodClass(func) {
|
|
@@ -1702,7 +1920,15 @@ export const expressionGenMethods = {
|
|
|
1702
1920
|
if (t?.kind !== "pointer" || t.base?.kind !== "named") return null;
|
|
1703
1921
|
const cls = t.base.name;
|
|
1704
1922
|
if (t.base.typeArgs?.length) return null;
|
|
1705
|
-
|
|
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;
|
|
1706
1932
|
},
|
|
1707
1933
|
|
|
1708
1934
|
// The leading zero-value factories of a call to a generic function
|
|
@@ -1741,79 +1967,7 @@ export const expressionGenMethods = {
|
|
|
1741
1967
|
return recvType?.name ?? recvType?.base?.name;
|
|
1742
1968
|
},
|
|
1743
1969
|
|
|
1744
|
-
_genJsonStreamCall(recvName, expr) {
|
|
1745
|
-
const isEnc = this._isJsonEncoderName(recvName);
|
|
1746
|
-
if (isEnc && expr.func.field === "Encode") {
|
|
1747
|
-
this.useHelper("json");
|
|
1748
|
-
const enc = this.genExpr(expr.func.expr);
|
|
1749
|
-
this._markJsonAnyEncoders();
|
|
1750
|
-
const argType = expr.args[0]?._type;
|
|
1751
|
-
const codec = this._getJsonEncoder(argType);
|
|
1752
|
-
const arg = this.genExpr(expr.args[0]);
|
|
1753
|
-
return `__json_encoder_encode(${enc}, ${codec}, ${arg})`;
|
|
1754
|
-
}
|
|
1755
|
-
const isDec =
|
|
1756
|
-
recvName === "json.Decoder" ||
|
|
1757
|
-
recvName === "encoding/json.Decoder" ||
|
|
1758
|
-
recvName?.startsWith("Decoder$$");
|
|
1759
|
-
if (isDec && expr.func.field === "Decode") {
|
|
1760
|
-
this.useHelper("json");
|
|
1761
|
-
const dec = this.genExpr(expr.func.expr);
|
|
1762
|
-
const targetType = expr.args[0]?._type;
|
|
1763
|
-
const codec = this._getJsonDecoder(targetType);
|
|
1764
|
-
const arg = this.genExpr(expr.args[0]);
|
|
1765
|
-
return `__json_decoder_decode(${dec}, ${codec}, ${arg}, ${this._jsonTargetArgs(targetType)})`;
|
|
1766
|
-
}
|
|
1767
|
-
return undefined;
|
|
1768
|
-
},
|
|
1769
|
-
|
|
1770
1970
|
_genReceiverTypeCall(recvName, expr) {
|
|
1771
|
-
const jsonStream = this._genJsonStreamCall(recvName, expr);
|
|
1772
|
-
if (jsonStream !== undefined) return jsonStream;
|
|
1773
|
-
if (recvName === "strings.Builder" || recvName === "bytes.Buffer")
|
|
1774
|
-
return this._genBuilderCall(recvName, expr.func.field, expr);
|
|
1775
|
-
if (recvName === "regexp.Regexp")
|
|
1776
|
-
return this._genRegexpMethodCall(expr.func.field, expr);
|
|
1777
|
-
if (recvName === "time.Time")
|
|
1778
|
-
return this._genTimeMethodCall(expr.func.field, expr);
|
|
1779
|
-
if (recvName === "time.Duration")
|
|
1780
|
-
return this._genDurationMethodCall(expr.func.field, expr);
|
|
1781
|
-
if (recvName === "time.Month") {
|
|
1782
|
-
this.useHelper("duration");
|
|
1783
|
-
return `__monthString(${this.genExpr(expr.func.expr)})`;
|
|
1784
|
-
}
|
|
1785
|
-
if (recvName === "time.Weekday") {
|
|
1786
|
-
this.useHelper("duration");
|
|
1787
|
-
return `__weekdayString(${this.genExpr(expr.func.expr)})`;
|
|
1788
|
-
}
|
|
1789
|
-
if (recvName === "time.Location") {
|
|
1790
|
-
const recv = this.genExpr(expr.func.expr);
|
|
1791
|
-
return `(${recv} ? ${recv}.String() : "UTC")`;
|
|
1792
|
-
}
|
|
1793
|
-
if (recvName === "time.Timer") {
|
|
1794
|
-
const recv = this.genExpr(expr.func.expr);
|
|
1795
|
-
if (expr.func.field === "Stop")
|
|
1796
|
-
return `(${recv} ? ${recv}.Stop() : false)`;
|
|
1797
|
-
if (expr.func.field === "Reset")
|
|
1798
|
-
return `(${recv} ? ${recv}.Reset(${this.genExpr(expr.args[0])}) : false)`;
|
|
1799
|
-
}
|
|
1800
|
-
if (
|
|
1801
|
-
recvName === "testing.T" ||
|
|
1802
|
-
recvName === "testing.B" ||
|
|
1803
|
-
recvName === "testing.M" ||
|
|
1804
|
-
recvName === "testing.TB"
|
|
1805
|
-
)
|
|
1806
|
-
return this._genTestingMethodCall(expr.func.field, expr);
|
|
1807
|
-
if (recvName === "strings.Replacer") {
|
|
1808
|
-
const recv = this.genExpr(expr.func.expr);
|
|
1809
|
-
if (expr.func.field === "WriteString") {
|
|
1810
|
-
const w = this.genExpr(expr.args[0]);
|
|
1811
|
-
const s = this.genExpr(expr.args[1]);
|
|
1812
|
-
return `((r,w,s) => { s = r ? r.Replace(s) : s; const x = w?.value ?? w; if (typeof x.WriteString === "function") return x.WriteString(s); if (typeof x._buf === "string") { x._buf += s; return [s.length, null]; } if (Array.isArray(x._buf)) { const bytes = new TextEncoder().encode(s); for (let i = 0; i < bytes.length; i++) x._buf.push(bytes[i]); return [bytes.length, null]; } if (typeof x.Write === "function") return x.Write(new TextEncoder().encode(s)); return [0, "strings.Replacer.WriteString: unsupported writer"]; })(${recv}, ${w}, ${s})`;
|
|
1813
|
-
}
|
|
1814
|
-
const arg = this.genExpr(expr.args[0]);
|
|
1815
|
-
return `(${recv} ? ${recv}.Replace(${arg}) : ${arg})`;
|
|
1816
|
-
}
|
|
1817
1971
|
if (recvName?.startsWith("shared.") && expr.func.field === "Subarray") {
|
|
1818
1972
|
const args = expr.args.map((a) => this.genExpr(a)).join(", ");
|
|
1819
1973
|
return `${this.genExpr(expr.func.expr)}.subarray(${args})`;
|
|
@@ -1842,28 +1996,6 @@ export const expressionGenMethods = {
|
|
|
1842
1996
|
}
|
|
1843
1997
|
if (expr.func.field === "Wait") return "(void 0)";
|
|
1844
1998
|
}
|
|
1845
|
-
if (
|
|
1846
|
-
recvName === "binary.LittleEndian" ||
|
|
1847
|
-
recvName === "binary.BigEndian" ||
|
|
1848
|
-
recvName === "encoding/binary.LittleEndian" ||
|
|
1849
|
-
recvName === "encoding/binary.BigEndian" ||
|
|
1850
|
-
recvName === "binary.ByteOrder" ||
|
|
1851
|
-
recvName === "encoding/binary.ByteOrder"
|
|
1852
|
-
) {
|
|
1853
|
-
this.useHelper("binary");
|
|
1854
|
-
const recv = this.genExpr(expr.func.expr);
|
|
1855
|
-
const args = expr.args.map((a) => this.genExpr(a)).join(", ");
|
|
1856
|
-
return `${recv}.${expr.func.field}(${args})`;
|
|
1857
|
-
}
|
|
1858
|
-
if (
|
|
1859
|
-
recvName === "base64.Encoding" ||
|
|
1860
|
-
recvName === "encoding/base64.Encoding"
|
|
1861
|
-
) {
|
|
1862
|
-
this.useHelper("base64");
|
|
1863
|
-
const recv = this.genExpr(expr.func.expr);
|
|
1864
|
-
const args = expr.args.map((a) => this.genExpr(a)).join(", ");
|
|
1865
|
-
return `${recv}.${expr.func.field}(${args})`;
|
|
1866
|
-
}
|
|
1867
1999
|
return undefined;
|
|
1868
2000
|
},
|
|
1869
2001
|
|
|
@@ -1889,12 +2021,44 @@ export const expressionGenMethods = {
|
|
|
1889
2021
|
const ns = expr.func.expr.name;
|
|
1890
2022
|
const fn = expr.func.field;
|
|
1891
2023
|
const result = this._genStdlibCall(ns, fn, expr);
|
|
1892
|
-
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
|
+
}
|
|
1893
2041
|
}
|
|
1894
2042
|
const recvType = expr.func.expr._type;
|
|
1895
2043
|
const recvName = this._resolveRecvName(recvType);
|
|
1896
2044
|
const typed = this._genReceiverTypeCall(recvName, expr);
|
|
1897
|
-
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
|
+
}
|
|
1898
2062
|
const namedCall = this._genNamedMethodCall(expr);
|
|
1899
2063
|
if (namedCall !== undefined) return namedCall;
|
|
1900
2064
|
if (this.strict && this._isInterfaceType(recvType)) {
|
|
@@ -1910,6 +2074,16 @@ export const expressionGenMethods = {
|
|
|
1910
2074
|
return undefined;
|
|
1911
2075
|
},
|
|
1912
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
|
+
|
|
1913
2087
|
_isInterfaceType(t) {
|
|
1914
2088
|
if (!t) return false;
|
|
1915
2089
|
if (t.kind === "interface") return true;
|
|
@@ -1917,13 +2091,13 @@ export const expressionGenMethods = {
|
|
|
1917
2091
|
},
|
|
1918
2092
|
|
|
1919
2093
|
_genBuiltinLen(expr) {
|
|
1920
|
-
if (expr._constLen != null) return
|
|
2094
|
+
if (expr._constLen != null) return this._genConstLen(expr);
|
|
1921
2095
|
const arg = expr.args[0];
|
|
1922
2096
|
const t = arg?._type;
|
|
1923
2097
|
const wrapField = this._namedWrapperField(t, arg);
|
|
1924
2098
|
if (wrapField) return `${this.genExpr(arg)}.${wrapField}.length`;
|
|
1925
2099
|
const js = this.genExpr(arg);
|
|
1926
|
-
if (t?.kind === "map") return `Object.keys(${js}).length`;
|
|
2100
|
+
if (t?.kind === "map") return `Object.keys(${js} ?? {}).length`;
|
|
1927
2101
|
this.useHelper("len");
|
|
1928
2102
|
return `__len(${js})`;
|
|
1929
2103
|
},
|
|
@@ -1954,9 +2128,9 @@ export const expressionGenMethods = {
|
|
|
1954
2128
|
// contributes its UTF-8 bytes.
|
|
1955
2129
|
_genSliceSource(node) {
|
|
1956
2130
|
const js = this.genExpr(node);
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
2131
|
+
if (!this._isStringExprType(node._type)) return js;
|
|
2132
|
+
this.useHelper("u8enc");
|
|
2133
|
+
return `__u8enc(${js})`;
|
|
1960
2134
|
},
|
|
1961
2135
|
|
|
1962
2136
|
genAppend(expr) {
|
|
@@ -1981,12 +2155,23 @@ export const expressionGenMethods = {
|
|
|
1981
2155
|
const st = expr._type ?? expr.args[0]._type;
|
|
1982
2156
|
const elemT = (st?.kind === "named" ? st.underlying : st)?.elem;
|
|
1983
2157
|
const spreadOf = (a) =>
|
|
1984
|
-
this._aggregateBase(elemT)
|
|
2158
|
+
this._aggregateBase(elemT)
|
|
1985
2159
|
? `...(${this.genExpr(a)} ?? []).map((__e) => ${this._cloneJs(elemT, "__e")})`
|
|
1986
2160
|
: `...(${this._genSliceSource(a)} ?? [])`;
|
|
1987
|
-
const
|
|
1988
|
-
|
|
1989
|
-
|
|
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
|
+
});
|
|
1990
2175
|
if (elems.length === 0) return slice;
|
|
1991
2176
|
this.useHelper("append");
|
|
1992
2177
|
const sliceType = expr._type ?? expr.args[0]._type;
|
|
@@ -1995,11 +2180,35 @@ export const expressionGenMethods = {
|
|
|
1995
2180
|
(sliceType.kind === "named" ? sliceType.underlying : sliceType).elem,
|
|
1996
2181
|
)
|
|
1997
2182
|
: null;
|
|
1998
|
-
if (typedCtor)
|
|
1999
|
-
|
|
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
|
+
}
|
|
2000
2201
|
return `__append(${slice}, ${elems.join(", ")})`;
|
|
2001
2202
|
},
|
|
2002
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
|
+
|
|
2003
2212
|
genMake(expr) {
|
|
2004
2213
|
// make([]T, n) or make([]T, n, cap) → new Array(n).fill(zero)
|
|
2005
2214
|
// make(map[K]V) → {}
|
|
@@ -2007,12 +2216,17 @@ export const expressionGenMethods = {
|
|
|
2007
2216
|
const typeNode = typeArg.kind === "TypeExpr" ? typeArg.type : typeArg;
|
|
2008
2217
|
// A defined type (`type S []int`) makes like its underlying type.
|
|
2009
2218
|
const madeType =
|
|
2010
|
-
typeArg.
|
|
2219
|
+
typeArg._coreType ??
|
|
2220
|
+
(typeArg._type?.kind === "named"
|
|
2011
2221
|
? typeArg._type.underlying
|
|
2012
|
-
: typeArg._type;
|
|
2222
|
+
: typeArg._type);
|
|
2013
2223
|
const resolvedKind = madeType?.kind;
|
|
2014
2224
|
if (typeNode.kind === "SliceType" || resolvedKind === "slice") {
|
|
2015
|
-
|
|
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");
|
|
2016
2230
|
const elemNode = typeNode.kind === "SliceType" ? typeNode.elem : null;
|
|
2017
2231
|
const elemResolved = madeType?.kind === "slice" ? madeType.elem : null;
|
|
2018
2232
|
const typedCtor =
|
|
@@ -2025,8 +2239,13 @@ export const expressionGenMethods = {
|
|
|
2025
2239
|
: null);
|
|
2026
2240
|
// make([]T, n, cap) allocates cap elements and returns a view of
|
|
2027
2241
|
// the first n (`__mkslice`), so append within cap shares them.
|
|
2028
|
-
|
|
2029
|
-
if (capArg
|
|
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
|
+
}
|
|
2030
2249
|
const withCap = (alloc) =>
|
|
2031
2250
|
capArg ? `__mkslice(${n}, ${alloc(capArg)})` : alloc(n);
|
|
2032
2251
|
if (typedCtor) {
|
|
@@ -2045,24 +2264,18 @@ export const expressionGenMethods = {
|
|
|
2045
2264
|
}
|
|
2046
2265
|
return withCap((k) => `new Array(${k}).fill(${zero})`);
|
|
2047
2266
|
}
|
|
2048
|
-
// 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)";
|
|
2049
2274
|
return "{}";
|
|
2050
2275
|
},
|
|
2051
2276
|
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
_genStructFields(elems) {
|
|
2055
|
-
return elems
|
|
2056
|
-
.map((e) => {
|
|
2057
|
-
if (e._positionalField) {
|
|
2058
|
-
return `${e._positionalField}: ${this.genExpr(e)}`;
|
|
2059
|
-
}
|
|
2060
|
-
if (e.kind !== "KeyValueExpr") return null;
|
|
2061
|
-
if (e._isEmbedInit) return `...${this.genExpr(e.value)}`;
|
|
2062
|
-
return `${e.key.name ?? this.genExpr(e.key)}: ${this.genExpr(e.value)}`;
|
|
2063
|
-
})
|
|
2064
|
-
.filter((s) => s !== null)
|
|
2065
|
-
.join(", ");
|
|
2277
|
+
_inJsonCodecFile() {
|
|
2278
|
+
return this._jsonCodecFileIdx === (this._currentSrcFileIdx ?? 0);
|
|
2066
2279
|
},
|
|
2067
2280
|
|
|
2068
2281
|
_isSliceOrArrayType(t) {
|
|
@@ -2322,10 +2535,6 @@ export const expressionGenMethods = {
|
|
|
2322
2535
|
const t = expr.typeExpr;
|
|
2323
2536
|
if (t === null) return this._genImplicitCompositeLit(expr);
|
|
2324
2537
|
const typeName = this.getTypeName(t);
|
|
2325
|
-
if (typeName === "bytes.Buffer" || typeName === "Buffer")
|
|
2326
|
-
return "{ _buf: [] }";
|
|
2327
|
-
if (typeName === "strings.Builder" || typeName === "Builder")
|
|
2328
|
-
return '{ _buf: "" }';
|
|
2329
2538
|
if (typeName === "simd.Mask32x4" || typeName === "Mask32x4")
|
|
2330
2539
|
return "Object.freeze([false, false, false, false])";
|
|
2331
2540
|
if (typeName === "simd.F32x4" || typeName === "F32x4") {
|
|
@@ -2445,26 +2654,44 @@ export const expressionGenMethods = {
|
|
|
2445
2654
|
// ── Helpers ───────────────────────────────────────────────────
|
|
2446
2655
|
|
|
2447
2656
|
_genComplexOperand(expr) {
|
|
2448
|
-
if (isComplex(expr._type)) return this.genExpr(expr);
|
|
2657
|
+
if (isComplex(expr._type) || expr._asComplex) return this.genExpr(expr);
|
|
2449
2658
|
return `{ re: ${this.genExpr(expr)}, im: 0 }`;
|
|
2450
2659
|
},
|
|
2451
2660
|
|
|
2452
2661
|
getTypeName(typeNode) {
|
|
2453
2662
|
if (!typeNode) return null;
|
|
2454
2663
|
if (typeNode._stdName) return typeNode._stdName;
|
|
2455
|
-
if (typeNode.kind === "TypeName")
|
|
2664
|
+
if (typeNode.kind === "TypeName")
|
|
2665
|
+
return this._aliasTarget(this._dequalify(typeNode.name));
|
|
2456
2666
|
if (typeNode.kind === "GenericTypeName") return typeNode.name;
|
|
2457
|
-
if (typeNode.kind === "Ident") return typeNode.name;
|
|
2667
|
+
if (typeNode.kind === "Ident") return this._aliasTarget(typeNode.name);
|
|
2458
2668
|
if (typeNode.kind === "InstantiationExpr")
|
|
2459
2669
|
return this.getTypeName(typeNode.expr);
|
|
2460
2670
|
if (typeNode.kind === "SelectorExpr") {
|
|
2461
2671
|
const base = this.getTypeName(typeNode.expr);
|
|
2462
|
-
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
|
+
}
|
|
2463
2679
|
return `${base}.${typeNode.field}`;
|
|
2464
2680
|
}
|
|
2465
2681
|
return null;
|
|
2466
2682
|
},
|
|
2467
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
|
+
|
|
2468
2695
|
// Bundled GoFront packages are inlined, so `pkg.T` names the class `T`.
|
|
2469
2696
|
_dequalify(name) {
|
|
2470
2697
|
const dot = name.indexOf(".");
|
|
@@ -2514,6 +2741,16 @@ export const expressionGenMethods = {
|
|
|
2514
2741
|
},
|
|
2515
2742
|
|
|
2516
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
|
+
|
|
2517
2754
|
_withTypeParamZeros(names, fn) {
|
|
2518
2755
|
const prev = this._typeParamZeros;
|
|
2519
2756
|
this._typeParamZeros = names
|
|
@@ -2534,6 +2771,7 @@ export const expressionGenMethods = {
|
|
|
2534
2771
|
if (bound) return bound;
|
|
2535
2772
|
}
|
|
2536
2773
|
this.useHelper("generics");
|
|
2774
|
+
if (this._is64BitUnsigned(t)) return "__zu64";
|
|
2537
2775
|
const zero = this.zeroValueForType(t);
|
|
2538
2776
|
return Object.hasOwn(SHARED_ZERO_FACTORIES, zero)
|
|
2539
2777
|
? SHARED_ZERO_FACTORIES[zero]
|
|
@@ -2722,6 +2960,8 @@ export const expressionGenMethods = {
|
|
|
2722
2960
|
return this._typeNameCheckExpr(typeNode.name, val);
|
|
2723
2961
|
const generic = this._genericInstanceCheck(typeNode, val);
|
|
2724
2962
|
if (generic) return generic;
|
|
2963
|
+
const dynamic = this._dynTypeCheck(typeNode, val);
|
|
2964
|
+
if (dynamic) return dynamic;
|
|
2725
2965
|
const extended = this._namedOrIfaceCheckExpr(typeNode, val);
|
|
2726
2966
|
if (extended) return extended;
|
|
2727
2967
|
if (
|
|
@@ -2821,6 +3061,9 @@ export const expressionGenMethods = {
|
|
|
2821
3061
|
const nilMap = this._genNilMapCompare(expr);
|
|
2822
3062
|
if (nilMap) return nilMap;
|
|
2823
3063
|
|
|
3064
|
+
const anyNil = this._genAnyNilCompare(expr);
|
|
3065
|
+
if (anyNil) return anyNil;
|
|
3066
|
+
|
|
2824
3067
|
const tpEq = this._genTypeParamEquality(expr);
|
|
2825
3068
|
if (tpEq) return tpEq;
|
|
2826
3069
|
|
|
@@ -2833,12 +3076,85 @@ export const expressionGenMethods = {
|
|
|
2833
3076
|
const ptrCmp = this._genPointerEquality(expr);
|
|
2834
3077
|
if (ptrCmp !== null) return ptrCmp;
|
|
2835
3078
|
|
|
2836
|
-
if (this.
|
|
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
|
+
) {
|
|
2837
3085
|
const l = this.genExpr(expr.left);
|
|
2838
3086
|
const r = this.genExpr(expr.right);
|
|
2839
|
-
return this._genStrictBinary(expr.op, l, r, expr._type);
|
|
3087
|
+
return this._genStrictBinary(expr.op, l, r, expr._type, expr.right._type);
|
|
2840
3088
|
}
|
|
2841
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
|
+
}
|
|
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
|
+
},
|
|
3156
|
+
|
|
3157
|
+
_genStandardBinaryExpr(expr) {
|
|
2842
3158
|
const jsOp = this._emittedBinaryOp(expr) ?? "/";
|
|
2843
3159
|
const operand = (child, isRight) =>
|
|
2844
3160
|
wrapForJsOp(
|
|
@@ -2847,17 +3163,29 @@ export const expressionGenMethods = {
|
|
|
2847
3163
|
jsOp,
|
|
2848
3164
|
isRight,
|
|
2849
3165
|
);
|
|
2850
|
-
|
|
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);
|
|
2851
3175
|
|
|
2852
3176
|
if (expr.op === "&^") {
|
|
2853
3177
|
const rCode = this.genExpr(expr.right);
|
|
2854
|
-
const
|
|
3178
|
+
const rP =
|
|
2855
3179
|
this._emittedBinaryOp(expr.right) !== null ? `(${rCode})` : rCode;
|
|
2856
|
-
return `${l} & ~${
|
|
3180
|
+
return this._wrapBinaryResult(expr, `${l} & ~${rP}`, isLeft64, isRight64);
|
|
2857
3181
|
}
|
|
2858
|
-
const r = operand(expr.right, true);
|
|
2859
3182
|
if (this._isIntDivision(expr)) return `Math.trunc(${l} / ${r})`;
|
|
2860
|
-
return
|
|
3183
|
+
return this._wrapBinaryResult(
|
|
3184
|
+
expr,
|
|
3185
|
+
`${l} ${jsOp} ${r}`,
|
|
3186
|
+
isLeft64,
|
|
3187
|
+
isRight64,
|
|
3188
|
+
);
|
|
2861
3189
|
},
|
|
2862
3190
|
|
|
2863
3191
|
// `m == nil` / `m != nil` on a map: true for a nil map (null, the strict
|
|
@@ -2872,7 +3200,7 @@ export const expressionGenMethods = {
|
|
|
2872
3200
|
if (!other) return null;
|
|
2873
3201
|
const t =
|
|
2874
3202
|
other._type?.kind === "named" ? other._type.underlying : other._type;
|
|
2875
|
-
return t?.kind === "map" ? other : null;
|
|
3203
|
+
return t?.kind === "map" || other._coreType?.kind === "map" ? other : null;
|
|
2876
3204
|
},
|
|
2877
3205
|
|
|
2878
3206
|
_genNilMapCompare(expr) {
|
|
@@ -2937,6 +3265,25 @@ export const expressionGenMethods = {
|
|
|
2937
3265
|
);
|
|
2938
3266
|
},
|
|
2939
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
|
+
|
|
2940
3287
|
_isNilLiteral(expr) {
|
|
2941
3288
|
return (
|
|
2942
3289
|
(expr.kind === "Ident" && expr.name === "nil") ||
|
|
@@ -2955,6 +3302,7 @@ export const expressionGenMethods = {
|
|
|
2955
3302
|
return null;
|
|
2956
3303
|
if (this._isStructEquality(expr) || this._isIntDivision(expr)) return null;
|
|
2957
3304
|
if (this._nilMapOperand(expr)) return null;
|
|
3305
|
+
if (this._anyNilOperand(expr)) return null;
|
|
2958
3306
|
if (this._isIfaceEquality(expr)) return null;
|
|
2959
3307
|
if (expr.op === "&^") return "&";
|
|
2960
3308
|
if (expr.op === "==" || expr.op === "!=") {
|
|
@@ -2985,6 +3333,10 @@ export const expressionGenMethods = {
|
|
|
2985
3333
|
return this._genArrayTypeConversion(expr, inner, t);
|
|
2986
3334
|
if (t?.kind === "SliceType")
|
|
2987
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();
|
|
2988
3340
|
const target = t?.name;
|
|
2989
3341
|
const structConv = this._genStructClassConversion(
|
|
2990
3342
|
target,
|
|
@@ -2995,6 +3347,7 @@ export const expressionGenMethods = {
|
|
|
2995
3347
|
if (target === "error") {
|
|
2996
3348
|
// A boxed named value that implements error is already an error.
|
|
2997
3349
|
if (this._isNamedBoxKind(expr.expr)) return inner;
|
|
3350
|
+
if (!this._isStringExprType(expr.expr._type)) return inner;
|
|
2998
3351
|
this.useHelper("error");
|
|
2999
3352
|
return `__error(${inner})`;
|
|
3000
3353
|
}
|
|
@@ -3025,11 +3378,25 @@ export const expressionGenMethods = {
|
|
|
3025
3378
|
// `[]T(nil)` is the nil slice, not a copy of anything.
|
|
3026
3379
|
if (this._isNilLiteral(expr.expr)) return "null";
|
|
3027
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
|
+
}
|
|
3028
3391
|
if (elem === "byte" || elem === "uint8") {
|
|
3029
|
-
if (this._isStringExprType(expr.expr._type))
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
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
|
+
}
|
|
3033
3400
|
return `new Uint8Array(${inner})`;
|
|
3034
3401
|
}
|
|
3035
3402
|
const typedCtor = typedArrayConstructorForElem(
|
|
@@ -3048,41 +3415,71 @@ export const expressionGenMethods = {
|
|
|
3048
3415
|
|
|
3049
3416
|
_genStringConversion(srcType, inner) {
|
|
3050
3417
|
const base = srcType?.kind === "named" ? srcType.underlying : srcType;
|
|
3051
|
-
if (base && this.isIntType(base))
|
|
3418
|
+
if (base && this.isIntType(base)) {
|
|
3419
|
+
this.useHelper("runestr");
|
|
3420
|
+
return `__rstr(${inner})`;
|
|
3421
|
+
}
|
|
3052
3422
|
if (base?.kind === "slice") {
|
|
3053
3423
|
const elem = base.elem?.name;
|
|
3054
|
-
if (elem === "byte" || elem === "uint8")
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
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
|
+
}
|
|
3058
3432
|
}
|
|
3059
3433
|
return `String(${inner})`;
|
|
3060
3434
|
},
|
|
3061
3435
|
|
|
3062
3436
|
_genPrimitiveConversion(expr, inner, target) {
|
|
3437
|
+
const srcType = expr.expr?._type;
|
|
3438
|
+
const isSrc64 = this._is64BitInt(srcType);
|
|
3063
3439
|
switch (target) {
|
|
3064
3440
|
case "string":
|
|
3065
|
-
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
|
+
}
|
|
3066
3453
|
case "int":
|
|
3067
|
-
case "int64":
|
|
3068
3454
|
case "uint":
|
|
3069
|
-
case "uint64":
|
|
3070
3455
|
case "uintptr":
|
|
3071
|
-
return `Math.trunc(Number(${inner}))`;
|
|
3456
|
+
return isSrc64 ? `Number(${inner})` : `Math.trunc(Number(${inner}))`;
|
|
3072
3457
|
case "int8":
|
|
3073
|
-
return
|
|
3458
|
+
return isSrc64
|
|
3459
|
+
? `Number(BigInt.asIntN(8, ${inner}))`
|
|
3460
|
+
: `(Number(${inner}) << 24 >> 24)`;
|
|
3074
3461
|
case "int16":
|
|
3075
|
-
return
|
|
3462
|
+
return isSrc64
|
|
3463
|
+
? `Number(BigInt.asIntN(16, ${inner}))`
|
|
3464
|
+
: `(Number(${inner}) << 16 >> 16)`;
|
|
3076
3465
|
case "int32":
|
|
3077
3466
|
case "rune":
|
|
3078
|
-
return
|
|
3467
|
+
return isSrc64
|
|
3468
|
+
? `Number(BigInt.asIntN(32, ${inner}))`
|
|
3469
|
+
: `(Number(${inner}) | 0)`;
|
|
3079
3470
|
case "uint8":
|
|
3080
3471
|
case "byte":
|
|
3081
|
-
return
|
|
3472
|
+
return isSrc64
|
|
3473
|
+
? `Number(BigInt.asUintN(8, ${inner}))`
|
|
3474
|
+
: `(Number(${inner}) & 0xFF)`;
|
|
3082
3475
|
case "uint16":
|
|
3083
|
-
return
|
|
3476
|
+
return isSrc64
|
|
3477
|
+
? `Number(BigInt.asUintN(16, ${inner}))`
|
|
3478
|
+
: `(Number(${inner}) & 0xFFFF)`;
|
|
3084
3479
|
case "uint32":
|
|
3085
|
-
return
|
|
3480
|
+
return isSrc64
|
|
3481
|
+
? `Number(BigInt.asUintN(32, ${inner}))`
|
|
3482
|
+
: `(Number(${inner}) >>> 0)`;
|
|
3086
3483
|
case "float32":
|
|
3087
3484
|
return `Math.fround(Number(${inner}))`;
|
|
3088
3485
|
case "float64":
|
|
@@ -3106,9 +3503,62 @@ export const expressionGenMethods = {
|
|
|
3106
3503
|
return STRICT_NUMERIC_TYPES.has(typeName);
|
|
3107
3504
|
},
|
|
3108
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
|
+
|
|
3109
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 === "&^";
|
|
3110
3559
|
if (!STRICT_OPS.has(op)) return false;
|
|
3111
3560
|
if (this._isStrictAtomicOp(op, this._typeName(type))) return true;
|
|
3561
|
+
if (op === "-" && this._isUintWord(type)) return true;
|
|
3112
3562
|
if (op === "<<" || op === ">>")
|
|
3113
3563
|
return WIDE_INT_NAMES.has(this._typeName(type));
|
|
3114
3564
|
return this.isIntType(type) && (op === "/" || op === "%");
|
|
@@ -3116,7 +3566,14 @@ export const expressionGenMethods = {
|
|
|
3116
3566
|
|
|
3117
3567
|
// Emits `lhs = strict(lhs op rhs)` for any lvalue shape; returns false when not applicable.
|
|
3118
3568
|
// Indexed / nested targets are split into object + key temps so sub-expressions run once.
|
|
3119
|
-
_genStrictCompound(
|
|
3569
|
+
_genStrictCompound(
|
|
3570
|
+
lhsNode,
|
|
3571
|
+
op,
|
|
3572
|
+
rhsJs,
|
|
3573
|
+
srcLine = null,
|
|
3574
|
+
lhsJs = null,
|
|
3575
|
+
countType = null,
|
|
3576
|
+
) {
|
|
3120
3577
|
const type = lhsNode._type;
|
|
3121
3578
|
if (!this._needsStrictOp(op, type)) return false;
|
|
3122
3579
|
if (lhsNode.kind === "IndexExpr" && lhsNode.expr?._type?.kind === "map")
|
|
@@ -3124,7 +3581,7 @@ export const expressionGenMethods = {
|
|
|
3124
3581
|
const l = lhsJs ?? this._genAssignLhsExpr(lhsNode);
|
|
3125
3582
|
if (/^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*)*$/.test(l)) {
|
|
3126
3583
|
this.line(
|
|
3127
|
-
`${l} = ${this._genStrictBinary(op, l, rhsJs, type)};`,
|
|
3584
|
+
`${l} = ${this._genStrictBinary(op, l, rhsJs, type, countType)};`,
|
|
3128
3585
|
srcLine,
|
|
3129
3586
|
);
|
|
3130
3587
|
return true;
|
|
@@ -3137,21 +3594,40 @@ export const expressionGenMethods = {
|
|
|
3137
3594
|
const k = `__k${this._tmpCounter}`;
|
|
3138
3595
|
const target = `${o}[${k}]`;
|
|
3139
3596
|
this.line(
|
|
3140
|
-
`{ 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)}; }`,
|
|
3141
3598
|
srcLine,
|
|
3142
3599
|
);
|
|
3143
3600
|
} else {
|
|
3144
3601
|
const target = `${o}.${split.prop}`;
|
|
3145
3602
|
this.line(
|
|
3146
|
-
`{ const ${o} = ${split.obj}; ${target} = ${this._genStrictBinary(op, target, rhsJs, type)}; }`,
|
|
3603
|
+
`{ const ${o} = ${split.obj}; ${target} = ${this._genStrictBinary(op, target, rhsJs, type, countType)}; }`,
|
|
3147
3604
|
srcLine,
|
|
3148
3605
|
);
|
|
3149
3606
|
}
|
|
3150
3607
|
return true;
|
|
3151
3608
|
},
|
|
3152
3609
|
|
|
3153
|
-
|
|
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
|
+
}
|
|
3154
3628
|
const tName = this._typeName(type);
|
|
3629
|
+
const isShift = op === "<<" || op === ">>";
|
|
3630
|
+
const rc = isShift ? this._guardShiftCount(r, countType) : r;
|
|
3155
3631
|
if (tName === "float32") {
|
|
3156
3632
|
return `Math.fround(${l} ${op} ${r})`;
|
|
3157
3633
|
}
|
|
@@ -3171,7 +3647,7 @@ export const expressionGenMethods = {
|
|
|
3171
3647
|
if (op === "<<" || op === ">>") {
|
|
3172
3648
|
const ovf = signed && op === ">>" ? `((${l}) < 0 ? -1 : 0)` : "0";
|
|
3173
3649
|
const jsOp = !signed && op === ">>" ? ">>>" : op;
|
|
3174
|
-
return `(((${
|
|
3650
|
+
return `(((${rc}) >= 32 ? ${ovf} : ((${l}) ${jsOp} (${r}))) ${cast})`;
|
|
3175
3651
|
}
|
|
3176
3652
|
if (op === "&^") return `(((${l}) & ~(${r})) ${cast})`;
|
|
3177
3653
|
return `((${l} ${op} ${r}) ${cast})`;
|
|
@@ -3184,7 +3660,7 @@ export const expressionGenMethods = {
|
|
|
3184
3660
|
if (op === "%")
|
|
3185
3661
|
return `((${r}) === 0 ? ${divZero} : (((((${l}) % (${r})) << ${sh}) >> ${sh})))`;
|
|
3186
3662
|
if (op === "<<" || op === ">>") {
|
|
3187
|
-
return `((${
|
|
3663
|
+
return `((${rc}) >= ${bits} ? (${op === ">>" ? `((${l}) < 0 ? -1 : 0)` : "0"}) : (((${l}) ${op} (${r})) << ${sh} >> ${sh}))`;
|
|
3188
3664
|
}
|
|
3189
3665
|
if (op === "&^") return `((((${l}) & ~(${r})) << ${sh}) >> ${sh})`;
|
|
3190
3666
|
return `((((${l} ${op} ${r})) << ${sh}) >> ${sh})`;
|
|
@@ -3198,11 +3674,13 @@ export const expressionGenMethods = {
|
|
|
3198
3674
|
return `((${r}) === 0 ? ${divZero} : (((${l}) % (${r})) & ${mask}))`;
|
|
3199
3675
|
if (op === "<<" || op === ">>") {
|
|
3200
3676
|
const jsOp = op === ">>" ? ">>>" : "<<";
|
|
3201
|
-
return `((${
|
|
3677
|
+
return `((${rc}) >= ${bits} ? 0 : (((${l}) ${jsOp} (${r})) & ${mask}))`;
|
|
3202
3678
|
}
|
|
3203
3679
|
if (op === "&^") return `(((${l}) & ~(${r})) & ${mask})`;
|
|
3204
3680
|
return `((${l} ${op} ${r}) & ${mask})`;
|
|
3205
3681
|
}
|
|
3682
|
+
if (op === "-" && this._isUintWord(type))
|
|
3683
|
+
return `((__d) => __d < 0 ? __d + 2 ** 64 : __d)((${l}) - (${r}))`;
|
|
3206
3684
|
if (op === "<<" || op === ">>") {
|
|
3207
3685
|
// 64-bit semantics: operand first, then count (Go order); a negative
|
|
3208
3686
|
// count panics and the result wraps to 64 bits.
|