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
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
isIteratorRangeFor,
|
|
10
10
|
isRangeFor,
|
|
11
11
|
} from "../../lower/range.js";
|
|
12
|
-
import { someChild } from "../../lower/walk.js";
|
|
12
|
+
import { hasCallOrReceive, someChild } from "../../lower/walk.js";
|
|
13
13
|
import {
|
|
14
14
|
getMapKeyValTypes,
|
|
15
15
|
isArrayType,
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
isSliceType,
|
|
25
25
|
isStringType,
|
|
26
26
|
isStructType,
|
|
27
|
+
isTaggedSliceType,
|
|
27
28
|
} from "./types.js";
|
|
28
29
|
|
|
29
30
|
function hasReturnInBlock(n) {
|
|
@@ -61,6 +62,15 @@ export class StmtsEmitter {
|
|
|
61
62
|
const names = spec.names ?? (spec.name ? [spec.name] : []);
|
|
62
63
|
const values = spec.value ?? (spec.init ? [spec.init] : []);
|
|
63
64
|
|
|
65
|
+
// `var a, b = f()` / `var v, ok = m[k]` bind like `a, b := ...`.
|
|
66
|
+
if (names.length > 1 && values.length === 1) {
|
|
67
|
+
this.emitDefineStmt({
|
|
68
|
+
lhs: names.map((name) => ({ kind: "Ident", name })),
|
|
69
|
+
rhs: values,
|
|
70
|
+
});
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
|
|
64
74
|
for (let i = 0; i < names.length; i++) {
|
|
65
75
|
const name = names[i];
|
|
66
76
|
const rawType = spec.type ?? values[i]?._type;
|
|
@@ -301,21 +311,24 @@ export class StmtsEmitter {
|
|
|
301
311
|
if (assigned.size === 0) return null;
|
|
302
312
|
const undo = [];
|
|
303
313
|
const names = [];
|
|
304
|
-
|
|
314
|
+
const visit = (l) => {
|
|
305
315
|
let keys;
|
|
306
316
|
if (l.kind === "IndexExpr") keys = ["expr", "index"];
|
|
307
317
|
else if (l.kind === "SelectorExpr" && !l._isMethodExpr) keys = ["expr"];
|
|
308
|
-
else
|
|
318
|
+
else return;
|
|
309
319
|
for (const key of keys) {
|
|
310
320
|
const operand = l[key];
|
|
311
321
|
if (!this._mentions(operand, assigned)) continue;
|
|
312
322
|
const goType = operand._type ?? this._resolveExprGoType(operand);
|
|
313
|
-
// A struct or array value names the variable's storage in Go
|
|
323
|
+
// A struct or array value names the variable's storage in Go: hoist
|
|
324
|
+
// the operands inside it (`ps[n].A`) instead of the value.
|
|
314
325
|
if (
|
|
315
326
|
key === "expr" &&
|
|
316
327
|
(this._isValueStruct(goType) || this._isValueArray(goType))
|
|
317
|
-
)
|
|
328
|
+
) {
|
|
329
|
+
visit(operand);
|
|
318
330
|
continue;
|
|
331
|
+
}
|
|
319
332
|
const wType = this.toWasmType(goType);
|
|
320
333
|
const name = `__lhs_tmp_${this.tempCounter++}`;
|
|
321
334
|
const idx = this.allocLocal(name, wType, goType, true);
|
|
@@ -325,7 +338,8 @@ export class StmtsEmitter {
|
|
|
325
338
|
names.push(name);
|
|
326
339
|
l[key] = { kind: "Ident", name, _type: goType, _parTmp: true };
|
|
327
340
|
}
|
|
328
|
-
}
|
|
341
|
+
};
|
|
342
|
+
for (const l of lhsNodes) visit(l);
|
|
329
343
|
if (undo.length === 0) return null;
|
|
330
344
|
return () => {
|
|
331
345
|
for (const [node, key, v] of undo) node[key] = v;
|
|
@@ -664,6 +678,10 @@ export class StmtsEmitter {
|
|
|
664
678
|
// A whole struct or array stored into an element whose address is
|
|
665
679
|
// taken writes into the existing element, so `p := &xs[0]` stays valid.
|
|
666
680
|
if (this._needsStableStore(l, elemGoType)) {
|
|
681
|
+
if (sliceInfo) {
|
|
682
|
+
this._emitSliceElemStableStore(l, elemGoType, r, sliceInfo, arrInfo);
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
667
685
|
this._emitStableStore(elemGoType, r, () => {
|
|
668
686
|
this._emitElemAddr(baseNode, l.index, sliceInfo, arrInfo);
|
|
669
687
|
this.pushInstruction({
|
|
@@ -823,6 +841,10 @@ export class StmtsEmitter {
|
|
|
823
841
|
)
|
|
824
842
|
)
|
|
825
843
|
return false;
|
|
844
|
+
// Any callee may hold `&xs[i]` of a slice element, so when the program
|
|
845
|
+
// takes one, a whole-value store into an element writes in place.
|
|
846
|
+
if (this.mod.lowerResult?.elemAddrTaken && this._isSliceElemTarget(l))
|
|
847
|
+
return true;
|
|
826
848
|
const root = rootIdentName(l);
|
|
827
849
|
if (!root) return false;
|
|
828
850
|
// Any code may hold the address of a package-level variable.
|
|
@@ -832,6 +854,45 @@ export class StmtsEmitter {
|
|
|
832
854
|
return Boolean(ctx?.fnMutates(root, true));
|
|
833
855
|
}
|
|
834
856
|
|
|
857
|
+
// `xs[i] = v` into a slice element in place. Capacity beyond the length
|
|
858
|
+
// may hold null elements (a reslice exposes them), so a null element is
|
|
859
|
+
// first replaced by a zero value that the store then fills.
|
|
860
|
+
_emitSliceElemStableStore(l, elemGoType, r, sliceInfo, arrInfo) {
|
|
861
|
+
const arrT = {
|
|
862
|
+
kind: "ref",
|
|
863
|
+
nullable: true,
|
|
864
|
+
typeIndex: arrInfo.typeIndex,
|
|
865
|
+
};
|
|
866
|
+
const arr = this.acquireTemp(arrT);
|
|
867
|
+
const idx = this.acquireTemp("i32");
|
|
868
|
+
this._emitElemAddr(l.expr, l.index, sliceInfo, arrInfo);
|
|
869
|
+
this.pushInstruction({ op: "local.set", index: idx });
|
|
870
|
+
this.pushInstruction({ op: "local.set", index: arr });
|
|
871
|
+
const getElem = () => {
|
|
872
|
+
this.pushInstruction({ op: "local.get", index: arr });
|
|
873
|
+
this.pushInstruction({ op: "local.get", index: idx });
|
|
874
|
+
this.pushInstruction({ op: "array.get", typeIndex: arrInfo.typeIndex });
|
|
875
|
+
};
|
|
876
|
+
getElem();
|
|
877
|
+
this.pushInstruction("ref.is_null");
|
|
878
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
879
|
+
this.pushInstruction({ op: "local.get", index: arr });
|
|
880
|
+
this.pushInstruction({ op: "local.get", index: idx });
|
|
881
|
+
this.emitZeroValue(elemGoType, arrInfo.elemWType);
|
|
882
|
+
this.pushInstruction({ op: "array.set", typeIndex: arrInfo.typeIndex });
|
|
883
|
+
this.pushInstruction("end");
|
|
884
|
+
this._emitStableStore(elemGoType, r, getElem);
|
|
885
|
+
this.releaseTemp(idx, "i32");
|
|
886
|
+
this.releaseTemp(arr, arrT);
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
// True when `l` is `xs[i]` on a slice (not an array or map).
|
|
890
|
+
_isSliceElemTarget(l) {
|
|
891
|
+
if (l.kind !== "IndexExpr") return false;
|
|
892
|
+
const t = l.expr._type ?? this._resolveExprGoType(l.expr);
|
|
893
|
+
return isSliceType(t, this.mod.checker) && !this._sharedInfo(t);
|
|
894
|
+
}
|
|
895
|
+
|
|
835
896
|
_emitStableStore(goType, r, emitDst) {
|
|
836
897
|
if (this._isValueArray(goType)) {
|
|
837
898
|
this._emitArrayStoreInto(goType, r, emitDst);
|
|
@@ -921,6 +982,7 @@ export class StmtsEmitter {
|
|
|
921
982
|
this.pushInstruction({ op: "local.set", index: tmpL });
|
|
922
983
|
this.emitExpr(r, baseWType);
|
|
923
984
|
this.pushInstruction({ op: "local.set", index: tmpR });
|
|
985
|
+
this._emitNilGuard(tmpL, operand._type);
|
|
924
986
|
for (let i = 0; i < structInfo.fields.length; i++) {
|
|
925
987
|
this.pushInstruction({ op: "local.get", index: tmpL });
|
|
926
988
|
this.pushInstruction({ op: "local.get", index: tmpR });
|
|
@@ -939,6 +1001,16 @@ export class StmtsEmitter {
|
|
|
939
1001
|
this.releaseTemp(tmpL, baseWType);
|
|
940
1002
|
}
|
|
941
1003
|
|
|
1004
|
+
// Raises Go's nil-dereference panic when the pointer held in `tmp` is nil
|
|
1005
|
+
// (a plain cast or field write would trap, which `recover` cannot see).
|
|
1006
|
+
_emitNilGuard(tmp, goType) {
|
|
1007
|
+
if (goType?.kind !== "pointer") return;
|
|
1008
|
+
if (this.resolveBranchDepthToRole("nil") == null) return;
|
|
1009
|
+
this.pushInstruction({ op: "local.get", index: tmp });
|
|
1010
|
+
this._emitNilBranch();
|
|
1011
|
+
this.pushInstruction("drop");
|
|
1012
|
+
}
|
|
1013
|
+
|
|
942
1014
|
_emitDerefAssign(l, r) {
|
|
943
1015
|
const operand = l.operand ?? l.expr;
|
|
944
1016
|
const structInfo = this._derefStructInfo(operand);
|
|
@@ -975,6 +1047,7 @@ export class StmtsEmitter {
|
|
|
975
1047
|
const valTmp = this.acquireTemp(box.wType);
|
|
976
1048
|
this.emitExpr(operand, ptrWType);
|
|
977
1049
|
this.pushInstruction({ op: "local.set", index: ptrTmp });
|
|
1050
|
+
this._emitNilGuard(ptrTmp, operandGoType);
|
|
978
1051
|
this.emitExpr(r, box.wType);
|
|
979
1052
|
this.pushInstruction({ op: "local.set", index: valTmp });
|
|
980
1053
|
|
|
@@ -996,8 +1069,26 @@ export class StmtsEmitter {
|
|
|
996
1069
|
this.releaseTemp(ptrTmp, ptrWType);
|
|
997
1070
|
}
|
|
998
1071
|
|
|
1072
|
+
// A plain variable target of interface type receiving a concrete result
|
|
1073
|
+
// (`var x any; x, y = two()`) needs the value converted first.
|
|
1074
|
+
_needsTupleConversion(lhsNodes, tupleTypes) {
|
|
1075
|
+
return lhsNodes.some((l, i) => {
|
|
1076
|
+
if (l.kind !== "Ident" || l.name === "_") return false;
|
|
1077
|
+
const dest = this.resolveLocal(l.name) ?? this.mod.resolveGlobal(l.name);
|
|
1078
|
+
const checker = this.mod.checker;
|
|
1079
|
+
return (
|
|
1080
|
+
isInterfaceType(dest?.goType ?? l._type, checker) &&
|
|
1081
|
+
!isInterfaceType(tupleTypes[i], checker)
|
|
1082
|
+
);
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
|
|
999
1086
|
_emitTupleAssign(lhsNodes, rhsNode, op) {
|
|
1000
|
-
if (
|
|
1087
|
+
if (
|
|
1088
|
+
op === "=" &&
|
|
1089
|
+
(lhsNodes.some((l) => !this._isPlainTupleTarget(l)) ||
|
|
1090
|
+
this._needsTupleConversion(lhsNodes, rhsNode._type.types))
|
|
1091
|
+
) {
|
|
1001
1092
|
this._emitTupleViaTemps(
|
|
1002
1093
|
lhsNodes,
|
|
1003
1094
|
rhsNode,
|
|
@@ -1093,16 +1184,79 @@ export class StmtsEmitter {
|
|
|
1093
1184
|
this.releaseTemp(okTmp, "i32");
|
|
1094
1185
|
}
|
|
1095
1186
|
|
|
1096
|
-
//
|
|
1097
|
-
//
|
|
1187
|
+
// Copies the struct or array a map entry holds (a range key or value is a
|
|
1188
|
+
// copy, not the stored value).
|
|
1189
|
+
_emitCopyMapEntry(goType, wType) {
|
|
1190
|
+
const isStruct =
|
|
1191
|
+
isStructType(goType, this.mod.checker, this.mod) &&
|
|
1192
|
+
!isPointerToStruct(goType, this.mod.checker, this.mod);
|
|
1193
|
+
if (isStruct) {
|
|
1194
|
+
const sInfo = this._resolveStructInfo({ _type: goType });
|
|
1195
|
+
if (sInfo) this.emitCloneStruct(sInfo, wType);
|
|
1196
|
+
} else if (
|
|
1197
|
+
goType?.kind !== "pointer" &&
|
|
1198
|
+
isArrayType(goType, this.mod.checker)
|
|
1199
|
+
) {
|
|
1200
|
+
this.emitCopyArray(goType);
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
// Pushes a comma-ok result (map value / asserted value). An array or
|
|
1205
|
+
// struct is a value: the stored one must not alias the map entry or boxed
|
|
1206
|
+
// interface.
|
|
1098
1207
|
_emitGetTempValue(tempIdx, goType) {
|
|
1099
1208
|
this.pushInstruction({ op: "local.get", index: tempIdx });
|
|
1100
1209
|
if (goType?.kind !== "pointer" && isArrayType(goType, this.mod.checker))
|
|
1101
1210
|
this.emitCopyArray(goType);
|
|
1211
|
+
else if (
|
|
1212
|
+
isStructType(goType, this.mod.checker, this.mod) &&
|
|
1213
|
+
!isPointerToStruct(goType, this.mod.checker, this.mod)
|
|
1214
|
+
) {
|
|
1215
|
+
// A struct read out of a map or an interface is a copy.
|
|
1216
|
+
const info =
|
|
1217
|
+
this._resolveStructInfo({ _type: goType }) ??
|
|
1218
|
+
this.mod.getStructType(goType?.name);
|
|
1219
|
+
if (info) this.emitCloneStruct(info, this.toWasmType(goType));
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
_storeTargetWType(lhsNode) {
|
|
1224
|
+
if (lhsNode.kind !== "Ident") return null;
|
|
1225
|
+
const local = this.resolveLocal(lhsNode.name);
|
|
1226
|
+
if (local)
|
|
1227
|
+
return local.isBoxed
|
|
1228
|
+
? local.boxInfo?.typeEntry?.fields?.[0]?.type
|
|
1229
|
+
: local.type;
|
|
1230
|
+
return this.mod.resolveGlobal(lhsNode.name)?.type ?? null;
|
|
1102
1231
|
}
|
|
1103
1232
|
|
|
1104
1233
|
_emitStoreLhsFromTemp(lhsNode, tempIdx, wType, goType, isDefine) {
|
|
1105
|
-
if (lhsNode.kind
|
|
1234
|
+
if (lhsNode.kind === "Ident" && lhsNode.name === "_") return;
|
|
1235
|
+
const redeclared =
|
|
1236
|
+
isDefine && lhsNode._redecl && this.resolveLocal(lhsNode.name);
|
|
1237
|
+
// `var x any; x, ok = m[k]`: a scalar or struct result is boxed on the
|
|
1238
|
+
// way into the interface-typed variable.
|
|
1239
|
+
const target = isDefine ? null : this._storeTargetWType(lhsNode);
|
|
1240
|
+
const boxesIntoAny = target === "anyref" && wType !== "anyref";
|
|
1241
|
+
if (lhsNode.kind !== "Ident" || redeclared || boxesIntoAny) {
|
|
1242
|
+
// An index, field or dereference target (or a name `:=` redeclares):
|
|
1243
|
+
// assign from a named view of the temporary.
|
|
1244
|
+
const name = `__ok_tmp_${this.tempCounter++}`;
|
|
1245
|
+
this.locals.set(name, {
|
|
1246
|
+
index: tempIdx,
|
|
1247
|
+
type: wType,
|
|
1248
|
+
goType,
|
|
1249
|
+
isParam: false,
|
|
1250
|
+
isBoxed: false,
|
|
1251
|
+
});
|
|
1252
|
+
this._emitAssignSingle(
|
|
1253
|
+
lhsNode,
|
|
1254
|
+
{ kind: "Ident", name, _type: goType, _parTmp: true },
|
|
1255
|
+
"=",
|
|
1256
|
+
);
|
|
1257
|
+
this.locals.delete(name);
|
|
1258
|
+
return;
|
|
1259
|
+
}
|
|
1106
1260
|
if (isDefine) {
|
|
1107
1261
|
const idx = this.allocLocal(lhsNode.name, wType, goType);
|
|
1108
1262
|
const local = this.locals.get(lhsNode.name);
|
|
@@ -1192,7 +1346,29 @@ export class StmtsEmitter {
|
|
|
1192
1346
|
);
|
|
1193
1347
|
}
|
|
1194
1348
|
|
|
1349
|
+
// A bare type name in a case list or assertion (`case L:`, `case error:`)
|
|
1350
|
+
// is the declared type, not its underlying one: a defined string type is
|
|
1351
|
+
// boxed apart from `string`, and `error` carries a method set.
|
|
1352
|
+
_resolveTestTarget(t) {
|
|
1353
|
+
if ((t?.kind !== "TypeName" && t?.kind !== "Ident") || !t.name) return t;
|
|
1354
|
+
if (t.name === "nil" || t.name === "any" || t.name === "interface{}")
|
|
1355
|
+
return t;
|
|
1356
|
+
return this.mod.checker?.types?.get(t.name) ?? t;
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
// A defined func type, or a defined map type that has methods, lives in
|
|
1360
|
+
// `any` as a box (so `iter.Seq[int]` is not a plain `func(func(int) bool)`).
|
|
1361
|
+
_isMethodNamedRef(goType) {
|
|
1362
|
+
const t = this._resolveTestTarget(goType);
|
|
1363
|
+
if (t?.kind !== "named") return false;
|
|
1364
|
+
return (
|
|
1365
|
+
isFuncType(t, this.mod.checker) ||
|
|
1366
|
+
(t.methods?.size > 0 && isMapType(t, this.mod.checker))
|
|
1367
|
+
);
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1195
1370
|
_emitTypeTest(targetGoType) {
|
|
1371
|
+
targetGoType = this._resolveTestTarget(targetGoType);
|
|
1196
1372
|
if (!targetGoType) {
|
|
1197
1373
|
this.pushInstruction("drop");
|
|
1198
1374
|
this.pushInstruction({ op: "i32.const", value: 1 });
|
|
@@ -1211,31 +1387,7 @@ export class StmtsEmitter {
|
|
|
1211
1387
|
}
|
|
1212
1388
|
|
|
1213
1389
|
if (isNonEmptyInterface(targetGoType, this.mod.checker)) {
|
|
1214
|
-
|
|
1215
|
-
if (candidates.length === 0) {
|
|
1216
|
-
this.pushInstruction("drop");
|
|
1217
|
-
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
1218
|
-
return;
|
|
1219
|
-
}
|
|
1220
|
-
const anyTmp = this.acquireTemp("anyref");
|
|
1221
|
-
this.pushInstruction({ op: "local.set", index: anyTmp });
|
|
1222
|
-
|
|
1223
|
-
this.pushInstruction({ op: "local.get", index: anyTmp });
|
|
1224
|
-
this.pushInstruction("ref.is_null");
|
|
1225
|
-
this.pushInstruction("i32.eqz");
|
|
1226
|
-
|
|
1227
|
-
for (let i = 0; i < candidates.length; i++) {
|
|
1228
|
-
this.pushInstruction({ op: "local.get", index: anyTmp });
|
|
1229
|
-
this.pushInstruction({
|
|
1230
|
-
op: "ref.test",
|
|
1231
|
-
typeIndex: candidates[i].typeIndex,
|
|
1232
|
-
});
|
|
1233
|
-
if (i > 0) {
|
|
1234
|
-
this.pushInstruction("i32.or");
|
|
1235
|
-
}
|
|
1236
|
-
}
|
|
1237
|
-
this.pushInstruction("i32.and");
|
|
1238
|
-
this.releaseTemp(anyTmp, "anyref");
|
|
1390
|
+
this._emitInterfaceTypeTest(targetGoType);
|
|
1239
1391
|
return;
|
|
1240
1392
|
}
|
|
1241
1393
|
|
|
@@ -1247,6 +1399,12 @@ export class StmtsEmitter {
|
|
|
1247
1399
|
return;
|
|
1248
1400
|
}
|
|
1249
1401
|
|
|
1402
|
+
if (this._isMethodNamedRef(targetGoType)) {
|
|
1403
|
+
const box = this.mod.getIfaceBoxType(targetGoType);
|
|
1404
|
+
this.pushInstruction({ op: "ref.test", typeIndex: box.typeIndex });
|
|
1405
|
+
return;
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1250
1408
|
if (isFuncType(targetGoType, this.mod.checker)) {
|
|
1251
1409
|
const closureInfo = this.mod.getClosureType(targetGoType);
|
|
1252
1410
|
this._emitRefTestOrNil(targetGoType, closureInfo.typeIndex);
|
|
@@ -1265,7 +1423,9 @@ export class StmtsEmitter {
|
|
|
1265
1423
|
|
|
1266
1424
|
if (isSliceType(targetGoType, this.mod.checker)) {
|
|
1267
1425
|
const elem = this._getSliceElemType(targetGoType);
|
|
1268
|
-
const sInfo = this.mod.
|
|
1426
|
+
const sInfo = isTaggedSliceType(targetGoType, this.mod.checker)
|
|
1427
|
+
? this.mod.getTaggedSliceType(targetGoType, elem)
|
|
1428
|
+
: this.mod.getSliceType(elem);
|
|
1269
1429
|
if (sInfo) {
|
|
1270
1430
|
this.pushInstruction({ op: "ref.test", typeIndex: sInfo.typeIndex });
|
|
1271
1431
|
return;
|
|
@@ -1276,7 +1436,7 @@ export class StmtsEmitter {
|
|
|
1276
1436
|
const elem = this._getArrayElemType(targetGoType);
|
|
1277
1437
|
const aInfo = this.mod.getArrayType(elem);
|
|
1278
1438
|
if (aInfo) {
|
|
1279
|
-
this.
|
|
1439
|
+
this._emitArrayTypeTest(targetGoType, aInfo);
|
|
1280
1440
|
return;
|
|
1281
1441
|
}
|
|
1282
1442
|
}
|
|
@@ -1303,6 +1463,71 @@ export class StmtsEmitter {
|
|
|
1303
1463
|
this.pushInstruction({ op: "i32.const", value: 1 });
|
|
1304
1464
|
}
|
|
1305
1465
|
|
|
1466
|
+
// `[3]int` and `[2]int` share one wasm array type, so the test also compares
|
|
1467
|
+
// the length.
|
|
1468
|
+
_emitArrayTypeTest(goType, aInfo) {
|
|
1469
|
+
let t = goType?._assertType ?? goType;
|
|
1470
|
+
for (let i = 0; i < 16 && t && t.kind !== "array"; i++)
|
|
1471
|
+
t = t.underlying ?? this.mod.checker?.types?.get(t.name);
|
|
1472
|
+
const size = typeof t?.size === "number" ? t.size : null;
|
|
1473
|
+
if (size === null) {
|
|
1474
|
+
this.pushInstruction({ op: "ref.test", typeIndex: aInfo.typeIndex });
|
|
1475
|
+
return;
|
|
1476
|
+
}
|
|
1477
|
+
const tmp = this.acquireTemp("anyref");
|
|
1478
|
+
this.pushInstruction({ op: "local.tee", index: tmp });
|
|
1479
|
+
this.pushInstruction({ op: "ref.test", typeIndex: aInfo.typeIndex });
|
|
1480
|
+
this.pushInstruction({ op: "if", blockType: "i32" });
|
|
1481
|
+
this.pushInstruction({ op: "local.get", index: tmp });
|
|
1482
|
+
this.pushInstruction({ op: "ref.cast", typeIndex: aInfo.typeIndex });
|
|
1483
|
+
this.pushInstruction("array.len");
|
|
1484
|
+
this.pushInstruction({ op: "i32.const", value: size });
|
|
1485
|
+
this.pushInstruction("i32.eq");
|
|
1486
|
+
this.pushInstruction("else");
|
|
1487
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
1488
|
+
this.pushInstruction("end");
|
|
1489
|
+
this.releaseTemp(tmp, "anyref");
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
// Whether the interface value on the stack holds a type implementing the
|
|
1493
|
+
// non-empty interface: a `ref.test` over every implementing type (a typed
|
|
1494
|
+
// nil `*S` marker included).
|
|
1495
|
+
_emitInterfaceTypeTest(targetGoType) {
|
|
1496
|
+
const candidates = this.mod.getTypesImplementingInterface(targetGoType);
|
|
1497
|
+
if (candidates.length === 0) {
|
|
1498
|
+
this.pushInstruction("drop");
|
|
1499
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
1500
|
+
return;
|
|
1501
|
+
}
|
|
1502
|
+
const anyTmp = this.acquireTemp("anyref");
|
|
1503
|
+
this.pushInstruction({ op: "local.set", index: anyTmp });
|
|
1504
|
+
|
|
1505
|
+
this.pushInstruction({ op: "local.get", index: anyTmp });
|
|
1506
|
+
this.pushInstruction("ref.is_null");
|
|
1507
|
+
this.pushInstruction("i32.eqz");
|
|
1508
|
+
|
|
1509
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
1510
|
+
this.pushInstruction({ op: "local.get", index: anyTmp });
|
|
1511
|
+
this.pushInstruction({
|
|
1512
|
+
op: "ref.test",
|
|
1513
|
+
typeIndex: candidates[i].typeIndex,
|
|
1514
|
+
});
|
|
1515
|
+
if (candidates[i].nilTypeIndex !== undefined) {
|
|
1516
|
+
this.pushInstruction({ op: "local.get", index: anyTmp });
|
|
1517
|
+
this.pushInstruction({
|
|
1518
|
+
op: "ref.test",
|
|
1519
|
+
typeIndex: candidates[i].nilTypeIndex,
|
|
1520
|
+
});
|
|
1521
|
+
this.pushInstruction("i32.or");
|
|
1522
|
+
}
|
|
1523
|
+
if (i > 0) {
|
|
1524
|
+
this.pushInstruction("i32.or");
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
this.pushInstruction("i32.and");
|
|
1528
|
+
this.releaseTemp(anyTmp, "anyref");
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1306
1531
|
// `ref.test` for a map, func or `*T`, which also accepts the typed-nil
|
|
1307
1532
|
// marker of the same Go type (see getNilBoxType).
|
|
1308
1533
|
_emitRefTestOrNil(goType, typeIndex) {
|
|
@@ -1337,10 +1562,19 @@ export class StmtsEmitter {
|
|
|
1337
1562
|
this.releaseTemp(tmp, "anyref");
|
|
1338
1563
|
}
|
|
1339
1564
|
|
|
1565
|
+
// An interface target keeps the value as is; a boxed defined func or map
|
|
1566
|
+
// type is unboxed. True when the cast is done.
|
|
1567
|
+
_emitCastNoRepack(targetGoType) {
|
|
1568
|
+
if (isInterfaceType(targetGoType, this.mod.checker)) return true;
|
|
1569
|
+
if (!this._isMethodNamedRef(targetGoType)) return false;
|
|
1570
|
+
this._emitUnbox(
|
|
1571
|
+
this.mod.getIfaceBoxType(this._resolveTestTarget(targetGoType)),
|
|
1572
|
+
);
|
|
1573
|
+
return true;
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1340
1576
|
_emitTypeCast(targetGoType, targetWType) {
|
|
1341
|
-
if (
|
|
1342
|
-
return;
|
|
1343
|
-
}
|
|
1577
|
+
if (this._emitCastNoRepack(targetGoType)) return;
|
|
1344
1578
|
|
|
1345
1579
|
if (isBareStringType(targetGoType, this.mod.checker)) {
|
|
1346
1580
|
this.pushInstruction("extern.convert_any");
|
|
@@ -1632,6 +1866,7 @@ export class StmtsEmitter {
|
|
|
1632
1866
|
const ptrTmp = this.acquireTemp(ptrWType);
|
|
1633
1867
|
this.emitExpr(operand, ptrWType);
|
|
1634
1868
|
this.pushInstruction({ op: "local.set", index: ptrTmp });
|
|
1869
|
+
this._emitNilGuard(ptrTmp, operandGoType);
|
|
1635
1870
|
|
|
1636
1871
|
this.pushInstruction({ op: "local.get", index: ptrTmp });
|
|
1637
1872
|
this.pushInstruction({
|
|
@@ -1770,6 +2005,7 @@ export class StmtsEmitter {
|
|
|
1770
2005
|
const ptrTmp = this.acquireTemp(ptrWType);
|
|
1771
2006
|
this.emitExpr(operand, ptrWType);
|
|
1772
2007
|
this.pushInstruction({ op: "local.set", index: ptrTmp });
|
|
2008
|
+
this._emitNilGuard(ptrTmp, operandGoType);
|
|
1773
2009
|
|
|
1774
2010
|
this.pushInstruction({ op: "local.get", index: ptrTmp });
|
|
1775
2011
|
this.pushInstruction({
|
|
@@ -2417,6 +2653,7 @@ export class StmtsEmitter {
|
|
|
2417
2653
|
typeIndex: mapInfo.entryTypeIndex,
|
|
2418
2654
|
fieldIndex: 0,
|
|
2419
2655
|
});
|
|
2656
|
+
this._emitCopyMapEntry(keyType, mapInfo.keyWType);
|
|
2420
2657
|
if (this.resolveLocal(lhs[0].name)) {
|
|
2421
2658
|
this.pushInstruction({
|
|
2422
2659
|
op: "local.set",
|
|
@@ -2438,20 +2675,7 @@ export class StmtsEmitter {
|
|
|
2438
2675
|
typeIndex: mapInfo.entryTypeIndex,
|
|
2439
2676
|
fieldIndex: 1,
|
|
2440
2677
|
});
|
|
2441
|
-
|
|
2442
|
-
isStructType(valType, this.mod.checker, this.mod) &&
|
|
2443
|
-
!isPointerToStruct(valType, this.mod.checker, this.mod);
|
|
2444
|
-
if (isValStruct) {
|
|
2445
|
-
const sInfo = this._resolveStructInfo({ _type: valType });
|
|
2446
|
-
if (sInfo) {
|
|
2447
|
-
this.emitCloneStruct(sInfo, mapInfo.valWType);
|
|
2448
|
-
}
|
|
2449
|
-
} else if (
|
|
2450
|
-
valType?.kind !== "pointer" &&
|
|
2451
|
-
isArrayType(valType, this.mod.checker)
|
|
2452
|
-
) {
|
|
2453
|
-
this.emitCopyArray(valType);
|
|
2454
|
-
}
|
|
2678
|
+
this._emitCopyMapEntry(valType, mapInfo.valWType);
|
|
2455
2679
|
if (this.resolveLocal(lhs[1].name)) {
|
|
2456
2680
|
this.pushInstruction({
|
|
2457
2681
|
op: "local.set",
|
|
@@ -2551,6 +2775,15 @@ export class StmtsEmitter {
|
|
|
2551
2775
|
});
|
|
2552
2776
|
}
|
|
2553
2777
|
this.releaseTemp(sliceTmp, sliceWType);
|
|
2778
|
+
} else if (!hasVal && !hasCallOrReceive(iterExpr)) {
|
|
2779
|
+
// Ranging over an array for its indexes only does not evaluate it
|
|
2780
|
+
// (`range p` on a nil array pointer is valid): the length is the
|
|
2781
|
+
// type's.
|
|
2782
|
+
this.pushInstruction({
|
|
2783
|
+
op: "i32.const",
|
|
2784
|
+
value: this._getArraySize(isPtrToArr ? iterType.base : iterType),
|
|
2785
|
+
});
|
|
2786
|
+
this.pushInstruction({ op: "local.set", index: lenTmp });
|
|
2554
2787
|
} else {
|
|
2555
2788
|
this.emitExpr(iterExpr, arrWType);
|
|
2556
2789
|
// Go ranges over a copy of an array (the range expression is
|
|
@@ -3134,16 +3367,7 @@ export class StmtsEmitter {
|
|
|
3134
3367
|
defaultIdx = i;
|
|
3135
3368
|
continue;
|
|
3136
3369
|
}
|
|
3137
|
-
|
|
3138
|
-
if (stmt.tag) {
|
|
3139
|
-
this.pushInstruction({ op: "local.get", index: tagTmp });
|
|
3140
|
-
this.emitExpr(vals[j], tagWType);
|
|
3141
|
-
this.emitBinaryOp("==", tagWType, tagGoType);
|
|
3142
|
-
} else {
|
|
3143
|
-
this.emitExpr(vals[j], "i32");
|
|
3144
|
-
}
|
|
3145
|
-
if (j > 0) this.pushInstruction("i32.or");
|
|
3146
|
-
}
|
|
3370
|
+
this._emitCaseListMatch(stmt, vals, tagTmp, tagWType, tagGoType);
|
|
3147
3371
|
// The innermost block ends the first clause, so clause i is depth i.
|
|
3148
3372
|
this.pushInstruction({ op: "br_if", depth: i });
|
|
3149
3373
|
}
|
|
@@ -3162,6 +3386,26 @@ export class StmtsEmitter {
|
|
|
3162
3386
|
this.pushInstruction("end");
|
|
3163
3387
|
}
|
|
3164
3388
|
|
|
3389
|
+
// Leaves an i32 on the stack: whether any expression of a case list matches.
|
|
3390
|
+
// Later expressions are not evaluated once one matches, as in Go.
|
|
3391
|
+
_emitCaseListMatch(stmt, vals, tagTmp, tagWType, tagGoType) {
|
|
3392
|
+
for (let j = 0; j < vals.length; j++) {
|
|
3393
|
+
if (stmt.tag) {
|
|
3394
|
+
this.pushInstruction({ op: "local.get", index: tagTmp });
|
|
3395
|
+
this.emitExpr(vals[j], tagWType);
|
|
3396
|
+
this._emitElemEq(tagGoType, tagWType);
|
|
3397
|
+
} else {
|
|
3398
|
+
this.emitExpr(vals[j], "i32");
|
|
3399
|
+
}
|
|
3400
|
+
if (j < vals.length - 1) {
|
|
3401
|
+
this.pushInstruction({ op: "if", blockType: "i32" });
|
|
3402
|
+
this.pushInstruction({ op: "i32.const", value: 1 });
|
|
3403
|
+
this.pushInstruction("else");
|
|
3404
|
+
}
|
|
3405
|
+
}
|
|
3406
|
+
for (let j = 0; j < vals.length - 1; j++) this.pushInstruction("end");
|
|
3407
|
+
}
|
|
3408
|
+
|
|
3165
3409
|
emitSwitchStmt(stmt) {
|
|
3166
3410
|
if (
|
|
3167
3411
|
(stmt.cases ?? []).some((c) => {
|
|
@@ -3202,16 +3446,7 @@ export class StmtsEmitter {
|
|
|
3202
3446
|
let ifCount = 0;
|
|
3203
3447
|
for (const c of nonDefaultCases) {
|
|
3204
3448
|
const vals = getCaseVals(c);
|
|
3205
|
-
|
|
3206
|
-
if (stmt.tag) {
|
|
3207
|
-
this.pushInstruction({ op: "local.get", index: tagTmp });
|
|
3208
|
-
this.emitExpr(vals[i], tagWType);
|
|
3209
|
-
this.emitBinaryOp("==", tagWType, tagGoType);
|
|
3210
|
-
} else {
|
|
3211
|
-
this.emitExpr(vals[i], "i32");
|
|
3212
|
-
}
|
|
3213
|
-
if (i > 0) this.pushInstruction("i32.or");
|
|
3214
|
-
}
|
|
3449
|
+
this._emitCaseListMatch(stmt, vals, tagTmp, tagWType, tagGoType);
|
|
3215
3450
|
this.pushInstruction({ op: "if", blockType: "void" });
|
|
3216
3451
|
ifCount++;
|
|
3217
3452
|
|
|
@@ -3233,6 +3468,17 @@ export class StmtsEmitter {
|
|
|
3233
3468
|
this.pushInstruction("end");
|
|
3234
3469
|
}
|
|
3235
3470
|
|
|
3471
|
+
// Wraps the value on the stack in a box when the just-declared local `name`
|
|
3472
|
+
// is captured and mutated.
|
|
3473
|
+
_emitBoxIfCaptured(name) {
|
|
3474
|
+
const info = this.locals.get(name);
|
|
3475
|
+
if (info?.isBoxed)
|
|
3476
|
+
this.pushInstruction({
|
|
3477
|
+
op: "struct.new",
|
|
3478
|
+
typeIndex: info.boxInfo.typeIndex,
|
|
3479
|
+
});
|
|
3480
|
+
}
|
|
3481
|
+
|
|
3236
3482
|
emitTypeSwitchStmt(stmt) {
|
|
3237
3483
|
this.pushInstruction({ op: "block", blockType: "void" });
|
|
3238
3484
|
this.pushControl("break", stmt.label);
|
|
@@ -3273,7 +3519,7 @@ export class StmtsEmitter {
|
|
|
3273
3519
|
const targetGoType = isNilCase
|
|
3274
3520
|
? (stmt.expr._type ?? { kind: "basic", name: "any" })
|
|
3275
3521
|
: types.length === 1
|
|
3276
|
-
? (types[0]._type ?? types[0])
|
|
3522
|
+
? (types[0]._type ?? this._resolveTestTarget(types[0]))
|
|
3277
3523
|
: { kind: "basic", name: "any" };
|
|
3278
3524
|
const targetWType = this.toWasmType(targetGoType);
|
|
3279
3525
|
const assignLocal = this.allocLocal(
|
|
@@ -3293,6 +3539,7 @@ export class StmtsEmitter {
|
|
|
3293
3539
|
} else {
|
|
3294
3540
|
this.pushInstruction({ op: "local.get", index: tswTmp });
|
|
3295
3541
|
}
|
|
3542
|
+
this._emitBoxIfCaptured(stmt.assign);
|
|
3296
3543
|
this.pushInstruction({ op: "local.set", index: assignLocal });
|
|
3297
3544
|
}
|
|
3298
3545
|
|
|
@@ -3308,6 +3555,7 @@ export class StmtsEmitter {
|
|
|
3308
3555
|
name: "any",
|
|
3309
3556
|
});
|
|
3310
3557
|
this.pushInstruction({ op: "local.get", index: tswTmp });
|
|
3558
|
+
this._emitBoxIfCaptured(stmt.assign);
|
|
3311
3559
|
this.pushInstruction({ op: "local.set", index: assignLocal });
|
|
3312
3560
|
}
|
|
3313
3561
|
this._emitClauseBody(defaultCase.stmts);
|
|
@@ -3510,6 +3758,8 @@ export class StmtsEmitter {
|
|
|
3510
3758
|
}
|
|
3511
3759
|
|
|
3512
3760
|
emitBranchStmt(stmt) {
|
|
3761
|
+
const alias = stmt.label && this._labelAliases?.get(stmt.label);
|
|
3762
|
+
if (alias) stmt = { ...stmt, label: alias };
|
|
3513
3763
|
if (this._tryEmitYieldBranch(stmt)) return;
|
|
3514
3764
|
const isContinue = stmt.keyword === "continue";
|
|
3515
3765
|
const depth = this.resolveBranchDepth(stmt.label, isContinue);
|