gofront 1.6.0 → 1.7.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 +35 -39
- package/package.json +37 -4
- package/src/backend/js/expressions.js +559 -144
- package/src/backend/js/index.js +174 -49
- package/src/backend/js/runtime.js +1518 -54
- package/src/backend/js/statements.js +91 -73
- package/src/backend/js/stdlib/base64.js +12 -0
- package/src/backend/js/stdlib/binary.js +102 -0
- package/src/backend/js/stdlib/bits.js +15 -1
- package/src/backend/js/stdlib/builder.js +28 -9
- package/src/backend/js/stdlib/bytes.js +75 -18
- package/src/backend/js/stdlib/errors.js +25 -1
- package/src/backend/js/stdlib/fmt.js +70 -13
- package/src/backend/js/stdlib/hex.js +17 -0
- package/src/backend/js/stdlib/html.js +1 -1
- package/src/backend/js/stdlib/index.js +28 -0
- package/src/backend/js/stdlib/io.js +105 -3
- package/src/backend/js/stdlib/json.js +972 -0
- package/src/backend/js/stdlib/math.js +13 -1
- package/src/backend/js/stdlib/path.js +18 -4
- package/src/backend/js/stdlib/rand.js +10 -5
- package/src/backend/js/stdlib/sha256.js +12 -0
- package/src/backend/js/stdlib/simd.js +24 -0
- package/src/backend/js/stdlib/slices.js +52 -26
- package/src/backend/js/stdlib/strconv.js +21 -5
- package/src/backend/js/stdlib/strings.js +148 -15
- package/src/backend/js/stdlib/sync.js +13 -0
- package/src/backend/js/stdlib/time.js +90 -19
- package/src/backend/js/stdlib/unicode.js +2 -2
- package/src/backend/wasm/boundary.js +1225 -41
- package/src/backend/wasm/const-fold.js +205 -0
- package/src/backend/wasm/emit-builtins.js +220 -76
- package/src/backend/wasm/emit-exprs.js +475 -276
- package/src/backend/wasm/emit-maps.js +144 -8
- package/src/backend/wasm/emit-stdlib.js +12 -3005
- package/src/backend/wasm/emit-stmts.js +264 -270
- package/src/backend/wasm/emit.js +124 -87
- package/src/backend/wasm/encode.js +140 -29
- package/src/backend/wasm/index.js +1001 -32
- package/src/backend/wasm/optimize.js +17 -6
- package/src/backend/wasm/stdlib/bytes.js +5039 -0
- package/src/backend/wasm/stdlib/core.js +8903 -0
- package/src/backend/wasm/stdlib/index.js +43 -0
- package/src/backend/wasm/stdlib/io.js +2161 -0
- package/src/backend/wasm/stdlib/packages.js +2400 -0
- package/src/backend/wasm/stdlib/pure.js +720 -0
- package/src/backend/wasm/stdlib/simd.js +234 -0
- package/src/backend/wasm/stdlib/time.js +1200 -0
- package/src/backend/wasm/types.js +174 -1
- package/src/backend/wasm/wat.js +37 -0
- package/src/cli-core.js +1 -1
- package/src/compiler.js +18 -4
- package/src/dts-parser.js +150 -140
- package/src/index.js +2 -6
- package/src/lexer.js +80 -29
- package/src/lower/boxing.js +1 -7
- package/src/lower/captures.js +2 -6
- package/src/lower/embedding.js +2 -1
- package/src/lower/ownership.js +30 -9
- package/src/minifier.js +20 -68
- package/src/parser/declarations.js +3 -6
- package/src/parser/expressions.js +3 -8
- package/src/parser/index.js +18 -3
- package/src/parser/statements.js +25 -38
- package/src/parser/struct-tag.js +144 -0
- package/src/parser/types.js +64 -8
- package/src/resolver.js +2 -27
- package/src/templ-lexer.js +29 -63
- package/src/templ-parser.js +22 -45
- package/src/typechecker/assignability.js +126 -13
- package/src/typechecker/expressions.js +168 -16
- package/src/typechecker/index.js +35 -8
- package/src/typechecker/resolve.js +187 -8
- package/src/typechecker/statements.js +68 -9
- package/src/typechecker/stdlib/core.js +280 -89
- package/src/typechecker/stdlib/extended.js +490 -1
- package/src/typechecker/stdlib/simd.js +73 -0
- package/src/typechecker/stdlib.js +2 -0
- package/src/typechecker/types.js +362 -9
- package/src/vendor.js +86 -11
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from "../../lower/index.js";
|
|
8
8
|
import {
|
|
9
9
|
ERROR,
|
|
10
|
+
isAny,
|
|
10
11
|
isComplex,
|
|
11
12
|
isTypedArraySlice,
|
|
12
13
|
typedArrayConstructorForElem,
|
|
@@ -40,6 +41,25 @@ const STRICT_OPS = new Set([
|
|
|
40
41
|
">>",
|
|
41
42
|
]);
|
|
42
43
|
|
|
44
|
+
const NAMED_ZERO_TABLE = {
|
|
45
|
+
"time.Time": [null, "{ _d: new Date(0), _isZero: true }"],
|
|
46
|
+
"strings.Builder": [null, '{ _buf: "" }'],
|
|
47
|
+
"bytes.Buffer": [null, "{ _buf: [] }"],
|
|
48
|
+
"strings.Replacer": [null, "{ _p: [], Replace(s) { return s; } }"],
|
|
49
|
+
"sync.Mutex": ["sync", "__sync_mutex()"],
|
|
50
|
+
"sync.RWMutex": ["sync", "__sync_rwmutex()"],
|
|
51
|
+
"sync.Once": ["sync", "__sync_once()"],
|
|
52
|
+
"sync.WaitGroup": ["sync", "__sync_waitgroup()"],
|
|
53
|
+
"binary.LittleEndian": ["binary", "__binary_le"],
|
|
54
|
+
"encoding/binary.LittleEndian": ["binary", "__binary_le"],
|
|
55
|
+
"binary.BigEndian": ["binary", "__binary_be"],
|
|
56
|
+
"encoding/binary.BigEndian": ["binary", "__binary_be"],
|
|
57
|
+
"base64.Encoding": ["base64", "__base64_std"],
|
|
58
|
+
"encoding/base64.Encoding": ["base64", "__base64_std"],
|
|
59
|
+
"simd.F32x4": [null, "Object.freeze([0, 0, 0, 0])"],
|
|
60
|
+
F32x4: [null, "Object.freeze([0, 0, 0, 0])"],
|
|
61
|
+
};
|
|
62
|
+
|
|
43
63
|
// Splits a generated JS lvalue at its outermost trailing access:
|
|
44
64
|
// `a.b[i]` → { obj: "a.b", key: "i" }, `a.b.c` → { obj: "a.b", prop: "c" }.
|
|
45
65
|
function splitLastAccess(js) {
|
|
@@ -78,18 +98,76 @@ const NS_CONSTANTS = {
|
|
|
78
98
|
math: {
|
|
79
99
|
Pi: "Math.PI",
|
|
80
100
|
E: "Math.E",
|
|
101
|
+
Phi: "1.618033988749895",
|
|
102
|
+
Sqrt2: "Math.SQRT2",
|
|
103
|
+
SqrtE: "1.6487212707001282",
|
|
104
|
+
SqrtPi: "1.7724538509055159",
|
|
105
|
+
SqrtPhi: "1.272019649514069",
|
|
106
|
+
Ln2: "Math.LN2",
|
|
107
|
+
Log2E: "Math.LOG2E",
|
|
108
|
+
Ln10: "Math.LN10",
|
|
109
|
+
Log10E: "Math.LOG10E",
|
|
110
|
+
MaxFloat32: "3.4028234663852886e38",
|
|
111
|
+
SmallestNonzeroFloat32: "1.401298464324817e-45",
|
|
81
112
|
MaxFloat64: "Number.MAX_VALUE",
|
|
82
113
|
SmallestNonzeroFloat64: "Number.MIN_VALUE",
|
|
83
114
|
MaxInt: "Number.MAX_SAFE_INTEGER",
|
|
84
115
|
MinInt: "Number.MIN_SAFE_INTEGER",
|
|
116
|
+
MaxInt8: "127",
|
|
117
|
+
MinInt8: "-128",
|
|
118
|
+
MaxInt16: "32767",
|
|
119
|
+
MinInt16: "-32768",
|
|
120
|
+
MaxInt32: "2147483647",
|
|
121
|
+
MinInt32: "-2147483648",
|
|
122
|
+
MaxInt64: "9223372036854775807",
|
|
123
|
+
MinInt64: "-9223372036854775808",
|
|
124
|
+
MaxUint8: "255",
|
|
125
|
+
MaxUint16: "65535",
|
|
126
|
+
MaxUint32: "4294967295",
|
|
127
|
+
MaxUint: "18446744073709551615",
|
|
128
|
+
MaxUint64: "18446744073709551615",
|
|
129
|
+
},
|
|
130
|
+
strconv: {
|
|
131
|
+
ErrSyntax: "__strconv_err_syntax",
|
|
132
|
+
ErrRange: "__strconv_err_range",
|
|
133
|
+
},
|
|
134
|
+
path: {
|
|
135
|
+
ErrBadPattern: "__path_err_bad_pattern",
|
|
136
|
+
},
|
|
137
|
+
filepath: {
|
|
138
|
+
ErrBadPattern: "__path_err_bad_pattern",
|
|
85
139
|
},
|
|
86
140
|
io: {
|
|
87
|
-
EOF:
|
|
141
|
+
EOF: "__io_eof",
|
|
88
142
|
Discard:
|
|
89
|
-
"{ WriteString(s) { return s.length; }, Write(b) { return b.length; } }",
|
|
143
|
+
"{ WriteString(s) { return [s.length, null]; }, Write(b) { return [b.length, null]; } }",
|
|
144
|
+
},
|
|
145
|
+
sha256: {
|
|
146
|
+
Size: "32",
|
|
147
|
+
BlockSize: "64",
|
|
148
|
+
},
|
|
149
|
+
hex: {
|
|
150
|
+
ErrLength: '__error("encoding/hex: odd length hex string")',
|
|
151
|
+
},
|
|
152
|
+
base64: {
|
|
153
|
+
StdPadding: "61",
|
|
154
|
+
NoPadding: "-1",
|
|
155
|
+
StdEncoding: "__base64_std",
|
|
156
|
+
URLEncoding: "__base64_url",
|
|
157
|
+
RawStdEncoding: "__base64_raw_std",
|
|
158
|
+
RawURLEncoding: "__base64_raw_url",
|
|
159
|
+
},
|
|
160
|
+
binary: {
|
|
161
|
+
LittleEndian: "__binary_le",
|
|
162
|
+
BigEndian: "__binary_be",
|
|
163
|
+
},
|
|
164
|
+
rand: {
|
|
165
|
+
Reader: "__crypto_rand_reader",
|
|
90
166
|
},
|
|
91
167
|
os: { Args: "process.argv" },
|
|
92
168
|
time: {
|
|
169
|
+
Nanosecond: "1",
|
|
170
|
+
Microsecond: "1000",
|
|
93
171
|
Millisecond: "1000000",
|
|
94
172
|
Second: "1000000000",
|
|
95
173
|
Minute: "60000000000",
|
|
@@ -274,12 +352,27 @@ const EXPR_GEN_DELEGATE = {
|
|
|
274
352
|
/** @type {ThisType<CodeGen>} */
|
|
275
353
|
export const expressionGenMethods = {
|
|
276
354
|
genExpr(expr) {
|
|
277
|
-
if (expr
|
|
355
|
+
if (this._isNamedIfaceBox(expr)) return this._genNamedIfaceBoxExpr(expr);
|
|
356
|
+
if (expr._ifaceBox && !expr._ifaceBoxing && !this._isNamedBoxKind(expr)) {
|
|
278
357
|
expr._ifaceBoxing = true;
|
|
279
358
|
try {
|
|
280
359
|
this.useHelper("ifaceBox");
|
|
281
|
-
const fn =
|
|
282
|
-
|
|
360
|
+
const fn =
|
|
361
|
+
expr._ifaceBox === "ptr"
|
|
362
|
+
? "__ifp"
|
|
363
|
+
: expr._ifaceBox === "ptr_slice"
|
|
364
|
+
? "__ifps"
|
|
365
|
+
: expr._ifaceBox === "ptr_map"
|
|
366
|
+
? "__ifpm"
|
|
367
|
+
: expr._ifaceBox === "map"
|
|
368
|
+
? "__ifm"
|
|
369
|
+
: expr._ifaceBox === "slice"
|
|
370
|
+
? "__ifs"
|
|
371
|
+
: "__ifv";
|
|
372
|
+
const extra = expr._ifacePtrFields?.length
|
|
373
|
+
? `, ${JSON.stringify(expr._ifacePtrFields)}`
|
|
374
|
+
: "";
|
|
375
|
+
return `${fn}(${this.genExpr(expr)}${extra})`;
|
|
283
376
|
} finally {
|
|
284
377
|
expr._ifaceBoxing = false;
|
|
285
378
|
}
|
|
@@ -307,6 +400,29 @@ export const expressionGenMethods = {
|
|
|
307
400
|
}
|
|
308
401
|
},
|
|
309
402
|
|
|
403
|
+
_isNamedBoxKind(expr) {
|
|
404
|
+
return expr._ifaceBox === "named" || expr._ifaceBox === "named_ptr";
|
|
405
|
+
},
|
|
406
|
+
|
|
407
|
+
_isNamedIfaceBox(expr) {
|
|
408
|
+
return (
|
|
409
|
+
this._isNamedBoxKind(expr) && !expr._ifaceBoxing && !expr._noNamedBox
|
|
410
|
+
);
|
|
411
|
+
},
|
|
412
|
+
|
|
413
|
+
_genNamedIfaceBoxExpr(expr) {
|
|
414
|
+
expr._ifaceBoxing = true;
|
|
415
|
+
try {
|
|
416
|
+
return this._genNamedIfaceBox(
|
|
417
|
+
this.genExpr(expr),
|
|
418
|
+
expr._ifaceNamedType,
|
|
419
|
+
expr._ifaceBox === "named_ptr",
|
|
420
|
+
);
|
|
421
|
+
} finally {
|
|
422
|
+
expr._ifaceBoxing = false;
|
|
423
|
+
}
|
|
424
|
+
},
|
|
425
|
+
|
|
310
426
|
_isStructType(t) {
|
|
311
427
|
if (!t) return false;
|
|
312
428
|
if (t.name && this.structNames.has(t.name)) return true;
|
|
@@ -453,8 +569,13 @@ export const expressionGenMethods = {
|
|
|
453
569
|
_emitParamCopies(params) {
|
|
454
570
|
for (const p of params) {
|
|
455
571
|
if (p.variadic || !p.name || p.name === "_") continue;
|
|
572
|
+
if (this._boxedVars.has(p.name)) {
|
|
573
|
+
// Address-taken parameter: rebind it to a { value } box.
|
|
574
|
+
this.line(`${p.name} = { value: ${p.name} };`);
|
|
575
|
+
continue;
|
|
576
|
+
}
|
|
456
577
|
const t = this._typeFromNode(p.type);
|
|
457
|
-
if (!t
|
|
578
|
+
if (!t) continue;
|
|
458
579
|
if (this._fnMutates(p.name)) {
|
|
459
580
|
this.line(`${p.name} = ${this._cloneJs(t, p.name)};`);
|
|
460
581
|
this._markOwnership(p.name, true);
|
|
@@ -500,6 +621,13 @@ export const expressionGenMethods = {
|
|
|
500
621
|
) {
|
|
501
622
|
return this.genExpr(expr.operand);
|
|
502
623
|
}
|
|
624
|
+
if (
|
|
625
|
+
expr.operand.kind === "SelectorExpr" ||
|
|
626
|
+
expr.operand.kind === "IndexExpr"
|
|
627
|
+
) {
|
|
628
|
+
const inner = this.genExpr(expr.operand);
|
|
629
|
+
return `{ get value() { return ${inner}; }, set value(v) { ${inner} = v; } }`;
|
|
630
|
+
}
|
|
503
631
|
// For non-boxed vars (primitives, etc.), wrap in { value: x }
|
|
504
632
|
return `{ value: ${this.genExpr(expr.operand)} }`;
|
|
505
633
|
}
|
|
@@ -525,11 +653,35 @@ export const expressionGenMethods = {
|
|
|
525
653
|
|
|
526
654
|
_genSelectorExpr(expr) {
|
|
527
655
|
if (expr._isMethodExpr) {
|
|
656
|
+
const namedExpr = this._namedMethodExprInfo(expr);
|
|
657
|
+
if (namedExpr) return `${namedExpr.cls}.$${expr.field}`;
|
|
528
658
|
return `((recv, ...args) => recv.${expr.field}(...args))`;
|
|
529
659
|
}
|
|
660
|
+
const namedValue = this._genNamedMethodValue(expr);
|
|
661
|
+
if (namedValue !== undefined) return namedValue;
|
|
530
662
|
if (expr.expr.kind === "Ident") {
|
|
531
663
|
const nsConst = NS_CONSTANTS[expr.expr.name]?.[expr.field];
|
|
532
|
-
if (nsConst !== undefined)
|
|
664
|
+
if (nsConst !== undefined) {
|
|
665
|
+
if (expr.expr.name === "strconv") {
|
|
666
|
+
this.useHelper("strconvError");
|
|
667
|
+
} else if (expr.expr.name === "path" || expr.expr.name === "filepath") {
|
|
668
|
+
if (expr.field === "ErrBadPattern") this.useHelper("pathError");
|
|
669
|
+
} else if (expr.expr.name === "io" && expr.field === "EOF") {
|
|
670
|
+
this.useHelper("ioEOF");
|
|
671
|
+
} else if (expr.expr.name === "binary") {
|
|
672
|
+
this.useHelper("binary");
|
|
673
|
+
} else if (expr.expr.name === "hex") {
|
|
674
|
+
this.useHelper("hex");
|
|
675
|
+
this.useHelper("error");
|
|
676
|
+
} else if (expr.expr.name === "base64") {
|
|
677
|
+
this.useHelper("base64");
|
|
678
|
+
} else if (expr.expr.name === "rand" && expr.field === "Reader") {
|
|
679
|
+
this.useHelper("cryptoRand");
|
|
680
|
+
} else if (expr.expr.name === "sha256") {
|
|
681
|
+
this.useHelper("sha256");
|
|
682
|
+
}
|
|
683
|
+
return nsConst;
|
|
684
|
+
}
|
|
533
685
|
}
|
|
534
686
|
const base = this.genExpr(expr.expr);
|
|
535
687
|
if (this.bundledPackages.has(base)) return expr.field;
|
|
@@ -556,6 +708,81 @@ export const expressionGenMethods = {
|
|
|
556
708
|
return f;
|
|
557
709
|
},
|
|
558
710
|
|
|
711
|
+
// ── Named non-struct types with methods: raw values, static dispatch ──
|
|
712
|
+
|
|
713
|
+
// Boxes a raw named value (or *T) entering an interface so dynamic dispatch works.
|
|
714
|
+
_genNamedIfaceBox(js, type, isPtr) {
|
|
715
|
+
const info = this._namedBoxInfo(type);
|
|
716
|
+
if (!info) return js;
|
|
717
|
+
return isPtr ? `new ${info.cls}$Ptr(${js})` : `new ${info.cls}(${js})`;
|
|
718
|
+
},
|
|
719
|
+
|
|
720
|
+
// Static target of `x.M` on a named non-struct type: its class and the
|
|
721
|
+
// receiver argument (the { value } box for pointer receivers).
|
|
722
|
+
_namedMethodTarget(sel) {
|
|
723
|
+
const recvType = sel.expr?._type;
|
|
724
|
+
const viaPtr = recvType?.kind === "pointer";
|
|
725
|
+
const info = this._namedBoxInfo(viaPtr ? recvType.base : recvType);
|
|
726
|
+
const method = info?.methods.get(sel.field);
|
|
727
|
+
if (!method) return null;
|
|
728
|
+
let recv;
|
|
729
|
+
if (method._ptrRecv) {
|
|
730
|
+
recv = viaPtr ? this.genExpr(sel.expr) : this._genAddressOfExpr(sel.expr);
|
|
731
|
+
} else {
|
|
732
|
+
recv = viaPtr
|
|
733
|
+
? `${this.genExpr(sel.expr)}.value`
|
|
734
|
+
: this.genExpr(sel.expr);
|
|
735
|
+
}
|
|
736
|
+
return { cls: info.cls, recv };
|
|
737
|
+
},
|
|
738
|
+
|
|
739
|
+
// `x.M` used as a value: binds the receiver now, as Go does.
|
|
740
|
+
_genNamedMethodValue(expr) {
|
|
741
|
+
if (!expr._isMethodValue || expr._callee) return undefined;
|
|
742
|
+
const target = this._namedMethodTarget(expr);
|
|
743
|
+
if (!target) return undefined;
|
|
744
|
+
return `((__r) => (...__a) => ${target.cls}.$${expr.field}(__r, ...__a))(${target.recv})`;
|
|
745
|
+
},
|
|
746
|
+
|
|
747
|
+
_genAddressOfExpr(operand) {
|
|
748
|
+
return this._genUnaryExpr({
|
|
749
|
+
kind: "UnaryExpr",
|
|
750
|
+
op: "&",
|
|
751
|
+
operand,
|
|
752
|
+
_type: { kind: "pointer", base: operand._type },
|
|
753
|
+
});
|
|
754
|
+
},
|
|
755
|
+
|
|
756
|
+
// `T.M` method expression on a named non-struct type.
|
|
757
|
+
_namedMethodExprInfo(sel) {
|
|
758
|
+
const t =
|
|
759
|
+
sel.expr?._type?.kind === "named"
|
|
760
|
+
? sel.expr._type
|
|
761
|
+
: this.checker?.types.get(sel.expr?.name);
|
|
762
|
+
const info = this._namedBoxInfo(t);
|
|
763
|
+
return info?.methods.has(sel.field) ? info : null;
|
|
764
|
+
},
|
|
765
|
+
|
|
766
|
+
_genNamedMethodCall(expr) {
|
|
767
|
+
const sel = expr.func;
|
|
768
|
+
if (sel._isMethodExpr) {
|
|
769
|
+
const info = this._namedMethodExprInfo(sel);
|
|
770
|
+
if (!info) return undefined;
|
|
771
|
+
return `${info.cls}.$${sel.field}(${this._genCallArgs(expr)})`;
|
|
772
|
+
}
|
|
773
|
+
const target = this._namedMethodTarget(sel);
|
|
774
|
+
if (!target) return undefined;
|
|
775
|
+
const args = this._genCallArgs(expr);
|
|
776
|
+
return `${target.cls}.$${sel.field}(${args ? `${target.recv}, ${args}` : target.recv})`;
|
|
777
|
+
},
|
|
778
|
+
|
|
779
|
+
_genCallArgs(expr) {
|
|
780
|
+
if (expr._multiForward) return `...${this.genExpr(expr.args[0])}`;
|
|
781
|
+
return expr.args
|
|
782
|
+
.map((a) => (a._spread ? `...${this.genExpr(a)}` : this.genExpr(a)))
|
|
783
|
+
.join(", ");
|
|
784
|
+
},
|
|
785
|
+
|
|
559
786
|
_genPointerSelectorField(expr, base) {
|
|
560
787
|
if (this._isStructPointerType(expr.expr._type)) {
|
|
561
788
|
const sel = `${base}.${expr.field}`;
|
|
@@ -590,9 +817,10 @@ export const expressionGenMethods = {
|
|
|
590
817
|
},
|
|
591
818
|
|
|
592
819
|
_isStringExprType(exprType) {
|
|
820
|
+
const t = exprType?.kind === "named" ? exprType.underlying : exprType;
|
|
593
821
|
return (
|
|
594
|
-
(
|
|
595
|
-
(
|
|
822
|
+
(t?.kind === "basic" && t.name === "string") ||
|
|
823
|
+
(t?.kind === "untyped" && t.base === "string")
|
|
596
824
|
);
|
|
597
825
|
},
|
|
598
826
|
|
|
@@ -635,22 +863,104 @@ export const expressionGenMethods = {
|
|
|
635
863
|
const val = hoist ? "__v" : src;
|
|
636
864
|
const check = this._typeCheckExpr(expr.type, val);
|
|
637
865
|
const wrap = (body) => (hoist ? `((__v) => ${body})(${src})` : body);
|
|
866
|
+
// Named non-struct values sit in interfaces as boxes; the asserted value is raw.
|
|
867
|
+
const out = this._isNamedBoxTypeNode(expr.type)
|
|
868
|
+
? `${val}.__gfUnbox()`
|
|
869
|
+
: val;
|
|
638
870
|
if (!expr._commaOk) {
|
|
639
871
|
// plain assertion: panic if check fails (matches Go behavior)
|
|
640
872
|
if (check === "true") return src; // can't check at runtime — pass through
|
|
641
873
|
return wrap(
|
|
642
|
-
`(${check} ? ${
|
|
874
|
+
`(${check} ? ${out} : (() => { throw new Error("interface conversion: type assertion failed"); })())`,
|
|
643
875
|
);
|
|
644
876
|
}
|
|
645
877
|
// comma-ok: emit [value-or-zero, runtimeTypeCheck]
|
|
646
878
|
const zero = this.zeroValueForTypeNode(expr.type);
|
|
647
|
-
return wrap(`(${check} ? [${
|
|
879
|
+
return wrap(`(${check} ? [${out}, true] : [${zero}, false])`);
|
|
880
|
+
},
|
|
881
|
+
|
|
882
|
+
_typeNameCheckExpr(name, val) {
|
|
883
|
+
if (TYPEOF_NUMBER_NAMES.has(name)) return `typeof ${val} === "number"`;
|
|
884
|
+
if (name === "string") return `typeof ${val} === "string"`;
|
|
885
|
+
if (name === "bool") return `typeof ${val} === "boolean"`;
|
|
886
|
+
if (name === "nil") return `${val} === null`;
|
|
887
|
+
if (name === "error")
|
|
888
|
+
return `(typeof ${val} === "object" && ${val} !== null && typeof ${val}.Error === "function")`;
|
|
889
|
+
if (this.structNames.has(name))
|
|
890
|
+
return `(${val} instanceof ${name} && ${val}.__p !== true)`;
|
|
891
|
+
const named = this.checker?.types.get(name);
|
|
892
|
+
const namedInfo = this._namedBoxInfo(named);
|
|
893
|
+
if (namedInfo) return `(${val} instanceof ${namedInfo.cls})`;
|
|
894
|
+
// User-declared interfaces: check the dynamic value has every method.
|
|
895
|
+
if (!name.includes(".") && named?.underlying?.kind === "interface")
|
|
896
|
+
return this._ifaceMethodCheck(val, [...named.underlying.methods.keys()]);
|
|
897
|
+
return "true"; // unknown type — can't check at runtime
|
|
898
|
+
},
|
|
899
|
+
|
|
900
|
+
// Anonymous interfaces and pointers to named non-struct types; null otherwise.
|
|
901
|
+
_namedOrIfaceCheckExpr(typeNode, val) {
|
|
902
|
+
if (typeNode.kind === "InterfaceType" && !(typeNode.embeds?.length > 0))
|
|
903
|
+
return this._ifaceMethodCheck(
|
|
904
|
+
val,
|
|
905
|
+
(typeNode.methods ?? []).map((m) => m.name),
|
|
906
|
+
);
|
|
907
|
+
if (typeNode.kind === "PointerType" && typeNode.base?.kind === "TypeName") {
|
|
908
|
+
const info = this._namedBoxInfo(
|
|
909
|
+
this.checker?.types.get(typeNode.base.name),
|
|
910
|
+
);
|
|
911
|
+
if (info) return `(${val} instanceof ${info.cls}$Ptr)`;
|
|
912
|
+
}
|
|
913
|
+
return null;
|
|
914
|
+
},
|
|
915
|
+
|
|
916
|
+
_ifaceMethodCheck(val, methodNames) {
|
|
917
|
+
const checks = methodNames.map((m) => `typeof ${val}.${m} === "function"`);
|
|
918
|
+
return `(${[`${val} != null`, ...checks].join(" && ")})`;
|
|
919
|
+
},
|
|
920
|
+
|
|
921
|
+
_isNamedBoxTypeNode(typeNode) {
|
|
922
|
+
if (typeNode?.kind === "TypeName")
|
|
923
|
+
return Boolean(
|
|
924
|
+
this._namedBoxInfo(this.checker?.types.get(typeNode.name)),
|
|
925
|
+
);
|
|
926
|
+
if (typeNode?.kind === "PointerType" && typeNode.base?.kind === "TypeName")
|
|
927
|
+
return Boolean(
|
|
928
|
+
this._namedBoxInfo(this.checker?.types.get(typeNode.base.name)),
|
|
929
|
+
);
|
|
930
|
+
return false;
|
|
648
931
|
},
|
|
649
932
|
|
|
650
933
|
_genTypeConversionCall(expr) {
|
|
651
|
-
const
|
|
934
|
+
const targetType = expr._conversionTargetType;
|
|
652
935
|
const arg = this.genExpr(expr.args[0]);
|
|
653
|
-
|
|
936
|
+
const isRaw =
|
|
937
|
+
targetType?.name === "json.RawMessage" ||
|
|
938
|
+
targetType?.name === "encoding/json.RawMessage";
|
|
939
|
+
const base =
|
|
940
|
+
targetType?.kind === "named" ? targetType.underlying : targetType;
|
|
941
|
+
if (
|
|
942
|
+
targetType?.kind === "named" &&
|
|
943
|
+
base?.kind === "basic" &&
|
|
944
|
+
this._namedBoxInfo(targetType)
|
|
945
|
+
)
|
|
946
|
+
return this._genPrimitiveConversion(
|
|
947
|
+
{ expr: expr.args[0] },
|
|
948
|
+
arg,
|
|
949
|
+
base.name,
|
|
950
|
+
);
|
|
951
|
+
if (base?.kind === "slice") {
|
|
952
|
+
const elem = base.elem?.name;
|
|
953
|
+
if (
|
|
954
|
+
(elem === "byte" || elem === "uint8") &&
|
|
955
|
+
this._isStringExprType(expr.args[0]?._type)
|
|
956
|
+
) {
|
|
957
|
+
const enc = `new TextEncoder().encode(${arg})`;
|
|
958
|
+
return isRaw ? `((u) => { u.__raw = true; return u; })(${enc})` : enc;
|
|
959
|
+
}
|
|
960
|
+
if (isRaw) {
|
|
961
|
+
return `((u) => { u.__raw = true; return u; })(${arg})`;
|
|
962
|
+
}
|
|
963
|
+
}
|
|
654
964
|
return arg;
|
|
655
965
|
},
|
|
656
966
|
|
|
@@ -705,12 +1015,93 @@ export const expressionGenMethods = {
|
|
|
705
1015
|
return this._genRegexpMethodCall(expr.func.field, expr);
|
|
706
1016
|
if (recvName === "time.Time")
|
|
707
1017
|
return this._genTimeMethodCall(expr.func.field, expr);
|
|
1018
|
+
if (recvName === "time.Duration")
|
|
1019
|
+
return this._genDurationMethodCall(expr.func.field, expr);
|
|
708
1020
|
if (recvName === "testing.T")
|
|
709
1021
|
return this._genTestingMethodCall(expr.func.field, expr);
|
|
1022
|
+
if (recvName === "strings.Replacer") {
|
|
1023
|
+
const recv = this.genExpr(expr.func.expr);
|
|
1024
|
+
const arg = this.genExpr(expr.args[0]);
|
|
1025
|
+
return `(${recv} ? ${recv}.Replace(${arg}) : ${arg})`;
|
|
1026
|
+
}
|
|
710
1027
|
if (recvName?.startsWith("shared.") && expr.func.field === "Subarray") {
|
|
711
1028
|
const args = expr.args.map((a) => this.genExpr(a)).join(", ");
|
|
712
1029
|
return `${this.genExpr(expr.func.expr)}.subarray(${args})`;
|
|
713
1030
|
}
|
|
1031
|
+
if (recvName === "simd.F32x4" || recvName === "F32x4") {
|
|
1032
|
+
return this._genSimdReceiverCall(expr);
|
|
1033
|
+
}
|
|
1034
|
+
if (recvName === "sync.Mutex" || recvName === "sync.RWMutex") {
|
|
1035
|
+
if (expr.func.field === "TryLock" || expr.func.field === "TryRLock")
|
|
1036
|
+
return "true";
|
|
1037
|
+
return "(void 0)";
|
|
1038
|
+
}
|
|
1039
|
+
if (recvName === "sync.Once" && expr.func.field === "Do") {
|
|
1040
|
+
const recv = this.genExpr(expr.func.expr);
|
|
1041
|
+
const f = this.genExpr(expr.args[0]);
|
|
1042
|
+
return `((o, fn) => { if (!o.done) { o.done = 1; fn(); } })(${recv}, ${f})`;
|
|
1043
|
+
}
|
|
1044
|
+
if (recvName === "sync.WaitGroup") {
|
|
1045
|
+
const recv = this.genExpr(expr.func.expr);
|
|
1046
|
+
if (expr.func.field === "Add") {
|
|
1047
|
+
return `((wg, d) => { wg.counter = (wg.counter || 0) + d; })(${recv}, ${this.genExpr(expr.args[0])})`;
|
|
1048
|
+
}
|
|
1049
|
+
if (expr.func.field === "Done") {
|
|
1050
|
+
return `((wg) => { wg.counter = (wg.counter || 0) - 1; })(${recv})`;
|
|
1051
|
+
}
|
|
1052
|
+
if (expr.func.field === "Wait") return "(void 0)";
|
|
1053
|
+
}
|
|
1054
|
+
if (
|
|
1055
|
+
recvName === "binary.LittleEndian" ||
|
|
1056
|
+
recvName === "binary.BigEndian" ||
|
|
1057
|
+
recvName === "encoding/binary.LittleEndian" ||
|
|
1058
|
+
recvName === "encoding/binary.BigEndian" ||
|
|
1059
|
+
recvName === "binary.ByteOrder" ||
|
|
1060
|
+
recvName === "encoding/binary.ByteOrder"
|
|
1061
|
+
) {
|
|
1062
|
+
this.useHelper("binary");
|
|
1063
|
+
const recv = this.genExpr(expr.func.expr);
|
|
1064
|
+
const args = expr.args.map((a) => this.genExpr(a)).join(", ");
|
|
1065
|
+
return `${recv}.${expr.func.field}(${args})`;
|
|
1066
|
+
}
|
|
1067
|
+
if (
|
|
1068
|
+
recvName === "base64.Encoding" ||
|
|
1069
|
+
recvName === "encoding/base64.Encoding"
|
|
1070
|
+
) {
|
|
1071
|
+
this.useHelper("base64");
|
|
1072
|
+
const recv = this.genExpr(expr.func.expr);
|
|
1073
|
+
const args = expr.args.map((a) => this.genExpr(a)).join(", ");
|
|
1074
|
+
return `${recv}.${expr.func.field}(${args})`;
|
|
1075
|
+
}
|
|
1076
|
+
return undefined;
|
|
1077
|
+
},
|
|
1078
|
+
|
|
1079
|
+
_genSimdReceiverCall(expr) {
|
|
1080
|
+
this.useHelper("simd");
|
|
1081
|
+
const method = expr.func.field;
|
|
1082
|
+
const recv = this.genExpr(expr.func.expr);
|
|
1083
|
+
const helpers = {
|
|
1084
|
+
Add: "__simd_add",
|
|
1085
|
+
Sub: "__simd_sub",
|
|
1086
|
+
Mul: "__simd_mul",
|
|
1087
|
+
Div: "__simd_div",
|
|
1088
|
+
Min: "__simd_min",
|
|
1089
|
+
Max: "__simd_max",
|
|
1090
|
+
Dot3: "__simd_dot3",
|
|
1091
|
+
Dot4: "__simd_dot4",
|
|
1092
|
+
};
|
|
1093
|
+
if (helpers[method]) {
|
|
1094
|
+
return `${helpers[method]}(${recv}, ${this.genExpr(expr.args[0])})`;
|
|
1095
|
+
}
|
|
1096
|
+
if (method === "Lane") {
|
|
1097
|
+
return `${recv}[${this.genExpr(expr.args[0])}]`;
|
|
1098
|
+
}
|
|
1099
|
+
if (method === "WithLane") {
|
|
1100
|
+
return `__simd_with_lane(${recv}, ${this.genExpr(expr.args[0])}, ${this.genExpr(expr.args[1])})`;
|
|
1101
|
+
}
|
|
1102
|
+
if (method === "Splat") {
|
|
1103
|
+
return `__simd_splat(${this.genExpr(expr.args[0])})`;
|
|
1104
|
+
}
|
|
714
1105
|
return undefined;
|
|
715
1106
|
},
|
|
716
1107
|
|
|
@@ -731,6 +1122,8 @@ export const expressionGenMethods = {
|
|
|
731
1122
|
const recvName = this._resolveRecvName(recvType);
|
|
732
1123
|
const typed = this._genReceiverTypeCall(recvName, expr);
|
|
733
1124
|
if (typed !== undefined) return typed;
|
|
1125
|
+
const namedCall = this._genNamedMethodCall(expr);
|
|
1126
|
+
if (namedCall !== undefined) return namedCall;
|
|
734
1127
|
if (this.strict && this._isInterfaceType(recvType)) {
|
|
735
1128
|
// Go panics with a nil-dereference runtime error; JS would throw a
|
|
736
1129
|
// TypeError with a different message.
|
|
@@ -933,7 +1326,9 @@ export const expressionGenMethods = {
|
|
|
933
1326
|
? this.genValueExpr(match.value ?? match)
|
|
934
1327
|
: fields[idx].zero;
|
|
935
1328
|
} else {
|
|
936
|
-
|
|
1329
|
+
const valExpr = this.genExpr(e.value);
|
|
1330
|
+
values[idx] =
|
|
1331
|
+
`(${valExpr} != null ? ${valExpr}.${ef} : ${fields[idx].zero})`;
|
|
937
1332
|
}
|
|
938
1333
|
return idx;
|
|
939
1334
|
},
|
|
@@ -951,6 +1346,11 @@ export const expressionGenMethods = {
|
|
|
951
1346
|
const idx = this._fillEmbedField(fields, ef, e, values);
|
|
952
1347
|
if (idx > maxIdx) maxIdx = idx;
|
|
953
1348
|
}
|
|
1349
|
+
const embedIdx = fields.findIndex((f) => f.name === k);
|
|
1350
|
+
if (embedIdx >= 0) {
|
|
1351
|
+
values[embedIdx] = this.genValueExpr(e.value);
|
|
1352
|
+
if (embedIdx > maxIdx) maxIdx = embedIdx;
|
|
1353
|
+
}
|
|
954
1354
|
} else {
|
|
955
1355
|
const idx = fields.findIndex((f) => f.name === k);
|
|
956
1356
|
if (idx >= 0) {
|
|
@@ -974,17 +1374,53 @@ export const expressionGenMethods = {
|
|
|
974
1374
|
const maxExplicitIdx = hasPositional
|
|
975
1375
|
? this._fillPositionalStructElems(fields, expr, values)
|
|
976
1376
|
: this._fillKeyedStructElems(fields, expr, values);
|
|
977
|
-
|
|
978
|
-
|
|
1377
|
+
const inst =
|
|
1378
|
+
maxExplicitIdx < 0
|
|
1379
|
+
? `new ${typeName}()`
|
|
1380
|
+
: `new ${typeName}(${values.slice(0, maxExplicitIdx + 1).join(", ")})`;
|
|
1381
|
+
return inst;
|
|
1382
|
+
},
|
|
1383
|
+
|
|
1384
|
+
_genSliceOrArrayLit(t, expr) {
|
|
1385
|
+
const elems = expr.elems
|
|
1386
|
+
.map((e) => this.genValueExpr(e.kind === "KeyValueExpr" ? e.value : e))
|
|
1387
|
+
.join(", ");
|
|
1388
|
+
const elemNode =
|
|
1389
|
+
t.kind === "SliceType" || t.kind === "ArrayType" ? t.elem : null;
|
|
1390
|
+
const elemResolved =
|
|
1391
|
+
expr._type?.kind === "slice" || expr._type?.kind === "array"
|
|
1392
|
+
? expr._type.elem
|
|
1393
|
+
: null;
|
|
1394
|
+
const typedCtor =
|
|
1395
|
+
typedArrayConstructorForElem(elemResolved) ??
|
|
1396
|
+
(elemNode?.name
|
|
1397
|
+
? typedArrayConstructorForElem({
|
|
1398
|
+
kind: "basic",
|
|
1399
|
+
name: elemNode.name,
|
|
1400
|
+
})
|
|
1401
|
+
: null);
|
|
1402
|
+
if (typedCtor) {
|
|
1403
|
+
return `new ${typedCtor}([${elems}])`;
|
|
979
1404
|
}
|
|
980
|
-
|
|
981
|
-
return `new ${typeName}(${args.join(", ")})`;
|
|
1405
|
+
return `[${elems}]`;
|
|
982
1406
|
},
|
|
983
1407
|
|
|
984
1408
|
genCompositeLit(expr) {
|
|
985
1409
|
const t = expr.typeExpr;
|
|
986
1410
|
if (t === null) return this._genImplicitCompositeLit(expr);
|
|
987
1411
|
const typeName = this.getTypeName(t);
|
|
1412
|
+
if (typeName === "bytes.Buffer" || typeName === "Buffer")
|
|
1413
|
+
return "{ _buf: [] }";
|
|
1414
|
+
if (typeName === "strings.Builder" || typeName === "Builder")
|
|
1415
|
+
return '{ _buf: "" }';
|
|
1416
|
+
if (typeName === "simd.F32x4" || typeName === "F32x4") {
|
|
1417
|
+
this.useHelper("simd");
|
|
1418
|
+
const a = expr.elems[0] ? this.genExpr(expr.elems[0]) : "0";
|
|
1419
|
+
const b = expr.elems[1] ? this.genExpr(expr.elems[1]) : "0";
|
|
1420
|
+
const c = expr.elems[2] ? this.genExpr(expr.elems[2]) : "0";
|
|
1421
|
+
const d = expr.elems[3] ? this.genExpr(expr.elems[3]) : "0";
|
|
1422
|
+
return `__simd_f32x4(${a}, ${b}, ${c}, ${d})`;
|
|
1423
|
+
}
|
|
988
1424
|
if (typeName && this.structNames.has(typeName))
|
|
989
1425
|
return this._genPositionalStructLit(typeName, expr);
|
|
990
1426
|
if (typeName && this.namedWrapperNames.has(typeName))
|
|
@@ -992,29 +1428,7 @@ export const expressionGenMethods = {
|
|
|
992
1428
|
if (expr.elems.some((e) => e._positionalField)) {
|
|
993
1429
|
if (typeName) return this._genPositionalStructLit(typeName, expr);
|
|
994
1430
|
}
|
|
995
|
-
if (this._isSliceOrArrayType(t))
|
|
996
|
-
const elems = expr.elems
|
|
997
|
-
.map((e) => this.genValueExpr(e.kind === "KeyValueExpr" ? e.value : e))
|
|
998
|
-
.join(", ");
|
|
999
|
-
const elemNode =
|
|
1000
|
-
t.kind === "SliceType" || t.kind === "ArrayType" ? t.elem : null;
|
|
1001
|
-
const elemResolved =
|
|
1002
|
-
expr._type?.kind === "slice" || expr._type?.kind === "array"
|
|
1003
|
-
? expr._type.elem
|
|
1004
|
-
: null;
|
|
1005
|
-
const typedCtor =
|
|
1006
|
-
typedArrayConstructorForElem(elemResolved) ??
|
|
1007
|
-
(elemNode?.name
|
|
1008
|
-
? typedArrayConstructorForElem({
|
|
1009
|
-
kind: "basic",
|
|
1010
|
-
name: elemNode.name,
|
|
1011
|
-
})
|
|
1012
|
-
: null);
|
|
1013
|
-
if (typedCtor) {
|
|
1014
|
-
return `new ${typedCtor}([${elems}])`;
|
|
1015
|
-
}
|
|
1016
|
-
return `[${elems}]`;
|
|
1017
|
-
}
|
|
1431
|
+
if (this._isSliceOrArrayType(t)) return this._genSliceOrArrayLit(t, expr);
|
|
1018
1432
|
if (this._isMapTypeNode(t))
|
|
1019
1433
|
return `{ ${this._genMapLitEntries(expr.elems)} }`;
|
|
1020
1434
|
return this._genFallbackCompositeLit(expr);
|
|
@@ -1049,25 +1463,13 @@ export const expressionGenMethods = {
|
|
|
1049
1463
|
const namedType = this.checker?.types.get(typeName);
|
|
1050
1464
|
const u = namedType?.underlying;
|
|
1051
1465
|
if (u?.kind === "map") {
|
|
1052
|
-
|
|
1053
|
-
.map((e) => {
|
|
1054
|
-
if (e.kind === "KeyValueExpr") {
|
|
1055
|
-
const k =
|
|
1056
|
-
e.key.litKind === "STRING"
|
|
1057
|
-
? JSON.stringify(e.key.value)
|
|
1058
|
-
: `[${this.genExpr(e.key)}]`;
|
|
1059
|
-
return `${k}: ${this.genValueExpr(e.value)}`;
|
|
1060
|
-
}
|
|
1061
|
-
return this.genValueExpr(e);
|
|
1062
|
-
})
|
|
1063
|
-
.join(", ");
|
|
1064
|
-
return `new ${typeName}({ ${entries} })`;
|
|
1466
|
+
return `{ ${this._genMapLitEntries(expr.elems)} }`;
|
|
1065
1467
|
}
|
|
1066
1468
|
// Default: slice
|
|
1067
1469
|
const elems = expr.elems
|
|
1068
1470
|
.map((e) => this.genValueExpr(e.kind === "KeyValueExpr" ? e.value : e))
|
|
1069
1471
|
.join(", ");
|
|
1070
|
-
return `
|
|
1472
|
+
return `[${elems}]`;
|
|
1071
1473
|
},
|
|
1072
1474
|
|
|
1073
1475
|
// ── Helpers ───────────────────────────────────────────────────
|
|
@@ -1109,11 +1511,14 @@ export const expressionGenMethods = {
|
|
|
1109
1511
|
},
|
|
1110
1512
|
|
|
1111
1513
|
// Returns true if the AST node contains a function call (side-effect risk).
|
|
1112
|
-
_hasCallExpr(node) {
|
|
1514
|
+
_hasCallExpr(node, seen = new Set()) {
|
|
1113
1515
|
if (!node || typeof node !== "object") return false;
|
|
1516
|
+
if (seen.has(node)) return false;
|
|
1517
|
+
seen.add(node);
|
|
1114
1518
|
if (node.kind === "CallExpr") return true;
|
|
1115
|
-
for (const v of Object.
|
|
1116
|
-
if (
|
|
1519
|
+
for (const [k, v] of Object.entries(node)) {
|
|
1520
|
+
if (k.startsWith("_") || k === "scope") continue;
|
|
1521
|
+
if (v && typeof v === "object" && this._hasCallExpr(v, seen)) return true;
|
|
1117
1522
|
}
|
|
1118
1523
|
return false;
|
|
1119
1524
|
},
|
|
@@ -1124,8 +1529,20 @@ export const expressionGenMethods = {
|
|
|
1124
1529
|
},
|
|
1125
1530
|
|
|
1126
1531
|
_zeroForNamedType(name) {
|
|
1127
|
-
|
|
1128
|
-
if (
|
|
1532
|
+
const entry = NAMED_ZERO_TABLE[name];
|
|
1533
|
+
if (entry) {
|
|
1534
|
+
if (entry[0]) this.useHelper(entry[0]);
|
|
1535
|
+
return entry[1];
|
|
1536
|
+
}
|
|
1537
|
+
const named = this.checker?.types.get(name);
|
|
1538
|
+
const u = named?.kind === "named" ? named.underlying : null;
|
|
1539
|
+
if (
|
|
1540
|
+
u &&
|
|
1541
|
+
u.kind !== "struct" &&
|
|
1542
|
+
u.kind !== "interface" &&
|
|
1543
|
+
u.kind !== "named"
|
|
1544
|
+
)
|
|
1545
|
+
return this.zeroValueForType(u);
|
|
1129
1546
|
name = this._dequalify(name);
|
|
1130
1547
|
if (this.structNames.has(name)) return `new ${name}()`;
|
|
1131
1548
|
return "null";
|
|
@@ -1214,18 +1631,10 @@ export const expressionGenMethods = {
|
|
|
1214
1631
|
// Emit a JS boolean expression that checks whether `val` matches type node `t`.
|
|
1215
1632
|
_typeCheckExpr(typeNode, val) {
|
|
1216
1633
|
if (!typeNode) return "true";
|
|
1217
|
-
if (typeNode.kind === "TypeName")
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
if (name === "bool") return `typeof ${val} === "boolean"`;
|
|
1222
|
-
if (name === "nil") return `${val} === null`;
|
|
1223
|
-
if (name === "error")
|
|
1224
|
-
return `(typeof ${val} === "object" && ${val} !== null && typeof ${val}.Error === "function")`;
|
|
1225
|
-
if (this.structNames.has(name))
|
|
1226
|
-
return `(${val} instanceof ${name} && ${val}.__p !== true)`;
|
|
1227
|
-
return "true"; // unknown type — can't check at runtime
|
|
1228
|
-
}
|
|
1634
|
+
if (typeNode.kind === "TypeName")
|
|
1635
|
+
return this._typeNameCheckExpr(typeNode.name, val);
|
|
1636
|
+
const extended = this._namedOrIfaceCheckExpr(typeNode, val);
|
|
1637
|
+
if (extended) return extended;
|
|
1229
1638
|
if (
|
|
1230
1639
|
typeNode.kind === "PointerType" &&
|
|
1231
1640
|
typeNode.base?.kind === "TypeName" &&
|
|
@@ -1297,6 +1706,12 @@ export const expressionGenMethods = {
|
|
|
1297
1706
|
return expr.op === "==" ? cmp : `!${cmp}`;
|
|
1298
1707
|
}
|
|
1299
1708
|
|
|
1709
|
+
if (this._isIfaceEquality(expr)) {
|
|
1710
|
+
this.useHelper("ifeq");
|
|
1711
|
+
const cmp = `__ifeq(${this._genIfaceOperand(expr.left)}, ${this._genIfaceOperand(expr.right)})`;
|
|
1712
|
+
return expr.op === "==" ? cmp : `!${cmp}`;
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1300
1715
|
if (this.strict && this._needsStrictOp(expr.op, expr._type)) {
|
|
1301
1716
|
const l = this.genExpr(expr.left);
|
|
1302
1717
|
const r = this.genExpr(expr.right);
|
|
@@ -1331,6 +1746,31 @@ export const expressionGenMethods = {
|
|
|
1331
1746
|
);
|
|
1332
1747
|
},
|
|
1333
1748
|
|
|
1749
|
+
_isIfaceType(t) {
|
|
1750
|
+
return isAny(t) || this._isInterfaceType(t);
|
|
1751
|
+
},
|
|
1752
|
+
|
|
1753
|
+
// Interface comparisons go through __ifeq so boxed named values compare by
|
|
1754
|
+
// dynamic type and value, as in Go (`err == ErrNotFound`).
|
|
1755
|
+
_isIfaceEquality(expr) {
|
|
1756
|
+
if (expr.op !== "==" && expr.op !== "!=") return false;
|
|
1757
|
+
if (this._isNilLiteral(expr.left) || this._isNilLiteral(expr.right))
|
|
1758
|
+
return false;
|
|
1759
|
+
return (
|
|
1760
|
+
this._isIfaceType(expr.left._type) || this._isIfaceType(expr.right._type)
|
|
1761
|
+
);
|
|
1762
|
+
},
|
|
1763
|
+
|
|
1764
|
+
// A concrete operand compared with an interface is converted to an interface first.
|
|
1765
|
+
_genIfaceOperand(e) {
|
|
1766
|
+
if (e._ifaceBox || this._isIfaceType(e._type)) return this.genExpr(e);
|
|
1767
|
+
const isPtr = e._type?.kind === "pointer";
|
|
1768
|
+
const named = isPtr ? e._type.base : e._type;
|
|
1769
|
+
if (this._namedBoxInfo(named))
|
|
1770
|
+
return this._genNamedIfaceBox(this.genExpr(e), named, isPtr);
|
|
1771
|
+
return this.genExpr(e);
|
|
1772
|
+
},
|
|
1773
|
+
|
|
1334
1774
|
_isIntDivision(expr) {
|
|
1335
1775
|
return (
|
|
1336
1776
|
expr.op === "/" &&
|
|
@@ -1356,6 +1796,7 @@ export const expressionGenMethods = {
|
|
|
1356
1796
|
)
|
|
1357
1797
|
return null;
|
|
1358
1798
|
if (this._isStructEquality(expr) || this._isIntDivision(expr)) return null;
|
|
1799
|
+
if (this._isIfaceEquality(expr)) return null;
|
|
1359
1800
|
if (expr.op === "&^") return "&";
|
|
1360
1801
|
if (expr.op === "==" || expr.op === "!=") {
|
|
1361
1802
|
if (this._isNilLiteral(expr.left) || this._isNilLiteral(expr.right))
|
|
@@ -1380,8 +1821,13 @@ export const expressionGenMethods = {
|
|
|
1380
1821
|
this.useHelper("error");
|
|
1381
1822
|
return `__error(${inner})`;
|
|
1382
1823
|
}
|
|
1383
|
-
if (target && this.namedWrapperNames.has(target))
|
|
1384
|
-
|
|
1824
|
+
if (target && this.namedWrapperNames.has(target)) {
|
|
1825
|
+
const named = this.checker?.types.get(target);
|
|
1826
|
+
const u = named?.underlying;
|
|
1827
|
+
return u?.kind === "basic"
|
|
1828
|
+
? this._genPrimitiveConversion(expr, inner, u.name)
|
|
1829
|
+
: inner;
|
|
1830
|
+
}
|
|
1385
1831
|
return this._genPrimitiveConversion(expr, inner, target);
|
|
1386
1832
|
},
|
|
1387
1833
|
|
|
@@ -1397,24 +1843,19 @@ export const expressionGenMethods = {
|
|
|
1397
1843
|
return inner;
|
|
1398
1844
|
},
|
|
1399
1845
|
|
|
1400
|
-
_isStringSource(srcType) {
|
|
1401
|
-
return (
|
|
1402
|
-
(srcType?.kind === "basic" && srcType?.name === "string") ||
|
|
1403
|
-
(srcType?.kind === "untyped" && srcType?.base === "string")
|
|
1404
|
-
);
|
|
1405
|
-
},
|
|
1406
|
-
|
|
1407
1846
|
_genSliceTypeConversion(expr, inner, t) {
|
|
1408
1847
|
const elem = t.elem?.name;
|
|
1409
1848
|
if (elem === "byte" || elem === "uint8") {
|
|
1410
|
-
if (this.
|
|
1849
|
+
if (this._isStringExprType(expr.expr._type))
|
|
1411
1850
|
return `new TextEncoder().encode(${inner})`;
|
|
1851
|
+
if (isAny(expr.expr._type) || !expr.expr._type)
|
|
1852
|
+
return `(typeof ${inner} === "string" ? new TextEncoder().encode(${inner}) : new Uint8Array(${inner}))`;
|
|
1412
1853
|
return `new Uint8Array(${inner})`;
|
|
1413
1854
|
}
|
|
1414
1855
|
const typedCtor = typedArrayConstructorForElem(
|
|
1415
1856
|
t.elem ? { kind: "basic", name: t.elem.name } : null,
|
|
1416
1857
|
);
|
|
1417
|
-
const fromString = this.
|
|
1858
|
+
const fromString = this._isStringExprType(expr.expr._type);
|
|
1418
1859
|
if (typedCtor) {
|
|
1419
1860
|
return fromString
|
|
1420
1861
|
? `${typedCtor}.from(${inner}, __c => __c.codePointAt(0))`
|
|
@@ -1426,10 +1867,10 @@ export const expressionGenMethods = {
|
|
|
1426
1867
|
},
|
|
1427
1868
|
|
|
1428
1869
|
_genStringConversion(srcType, inner) {
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
if (
|
|
1432
|
-
const elem =
|
|
1870
|
+
const base = srcType?.kind === "named" ? srcType.underlying : srcType;
|
|
1871
|
+
if (base && this.isIntType(base)) return `String.fromCodePoint(${inner})`;
|
|
1872
|
+
if (base?.kind === "slice") {
|
|
1873
|
+
const elem = base.elem?.name;
|
|
1433
1874
|
if (elem === "byte" || elem === "uint8")
|
|
1434
1875
|
return `new TextDecoder().decode(new Uint8Array(${inner}))`;
|
|
1435
1876
|
if (elem === "rune" || elem === "int" || elem === "int32")
|
|
@@ -1532,81 +1973,55 @@ export const expressionGenMethods = {
|
|
|
1532
1973
|
if (tName === "float32") {
|
|
1533
1974
|
return `Math.fround(${l} ${op} ${r})`;
|
|
1534
1975
|
}
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
if (op === "<<" || op === ">>") {
|
|
1542
|
-
return `((${r}) >= 32 ? (${op === ">>" ? `((${l}) < 0 ? -1 : 0)` : "0"}) : ((${l}) ${op} (${r}))) | 0`;
|
|
1543
|
-
}
|
|
1544
|
-
if (op === "&^") return `((${l}) & ~(${r})) | 0`;
|
|
1545
|
-
return `((${l} ${op} ${r}) | 0)`;
|
|
1546
|
-
}
|
|
1547
|
-
if (tName === "uint32") {
|
|
1548
|
-
if (op === "/")
|
|
1549
|
-
return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : (Math.trunc((${l}) / (${r})) >>> 0))`;
|
|
1550
|
-
if (op === "%")
|
|
1551
|
-
return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : (((${l}) % (${r})) >>> 0))`;
|
|
1552
|
-
if (op === "<<" || op === ">>") {
|
|
1553
|
-
const jsOp = op === ">>" ? ">>>" : "<<";
|
|
1554
|
-
return `((${r}) >= 32 ? 0 : ((${l}) ${jsOp} (${r}))) >>> 0`;
|
|
1555
|
-
}
|
|
1556
|
-
if (op === "&^") return `((${l}) & ~(${r})) >>> 0`;
|
|
1557
|
-
return `((${l} ${op} ${r}) >>> 0)`;
|
|
1558
|
-
}
|
|
1559
|
-
if (tName === "int8") {
|
|
1560
|
-
if (op === "/")
|
|
1561
|
-
return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : ((Math.trunc((${l}) / (${r})) << 24) >> 24))`;
|
|
1562
|
-
if (op === "%")
|
|
1563
|
-
return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : (((((${l}) % (${r})) << 24) >> 24)))`;
|
|
1564
|
-
if (op === "<<" || op === ">>") {
|
|
1565
|
-
return `((${r}) >= 8 ? (${op === ">>" ? `((${l}) < 0 ? -1 : 0)` : "0"}) : (((${l}) ${op} (${r})) << 24 >> 24))`;
|
|
1566
|
-
}
|
|
1567
|
-
if (op === "&^") return `((((${l}) & ~(${r})) << 24) >> 24)`;
|
|
1568
|
-
return `((((${l} ${op} ${r})) << 24) >> 24)`;
|
|
1569
|
-
}
|
|
1570
|
-
if (tName === "int16") {
|
|
1976
|
+
const divZero =
|
|
1977
|
+
'(() => { throw new Error("runtime error: integer divide by zero"); })()';
|
|
1978
|
+
if (tName === "int32" || tName === "rune" || tName === "uint32") {
|
|
1979
|
+
const signed = tName !== "uint32";
|
|
1980
|
+
const cast = signed ? "| 0" : ">>> 0";
|
|
1981
|
+
if (signed && op === "*") return `Math.imul(${l}, ${r})`;
|
|
1571
1982
|
if (op === "/")
|
|
1572
|
-
return `((${r}) === 0 ?
|
|
1983
|
+
return `((${r}) === 0 ? ${divZero} : (Math.trunc((${l}) / (${r})) ${cast}))`;
|
|
1573
1984
|
if (op === "%")
|
|
1574
|
-
return `((${r}) === 0 ?
|
|
1985
|
+
return `((${r}) === 0 ? ${divZero} : (((${l}) % (${r})) ${cast}))`;
|
|
1575
1986
|
if (op === "<<" || op === ">>") {
|
|
1576
|
-
|
|
1987
|
+
const ovf = signed && op === ">>" ? `((${l}) < 0 ? -1 : 0)` : "0";
|
|
1988
|
+
const jsOp = !signed && op === ">>" ? ">>>" : op;
|
|
1989
|
+
return `((${r}) >= 32 ? ${ovf} : ((${l}) ${jsOp} (${r}))) ${cast}`;
|
|
1577
1990
|
}
|
|
1578
|
-
if (op === "&^") return `((
|
|
1579
|
-
return `((
|
|
1991
|
+
if (op === "&^") return `((${l}) & ~(${r})) ${cast}`;
|
|
1992
|
+
return `((${l} ${op} ${r}) ${cast})`;
|
|
1580
1993
|
}
|
|
1581
|
-
if (tName === "
|
|
1994
|
+
if (tName === "int8" || tName === "int16") {
|
|
1995
|
+
const bits = tName === "int8" ? 8 : 16;
|
|
1996
|
+
const sh = 32 - bits;
|
|
1582
1997
|
if (op === "/")
|
|
1583
|
-
return `((${r}) === 0 ?
|
|
1998
|
+
return `((${r}) === 0 ? ${divZero} : ((Math.trunc((${l}) / (${r})) << ${sh}) >> ${sh}))`;
|
|
1584
1999
|
if (op === "%")
|
|
1585
|
-
return `((${r}) === 0 ?
|
|
2000
|
+
return `((${r}) === 0 ? ${divZero} : (((((${l}) % (${r})) << ${sh}) >> ${sh})))`;
|
|
1586
2001
|
if (op === "<<" || op === ">>") {
|
|
1587
|
-
|
|
1588
|
-
return `((${r}) >= 8 ? 0 : (((${l}) ${jsOp} (${r})) & 0xFF))`;
|
|
2002
|
+
return `((${r}) >= ${bits} ? (${op === ">>" ? `((${l}) < 0 ? -1 : 0)` : "0"}) : (((${l}) ${op} (${r})) << ${sh} >> ${sh}))`;
|
|
1589
2003
|
}
|
|
1590
|
-
if (op === "&^") return `(((${l}) & ~(${r}))
|
|
1591
|
-
return `((${l} ${op} ${r})
|
|
2004
|
+
if (op === "&^") return `((((${l}) & ~(${r})) << ${sh}) >> ${sh})`;
|
|
2005
|
+
return `((((${l} ${op} ${r})) << ${sh}) >> ${sh})`;
|
|
1592
2006
|
}
|
|
1593
|
-
if (tName === "uint16") {
|
|
2007
|
+
if (tName === "uint8" || tName === "byte" || tName === "uint16") {
|
|
2008
|
+
const bits = tName === "uint16" ? 16 : 8;
|
|
2009
|
+
const mask = bits === 16 ? "0xFFFF" : "0xFF";
|
|
1594
2010
|
if (op === "/")
|
|
1595
|
-
return `((${r}) === 0 ?
|
|
2011
|
+
return `((${r}) === 0 ? ${divZero} : (Math.trunc((${l}) / (${r})) & ${mask}))`;
|
|
1596
2012
|
if (op === "%")
|
|
1597
|
-
return `((${r}) === 0 ?
|
|
2013
|
+
return `((${r}) === 0 ? ${divZero} : (((${l}) % (${r})) & ${mask}))`;
|
|
1598
2014
|
if (op === "<<" || op === ">>") {
|
|
1599
2015
|
const jsOp = op === ">>" ? ">>>" : "<<";
|
|
1600
|
-
return `((${r}) >=
|
|
2016
|
+
return `((${r}) >= ${bits} ? 0 : (((${l}) ${jsOp} (${r})) & ${mask}))`;
|
|
1601
2017
|
}
|
|
1602
|
-
if (op === "&^") return `(((${l}) & ~(${r})) &
|
|
1603
|
-
return `((${l} ${op} ${r}) &
|
|
2018
|
+
if (op === "&^") return `(((${l}) & ~(${r})) & ${mask})`;
|
|
2019
|
+
return `((${l} ${op} ${r}) & ${mask})`;
|
|
1604
2020
|
}
|
|
1605
2021
|
if (this.isIntType(type)) {
|
|
1606
2022
|
if (op === "/")
|
|
1607
|
-
return `((${r}) === 0 ?
|
|
1608
|
-
if (op === "%")
|
|
1609
|
-
return `((${r}) === 0 ? (() => { throw new Error("runtime error: integer divide by zero"); })() : ((${l}) % (${r})))`;
|
|
2023
|
+
return `((${r}) === 0 ? ${divZero} : Math.trunc((${l}) / (${r})))`;
|
|
2024
|
+
if (op === "%") return `((${r}) === 0 ? ${divZero} : ((${l}) % (${r})))`;
|
|
1610
2025
|
}
|
|
1611
2026
|
return `${l} ${op} ${r}`;
|
|
1612
2027
|
},
|