gofront 1.7.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -14
- package/package.json +10 -38
- package/src/backend/js/expressions.js +2060 -389
- package/src/backend/js/index.js +586 -174
- package/src/backend/js/natives.js +383 -0
- package/src/backend/js/runtime.js +936 -1644
- package/src/backend/js/statements.js +1349 -132
- package/src/backend/js/stdlib/index.js +51 -94
- package/src/backend/js/templ.js +67 -118
- package/src/backend/js/type-info.js +436 -0
- package/src/backend/shared/fixed-float.js +20 -0
- package/src/backend/shared/fmt-types.js +80 -0
- package/src/backend/shared/host-unicode.js +36 -0
- package/src/backend/wasm/boundary.js +753 -872
- package/src/backend/wasm/emit-builtins.js +914 -237
- package/src/backend/wasm/emit-exprs.js +1456 -195
- package/src/backend/wasm/emit-maps.js +78 -380
- package/src/backend/wasm/emit-stmts.js +1659 -214
- package/src/backend/wasm/emit.js +614 -88
- package/src/backend/wasm/encode.js +43 -13
- package/src/backend/wasm/globals.js +491 -0
- package/src/backend/wasm/glue.js +2 -4
- package/src/backend/wasm/index.js +406 -3195
- package/src/backend/wasm/method-expr.js +392 -0
- package/src/backend/wasm/module-emitter.js +1615 -0
- package/src/backend/wasm/monomorph.js +164 -9
- package/src/backend/wasm/natives.js +596 -0
- package/src/backend/wasm/prescan.js +249 -0
- package/src/backend/wasm/prune-funcs.js +89 -0
- package/src/backend/wasm/stdlib/index.js +2 -37
- package/src/backend/wasm/stdlib/simd.js +293 -28
- package/src/backend/wasm/types.js +260 -27
- package/src/backend/wasm/variadic.js +93 -0
- package/src/backend/wasm/wat.js +47 -20
- package/src/{cli-core.js → cli/cli-core.js} +241 -11
- package/src/{test-runner.js → cli/test-runner.js} +188 -122
- package/src/{vendor.js → cli/vendor.js} +3 -4
- package/src/compiler.js +202 -386
- package/src/index.js +15 -169
- package/src/{lexer.js → lexer/lexer.js} +203 -62
- package/src/{templ-lexer.js → lexer/templ-lexer.js} +61 -18
- package/src/{tokens.js → lexer/tokens.js} +2 -0
- package/src/link/sourcemap.js +97 -0
- package/src/link/stdlib.js +737 -0
- package/src/link/type-names.js +148 -0
- package/src/link/wasm.js +318 -0
- package/src/lower/bounds.js +547 -0
- package/src/lower/boxing.js +1 -1
- package/src/lower/captures.js +267 -64
- package/src/lower/embedding.js +32 -3
- package/src/lower/errors-as.js +196 -0
- package/src/lower/fmt-formatters.js +905 -0
- package/src/lower/functions.js +2 -0
- package/src/lower/gen-types.js +274 -0
- package/src/lower/index.js +33 -19
- package/src/lower/json-codecs.js +1379 -0
- package/src/lower/ownership.js +47 -8
- package/src/lower/range.js +86 -1
- package/src/lower/reachability.js +150 -0
- package/src/lower/walk.js +12 -0
- package/src/parser/declarations.js +83 -15
- package/src/parser/expressions.js +110 -18
- package/src/parser/index.js +2 -1
- package/src/parser/statements.js +38 -22
- package/src/{templ-parser.js → parser/templ.js} +179 -17
- package/src/parser/types.js +70 -9
- package/src/resolver.js +21 -1
- package/src/typechecker/assignability.js +291 -19
- package/src/typechecker/const-fold.js +399 -0
- package/src/typechecker/constants.js +282 -0
- package/src/typechecker/expressions.js +1220 -183
- package/src/typechecker/index.js +659 -92
- package/src/typechecker/init-order.js +426 -0
- package/src/typechecker/operand-text.js +40 -0
- package/src/typechecker/resolve.js +613 -62
- package/src/typechecker/statements.js +212 -20
- package/src/typechecker/stdlib/excluded.js +902 -0
- package/src/typechecker/stdlib/index.js +38 -0
- package/src/typechecker/stdlib/simd.js +65 -15
- package/src/typechecker/termination.js +11 -2
- package/src/typechecker/types.js +268 -514
- package/stdlib/bytes/buffer.go +479 -0
- package/stdlib/bytes/bytes.go +1010 -0
- package/stdlib/bytes/reader.go +161 -0
- package/stdlib/cmp/cmp.go +65 -0
- package/stdlib/context/context.go +115 -0
- package/stdlib/crypto/rand/rand.go +34 -0
- package/stdlib/crypto/sha256/sha256.go +249 -0
- package/stdlib/encoding/base64/base64.go +331 -0
- package/stdlib/encoding/binary/binary.go +332 -0
- package/stdlib/encoding/hex/hex.go +140 -0
- package/stdlib/encoding/json/errors.go +31 -0
- package/stdlib/encoding/json/indent.go +71 -0
- package/stdlib/encoding/json/json.go +62 -0
- package/stdlib/encoding/json/number.go +12 -0
- package/stdlib/encoding/json/stream.go +173 -0
- package/stdlib/errors/errors.go +174 -0
- package/stdlib/fmt/errors.go +110 -0
- package/stdlib/fmt/format.go +732 -0
- package/stdlib/fmt/print.go +1232 -0
- package/stdlib/gom/gom.go +293 -0
- package/stdlib/hash/hash.go +53 -0
- package/stdlib/html/html.go +251 -0
- package/stdlib/internal/fmtrt/fmtrt.go +163 -0
- package/stdlib/internal/jsonrt/dec.go +871 -0
- package/stdlib/internal/jsonrt/enc.go +516 -0
- package/stdlib/internal/jsonrt/indent.go +135 -0
- package/stdlib/internal/jsonrt/number.go +27 -0
- package/stdlib/internal/jsonrt/reader.go +73 -0
- package/stdlib/internal/jsonrt/scanner.go +658 -0
- package/stdlib/io/io.go +502 -0
- package/stdlib/iter/iter.go +18 -0
- package/stdlib/maps/maps.go +131 -0
- package/stdlib/math/bits/bits.go +507 -0
- package/stdlib/math/extra.go +272 -0
- package/stdlib/math/math.go +439 -0
- package/stdlib/math/rand/rand.go +297 -0
- package/stdlib/os/os.go +93 -0
- package/stdlib/path/filepath/filepath.go +47 -0
- package/stdlib/path/path.go +394 -0
- package/stdlib/regexp/regexp.go +231 -0
- package/stdlib/slices/slices.go +526 -0
- package/stdlib/slices/sort.go +209 -0
- package/stdlib/slices/zsort_cmp.go +480 -0
- package/stdlib/slices/zsort_ordered.go +482 -0
- package/stdlib/sort/sort.go +500 -0
- package/stdlib/sort/zsort_func.go +480 -0
- package/stdlib/sort/zsort_iface.go +480 -0
- package/stdlib/strconv/float.go +1699 -0
- package/stdlib/strconv/isprint.go +752 -0
- package/stdlib/strconv/quote.go +526 -0
- package/stdlib/strconv/strconv.go +616 -0
- package/stdlib/strings/builder.go +95 -0
- package/stdlib/strings/reader.go +163 -0
- package/stdlib/strings/replacer.go +102 -0
- package/stdlib/strings/strings.go +807 -0
- package/stdlib/sync/sync.go +268 -0
- package/stdlib/testing/async_js.go +61 -0
- package/stdlib/testing/benchmark.go +297 -0
- package/stdlib/testing/testing.go +523 -0
- package/stdlib/time/duration.go +233 -0
- package/stdlib/time/format.go +745 -0
- package/stdlib/time/parse.go +790 -0
- package/stdlib/time/sleep_js.go +63 -0
- package/stdlib/time/time.go +834 -0
- package/stdlib/unicode/unicode.go +250 -0
- package/stdlib/unicode/utf8/utf8.go +464 -0
- package/src/backend/js/stdlib/base64.js +0 -12
- package/src/backend/js/stdlib/binary.js +0 -102
- package/src/backend/js/stdlib/bits.js +0 -46
- package/src/backend/js/stdlib/builder.js +0 -63
- package/src/backend/js/stdlib/bytes.js +0 -122
- package/src/backend/js/stdlib/errors.js +0 -44
- package/src/backend/js/stdlib/fmt.js +0 -178
- package/src/backend/js/stdlib/gom.js +0 -155
- package/src/backend/js/stdlib/hex.js +0 -17
- package/src/backend/js/stdlib/html.js +0 -20
- package/src/backend/js/stdlib/io.js +0 -137
- package/src/backend/js/stdlib/json.js +0 -972
- package/src/backend/js/stdlib/maps.js +0 -45
- package/src/backend/js/stdlib/math.js +0 -69
- package/src/backend/js/stdlib/os.js +0 -13
- package/src/backend/js/stdlib/path.js +0 -48
- package/src/backend/js/stdlib/rand.js +0 -32
- package/src/backend/js/stdlib/regexp.js +0 -54
- package/src/backend/js/stdlib/sha256.js +0 -12
- package/src/backend/js/stdlib/shared.js +0 -17
- package/src/backend/js/stdlib/simd.js +0 -24
- package/src/backend/js/stdlib/slices.js +0 -77
- package/src/backend/js/stdlib/sort.js +0 -30
- package/src/backend/js/stdlib/strconv.js +0 -74
- package/src/backend/js/stdlib/strings.js +0 -222
- package/src/backend/js/stdlib/sync.js +0 -13
- package/src/backend/js/stdlib/testing.js +0 -34
- package/src/backend/js/stdlib/time.js +0 -128
- package/src/backend/js/stdlib/unicode.js +0 -25
- package/src/backend/js/stdlib/utf8.js +0 -26
- package/src/backend/wasm/const-fold.js +0 -205
- package/src/backend/wasm/emit-stdlib.js +0 -13
- package/src/backend/wasm/stdlib/bytes.js +0 -5039
- package/src/backend/wasm/stdlib/core.js +0 -8903
- package/src/backend/wasm/stdlib/io.js +0 -2161
- package/src/backend/wasm/stdlib/packages.js +0 -2400
- package/src/backend/wasm/stdlib/pure.js +0 -720
- package/src/backend/wasm/stdlib/time.js +0 -1200
- package/src/typechecker/stdlib/core.js +0 -801
- package/src/typechecker/stdlib/extended.js +0 -914
- package/src/typechecker/stdlib.js +0 -22
- /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
- /package/src/{colors.js → cli/colors.js} +0 -0
- /package/src/{dev-server.js → cli/dev-server.js} +0 -0
- /package/src/{minifier.js → cli/minifier.js} +0 -0
- /package/src/{project-config.js → cli/project-config.js} +0 -0
- /package/src/{pwa.js → cli/pwa.js} +0 -0
|
@@ -1,21 +1,55 @@
|
|
|
1
1
|
// src/backend/wasm/emit-exprs.js
|
|
2
2
|
// FunctionEmitter mixin: expression emission (literals, operators, indexing, conversions).
|
|
3
3
|
|
|
4
|
-
import { foldConstExpr } from "./const-fold.js";
|
|
5
4
|
import {
|
|
5
|
+
constFromLit,
|
|
6
|
+
constIntValue,
|
|
7
|
+
constToLit,
|
|
8
|
+
foldConstExpr,
|
|
9
|
+
} from "../../typechecker/const-fold.js";
|
|
10
|
+
import {
|
|
11
|
+
compositeLitIndices,
|
|
12
|
+
compositeLitMaxIndex,
|
|
13
|
+
} from "../../typechecker/types.js";
|
|
14
|
+
import { goTypeName } from "../shared/fmt-types.js";
|
|
15
|
+
import { emitWasmNative } from "./natives.js";
|
|
16
|
+
import {
|
|
17
|
+
getInterfaceMethodList,
|
|
6
18
|
getMapKeyValTypes,
|
|
7
19
|
isAnyType,
|
|
8
20
|
isArrayType,
|
|
21
|
+
isComplex,
|
|
22
|
+
isComplex64,
|
|
9
23
|
isFuncType,
|
|
10
24
|
isMapType,
|
|
25
|
+
isNonEmptyInterface,
|
|
11
26
|
isPointerToStruct,
|
|
27
|
+
isRuneSlice,
|
|
28
|
+
isScalarType,
|
|
12
29
|
isSigned,
|
|
13
30
|
isSliceType,
|
|
14
31
|
isStringType,
|
|
15
32
|
isStructType,
|
|
33
|
+
isTaggedSliceType,
|
|
16
34
|
toWasmType,
|
|
17
35
|
} from "./types.js";
|
|
18
36
|
|
|
37
|
+
// Engine limit on `array.new_fixed` operands.
|
|
38
|
+
const MAX_NEW_FIXED = 10000;
|
|
39
|
+
|
|
40
|
+
// An untyped integral float constant (`1e18 + 1`) in an integer context is
|
|
41
|
+
// emitted from its exact integer value.
|
|
42
|
+
function constLitFor(val, targetWasmType) {
|
|
43
|
+
const i = constIntValue(val);
|
|
44
|
+
if (
|
|
45
|
+
val.k === "FLOAT" &&
|
|
46
|
+
i !== null &&
|
|
47
|
+
(targetWasmType === "i64" || targetWasmType === "i32")
|
|
48
|
+
)
|
|
49
|
+
return constToLit({ k: "INT", v: i });
|
|
50
|
+
return constToLit(val);
|
|
51
|
+
}
|
|
52
|
+
|
|
19
53
|
export class ExprsEmitter {
|
|
20
54
|
emitExpr(expr, targetWasmType = null) {
|
|
21
55
|
if (!expr) return;
|
|
@@ -36,7 +70,7 @@ export class ExprsEmitter {
|
|
|
36
70
|
}
|
|
37
71
|
|
|
38
72
|
if (
|
|
39
|
-
isStringType(goType, this.mod.checker) ||
|
|
73
|
+
(isStringType(goType, this.mod.checker) && goType?.kind !== "named") ||
|
|
40
74
|
(expr.kind === "BasicLit" && expr.litKind === "STRING")
|
|
41
75
|
) {
|
|
42
76
|
this._emitRawExpr(expr, "externref");
|
|
@@ -50,6 +84,7 @@ export class ExprsEmitter {
|
|
|
50
84
|
// for type switches and assertions.
|
|
51
85
|
if (isPointerToStruct(goType, this.mod.checker, this.mod)) {
|
|
52
86
|
this._emitRawExpr(expr, this.toWasmType(goType));
|
|
87
|
+
this._emitNilAwareAny(goType, expr);
|
|
53
88
|
return;
|
|
54
89
|
}
|
|
55
90
|
if (isStructType(goType, this.mod.checker, this.mod)) {
|
|
@@ -70,6 +105,15 @@ export class ExprsEmitter {
|
|
|
70
105
|
return;
|
|
71
106
|
}
|
|
72
107
|
|
|
108
|
+
// A defined func or map type with methods is boxed to keep its identity,
|
|
109
|
+
// so interface dispatch can tell it from the plain func or map type.
|
|
110
|
+
if (this._isMethodNamedRef(goType)) {
|
|
111
|
+
const box = this.mod.getIfaceBoxType(this._resolveTestTarget(goType));
|
|
112
|
+
this._emitRawExpr(expr, box.wType);
|
|
113
|
+
this._emitBoxValue(box);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
73
117
|
if (
|
|
74
118
|
isSliceType(goType, this.mod.checker) ||
|
|
75
119
|
isArrayType(goType, this.mod.checker) ||
|
|
@@ -78,6 +122,16 @@ export class ExprsEmitter {
|
|
|
78
122
|
) {
|
|
79
123
|
const refWType = this.toWasmType(goType);
|
|
80
124
|
this._emitRawExpr(expr, refWType);
|
|
125
|
+
// An array stored in an interface is a copy.
|
|
126
|
+
if (isArrayType(goType, this.mod.checker))
|
|
127
|
+
this._emitCopyIfValue(expr, goType, refWType);
|
|
128
|
+
if (isTaggedSliceType(goType, this.mod.checker))
|
|
129
|
+
this._emitTagSlice(goType, refWType);
|
|
130
|
+
if (
|
|
131
|
+
isFuncType(goType, this.mod.checker) ||
|
|
132
|
+
isMapType(goType, this.mod.checker)
|
|
133
|
+
)
|
|
134
|
+
this._emitNilAwareAny(goType, expr);
|
|
81
135
|
return;
|
|
82
136
|
}
|
|
83
137
|
|
|
@@ -92,6 +146,102 @@ export class ExprsEmitter {
|
|
|
92
146
|
this._emitRawExpr(expr, targetWasmType);
|
|
93
147
|
}
|
|
94
148
|
|
|
149
|
+
// Panics with Go's failed type assertion message: the static interface
|
|
150
|
+
// type, the dynamic type of the value in `anyTmp` and the target type.
|
|
151
|
+
_emitTypeAssertPanic(expr, anyTmp) {
|
|
152
|
+
const checker = this.mod.checker;
|
|
153
|
+
const target = this._resolveTestTarget(expr._type ?? expr.type);
|
|
154
|
+
const iface = isNonEmptyInterface(target, checker);
|
|
155
|
+
this.pushInstruction({ op: "local.get", index: anyTmp });
|
|
156
|
+
if (iface) {
|
|
157
|
+
const names = getInterfaceMethodList(target.underlying ?? target)
|
|
158
|
+
.map((m) => m.name)
|
|
159
|
+
.sort();
|
|
160
|
+
this._pushStringConst(names[0] ?? "");
|
|
161
|
+
} else {
|
|
162
|
+
this._pushStringConst(this._assertSrcName(expr.expr?._type));
|
|
163
|
+
}
|
|
164
|
+
this._pushStringConst(
|
|
165
|
+
target.name === "error" ? "error" : goTypeName(target),
|
|
166
|
+
);
|
|
167
|
+
this.pushInstruction({ op: "i32.const", value: iface ? 1 : 0 });
|
|
168
|
+
this.pushInstruction({
|
|
169
|
+
op: "call",
|
|
170
|
+
funcIndex: this.mod.resolveFuncIndex("__ta_fail"),
|
|
171
|
+
});
|
|
172
|
+
this.pushInstruction("unreachable");
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// The static interface type of an assertion operand as Go names it.
|
|
176
|
+
_assertSrcName(srcType) {
|
|
177
|
+
if (!srcType || !isNonEmptyInterface(srcType, this.mod.checker))
|
|
178
|
+
return "interface {}";
|
|
179
|
+
return srcType.name === "error" ? "error" : goTypeName(srcType);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Pushes the interned string `text` as an externref.
|
|
183
|
+
_pushStringConst(text) {
|
|
184
|
+
this.pushInstruction({
|
|
185
|
+
op: "i32.const",
|
|
186
|
+
value: this.mod.internString(text),
|
|
187
|
+
});
|
|
188
|
+
this.pushInstruction({
|
|
189
|
+
op: "call",
|
|
190
|
+
funcIndex: this.mod.getStringImportIndex(),
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// The slice on the stack becomes the tagged subtype of its Go type, so a
|
|
195
|
+
// type test can tell `[]byte` from `[]bool` (see getTaggedSliceType). The
|
|
196
|
+
// copy shares the backing array; slice headers are immutable.
|
|
197
|
+
_emitTagSlice(goType, refWType) {
|
|
198
|
+
const tagged = this.mod.getTaggedSliceType(
|
|
199
|
+
goType,
|
|
200
|
+
this._getSliceElemType(goType),
|
|
201
|
+
);
|
|
202
|
+
const base = tagged.base;
|
|
203
|
+
const tmp = this.acquireTemp(refWType);
|
|
204
|
+
this.pushInstruction({ op: "local.tee", index: tmp });
|
|
205
|
+
this.pushInstruction("ref.is_null");
|
|
206
|
+
this.pushInstruction({
|
|
207
|
+
op: "if",
|
|
208
|
+
blockType: { kind: "ref", nullable: true, typeIndex: base.typeIndex },
|
|
209
|
+
});
|
|
210
|
+
this.pushInstruction({ op: "global.get", index: base.emptyGlobalIndex });
|
|
211
|
+
this.pushInstruction("else");
|
|
212
|
+
this.pushInstruction({ op: "local.get", index: tmp });
|
|
213
|
+
this.pushInstruction("end");
|
|
214
|
+
this.pushInstruction({ op: "local.set", index: tmp });
|
|
215
|
+
for (let i = 0; i < 4; i++) {
|
|
216
|
+
this.pushInstruction({ op: "local.get", index: tmp });
|
|
217
|
+
this.pushInstruction({
|
|
218
|
+
op: "struct.get",
|
|
219
|
+
typeIndex: base.typeIndex,
|
|
220
|
+
fieldIndex: i,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
this.pushInstruction({ op: "ref.null", heapType: tagged.tagTypeIndex });
|
|
224
|
+
this.pushInstruction({ op: "struct.new", typeIndex: tagged.typeIndex });
|
|
225
|
+
this.releaseTemp(tmp, refWType);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// The nullable ref on the stack becomes an interface value: a nil map, func
|
|
229
|
+
// or pointer turns into the marker of its type (never a nil interface).
|
|
230
|
+
_emitNilAwareAny(goType, expr) {
|
|
231
|
+
if (expr.kind === "UnaryExpr" && expr.op === "&") return;
|
|
232
|
+
if (expr.kind === "CompositeLit" || expr.kind === "FuncLit") return;
|
|
233
|
+
const nilBox = this.mod.getNilBoxType(goType);
|
|
234
|
+
const tmp = this.acquireTemp(this.toWasmType(goType));
|
|
235
|
+
this.pushInstruction({ op: "local.tee", index: tmp });
|
|
236
|
+
this.pushInstruction("ref.is_null");
|
|
237
|
+
this.pushInstruction({ op: "if", blockType: "anyref" });
|
|
238
|
+
this.pushInstruction({ op: "global.get", index: nilBox.globalIndex });
|
|
239
|
+
this.pushInstruction("else");
|
|
240
|
+
this.pushInstruction({ op: "local.get", index: tmp });
|
|
241
|
+
this.pushInstruction("end");
|
|
242
|
+
this.releaseTemp(tmp, this.toWasmType(goType));
|
|
243
|
+
}
|
|
244
|
+
|
|
95
245
|
_emitBoxValue(box) {
|
|
96
246
|
this.pushInstruction({
|
|
97
247
|
op: "ref.null",
|
|
@@ -116,6 +266,9 @@ export class ExprsEmitter {
|
|
|
116
266
|
return { kind: "nil", name: "nil" };
|
|
117
267
|
}
|
|
118
268
|
}
|
|
269
|
+
if (expr.kind === "ImagLit") {
|
|
270
|
+
return { kind: "untyped", base: "complex128" };
|
|
271
|
+
}
|
|
119
272
|
if (expr.kind === "Ident") {
|
|
120
273
|
const local = this.resolveLocal(expr.name);
|
|
121
274
|
if (local) return local.goType;
|
|
@@ -128,23 +281,46 @@ export class ExprsEmitter {
|
|
|
128
281
|
return null;
|
|
129
282
|
}
|
|
130
283
|
|
|
284
|
+
_emitLocalConst(ident, targetWasmType) {
|
|
285
|
+
const lit = constLitFor(ident._const, targetWasmType);
|
|
286
|
+
if (!lit) return false;
|
|
287
|
+
lit._type = ident._type;
|
|
288
|
+
this.emitBasicLit(lit, targetWasmType);
|
|
289
|
+
return true;
|
|
290
|
+
}
|
|
291
|
+
|
|
131
292
|
_emitRawExpr(expr, targetWasmType = null) {
|
|
132
293
|
if (expr.kind === "BinaryExpr" || expr.kind === "UnaryExpr") {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
294
|
+
// The checker's exact value (src/typechecker/const-fold.js) when it has one.
|
|
295
|
+
const folded = expr._const
|
|
296
|
+
? constLitFor(expr._const, targetWasmType)
|
|
297
|
+
: foldConstExpr(expr, (name) => this.mod.pkgConsts?.get(name));
|
|
136
298
|
if (folded) {
|
|
137
299
|
folded._type = expr._type;
|
|
138
300
|
this.emitBasicLit(folded, targetWasmType);
|
|
139
301
|
return;
|
|
140
302
|
}
|
|
141
303
|
}
|
|
304
|
+
// A function-local constant is a local slot (so closures can capture it)
|
|
305
|
+
// but reads as the checker's exact value, which adapts to the context.
|
|
306
|
+
if (
|
|
307
|
+
expr.kind === "Ident" &&
|
|
308
|
+
expr._const &&
|
|
309
|
+
this.resolveLocal(expr.name) &&
|
|
310
|
+
this._emitLocalConst(expr, targetWasmType)
|
|
311
|
+
)
|
|
312
|
+
return;
|
|
313
|
+
if (this._emitFoldedIntConversion(expr, targetWasmType)) return;
|
|
142
314
|
|
|
143
315
|
switch (expr.kind) {
|
|
144
316
|
case "BasicLit":
|
|
145
317
|
this.emitBasicLit(expr, targetWasmType);
|
|
146
318
|
break;
|
|
147
319
|
|
|
320
|
+
case "ImagLit":
|
|
321
|
+
this.emitImagLit(expr, targetWasmType);
|
|
322
|
+
break;
|
|
323
|
+
|
|
148
324
|
case "Ident":
|
|
149
325
|
this.emitIdent(expr, targetWasmType);
|
|
150
326
|
break;
|
|
@@ -158,7 +334,8 @@ export class ExprsEmitter {
|
|
|
158
334
|
break;
|
|
159
335
|
|
|
160
336
|
case "CallExpr":
|
|
161
|
-
this
|
|
337
|
+
if (!emitWasmNative(this, expr, targetWasmType))
|
|
338
|
+
this.emitCallExpr(expr, targetWasmType);
|
|
162
339
|
break;
|
|
163
340
|
|
|
164
341
|
case "TypeConversion":
|
|
@@ -172,20 +349,6 @@ export class ExprsEmitter {
|
|
|
172
349
|
case "SelectorExpr": {
|
|
173
350
|
const deq = this._dequalify(expr);
|
|
174
351
|
if (deq) this.emitIdent(deq, targetWasmType);
|
|
175
|
-
else if (this._isMathConst(expr))
|
|
176
|
-
this.emitMathConst(expr.field, targetWasmType);
|
|
177
|
-
else if (this._isUtf8Const(expr))
|
|
178
|
-
this.emitUtf8Const(expr.field, targetWasmType);
|
|
179
|
-
else if (this._isStrconvConst(expr))
|
|
180
|
-
this.emitStrconvConst(expr.field, targetWasmType);
|
|
181
|
-
else if (this._isPathConst(expr))
|
|
182
|
-
this.emitPathConst(expr.field, targetWasmType);
|
|
183
|
-
else if (this._isIoConst(expr))
|
|
184
|
-
this.emitIoConst(expr.field, targetWasmType);
|
|
185
|
-
else if (this._isTimeConst(expr))
|
|
186
|
-
this.emitTimeConst(expr.field, targetWasmType);
|
|
187
|
-
else if (this._isExtendedPkgConst?.(expr))
|
|
188
|
-
this.emitExtendedPkgConst(expr, targetWasmType);
|
|
189
352
|
else this.emitSelectorExpr(expr);
|
|
190
353
|
break;
|
|
191
354
|
}
|
|
@@ -217,7 +380,7 @@ export class ExprsEmitter {
|
|
|
217
380
|
this._emitTypeTest(targetGoType);
|
|
218
381
|
this.pushInstruction("i32.eqz");
|
|
219
382
|
this.pushInstruction({ op: "if", blockType: "void" });
|
|
220
|
-
this.
|
|
383
|
+
this._emitTypeAssertPanic(expr, anyTmp);
|
|
221
384
|
this.pushInstruction("end");
|
|
222
385
|
|
|
223
386
|
this.pushInstruction({ op: "local.get", index: anyTmp });
|
|
@@ -247,7 +410,7 @@ export class ExprsEmitter {
|
|
|
247
410
|
if (capturedNames.length === 0) {
|
|
248
411
|
this.pushInstruction({ op: "ref.null", heapType: "any" });
|
|
249
412
|
} else {
|
|
250
|
-
const envFields = this._buildEnvFields(capturedNames);
|
|
413
|
+
const envFields = this._buildEnvFields(capturedNames, funcLit);
|
|
251
414
|
const envInfo = this.mod.getEnvType(envFields);
|
|
252
415
|
|
|
253
416
|
for (const field of envFields) {
|
|
@@ -302,7 +465,7 @@ export class ExprsEmitter {
|
|
|
302
465
|
this.pushInstruction({ op: "local.get", index: mTmp });
|
|
303
466
|
this.emitExpr(elem.key, mapInfo.keyWType);
|
|
304
467
|
this.emitExpr(elem.value, mapInfo.valWType);
|
|
305
|
-
this.
|
|
468
|
+
this._emitCopyIfValue(elem.value, valType, mapInfo.valWType);
|
|
306
469
|
this.pushInstruction({
|
|
307
470
|
op: "call",
|
|
308
471
|
funcIndex: this.mod.resolveFuncIndex(mapInfo.setFuncName),
|
|
@@ -315,33 +478,11 @@ export class ExprsEmitter {
|
|
|
315
478
|
}
|
|
316
479
|
|
|
317
480
|
if (isSliceType(litType, this.mod.checker)) {
|
|
318
|
-
const elemType = litType
|
|
481
|
+
const elemType = this._getArrayElemType(litType);
|
|
319
482
|
const sliceInfo = this.mod.getSliceType(elemType);
|
|
320
|
-
const
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
this.pushInstruction({
|
|
324
|
-
op: "array.new_default",
|
|
325
|
-
typeIndex: sliceInfo.arrInfo.typeIndex,
|
|
326
|
-
});
|
|
327
|
-
this.pushInstruction({ op: "i32.const", value: 0 }); // offset
|
|
328
|
-
this.pushInstruction({ op: "i32.const", value: 0 }); // len
|
|
329
|
-
this.pushInstruction({ op: "i32.const", value: 0 }); // cap
|
|
330
|
-
this.pushInstruction({
|
|
331
|
-
op: "struct.new",
|
|
332
|
-
typeIndex: sliceInfo.typeIndex,
|
|
333
|
-
});
|
|
334
|
-
return;
|
|
335
|
-
}
|
|
336
|
-
for (const elem of lit.elems) {
|
|
337
|
-
this.emitExpr(elem, sliceInfo.elemWType);
|
|
338
|
-
this._emitCopyIfValueStruct(elem, elem._type, sliceInfo.elemWType);
|
|
339
|
-
}
|
|
340
|
-
this.pushInstruction({
|
|
341
|
-
op: "array.new_fixed",
|
|
342
|
-
typeIndex: sliceInfo.arrInfo.typeIndex,
|
|
343
|
-
size: count,
|
|
344
|
-
});
|
|
483
|
+
const elems = lit.elems ?? [];
|
|
484
|
+
const count = compositeLitMaxIndex(elems) + 1;
|
|
485
|
+
this._emitArrayLitElems(elems, elemType, sliceInfo.arrInfo, count);
|
|
345
486
|
this.pushInstruction({ op: "i32.const", value: 0 }); // offset
|
|
346
487
|
this.pushInstruction({ op: "i32.const", value: count }); // len
|
|
347
488
|
this.pushInstruction({ op: "i32.const", value: count }); // cap
|
|
@@ -353,22 +494,12 @@ export class ExprsEmitter {
|
|
|
353
494
|
}
|
|
354
495
|
|
|
355
496
|
if (isArrayType(litType, this.mod.checker)) {
|
|
356
|
-
const elemType = litType
|
|
497
|
+
const elemType = this._getArrayElemType(litType);
|
|
357
498
|
const arrInfo = this.mod.getArrayType(elemType);
|
|
358
|
-
const
|
|
499
|
+
const elems = lit.elems ?? [];
|
|
500
|
+
const count = compositeLitMaxIndex(elems) + 1;
|
|
359
501
|
const targetSize = this._getArraySize(litType, count);
|
|
360
|
-
|
|
361
|
-
this.emitExpr(elem, arrInfo.elemWType);
|
|
362
|
-
this._emitCopyIfValueStruct(elem, elem._type, arrInfo.elemWType);
|
|
363
|
-
}
|
|
364
|
-
for (let i = count; i < targetSize; i++) {
|
|
365
|
-
this.emitZeroValue(elemType, arrInfo.elemWType);
|
|
366
|
-
}
|
|
367
|
-
this.pushInstruction({
|
|
368
|
-
op: "array.new_fixed",
|
|
369
|
-
typeIndex: arrInfo.typeIndex,
|
|
370
|
-
size: targetSize,
|
|
371
|
-
});
|
|
502
|
+
this._emitArrayLitElems(elems, elemType, arrInfo, targetSize);
|
|
372
503
|
return;
|
|
373
504
|
}
|
|
374
505
|
|
|
@@ -398,13 +529,13 @@ export class ExprsEmitter {
|
|
|
398
529
|
const f = fields[i];
|
|
399
530
|
if (isKeyed) {
|
|
400
531
|
if (map.has(f.name)) {
|
|
401
|
-
this.
|
|
532
|
+
this._emitFieldInit(map.get(f.name), f);
|
|
402
533
|
} else {
|
|
403
534
|
this.emitZeroValue(f.goType, f.wType);
|
|
404
535
|
}
|
|
405
536
|
} else {
|
|
406
537
|
if (i < (lit.elems?.length ?? 0)) {
|
|
407
|
-
this.
|
|
538
|
+
this._emitFieldInit(lit.elems[i], f);
|
|
408
539
|
} else {
|
|
409
540
|
this.emitZeroValue(f.goType, f.wType);
|
|
410
541
|
}
|
|
@@ -417,9 +548,121 @@ export class ExprsEmitter {
|
|
|
417
548
|
});
|
|
418
549
|
}
|
|
419
550
|
|
|
551
|
+
// Pushes a new array of `size` elements built from literal `elems`.
|
|
552
|
+
// Keyed elements (`{2: 7}`) go to their index; every other slot is the
|
|
553
|
+
// element zero value. With ascending keys and at most
|
|
554
|
+
// MAX_NEW_FIXED elements this is one `array.new_fixed`; otherwise the
|
|
555
|
+
// array is allocated first and the elements are stored in source order
|
|
556
|
+
// (Go evaluates them left to right whatever their keys).
|
|
557
|
+
_emitArrayLitElems(elems, elemType, arrInfo, size) {
|
|
558
|
+
const indices = compositeLitIndices(elems);
|
|
559
|
+
const ascending = indices.every((v, i) => i === 0 || v > indices[i - 1]);
|
|
560
|
+
const value = (e) => (e.kind === "KeyValueExpr" ? e.value : e);
|
|
561
|
+
const emitElem = (e) => {
|
|
562
|
+
this.emitExpr(e, arrInfo.elemWType);
|
|
563
|
+
// Copy by the element type: a struct stored in an `any` element
|
|
564
|
+
// is already boxed, and a keyed element wraps its value.
|
|
565
|
+
this._emitCopyIfValue(e, elemType, arrInfo.elemWType);
|
|
566
|
+
};
|
|
567
|
+
if (ascending && size <= MAX_NEW_FIXED) {
|
|
568
|
+
const slots = new Array(size).fill(null);
|
|
569
|
+
elems.forEach((e, i) => {
|
|
570
|
+
slots[indices[i]] = value(e);
|
|
571
|
+
});
|
|
572
|
+
for (const e of slots) {
|
|
573
|
+
if (e) emitElem(e);
|
|
574
|
+
else this.emitZeroValue(elemType, arrInfo.elemWType);
|
|
575
|
+
}
|
|
576
|
+
this.pushInstruction({
|
|
577
|
+
op: "array.new_fixed",
|
|
578
|
+
typeIndex: arrInfo.typeIndex,
|
|
579
|
+
size,
|
|
580
|
+
});
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
const wType = { kind: "ref", nullable: true, typeIndex: arrInfo.typeIndex };
|
|
584
|
+
const arr = this.acquireTemp(wType);
|
|
585
|
+
const set = (idx, emit) => {
|
|
586
|
+
this.pushInstruction({ op: "local.get", index: arr });
|
|
587
|
+
this.pushInstruction({ op: "i32.const", value: idx });
|
|
588
|
+
emit();
|
|
589
|
+
this.pushInstruction({ op: "array.set", typeIndex: arrInfo.typeIndex });
|
|
590
|
+
};
|
|
591
|
+
this.pushInstruction({ op: "i32.const", value: size });
|
|
592
|
+
this.pushInstruction({
|
|
593
|
+
op: "array.new_default",
|
|
594
|
+
typeIndex: arrInfo.typeIndex,
|
|
595
|
+
});
|
|
596
|
+
this.pushInstruction({ op: "local.set", index: arr });
|
|
597
|
+
// Numeric zero values are the array default; others are stored.
|
|
598
|
+
if (typeof arrInfo.elemWType !== "string") {
|
|
599
|
+
const given = new Set(indices);
|
|
600
|
+
for (let i = 0; i < size; i++)
|
|
601
|
+
if (!given.has(i))
|
|
602
|
+
set(i, () => this.emitZeroValue(elemType, arrInfo.elemWType));
|
|
603
|
+
}
|
|
604
|
+
elems.forEach((e, i) => {
|
|
605
|
+
set(indices[i], () => emitElem(value(e)));
|
|
606
|
+
});
|
|
607
|
+
this.pushInstruction({ op: "local.get", index: arr });
|
|
608
|
+
this.releaseTemp(arr, wType);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// A struct literal field initialiser; array values are copied in.
|
|
612
|
+
_emitFieldInit(value, f) {
|
|
613
|
+
this.emitExpr(value, f.wType);
|
|
614
|
+
if (f.goType?.kind !== "pointer" && isArrayType(f.goType, this.mod.checker))
|
|
615
|
+
this._emitCopyIfValue(value, f.goType, f.wType);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// With a pointer on the stack, branches to the function's `nil` block
|
|
619
|
+
// (_openNilBlock) when it is null. Outside one, the access traps.
|
|
620
|
+
_emitNilCheck(goType) {
|
|
621
|
+
if (goType?.kind !== "pointer") return;
|
|
622
|
+
this._emitNilBranch();
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
_emitNilBranch() {
|
|
626
|
+
const depth = this.resolveBranchDepthToRole("nil");
|
|
627
|
+
if (depth == null) return;
|
|
628
|
+
this.pushInstruction({ op: "br_on_null", depth });
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// Emits the struct (or struct pointer) a field access or store goes
|
|
632
|
+
// through, nil-checked. A `*p` base is the pointee itself, not the copy a
|
|
633
|
+
// value read of `*p` makes, so a store through it lands in the pointee.
|
|
634
|
+
// Every selector read and every field store/op-assign/inc-dec starts here.
|
|
635
|
+
emitSelectorBase(node, wType = null, goType = node._type) {
|
|
636
|
+
const pointer = this._structDerefPointer(node);
|
|
637
|
+
if (pointer) {
|
|
638
|
+
this.emitSelectorBase(pointer);
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
this.emitExpr(node, wType ?? this.toWasmType(goType));
|
|
642
|
+
this._emitNilCheck(goType);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// For `*p` (possibly parenthesised) with `p` pointing at a struct, the
|
|
646
|
+
// pointer `p`, which is the struct's own reference; otherwise null.
|
|
647
|
+
_structDerefPointer(node) {
|
|
648
|
+
let base = node;
|
|
649
|
+
while (base?.kind === "ParenExpr") base = base.expr;
|
|
650
|
+
if (
|
|
651
|
+
(base?.kind !== "UnaryExpr" || base.op !== "*") &&
|
|
652
|
+
base?.kind !== "StarExpr"
|
|
653
|
+
)
|
|
654
|
+
return null;
|
|
655
|
+
const operand = base.operand ?? base.expr;
|
|
656
|
+
return this._derefStructInfo(operand) ? operand : null;
|
|
657
|
+
}
|
|
658
|
+
|
|
420
659
|
emitSelectorExpr(expr) {
|
|
421
660
|
const structInfo = this._resolveStructInfo(expr.expr);
|
|
422
661
|
if (!structInfo) {
|
|
662
|
+
if (expr.expr?.kind === "Ident" && expr.expr._type?.kind === "namespace")
|
|
663
|
+
throw new Error(
|
|
664
|
+
`${expr.expr.name}.${expr.field} cannot be used as a value in WASM`,
|
|
665
|
+
);
|
|
423
666
|
throw new Error(`Cannot resolve struct for selector: ${expr.field}`);
|
|
424
667
|
}
|
|
425
668
|
const path = this._resolveFieldPath(structInfo, expr.field);
|
|
@@ -429,8 +672,7 @@ export class ExprsEmitter {
|
|
|
429
672
|
);
|
|
430
673
|
}
|
|
431
674
|
|
|
432
|
-
|
|
433
|
-
this.emitExpr(expr.expr, baseWType);
|
|
675
|
+
this.emitSelectorBase(expr.expr);
|
|
434
676
|
|
|
435
677
|
for (let i = 0; i < path.length; i++) {
|
|
436
678
|
const step = path[i];
|
|
@@ -506,11 +748,9 @@ export class ExprsEmitter {
|
|
|
506
748
|
);
|
|
507
749
|
}
|
|
508
750
|
|
|
509
|
-
const elemGoType =
|
|
510
|
-
? baseType.
|
|
511
|
-
|
|
512
|
-
? baseType.base.elem
|
|
513
|
-
: baseType.elem;
|
|
751
|
+
const elemGoType = this._getArrayElemType(
|
|
752
|
+
isPtrToArr ? baseType.base : baseType,
|
|
753
|
+
);
|
|
514
754
|
const arrInfo = this.mod.getArrayType(elemGoType);
|
|
515
755
|
const sliceInfo = isSlice ? this.mod.getSliceType(elemGoType) : null;
|
|
516
756
|
|
|
@@ -604,11 +844,9 @@ export class ExprsEmitter {
|
|
|
604
844
|
);
|
|
605
845
|
}
|
|
606
846
|
|
|
607
|
-
const elemGoType =
|
|
608
|
-
? baseType.
|
|
609
|
-
|
|
610
|
-
? baseType.base.elem
|
|
611
|
-
: baseType.elem;
|
|
847
|
+
const elemGoType = this._getArrayElemType(
|
|
848
|
+
isPtrToArr ? baseType.base : baseType,
|
|
849
|
+
);
|
|
612
850
|
const arrInfo = this.mod.getArrayType(elemGoType);
|
|
613
851
|
const sliceInfo = this.mod.getSliceType(elemGoType);
|
|
614
852
|
|
|
@@ -757,8 +995,11 @@ export class ExprsEmitter {
|
|
|
757
995
|
const isValStruct =
|
|
758
996
|
isStructType(elemGoType, this.mod.checker, this.mod) &&
|
|
759
997
|
!isPointerToStruct(elemGoType, this.mod.checker, this.mod);
|
|
998
|
+
const isValArray =
|
|
999
|
+
elemGoType?.kind !== "pointer" &&
|
|
1000
|
+
isArrayType(elemGoType, this.mod.checker);
|
|
760
1001
|
|
|
761
|
-
if (!isValStruct) {
|
|
1002
|
+
if (!isValStruct && !isValArray) {
|
|
762
1003
|
this.pushInstruction({
|
|
763
1004
|
op: "array.copy",
|
|
764
1005
|
typeIndexDst: arrInfo.typeIndex,
|
|
@@ -831,7 +1072,9 @@ export class ExprsEmitter {
|
|
|
831
1072
|
this.pushInstruction("i32.add");
|
|
832
1073
|
this.pushInstruction({ op: "array.get", typeIndex: arrInfo.typeIndex });
|
|
833
1074
|
|
|
834
|
-
if (
|
|
1075
|
+
if (isValArray) {
|
|
1076
|
+
this.emitCopyArray(elemGoType);
|
|
1077
|
+
} else if (sInfo) {
|
|
835
1078
|
this.emitCloneStruct(sInfo, arrInfo.elemWType);
|
|
836
1079
|
}
|
|
837
1080
|
|
|
@@ -869,7 +1112,9 @@ export class ExprsEmitter {
|
|
|
869
1112
|
this.pushInstruction("i32.add");
|
|
870
1113
|
this.pushInstruction({ op: "array.get", typeIndex: arrInfo.typeIndex });
|
|
871
1114
|
|
|
872
|
-
if (
|
|
1115
|
+
if (isValArray) {
|
|
1116
|
+
this.emitCopyArray(elemGoType);
|
|
1117
|
+
} else if (sInfo) {
|
|
873
1118
|
this.emitCloneStruct(sInfo, arrInfo.elemWType);
|
|
874
1119
|
}
|
|
875
1120
|
|
|
@@ -942,53 +1187,95 @@ export class ExprsEmitter {
|
|
|
942
1187
|
return this.toWasmType(expr._type) === "i32";
|
|
943
1188
|
}
|
|
944
1189
|
|
|
1190
|
+
// Panics with the generic out-of-range message (shared buffers, SIMD
|
|
1191
|
+
// loads) when the condition on the stack is true.
|
|
945
1192
|
_emitBoundsBranch() {
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
1193
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1194
|
+
this.pushInstruction({
|
|
1195
|
+
op: "call",
|
|
1196
|
+
funcIndex: this.mod.getBoundsPanicFuncIndex(),
|
|
1197
|
+
});
|
|
1198
|
+
this.pushInstruction("unreachable");
|
|
1199
|
+
this.pushInstruction("end");
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
// If the condition on the stack is true, panics with Go's index message;
|
|
1203
|
+
// `pushIdx64` and `emitLen` push the i64 index and the i32 length.
|
|
1204
|
+
_emitIndexPanicIf(pushIdx64, emitLen) {
|
|
1205
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1206
|
+
pushIdx64();
|
|
1207
|
+
emitLen();
|
|
1208
|
+
this.pushInstruction({
|
|
1209
|
+
op: "call",
|
|
1210
|
+
funcIndex: this.mod.getIndexPanicFuncIndex(),
|
|
1211
|
+
});
|
|
1212
|
+
this.pushInstruction("unreachable");
|
|
1213
|
+
this.pushInstruction("end");
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
// Registers an index-check site and pushes its id: a failing check
|
|
1217
|
+
// carries the index and this id to the handler, which re-emits `emitLen`
|
|
1218
|
+
// for that site (its temps still hold the operand at the branch).
|
|
1219
|
+
_pushOobSite(emitLen) {
|
|
1220
|
+
this.oobSites ??= [];
|
|
1221
|
+
this.oobSites.push(emitLen);
|
|
1222
|
+
this.pushInstruction({ op: "i32.const", value: this.oobSites.length - 1 });
|
|
958
1223
|
}
|
|
959
1224
|
|
|
960
1225
|
// Pushes the bounds-checked i32 index onto the stack. `emitLen` must push
|
|
961
|
-
// the i32 length.
|
|
962
|
-
//
|
|
1226
|
+
// the i32 length. Inside a function body a failing check branches to the
|
|
1227
|
+
// shared handler (`_closeOobBlocks`) with the index and a site id, and the
|
|
1228
|
+
// handler reports Go's message; elsewhere the check panics inline.
|
|
963
1229
|
_emitCheckedIndex(indexNode, emitLen) {
|
|
964
|
-
const
|
|
965
|
-
(indexNode.kind === "BasicLit" && indexNode.litKind === "INT") ||
|
|
966
|
-
(indexNode.kind === "Ident" && this.resolveLocal(indexNode.name));
|
|
1230
|
+
const depth32 = this.resolveBranchDepthToOob(32);
|
|
967
1231
|
if (this._isI32IndexExpr(indexNode)) {
|
|
1232
|
+
const idx = this.acquireTemp("i32");
|
|
968
1233
|
this.emitExpr(indexNode, "i32");
|
|
969
|
-
|
|
970
|
-
if (
|
|
971
|
-
this.
|
|
1234
|
+
this.pushInstruction({ op: "local.tee", index: idx });
|
|
1235
|
+
if (depth32 !== null) {
|
|
1236
|
+
this._pushOobSite(emitLen);
|
|
1237
|
+
this.pushInstruction({ op: "local.get", index: idx });
|
|
1238
|
+
emitLen();
|
|
1239
|
+
this.pushInstruction("i32.ge_u");
|
|
1240
|
+
this.pushInstruction({ op: "br_if", depth: depth32 });
|
|
1241
|
+
this.pushInstruction("drop");
|
|
972
1242
|
} else {
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
this.pushInstruction(
|
|
1243
|
+
this.pushInstruction({ op: "local.get", index: idx });
|
|
1244
|
+
emitLen();
|
|
1245
|
+
this.pushInstruction("i32.ge_u");
|
|
1246
|
+
this._emitIndexPanicIf(() => {
|
|
1247
|
+
this.pushInstruction({ op: "local.get", index: idx });
|
|
1248
|
+
this.pushInstruction("i64.extend_i32_s");
|
|
1249
|
+
}, emitLen);
|
|
976
1250
|
}
|
|
977
|
-
|
|
978
|
-
this.pushInstruction("i32.ge_u");
|
|
979
|
-
this._emitBoundsBranch();
|
|
980
|
-
if (tmp !== null) this.releaseTemp(tmp, "i32");
|
|
1251
|
+
this.releaseTemp(idx, "i32");
|
|
981
1252
|
return;
|
|
982
1253
|
}
|
|
983
1254
|
|
|
984
1255
|
const idx64 = this.acquireTemp("i64");
|
|
985
1256
|
this.emitExpr(indexNode, "i64");
|
|
986
1257
|
this.pushInstruction({ op: "local.tee", index: idx64 });
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
1258
|
+
if (depth32 !== null) {
|
|
1259
|
+
this._pushOobSite(emitLen);
|
|
1260
|
+
this.pushInstruction({ op: "local.get", index: idx64 });
|
|
1261
|
+
emitLen();
|
|
1262
|
+
this.pushInstruction("i64.extend_i32_u");
|
|
1263
|
+
this.pushInstruction("i64.ge_u");
|
|
1264
|
+
this.pushInstruction({
|
|
1265
|
+
op: "br_if",
|
|
1266
|
+
depth: this.resolveBranchDepthToOob(64),
|
|
1267
|
+
});
|
|
1268
|
+
this.pushInstruction("drop");
|
|
1269
|
+
} else {
|
|
1270
|
+
emitLen();
|
|
1271
|
+
this.pushInstruction("i64.extend_i32_u");
|
|
1272
|
+
this.pushInstruction("i64.ge_u");
|
|
1273
|
+
this._emitIndexPanicIf(
|
|
1274
|
+
() => this.pushInstruction({ op: "local.get", index: idx64 }),
|
|
1275
|
+
emitLen,
|
|
1276
|
+
);
|
|
1277
|
+
this.pushInstruction({ op: "local.get", index: idx64 });
|
|
1278
|
+
}
|
|
992
1279
|
this.pushInstruction("i32.wrap_i64");
|
|
993
1280
|
this.releaseTemp(idx64, "i64");
|
|
994
1281
|
}
|
|
@@ -1055,12 +1342,14 @@ export class ExprsEmitter {
|
|
|
1055
1342
|
constIdx < arrayLen
|
|
1056
1343
|
) {
|
|
1057
1344
|
this.emitExpr(baseNode, arrWType);
|
|
1345
|
+
this._emitNilCheck(rawBaseType);
|
|
1058
1346
|
this.pushInstruction({ op: "i32.const", value: constIdx });
|
|
1059
1347
|
return;
|
|
1060
1348
|
}
|
|
1061
1349
|
|
|
1062
1350
|
const arrTmp = this.acquireTemp(arrWType);
|
|
1063
1351
|
this.emitExpr(baseNode, arrWType);
|
|
1352
|
+
this._emitNilCheck(rawBaseType);
|
|
1064
1353
|
this.pushInstruction({ op: "local.tee", index: arrTmp });
|
|
1065
1354
|
this._emitCheckedIndex(indexNode, () => {
|
|
1066
1355
|
this.pushInstruction({ op: "local.get", index: arrTmp });
|
|
@@ -1086,7 +1375,92 @@ export class ExprsEmitter {
|
|
|
1086
1375
|
}
|
|
1087
1376
|
}
|
|
1088
1377
|
|
|
1378
|
+
// Integer conversions of constants (`int64(1e18 + 1)`) keep the exact value.
|
|
1379
|
+
_emitFoldedIntConversion(expr, targetWasmType) {
|
|
1380
|
+
if (expr.kind !== "TypeConversion" && expr.kind !== "CallExpr")
|
|
1381
|
+
return false;
|
|
1382
|
+
if (expr._const?.k !== "INT") return false;
|
|
1383
|
+
if (
|
|
1384
|
+
targetWasmType != null &&
|
|
1385
|
+
targetWasmType !== "i32" &&
|
|
1386
|
+
targetWasmType !== "i64"
|
|
1387
|
+
)
|
|
1388
|
+
return false;
|
|
1389
|
+
const lit = constToLit(expr._const);
|
|
1390
|
+
lit._type = expr._type;
|
|
1391
|
+
this.emitBasicLit(lit, targetWasmType);
|
|
1392
|
+
return true;
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
// A float literal; an integral one in an integer context (`var y int = 2.0`)
|
|
1396
|
+
// emits its exact integer value.
|
|
1397
|
+
_emitFloatLit(lit, wType) {
|
|
1398
|
+
const i =
|
|
1399
|
+
wType === "i32" || wType === "i64"
|
|
1400
|
+
? constIntValue(constFromLit(lit) ?? {})
|
|
1401
|
+
: null;
|
|
1402
|
+
if (i !== null) {
|
|
1403
|
+
this.pushInstruction(
|
|
1404
|
+
wType === "i32"
|
|
1405
|
+
? { op: "i32.const", value: Number(BigInt.asIntN(32, i)) }
|
|
1406
|
+
: { op: "i64.const", value: BigInt.asIntN(64, i) },
|
|
1407
|
+
);
|
|
1408
|
+
return;
|
|
1409
|
+
}
|
|
1410
|
+
const n = Number(lit.value);
|
|
1411
|
+
if (wType === "f32") {
|
|
1412
|
+
this.pushInstruction({ op: "f32.const", value: n });
|
|
1413
|
+
} else {
|
|
1414
|
+
this.pushInstruction({ op: "f64.const", value: n });
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
// "complex64" | "complex128" when `w` is the struct type of a complex value.
|
|
1419
|
+
_complexKindOfWType(w) {
|
|
1420
|
+
if (typeof w?.typeIndex !== "number") return null;
|
|
1421
|
+
for (const [kind, info] of this.mod.complexTypes) {
|
|
1422
|
+
if (info.typeIndex === w.typeIndex) return kind;
|
|
1423
|
+
}
|
|
1424
|
+
return null;
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
// True when the target wasm type is the `$complex64` struct, whether it is
|
|
1428
|
+
// named or only carries the struct's type index.
|
|
1429
|
+
_isComplex64Target(w) {
|
|
1430
|
+
return (
|
|
1431
|
+
w?.typeName === "$complex64" ||
|
|
1432
|
+
this._complexKindOfWType(w) === "complex64"
|
|
1433
|
+
);
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1089
1436
|
emitBasicLit(lit, targetWasmType) {
|
|
1437
|
+
const targetComplex = this._complexKindOfWType(targetWasmType);
|
|
1438
|
+
const isTargetComplex64 =
|
|
1439
|
+
lit._type?.name === "complex64" ||
|
|
1440
|
+
targetWasmType?.typeName === "$complex64" ||
|
|
1441
|
+
targetComplex === "complex64";
|
|
1442
|
+
const isTargetComplex128 =
|
|
1443
|
+
lit._type?.name === "complex128" ||
|
|
1444
|
+
lit._type?.base === "complex128" ||
|
|
1445
|
+
targetWasmType?.typeName === "$complex128" ||
|
|
1446
|
+
targetComplex === "complex128";
|
|
1447
|
+
if (isTargetComplex64 || isTargetComplex128) {
|
|
1448
|
+
const info = this.mod.getComplexType(
|
|
1449
|
+
isTargetComplex64 ? "complex64" : "complex128",
|
|
1450
|
+
);
|
|
1451
|
+
const s = String(lit.value);
|
|
1452
|
+
const clean = /^0[0-7]+$/.test(s) ? `0o${s.slice(1)}` : s;
|
|
1453
|
+
const n = Number(clean);
|
|
1454
|
+
this.pushInstruction({ op: `${info.elemWType}.const`, value: n });
|
|
1455
|
+
this.pushInstruction({ op: `${info.elemWType}.const`, value: 0.0 });
|
|
1456
|
+
this.pushInstruction({
|
|
1457
|
+
op: "struct.new",
|
|
1458
|
+
typeIndex: info.typeIndex,
|
|
1459
|
+
typeName: info.name,
|
|
1460
|
+
});
|
|
1461
|
+
return;
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1090
1464
|
const wType =
|
|
1091
1465
|
targetWasmType ??
|
|
1092
1466
|
toWasmType(lit._type, this.mod.checker) ??
|
|
@@ -1108,12 +1482,7 @@ export class ExprsEmitter {
|
|
|
1108
1482
|
}
|
|
1109
1483
|
|
|
1110
1484
|
if (lit.litKind === "FLOAT") {
|
|
1111
|
-
|
|
1112
|
-
if (wType === "f32") {
|
|
1113
|
-
this.pushInstruction({ op: "f32.const", value: n });
|
|
1114
|
-
} else {
|
|
1115
|
-
this.pushInstruction({ op: "f64.const", value: n });
|
|
1116
|
-
}
|
|
1485
|
+
this._emitFloatLit(lit, wType);
|
|
1117
1486
|
return;
|
|
1118
1487
|
}
|
|
1119
1488
|
|
|
@@ -1141,6 +1510,22 @@ export class ExprsEmitter {
|
|
|
1141
1510
|
}
|
|
1142
1511
|
}
|
|
1143
1512
|
|
|
1513
|
+
emitImagLit(lit, targetWasmType) {
|
|
1514
|
+
const is64 =
|
|
1515
|
+
lit._type?.name === "complex64" ||
|
|
1516
|
+
this._isComplex64Target(targetWasmType);
|
|
1517
|
+
const complexName = is64 ? "complex64" : "complex128";
|
|
1518
|
+
const info = this.mod.getComplexType(complexName);
|
|
1519
|
+
const imVal = Number(lit.value);
|
|
1520
|
+
this.pushInstruction({ op: `${info.elemWType}.const`, value: 0.0 });
|
|
1521
|
+
this.pushInstruction({ op: `${info.elemWType}.const`, value: imVal });
|
|
1522
|
+
this.pushInstruction({
|
|
1523
|
+
op: "struct.new",
|
|
1524
|
+
typeIndex: info.typeIndex,
|
|
1525
|
+
typeName: info.name,
|
|
1526
|
+
});
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1144
1529
|
_emitNilValue(goType, targetWasmType) {
|
|
1145
1530
|
const effectiveType = targetWasmType ?? this.toWasmType(goType);
|
|
1146
1531
|
if (
|
|
@@ -1264,6 +1649,11 @@ export class ExprsEmitter {
|
|
|
1264
1649
|
emitUnaryExpr(expr, targetWasmType = null) {
|
|
1265
1650
|
const { op, operand } = expr;
|
|
1266
1651
|
|
|
1652
|
+
if (isComplex(expr._type) || isComplex(operand._type)) {
|
|
1653
|
+
this._emitComplexUnary(expr, op, operand, targetWasmType);
|
|
1654
|
+
return;
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1267
1657
|
// Optimize negative numeric literals directly
|
|
1268
1658
|
if (op === "-" && operand.kind === "BasicLit") {
|
|
1269
1659
|
if (operand.litKind === "INT" || operand.litKind === "FLOAT") {
|
|
@@ -1321,91 +1711,470 @@ export class ExprsEmitter {
|
|
|
1321
1711
|
}
|
|
1322
1712
|
break;
|
|
1323
1713
|
|
|
1324
|
-
case "&":
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
}
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1714
|
+
case "&":
|
|
1715
|
+
return this._emitAddressOf(operand);
|
|
1716
|
+
|
|
1717
|
+
case "*":
|
|
1718
|
+
return this._emitDerefExpr(operand);
|
|
1719
|
+
|
|
1720
|
+
default:
|
|
1721
|
+
throw new Error(`Unsupported unary operator: ${op}`);
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
_emitComplexUnary(expr, op, operand, targetWasmType) {
|
|
1726
|
+
if (op === "+") {
|
|
1727
|
+
this.emitExpr(operand, targetWasmType);
|
|
1728
|
+
return;
|
|
1729
|
+
}
|
|
1730
|
+
if (op === "-") {
|
|
1731
|
+
if (operand.kind === "ImagLit") {
|
|
1732
|
+
this.emitImagLit(
|
|
1733
|
+
{ ...operand, value: `-${operand.value}` },
|
|
1734
|
+
targetWasmType,
|
|
1735
|
+
);
|
|
1345
1736
|
return;
|
|
1346
1737
|
}
|
|
1738
|
+
const is64 =
|
|
1739
|
+
isComplex64(expr._type) ||
|
|
1740
|
+
isComplex64(operand._type) ||
|
|
1741
|
+
this._isComplex64Target(targetWasmType);
|
|
1742
|
+
const info = this.mod.getComplexType(is64 ? "complex64" : "complex128");
|
|
1743
|
+
const cWType = {
|
|
1744
|
+
kind: "ref",
|
|
1745
|
+
nullable: true,
|
|
1746
|
+
typeIndex: info.typeIndex,
|
|
1747
|
+
typeName: info.name,
|
|
1748
|
+
};
|
|
1749
|
+
const opTmp = this.acquireTemp(cWType);
|
|
1750
|
+
this.emitExpr(operand, cWType);
|
|
1751
|
+
this.pushInstruction({ op: "local.set", index: opTmp });
|
|
1347
1752
|
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
if (structInfo) {
|
|
1351
|
-
const baseWType = this.toWasmType(operand._type);
|
|
1352
|
-
this.emitExpr(operand, baseWType);
|
|
1353
|
-
this.emitCloneStruct(structInfo, baseWType);
|
|
1354
|
-
return;
|
|
1355
|
-
}
|
|
1356
|
-
const box = this.mod.getBoxType(operand._type?.base ?? operand._type);
|
|
1357
|
-
const baseWType = {
|
|
1358
|
-
kind: "ref",
|
|
1359
|
-
nullable: true,
|
|
1360
|
-
typeIndex: box.typeIndex,
|
|
1361
|
-
};
|
|
1362
|
-
this.emitExpr(operand, baseWType);
|
|
1753
|
+
for (let i = 0; i < 2; i++) {
|
|
1754
|
+
this.pushInstruction({ op: "local.get", index: opTmp });
|
|
1363
1755
|
this.pushInstruction({
|
|
1364
1756
|
op: "struct.get",
|
|
1365
|
-
typeIndex:
|
|
1366
|
-
|
|
1757
|
+
typeIndex: info.typeIndex,
|
|
1758
|
+
typeName: info.name,
|
|
1759
|
+
fieldIndex: i,
|
|
1367
1760
|
});
|
|
1368
|
-
|
|
1761
|
+
this.pushInstruction(`${info.elemWType}.neg`);
|
|
1369
1762
|
}
|
|
1370
1763
|
|
|
1371
|
-
|
|
1372
|
-
|
|
1764
|
+
this.pushInstruction({
|
|
1765
|
+
op: "struct.new",
|
|
1766
|
+
typeIndex: info.typeIndex,
|
|
1767
|
+
typeName: info.name,
|
|
1768
|
+
});
|
|
1769
|
+
this.releaseTemp(opTmp, cWType);
|
|
1373
1770
|
}
|
|
1374
1771
|
}
|
|
1375
1772
|
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1773
|
+
_emitAddressOf(operand) {
|
|
1774
|
+
if (operand.kind === "Ident") {
|
|
1775
|
+
const loc = this.resolveLocal(operand.name);
|
|
1776
|
+
if (loc?.isBoxed) {
|
|
1777
|
+
this.pushInstruction({ op: "local.get", index: loc.index });
|
|
1778
|
+
// A pointer to an array or struct is its reference itself.
|
|
1779
|
+
const t = loc.goType;
|
|
1780
|
+
if (
|
|
1781
|
+
this._isValueArray(t) ||
|
|
1782
|
+
(t?.kind !== "pointer" &&
|
|
1783
|
+
isStructType(t, this.mod.checker, this.mod) &&
|
|
1784
|
+
!isPointerToStruct(t, this.mod.checker, this.mod))
|
|
1785
|
+
)
|
|
1786
|
+
this.pushInstruction({
|
|
1787
|
+
op: "struct.get",
|
|
1788
|
+
typeIndex: loc.boxInfo.typeIndex,
|
|
1789
|
+
fieldIndex: 0,
|
|
1790
|
+
});
|
|
1791
|
+
return;
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
if (operand.kind === "CompositeLit") {
|
|
1795
|
+
if (isStructType(operand._type, this.mod.checker, this.mod)) {
|
|
1796
|
+
this.emitExpr(operand);
|
|
1797
|
+
return;
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
const structInfo = this._resolveStructInfo(operand);
|
|
1801
|
+
if (structInfo) {
|
|
1802
|
+
this.emitExpr(operand);
|
|
1387
1803
|
return;
|
|
1388
1804
|
}
|
|
1389
|
-
|
|
1390
|
-
if (
|
|
1391
|
-
this.emitExpr(
|
|
1392
|
-
this.pushInstruction({ op: "if", blockType: "i32" });
|
|
1393
|
-
this.pushInstruction({ op: "i32.const", value: 1 });
|
|
1394
|
-
this.pushInstruction("else");
|
|
1395
|
-
this.emitExpr(right, "i32");
|
|
1396
|
-
this.pushInstruction("end");
|
|
1805
|
+
const opWType = this.toWasmType(operand._type);
|
|
1806
|
+
if (!isScalarType(operand._type, this.mod.checker, this.mod)) {
|
|
1807
|
+
this.emitExpr(operand, opWType);
|
|
1397
1808
|
return;
|
|
1398
1809
|
}
|
|
1810
|
+
const box = this.mod.getBoxType(operand._type);
|
|
1811
|
+
this.emitExpr(operand, opWType);
|
|
1812
|
+
this.pushInstruction({
|
|
1813
|
+
op: "struct.new",
|
|
1814
|
+
typeIndex: box.typeIndex,
|
|
1815
|
+
typeName: box.name,
|
|
1816
|
+
});
|
|
1817
|
+
}
|
|
1399
1818
|
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1819
|
+
_isPointerGoType(t) {
|
|
1820
|
+
let x = t;
|
|
1821
|
+
if (x?.kind === "named" && x.underlying) x = x.underlying;
|
|
1822
|
+
return x?.kind === "pointer" || x?.kind === "PointerType";
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
_ptrBaseType(t) {
|
|
1826
|
+
let x = t;
|
|
1827
|
+
if (x?.kind === "named" && x.underlying) x = x.underlying;
|
|
1828
|
+
return x?.kind === "pointer" ? x.base : null;
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
// The struct a `*operand` reads or writes, or null when the operand points
|
|
1832
|
+
// at something else: `*pp` with `pp **T` loads a pointer, not a struct.
|
|
1833
|
+
_derefStructInfo(operand) {
|
|
1834
|
+
if (this._isPointerGoType(this._ptrBaseType(operand._type))) return null;
|
|
1835
|
+
return this._resolveStructInfo(operand);
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
_emitDerefExpr(operand) {
|
|
1839
|
+
const structInfo = this._derefStructInfo(operand);
|
|
1840
|
+
if (structInfo) {
|
|
1841
|
+
const baseWType = this.toWasmType(operand._type);
|
|
1842
|
+
this.emitExpr(operand, baseWType);
|
|
1843
|
+
this._emitNilCheck(operand._type);
|
|
1844
|
+
this.emitCloneStruct(structInfo, baseWType);
|
|
1403
1845
|
return;
|
|
1404
1846
|
}
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1847
|
+
const operandGoType =
|
|
1848
|
+
operand._type ??
|
|
1849
|
+
(operand.kind === "Ident"
|
|
1850
|
+
? this.resolveLocal(operand.name)?.goType
|
|
1851
|
+
: null) ??
|
|
1852
|
+
this._resolveExprGoType(operand);
|
|
1853
|
+
const baseGoType = operandGoType?.base ?? operandGoType;
|
|
1854
|
+
if (!isScalarType(baseGoType, this.mod.checker, this.mod)) {
|
|
1855
|
+
const baseWType = this.toWasmType(baseGoType);
|
|
1856
|
+
this.emitExpr(operand, baseWType);
|
|
1857
|
+
this._emitNilCheck(operandGoType);
|
|
1858
|
+
return;
|
|
1859
|
+
}
|
|
1860
|
+
const box = this.mod.getBoxType(baseGoType);
|
|
1861
|
+
const ptr = this.mod.getPtrType(baseGoType);
|
|
1862
|
+
const ptrWType = {
|
|
1863
|
+
kind: "ref",
|
|
1864
|
+
nullable: true,
|
|
1865
|
+
typeIndex: ptr.typeIndex,
|
|
1866
|
+
typeName: ptr.name,
|
|
1867
|
+
};
|
|
1868
|
+
this.emitExpr(operand, ptrWType);
|
|
1869
|
+
this._emitNilCheck(operandGoType);
|
|
1870
|
+
this.pushInstruction({
|
|
1871
|
+
op: "ref.cast",
|
|
1872
|
+
typeIndex: box.typeIndex,
|
|
1873
|
+
typeName: box.name,
|
|
1874
|
+
});
|
|
1875
|
+
this.pushInstruction({
|
|
1876
|
+
op: "struct.get",
|
|
1877
|
+
typeIndex: box.typeIndex,
|
|
1878
|
+
typeName: box.name,
|
|
1879
|
+
fieldIndex: 0,
|
|
1880
|
+
});
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
_emitComplexEq(aTmp, bTmp, info, f, op) {
|
|
1884
|
+
for (let i = 0; i < 2; i++) {
|
|
1885
|
+
this.pushInstruction({ op: "local.get", index: aTmp });
|
|
1886
|
+
this.pushInstruction({
|
|
1887
|
+
op: "struct.get",
|
|
1888
|
+
typeIndex: info.typeIndex,
|
|
1889
|
+
typeName: info.name,
|
|
1890
|
+
fieldIndex: i,
|
|
1891
|
+
});
|
|
1892
|
+
this.pushInstruction({ op: "local.get", index: bTmp });
|
|
1893
|
+
this.pushInstruction({
|
|
1894
|
+
op: "struct.get",
|
|
1895
|
+
typeIndex: info.typeIndex,
|
|
1896
|
+
typeName: info.name,
|
|
1897
|
+
fieldIndex: i,
|
|
1898
|
+
});
|
|
1899
|
+
this.pushInstruction(`${f}.eq`);
|
|
1900
|
+
}
|
|
1901
|
+
this.pushInstruction("i32.and");
|
|
1902
|
+
if (op === "!=") this.pushInstruction("i32.eqz");
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
_emitComplexAddSub(aTmp, bTmp, info, f, op) {
|
|
1906
|
+
const wasmOp = op === "+" ? `${f}.add` : `${f}.sub`;
|
|
1907
|
+
for (let i = 0; i < 2; i++) {
|
|
1908
|
+
this.pushInstruction({ op: "local.get", index: aTmp });
|
|
1909
|
+
this.pushInstruction({
|
|
1910
|
+
op: "struct.get",
|
|
1911
|
+
typeIndex: info.typeIndex,
|
|
1912
|
+
typeName: info.name,
|
|
1913
|
+
fieldIndex: i,
|
|
1914
|
+
});
|
|
1915
|
+
this.pushInstruction({ op: "local.get", index: bTmp });
|
|
1916
|
+
this.pushInstruction({
|
|
1917
|
+
op: "struct.get",
|
|
1918
|
+
typeIndex: info.typeIndex,
|
|
1919
|
+
typeName: info.name,
|
|
1920
|
+
fieldIndex: i,
|
|
1921
|
+
});
|
|
1922
|
+
this.pushInstruction(wasmOp);
|
|
1923
|
+
}
|
|
1924
|
+
this.pushInstruction({
|
|
1925
|
+
op: "struct.new",
|
|
1926
|
+
typeIndex: info.typeIndex,
|
|
1927
|
+
typeName: info.name,
|
|
1928
|
+
});
|
|
1929
|
+
}
|
|
1930
|
+
|
|
1931
|
+
_emitComplexMul(aTmp, bTmp, info, f) {
|
|
1932
|
+
this.pushInstruction({ op: "local.get", index: aTmp });
|
|
1933
|
+
this.pushInstruction({
|
|
1934
|
+
op: "struct.get",
|
|
1935
|
+
typeIndex: info.typeIndex,
|
|
1936
|
+
typeName: info.name,
|
|
1937
|
+
fieldIndex: 0,
|
|
1938
|
+
});
|
|
1939
|
+
this.pushInstruction({ op: "local.get", index: bTmp });
|
|
1940
|
+
this.pushInstruction({
|
|
1941
|
+
op: "struct.get",
|
|
1942
|
+
typeIndex: info.typeIndex,
|
|
1943
|
+
typeName: info.name,
|
|
1944
|
+
fieldIndex: 0,
|
|
1945
|
+
});
|
|
1946
|
+
this.pushInstruction(`${f}.mul`);
|
|
1947
|
+
this.pushInstruction({ op: "local.get", index: aTmp });
|
|
1948
|
+
this.pushInstruction({
|
|
1949
|
+
op: "struct.get",
|
|
1950
|
+
typeIndex: info.typeIndex,
|
|
1951
|
+
typeName: info.name,
|
|
1952
|
+
fieldIndex: 1,
|
|
1953
|
+
});
|
|
1954
|
+
this.pushInstruction({ op: "local.get", index: bTmp });
|
|
1955
|
+
this.pushInstruction({
|
|
1956
|
+
op: "struct.get",
|
|
1957
|
+
typeIndex: info.typeIndex,
|
|
1958
|
+
typeName: info.name,
|
|
1959
|
+
fieldIndex: 1,
|
|
1960
|
+
});
|
|
1961
|
+
this.pushInstruction(`${f}.mul`);
|
|
1962
|
+
this.pushInstruction(`${f}.sub`);
|
|
1963
|
+
|
|
1964
|
+
this.pushInstruction({ op: "local.get", index: aTmp });
|
|
1965
|
+
this.pushInstruction({
|
|
1966
|
+
op: "struct.get",
|
|
1967
|
+
typeIndex: info.typeIndex,
|
|
1968
|
+
typeName: info.name,
|
|
1969
|
+
fieldIndex: 0,
|
|
1970
|
+
});
|
|
1971
|
+
this.pushInstruction({ op: "local.get", index: bTmp });
|
|
1972
|
+
this.pushInstruction({
|
|
1973
|
+
op: "struct.get",
|
|
1974
|
+
typeIndex: info.typeIndex,
|
|
1975
|
+
typeName: info.name,
|
|
1976
|
+
fieldIndex: 1,
|
|
1977
|
+
});
|
|
1978
|
+
this.pushInstruction(`${f}.mul`);
|
|
1979
|
+
this.pushInstruction({ op: "local.get", index: aTmp });
|
|
1980
|
+
this.pushInstruction({
|
|
1981
|
+
op: "struct.get",
|
|
1982
|
+
typeIndex: info.typeIndex,
|
|
1983
|
+
typeName: info.name,
|
|
1984
|
+
fieldIndex: 1,
|
|
1985
|
+
});
|
|
1986
|
+
this.pushInstruction({ op: "local.get", index: bTmp });
|
|
1987
|
+
this.pushInstruction({
|
|
1988
|
+
op: "struct.get",
|
|
1989
|
+
typeIndex: info.typeIndex,
|
|
1990
|
+
typeName: info.name,
|
|
1991
|
+
fieldIndex: 0,
|
|
1992
|
+
});
|
|
1993
|
+
this.pushInstruction(`${f}.mul`);
|
|
1994
|
+
this.pushInstruction(`${f}.add`);
|
|
1995
|
+
|
|
1996
|
+
this.pushInstruction({
|
|
1997
|
+
op: "struct.new",
|
|
1998
|
+
typeIndex: info.typeIndex,
|
|
1999
|
+
typeName: info.name,
|
|
2000
|
+
});
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
_emitComplexDiv(aTmp, bTmp, info, f) {
|
|
2004
|
+
const dTmp = this.acquireTemp(f);
|
|
2005
|
+
for (let i = 0; i < 2; i++) {
|
|
2006
|
+
this.pushInstruction({ op: "local.get", index: bTmp });
|
|
2007
|
+
this.pushInstruction({
|
|
2008
|
+
op: "struct.get",
|
|
2009
|
+
typeIndex: info.typeIndex,
|
|
2010
|
+
typeName: info.name,
|
|
2011
|
+
fieldIndex: i,
|
|
2012
|
+
});
|
|
2013
|
+
this.pushInstruction({ op: "local.get", index: bTmp });
|
|
2014
|
+
this.pushInstruction({
|
|
2015
|
+
op: "struct.get",
|
|
2016
|
+
typeIndex: info.typeIndex,
|
|
2017
|
+
typeName: info.name,
|
|
2018
|
+
fieldIndex: i,
|
|
2019
|
+
});
|
|
2020
|
+
this.pushInstruction(`${f}.mul`);
|
|
2021
|
+
if (i > 0) this.pushInstruction(`${f}.add`);
|
|
2022
|
+
}
|
|
2023
|
+
this.pushInstruction({ op: "local.set", index: dTmp });
|
|
2024
|
+
|
|
2025
|
+
this.pushInstruction({ op: "local.get", index: aTmp });
|
|
2026
|
+
this.pushInstruction({
|
|
2027
|
+
op: "struct.get",
|
|
2028
|
+
typeIndex: info.typeIndex,
|
|
2029
|
+
typeName: info.name,
|
|
2030
|
+
fieldIndex: 0,
|
|
2031
|
+
});
|
|
2032
|
+
this.pushInstruction({ op: "local.get", index: bTmp });
|
|
2033
|
+
this.pushInstruction({
|
|
2034
|
+
op: "struct.get",
|
|
2035
|
+
typeIndex: info.typeIndex,
|
|
2036
|
+
typeName: info.name,
|
|
2037
|
+
fieldIndex: 0,
|
|
2038
|
+
});
|
|
2039
|
+
this.pushInstruction(`${f}.mul`);
|
|
2040
|
+
this.pushInstruction({ op: "local.get", index: aTmp });
|
|
2041
|
+
this.pushInstruction({
|
|
2042
|
+
op: "struct.get",
|
|
2043
|
+
typeIndex: info.typeIndex,
|
|
2044
|
+
typeName: info.name,
|
|
2045
|
+
fieldIndex: 1,
|
|
2046
|
+
});
|
|
2047
|
+
this.pushInstruction({ op: "local.get", index: bTmp });
|
|
2048
|
+
this.pushInstruction({
|
|
2049
|
+
op: "struct.get",
|
|
2050
|
+
typeIndex: info.typeIndex,
|
|
2051
|
+
typeName: info.name,
|
|
2052
|
+
fieldIndex: 1,
|
|
2053
|
+
});
|
|
2054
|
+
this.pushInstruction(`${f}.mul`);
|
|
2055
|
+
this.pushInstruction(`${f}.add`);
|
|
2056
|
+
this.pushInstruction({ op: "local.get", index: dTmp });
|
|
2057
|
+
this.pushInstruction(`${f}.div`);
|
|
2058
|
+
|
|
2059
|
+
this.pushInstruction({ op: "local.get", index: aTmp });
|
|
2060
|
+
this.pushInstruction({
|
|
2061
|
+
op: "struct.get",
|
|
2062
|
+
typeIndex: info.typeIndex,
|
|
2063
|
+
typeName: info.name,
|
|
2064
|
+
fieldIndex: 1,
|
|
2065
|
+
});
|
|
2066
|
+
this.pushInstruction({ op: "local.get", index: bTmp });
|
|
2067
|
+
this.pushInstruction({
|
|
2068
|
+
op: "struct.get",
|
|
2069
|
+
typeIndex: info.typeIndex,
|
|
2070
|
+
typeName: info.name,
|
|
2071
|
+
fieldIndex: 0,
|
|
2072
|
+
});
|
|
2073
|
+
this.pushInstruction(`${f}.mul`);
|
|
2074
|
+
this.pushInstruction({ op: "local.get", index: aTmp });
|
|
2075
|
+
this.pushInstruction({
|
|
2076
|
+
op: "struct.get",
|
|
2077
|
+
typeIndex: info.typeIndex,
|
|
2078
|
+
typeName: info.name,
|
|
2079
|
+
fieldIndex: 0,
|
|
2080
|
+
});
|
|
2081
|
+
this.pushInstruction({ op: "local.get", index: bTmp });
|
|
2082
|
+
this.pushInstruction({
|
|
2083
|
+
op: "struct.get",
|
|
2084
|
+
typeIndex: info.typeIndex,
|
|
2085
|
+
typeName: info.name,
|
|
2086
|
+
fieldIndex: 1,
|
|
2087
|
+
});
|
|
2088
|
+
this.pushInstruction(`${f}.mul`);
|
|
2089
|
+
this.pushInstruction(`${f}.sub`);
|
|
2090
|
+
this.pushInstruction({ op: "local.get", index: dTmp });
|
|
2091
|
+
this.pushInstruction(`${f}.div`);
|
|
2092
|
+
|
|
2093
|
+
this.pushInstruction({
|
|
2094
|
+
op: "struct.new",
|
|
2095
|
+
typeIndex: info.typeIndex,
|
|
2096
|
+
typeName: info.name,
|
|
2097
|
+
});
|
|
2098
|
+
this.releaseTemp(dTmp, f);
|
|
2099
|
+
}
|
|
2100
|
+
|
|
2101
|
+
emitComplexBinary(expr, targetWasmType) {
|
|
2102
|
+
const { op, left, right } = expr;
|
|
2103
|
+
const is64 =
|
|
2104
|
+
isComplex64(expr._type) ||
|
|
2105
|
+
isComplex64(left._type) ||
|
|
2106
|
+
isComplex64(right._type) ||
|
|
2107
|
+
this._isComplex64Target(targetWasmType);
|
|
2108
|
+
const info = this.mod.getComplexType(is64 ? "complex64" : "complex128");
|
|
2109
|
+
const f = info.elemWType;
|
|
2110
|
+
const cWType = {
|
|
2111
|
+
kind: "ref",
|
|
2112
|
+
nullable: true,
|
|
2113
|
+
typeIndex: info.typeIndex,
|
|
2114
|
+
typeName: info.name,
|
|
2115
|
+
};
|
|
2116
|
+
const aTmp = this.acquireTemp(cWType);
|
|
2117
|
+
const bTmp = this.acquireTemp(cWType);
|
|
2118
|
+
this.emitExpr(left, cWType);
|
|
2119
|
+
this.pushInstruction({ op: "local.set", index: aTmp });
|
|
2120
|
+
this.emitExpr(right, cWType);
|
|
2121
|
+
this.pushInstruction({ op: "local.set", index: bTmp });
|
|
2122
|
+
|
|
2123
|
+
if (op === "==" || op === "!=") {
|
|
2124
|
+
this._emitComplexEq(aTmp, bTmp, info, f, op);
|
|
2125
|
+
} else if (op === "+" || op === "-") {
|
|
2126
|
+
this._emitComplexAddSub(aTmp, bTmp, info, f, op);
|
|
2127
|
+
} else if (op === "*") {
|
|
2128
|
+
this._emitComplexMul(aTmp, bTmp, info, f);
|
|
2129
|
+
} else if (op === "/") {
|
|
2130
|
+
this._emitComplexDiv(aTmp, bTmp, info, f);
|
|
2131
|
+
}
|
|
2132
|
+
this.releaseTemp(bTmp, cWType);
|
|
2133
|
+
this.releaseTemp(aTmp, cWType);
|
|
2134
|
+
}
|
|
2135
|
+
|
|
2136
|
+
emitBinaryExpr(expr, targetWasmType = null) {
|
|
2137
|
+
const { op, left, right } = expr;
|
|
2138
|
+
|
|
2139
|
+
if (
|
|
2140
|
+
isComplex(expr._type) ||
|
|
2141
|
+
isComplex(left._type) ||
|
|
2142
|
+
isComplex(right._type)
|
|
2143
|
+
) {
|
|
2144
|
+
this.emitComplexBinary(expr, targetWasmType);
|
|
2145
|
+
return;
|
|
2146
|
+
}
|
|
2147
|
+
|
|
2148
|
+
// Short-circuiting logical operators
|
|
2149
|
+
if (op === "&&") {
|
|
2150
|
+
this.emitExpr(left, "i32");
|
|
2151
|
+
this.pushInstruction({ op: "if", blockType: "i32" });
|
|
2152
|
+
this.emitExpr(right, "i32");
|
|
2153
|
+
this.pushInstruction("else");
|
|
2154
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
2155
|
+
this.pushInstruction("end");
|
|
2156
|
+
return;
|
|
2157
|
+
}
|
|
2158
|
+
|
|
2159
|
+
if (op === "||") {
|
|
2160
|
+
this.emitExpr(left, "i32");
|
|
2161
|
+
this.pushInstruction({ op: "if", blockType: "i32" });
|
|
2162
|
+
this.pushInstruction({ op: "i32.const", value: 1 });
|
|
2163
|
+
this.pushInstruction("else");
|
|
2164
|
+
this.emitExpr(right, "i32");
|
|
2165
|
+
this.pushInstruction("end");
|
|
2166
|
+
return;
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2169
|
+
// Division & Modulo with zero-guard and overflow-guard
|
|
2170
|
+
if (op === "/" || op === "%") {
|
|
2171
|
+
this.emitDivRem(op, left, right, targetWasmType);
|
|
2172
|
+
return;
|
|
2173
|
+
}
|
|
2174
|
+
|
|
2175
|
+
// Shifts with Go semantics
|
|
2176
|
+
if (op === "<<" || op === ">>") {
|
|
2177
|
+
this.emitShift(op, left, right, targetWasmType);
|
|
1409
2178
|
return;
|
|
1410
2179
|
}
|
|
1411
2180
|
|
|
@@ -1453,10 +2222,17 @@ export class ExprsEmitter {
|
|
|
1453
2222
|
return;
|
|
1454
2223
|
}
|
|
1455
2224
|
|
|
1456
|
-
|
|
2225
|
+
// An untyped constant operand takes the type of the typed one
|
|
2226
|
+
// (`'a' < rune(b)`), so both sides share one wasm representation.
|
|
2227
|
+
const leftType =
|
|
2228
|
+
left._type?.kind === "untyped" &&
|
|
2229
|
+
right._type &&
|
|
2230
|
+
right._type.kind !== "untyped"
|
|
2231
|
+
? right._type
|
|
2232
|
+
: left._type;
|
|
1457
2233
|
const isLeftI32 = this._isI32IndexExpr(left);
|
|
1458
2234
|
const isRightI32 = this._isI32IndexExpr(right);
|
|
1459
|
-
const leftGoType =
|
|
2235
|
+
const leftGoType = leftType ?? this._resolveExprGoType(left);
|
|
1460
2236
|
const isIntType =
|
|
1461
2237
|
!leftGoType ||
|
|
1462
2238
|
(leftGoType.kind === "basic" &&
|
|
@@ -1497,6 +2273,72 @@ export class ExprsEmitter {
|
|
|
1497
2273
|
this.emitBinaryOp(op, wType, leftType);
|
|
1498
2274
|
}
|
|
1499
2275
|
|
|
2276
|
+
_findScalarPtrByWType(wType, map) {
|
|
2277
|
+
if (!map) return null;
|
|
2278
|
+
for (const info of map.values()) {
|
|
2279
|
+
if (info.typeIndex === wType.typeIndex || info.name === wType.typeName) {
|
|
2280
|
+
return info;
|
|
2281
|
+
}
|
|
2282
|
+
}
|
|
2283
|
+
return null;
|
|
2284
|
+
}
|
|
2285
|
+
|
|
2286
|
+
_getScalarPtrFromWType(wType) {
|
|
2287
|
+
if (!wType || typeof wType !== "object") return null;
|
|
2288
|
+
const ptr = this._findScalarPtrByWType(wType, this.mod.ptrTypes);
|
|
2289
|
+
if (ptr) {
|
|
2290
|
+
return {
|
|
2291
|
+
ptr,
|
|
2292
|
+
box: this.mod.getBoxType(ptr.goType),
|
|
2293
|
+
};
|
|
2294
|
+
}
|
|
2295
|
+
const box = this._findScalarPtrByWType(wType, this.mod.boxTypes);
|
|
2296
|
+
if (box) {
|
|
2297
|
+
return {
|
|
2298
|
+
ptr: this.mod.getPtrType(box.goType),
|
|
2299
|
+
box,
|
|
2300
|
+
};
|
|
2301
|
+
}
|
|
2302
|
+
return null;
|
|
2303
|
+
}
|
|
2304
|
+
|
|
2305
|
+
_getScalarPtrFromGoType(goType) {
|
|
2306
|
+
let g = goType;
|
|
2307
|
+
if (g?.kind === "TypeName" && this.mod.checker?.types?.has(g.name)) {
|
|
2308
|
+
g = this.mod.checker.types.get(g.name);
|
|
2309
|
+
}
|
|
2310
|
+
while (g?.kind === "named" && g.underlying) {
|
|
2311
|
+
g = g.underlying;
|
|
2312
|
+
}
|
|
2313
|
+
const isPtr =
|
|
2314
|
+
g?.kind === "pointer" ||
|
|
2315
|
+
g?.kind === "PointerType" ||
|
|
2316
|
+
g?.kind === "StarExpr";
|
|
2317
|
+
if (!isPtr || (!g.base && !g.elem)) return null;
|
|
2318
|
+
const base = g.base || g.elem;
|
|
2319
|
+
if (!isScalarType(base, this.mod.checker, this.mod)) {
|
|
2320
|
+
return null;
|
|
2321
|
+
}
|
|
2322
|
+
return {
|
|
2323
|
+
ptr: this.mod.getPtrType(base),
|
|
2324
|
+
box: this.mod.getBoxType(base),
|
|
2325
|
+
};
|
|
2326
|
+
}
|
|
2327
|
+
|
|
2328
|
+
_getScalarPtrInfo(wType, goType = null) {
|
|
2329
|
+
return (
|
|
2330
|
+
this._getScalarPtrFromGoType(goType) ?? this._getScalarPtrFromWType(wType)
|
|
2331
|
+
);
|
|
2332
|
+
}
|
|
2333
|
+
|
|
2334
|
+
_isScalarPtrType(goType, wType = null) {
|
|
2335
|
+
return this._getScalarPtrInfo(wType, goType) !== null;
|
|
2336
|
+
}
|
|
2337
|
+
|
|
2338
|
+
_emitScalarPtrEq(_ptrInfo) {
|
|
2339
|
+
this.pushInstruction("ref.eq");
|
|
2340
|
+
}
|
|
2341
|
+
|
|
1500
2342
|
_emitStructEq(structInfo) {
|
|
1501
2343
|
const bTmp = this.acquireTemp({
|
|
1502
2344
|
kind: "ref",
|
|
@@ -1543,11 +2385,18 @@ export class ExprsEmitter {
|
|
|
1543
2385
|
(f.goType?.kind === "named" ? f.goType.name : null),
|
|
1544
2386
|
);
|
|
1545
2387
|
this._emitStructEq(sInfo);
|
|
2388
|
+
} else if (this._isValueArray(f.goType)) {
|
|
2389
|
+
this._emitArrayEq(f.goType);
|
|
1546
2390
|
} else if (isStringType(f.goType, this.mod.checker)) {
|
|
1547
2391
|
const cmpIdx = this.mod.getStringCmpImportIndex("==");
|
|
1548
2392
|
this.pushInstruction({ op: "call", funcIndex: cmpIdx });
|
|
2393
|
+
} else if (f.wType === "v128") {
|
|
2394
|
+
this._emitSimdEq(f.goType);
|
|
1549
2395
|
} else if (f.wType === "anyref") {
|
|
1550
2396
|
this._emitAnyEq();
|
|
2397
|
+
} else if (this._isScalarPtrType(f.goType, f.wType)) {
|
|
2398
|
+
const ptrInfo = this._getScalarPtrInfo(f.wType, f.goType);
|
|
2399
|
+
this._emitScalarPtrEq(ptrInfo);
|
|
1551
2400
|
} else if (typeof f.wType === "object" && f.wType !== null) {
|
|
1552
2401
|
this.pushInstruction("ref.eq");
|
|
1553
2402
|
} else {
|
|
@@ -1578,7 +2427,110 @@ export class ExprsEmitter {
|
|
|
1578
2427
|
});
|
|
1579
2428
|
}
|
|
1580
2429
|
|
|
2430
|
+
_isValueArray(goType) {
|
|
2431
|
+
return goType?.kind !== "pointer" && isArrayType(goType, this.mod.checker);
|
|
2432
|
+
}
|
|
2433
|
+
|
|
2434
|
+
// Go array equality: element-wise (arrays are values). Pops two arrays
|
|
2435
|
+
// and pushes an i32. A null array (a never-written slot) is the zero
|
|
2436
|
+
// array.
|
|
2437
|
+
_emitArrayEq(goType) {
|
|
2438
|
+
const elemType = this._getArrayElemType(goType);
|
|
2439
|
+
const arrInfo = this.mod.getArrayType(elemType);
|
|
2440
|
+
const arrWType = {
|
|
2441
|
+
kind: "ref",
|
|
2442
|
+
nullable: true,
|
|
2443
|
+
typeIndex: arrInfo.typeIndex,
|
|
2444
|
+
};
|
|
2445
|
+
const bTmp = this.acquireTemp(arrWType);
|
|
2446
|
+
const aTmp = this.acquireTemp(arrWType);
|
|
2447
|
+
for (const tmp of [bTmp, aTmp]) {
|
|
2448
|
+
this.pushInstruction({ op: "local.tee", index: tmp });
|
|
2449
|
+
this.pushInstruction("ref.is_null");
|
|
2450
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
2451
|
+
this.emitZeroValue(goType, arrWType);
|
|
2452
|
+
this.pushInstruction({ op: "local.set", index: tmp });
|
|
2453
|
+
this.pushInstruction("end");
|
|
2454
|
+
}
|
|
2455
|
+
const i = this.acquireTemp("i32");
|
|
2456
|
+
const resTmp = this.acquireTemp("i32");
|
|
2457
|
+
this.pushInstruction({ op: "i32.const", value: 1 });
|
|
2458
|
+
this.pushInstruction({ op: "local.set", index: resTmp });
|
|
2459
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
2460
|
+
this.pushInstruction({ op: "local.set", index: i });
|
|
2461
|
+
this.pushInstruction({ op: "block", blockType: "void" });
|
|
2462
|
+
this.pushInstruction({ op: "loop", blockType: "void" });
|
|
2463
|
+
this.pushInstruction({ op: "local.get", index: i });
|
|
2464
|
+
this.pushInstruction({ op: "local.get", index: aTmp });
|
|
2465
|
+
this.pushInstruction("array.len");
|
|
2466
|
+
this.pushInstruction("i32.ge_u");
|
|
2467
|
+
this.pushInstruction({ op: "br_if", depth: 1 });
|
|
2468
|
+
for (const tmp of [aTmp, bTmp]) {
|
|
2469
|
+
this.pushInstruction({ op: "local.get", index: tmp });
|
|
2470
|
+
this.pushInstruction({ op: "local.get", index: i });
|
|
2471
|
+
this.pushInstruction({ op: "array.get", typeIndex: arrInfo.typeIndex });
|
|
2472
|
+
}
|
|
2473
|
+
this._emitElemEq(elemType, arrInfo.elemWType);
|
|
2474
|
+
this.pushInstruction("i32.eqz");
|
|
2475
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
2476
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
2477
|
+
this.pushInstruction({ op: "local.set", index: resTmp });
|
|
2478
|
+
this.pushInstruction({ op: "br", depth: 2 });
|
|
2479
|
+
this.pushInstruction("end");
|
|
2480
|
+
this.pushInstruction({ op: "local.get", index: i });
|
|
2481
|
+
this.pushInstruction({ op: "i32.const", value: 1 });
|
|
2482
|
+
this.pushInstruction("i32.add");
|
|
2483
|
+
this.pushInstruction({ op: "local.set", index: i });
|
|
2484
|
+
this.pushInstruction({ op: "br", depth: 0 });
|
|
2485
|
+
this.pushInstruction("end");
|
|
2486
|
+
this.pushInstruction("end");
|
|
2487
|
+
this.pushInstruction({ op: "local.get", index: resTmp });
|
|
2488
|
+
this.releaseTemp(resTmp, "i32");
|
|
2489
|
+
this.releaseTemp(i, "i32");
|
|
2490
|
+
this.releaseTemp(aTmp, arrWType);
|
|
2491
|
+
this.releaseTemp(bTmp, arrWType);
|
|
2492
|
+
}
|
|
2493
|
+
|
|
2494
|
+
// Equality of two array elements on the stack -> i32.
|
|
2495
|
+
_emitElemEq(goType, wType) {
|
|
2496
|
+
if (this._isValueArray(goType)) {
|
|
2497
|
+
this._emitArrayEq(goType);
|
|
2498
|
+
} else if (
|
|
2499
|
+
isStructType(goType, this.mod.checker, this.mod) &&
|
|
2500
|
+
!isPointerToStruct(goType, this.mod.checker, this.mod)
|
|
2501
|
+
) {
|
|
2502
|
+
this._emitStructEq(this._resolveStructInfo({ _type: goType }));
|
|
2503
|
+
} else {
|
|
2504
|
+
this.emitBinaryOp("==", wType, goType);
|
|
2505
|
+
}
|
|
2506
|
+
}
|
|
2507
|
+
|
|
1581
2508
|
emitBinaryOp(op, wType, goType) {
|
|
2509
|
+
if ((op === "==" || op === "!=") && this._isValueArray(goType)) {
|
|
2510
|
+
this._emitArrayEq(goType);
|
|
2511
|
+
if (op === "!=") this.pushInstruction("i32.eqz");
|
|
2512
|
+
return;
|
|
2513
|
+
}
|
|
2514
|
+
if (isComplex(goType)) {
|
|
2515
|
+
const is64 = isComplex64(goType);
|
|
2516
|
+
const info = this.mod.getComplexType(is64 ? "complex64" : "complex128");
|
|
2517
|
+
const f = info.elemWType;
|
|
2518
|
+
const bTmp = this.acquireTemp(wType);
|
|
2519
|
+
const aTmp = this.acquireTemp(wType);
|
|
2520
|
+
this.pushInstruction({ op: "local.set", index: bTmp });
|
|
2521
|
+
this.pushInstruction({ op: "local.set", index: aTmp });
|
|
2522
|
+
if (op === "+" || op === "-") {
|
|
2523
|
+
this._emitComplexAddSub(aTmp, bTmp, info, f, op);
|
|
2524
|
+
} else if (op === "*") {
|
|
2525
|
+
this._emitComplexMul(aTmp, bTmp, info, f);
|
|
2526
|
+
} else if (op === "/") {
|
|
2527
|
+
this._emitComplexDiv(aTmp, bTmp, info, f);
|
|
2528
|
+
}
|
|
2529
|
+
this.releaseTemp(aTmp, wType);
|
|
2530
|
+
this.releaseTemp(bTmp, wType);
|
|
2531
|
+
return;
|
|
2532
|
+
}
|
|
2533
|
+
|
|
1582
2534
|
const signed = isSigned(goType);
|
|
1583
2535
|
|
|
1584
2536
|
switch (op) {
|
|
@@ -1646,8 +2598,13 @@ export class ExprsEmitter {
|
|
|
1646
2598
|
if (isStringType(goType, this.mod.checker) || wType === "externref") {
|
|
1647
2599
|
const cmpIdx = this.mod.getStringCmpImportIndex("==");
|
|
1648
2600
|
this.pushInstruction({ op: "call", funcIndex: cmpIdx });
|
|
2601
|
+
} else if (wType === "v128") {
|
|
2602
|
+
this._emitSimdEq(goType);
|
|
1649
2603
|
} else if (wType === "anyref") {
|
|
1650
2604
|
this._emitAnyEq();
|
|
2605
|
+
} else if (this._isScalarPtrType(goType, wType)) {
|
|
2606
|
+
const ptrInfo = this._getScalarPtrInfo(wType, goType);
|
|
2607
|
+
this._emitScalarPtrEq(ptrInfo);
|
|
1651
2608
|
} else if (typeof wType === "object" && wType !== null) {
|
|
1652
2609
|
this.pushInstruction("ref.eq");
|
|
1653
2610
|
} else {
|
|
@@ -1658,9 +2615,16 @@ export class ExprsEmitter {
|
|
|
1658
2615
|
if (isStringType(goType, this.mod.checker) || wType === "externref") {
|
|
1659
2616
|
const cmpIdx = this.mod.getStringCmpImportIndex("!=");
|
|
1660
2617
|
this.pushInstruction({ op: "call", funcIndex: cmpIdx });
|
|
2618
|
+
} else if (wType === "v128") {
|
|
2619
|
+
this._emitSimdEq(goType);
|
|
2620
|
+
this.pushInstruction("i32.eqz");
|
|
1661
2621
|
} else if (wType === "anyref") {
|
|
1662
2622
|
this._emitAnyEq();
|
|
1663
2623
|
this.pushInstruction("i32.eqz");
|
|
2624
|
+
} else if (this._isScalarPtrType(goType, wType)) {
|
|
2625
|
+
const ptrInfo = this._getScalarPtrInfo(wType, goType);
|
|
2626
|
+
this._emitScalarPtrEq(ptrInfo);
|
|
2627
|
+
this.pushInstruction("i32.eqz");
|
|
1664
2628
|
} else if (typeof wType === "object" && wType !== null) {
|
|
1665
2629
|
this.pushInstruction("ref.eq");
|
|
1666
2630
|
this.pushInstruction("i32.eqz");
|
|
@@ -1693,6 +2657,8 @@ export class ExprsEmitter {
|
|
|
1693
2657
|
emitNarrowIntWrap(goType) {
|
|
1694
2658
|
if (!goType) return;
|
|
1695
2659
|
if (goType.kind === "TypeName" || goType.kind === "Ident") {
|
|
2660
|
+
const declared = this.mod.checker?.types?.get(goType.name);
|
|
2661
|
+
if (declared?.kind === "named") return this.emitNarrowIntWrap(declared);
|
|
1696
2662
|
return this.emitNarrowIntWrap({ kind: "basic", name: goType.name });
|
|
1697
2663
|
}
|
|
1698
2664
|
if (goType.kind === "named" && goType.underlying) {
|
|
@@ -1720,7 +2686,14 @@ export class ExprsEmitter {
|
|
|
1720
2686
|
}
|
|
1721
2687
|
|
|
1722
2688
|
emitDivRem(op, left, right, targetWasmType = null) {
|
|
1723
|
-
|
|
2689
|
+
// An untyped constant left operand takes the type of the typed right
|
|
2690
|
+
// one (`(1<<31) % uint32(n)` divides unsigned).
|
|
2691
|
+
const goType =
|
|
2692
|
+
left._type?.kind === "untyped" &&
|
|
2693
|
+
right._type &&
|
|
2694
|
+
right._type.kind !== "untyped"
|
|
2695
|
+
? right._type
|
|
2696
|
+
: left._type;
|
|
1724
2697
|
const wType = targetWasmType ?? toWasmType(goType, this.mod.checker);
|
|
1725
2698
|
|
|
1726
2699
|
if (wType === "f32" || wType === "f64") {
|
|
@@ -1848,6 +2821,21 @@ export class ExprsEmitter {
|
|
|
1848
2821
|
this.pushInstruction({ op: "local.set", index: tmpVal });
|
|
1849
2822
|
|
|
1850
2823
|
this.emitExpr(right, rightWType);
|
|
2824
|
+
// A negative signed count panics before the shift is evaluated.
|
|
2825
|
+
if (isSigned(right._type) && right.kind !== "BasicLit") {
|
|
2826
|
+
const tmpCount = this.acquireTemp(rightWType);
|
|
2827
|
+
this.pushInstruction({ op: "local.tee", index: tmpCount });
|
|
2828
|
+
this.pushInstruction({
|
|
2829
|
+
op: `${rightWType}.const`,
|
|
2830
|
+
value: rightWType === "i64" ? 0n : 0,
|
|
2831
|
+
});
|
|
2832
|
+
this.pushInstruction(`${rightWType}.lt_s`);
|
|
2833
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
2834
|
+
this.emitPanic("runtime error: negative shift amount");
|
|
2835
|
+
this.pushInstruction("end");
|
|
2836
|
+
this.pushInstruction({ op: "local.get", index: tmpCount });
|
|
2837
|
+
this.releaseTemp(tmpCount, rightWType);
|
|
2838
|
+
}
|
|
1851
2839
|
if (wType === "i64" && rightWType === "i32") {
|
|
1852
2840
|
this.pushInstruction("i64.extend_i32_u");
|
|
1853
2841
|
} else if (wType === "i32" && rightWType === "i64") {
|
|
@@ -1890,32 +2878,91 @@ export class ExprsEmitter {
|
|
|
1890
2878
|
|
|
1891
2879
|
emitTypeConversion(conv) {
|
|
1892
2880
|
const { targetType, expr } = conv;
|
|
1893
|
-
const fromType = expr._type;
|
|
1894
|
-
const fromWType = toWasmType(fromType
|
|
1895
|
-
const toWType = toWasmType(targetType
|
|
2881
|
+
const fromType = expr._type ?? this._resolveExprGoType(expr);
|
|
2882
|
+
const fromWType = this.toWasmType(fromType);
|
|
2883
|
+
const toWType = this.toWasmType(targetType);
|
|
2884
|
+
|
|
2885
|
+
// `[]T(nil)` and `(*T)(nil)`: the target type's nil, not a converted value.
|
|
2886
|
+
if (
|
|
2887
|
+
(expr.kind === "Ident" && expr.name === "nil") ||
|
|
2888
|
+
(expr.kind === "BasicLit" && expr.litKind === "NIL")
|
|
2889
|
+
) {
|
|
2890
|
+
this._emitNilValue(targetType, toWType);
|
|
2891
|
+
return;
|
|
2892
|
+
}
|
|
2893
|
+
|
|
2894
|
+
// `any(x)` boxes x exactly like an assignment to an `any` variable.
|
|
2895
|
+
if (isAnyType(targetType, this.mod.checker)) {
|
|
2896
|
+
this.emitExpr(expr, "anyref");
|
|
2897
|
+
return;
|
|
2898
|
+
}
|
|
1896
2899
|
|
|
1897
2900
|
this.emitExpr(expr, fromWType);
|
|
1898
2901
|
|
|
2902
|
+
if (isComplex(targetType)) {
|
|
2903
|
+
this._emitComplexConversion(targetType, fromType, fromWType);
|
|
2904
|
+
return;
|
|
2905
|
+
}
|
|
2906
|
+
|
|
2907
|
+
// Struct to a different defined struct type: a copy in the target's type.
|
|
2908
|
+
if (
|
|
2909
|
+
isStructType(fromType, this.mod.checker, this.mod) &&
|
|
2910
|
+
isStructType(targetType, this.mod.checker, this.mod) &&
|
|
2911
|
+
!isPointerToStruct(fromType, this.mod.checker, this.mod) &&
|
|
2912
|
+
!isPointerToStruct(targetType, this.mod.checker, this.mod)
|
|
2913
|
+
) {
|
|
2914
|
+
const src = this._resolveStructInfo({ _type: fromType });
|
|
2915
|
+
const dst = this._resolveStructInfo({ _type: targetType });
|
|
2916
|
+
if (src && dst && src.typeIndex !== dst.typeIndex) {
|
|
2917
|
+
this.emitCloneStruct(src, fromWType, dst);
|
|
2918
|
+
return;
|
|
2919
|
+
}
|
|
2920
|
+
}
|
|
2921
|
+
|
|
1899
2922
|
if (fromWType === toWType) {
|
|
1900
2923
|
this.emitNarrowIntWrap(targetType);
|
|
1901
2924
|
return;
|
|
1902
2925
|
}
|
|
1903
2926
|
|
|
2927
|
+
// A named byte slice (`type MyBytes []byte`) converts like `[]byte`.
|
|
2928
|
+
const isByteElem = (t) => {
|
|
2929
|
+
const elem = this._getSliceElemType(t);
|
|
2930
|
+
return elem?.name === "byte" || elem?.name === "uint8";
|
|
2931
|
+
};
|
|
1904
2932
|
if (
|
|
1905
2933
|
fromWType === "externref" &&
|
|
1906
2934
|
isSliceType(targetType, this.mod.checker) &&
|
|
1907
|
-
(targetType
|
|
2935
|
+
isByteElem(targetType)
|
|
1908
2936
|
) {
|
|
1909
|
-
this._emitStringToByteSlice(
|
|
2937
|
+
this._emitStringToByteSlice({
|
|
2938
|
+
kind: "slice",
|
|
2939
|
+
elem: this._getSliceElemType(targetType),
|
|
2940
|
+
});
|
|
1910
2941
|
return;
|
|
1911
2942
|
}
|
|
1912
2943
|
|
|
1913
2944
|
if (
|
|
1914
2945
|
toWType === "externref" &&
|
|
1915
2946
|
isSliceType(fromType, this.mod.checker) &&
|
|
1916
|
-
(fromType
|
|
2947
|
+
isByteElem(fromType)
|
|
1917
2948
|
) {
|
|
1918
|
-
this._emitByteSliceToString(
|
|
2949
|
+
this._emitByteSliceToString({
|
|
2950
|
+
kind: "slice",
|
|
2951
|
+
elem: this._getSliceElemType(fromType),
|
|
2952
|
+
});
|
|
2953
|
+
return;
|
|
2954
|
+
}
|
|
2955
|
+
|
|
2956
|
+
if (
|
|
2957
|
+
fromWType === "externref" &&
|
|
2958
|
+
isRuneSlice(targetType, this.mod.checker)
|
|
2959
|
+
) {
|
|
2960
|
+
this._emitStringToRuneSlice(targetType);
|
|
2961
|
+
return;
|
|
2962
|
+
}
|
|
2963
|
+
|
|
2964
|
+
if (toWType === "externref" && isRuneSlice(fromType, this.mod.checker)) {
|
|
2965
|
+
this._emitRuneSliceToString(fromType);
|
|
1919
2966
|
return;
|
|
1920
2967
|
}
|
|
1921
2968
|
|
|
@@ -1960,6 +3007,64 @@ export class ExprsEmitter {
|
|
|
1960
3007
|
}
|
|
1961
3008
|
}
|
|
1962
3009
|
|
|
3010
|
+
_emitComplexConversion(targetType, fromType, fromWType) {
|
|
3011
|
+
const isTarget64 = isComplex64(targetType);
|
|
3012
|
+
const targetInfo = this.mod.getComplexType(
|
|
3013
|
+
isTarget64 ? "complex64" : "complex128",
|
|
3014
|
+
);
|
|
3015
|
+
if (isComplex(fromType)) {
|
|
3016
|
+
const isFrom64 = isComplex64(fromType);
|
|
3017
|
+
if (isTarget64 === isFrom64) return;
|
|
3018
|
+
const fromInfo = this.mod.getComplexType(
|
|
3019
|
+
isFrom64 ? "complex64" : "complex128",
|
|
3020
|
+
);
|
|
3021
|
+
const fromWTypeRef = {
|
|
3022
|
+
kind: "ref",
|
|
3023
|
+
nullable: true,
|
|
3024
|
+
typeIndex: fromInfo.typeIndex,
|
|
3025
|
+
typeName: fromInfo.name,
|
|
3026
|
+
};
|
|
3027
|
+
const cTmp = this.acquireTemp(fromWTypeRef);
|
|
3028
|
+
this.pushInstruction({ op: "local.set", index: cTmp });
|
|
3029
|
+
|
|
3030
|
+
for (let i = 0; i < 2; i++) {
|
|
3031
|
+
this.pushInstruction({ op: "local.get", index: cTmp });
|
|
3032
|
+
this.pushInstruction({
|
|
3033
|
+
op: "struct.get",
|
|
3034
|
+
typeIndex: fromInfo.typeIndex,
|
|
3035
|
+
typeName: fromInfo.name,
|
|
3036
|
+
fieldIndex: i,
|
|
3037
|
+
});
|
|
3038
|
+
this.pushInstruction(isTarget64 ? "f32.demote_f64" : "f64.promote_f32");
|
|
3039
|
+
}
|
|
3040
|
+
|
|
3041
|
+
this.pushInstruction({
|
|
3042
|
+
op: "struct.new",
|
|
3043
|
+
typeIndex: targetInfo.typeIndex,
|
|
3044
|
+
typeName: targetInfo.name,
|
|
3045
|
+
});
|
|
3046
|
+
this.releaseTemp(cTmp, fromWTypeRef);
|
|
3047
|
+
return;
|
|
3048
|
+
}
|
|
3049
|
+
|
|
3050
|
+
// From numeric scalar
|
|
3051
|
+
const targetF = targetInfo.elemWType;
|
|
3052
|
+
const srcSign = isSigned(fromType) ? "s" : "u";
|
|
3053
|
+
if (fromWType === "i32" || fromWType === "i64") {
|
|
3054
|
+
this.pushInstruction(`${targetF}.convert_${fromWType}_${srcSign}`);
|
|
3055
|
+
} else if (fromWType === "f32" && targetF === "f64") {
|
|
3056
|
+
this.pushInstruction("f64.promote_f32");
|
|
3057
|
+
} else if (fromWType === "f64" && targetF === "f32") {
|
|
3058
|
+
this.pushInstruction("f32.demote_f64");
|
|
3059
|
+
}
|
|
3060
|
+
this.pushInstruction({ op: `${targetF}.const`, value: 0.0 });
|
|
3061
|
+
this.pushInstruction({
|
|
3062
|
+
op: "struct.new",
|
|
3063
|
+
typeIndex: targetInfo.typeIndex,
|
|
3064
|
+
typeName: targetInfo.name,
|
|
3065
|
+
});
|
|
3066
|
+
}
|
|
3067
|
+
|
|
1963
3068
|
_emitStringToByteSlice(targetType) {
|
|
1964
3069
|
const elemGo = targetType.elem;
|
|
1965
3070
|
const sliceInfo = this.mod.getSliceType(elemGo);
|
|
@@ -2030,6 +3135,162 @@ export class ExprsEmitter {
|
|
|
2030
3135
|
this.releaseTemp(strTmp, "externref");
|
|
2031
3136
|
}
|
|
2032
3137
|
|
|
3138
|
+
// Pushes the code point at UTF-16 index `iTmp` of `strTmp` into `cpTmp`
|
|
3139
|
+
// and advances `iTmp` past it (two units for a surrogate pair).
|
|
3140
|
+
_emitNextCodePoint(strTmp, iTmp, cpTmp) {
|
|
3141
|
+
this.pushInstruction({ op: "local.get", index: strTmp });
|
|
3142
|
+
this.pushInstruction({ op: "local.get", index: iTmp });
|
|
3143
|
+
this.pushInstruction({
|
|
3144
|
+
op: "call",
|
|
3145
|
+
funcIndex: this.mod.getStringCodePointAtImportIndex(),
|
|
3146
|
+
});
|
|
3147
|
+
this.pushInstruction({ op: "local.set", index: cpTmp });
|
|
3148
|
+
this.pushInstruction({ op: "local.get", index: iTmp });
|
|
3149
|
+
this.pushInstruction({ op: "i32.const", value: 1 });
|
|
3150
|
+
this.pushInstruction("i32.add");
|
|
3151
|
+
this.pushInstruction({ op: "local.get", index: cpTmp });
|
|
3152
|
+
this.pushInstruction({ op: "i32.const", value: 0xffff });
|
|
3153
|
+
this.pushInstruction("i32.gt_u");
|
|
3154
|
+
this.pushInstruction("i32.add");
|
|
3155
|
+
this.pushInstruction({ op: "local.set", index: iTmp });
|
|
3156
|
+
}
|
|
3157
|
+
|
|
3158
|
+
// Emits `block loop (br_if 1 (i >= n)) body br 0 end end`.
|
|
3159
|
+
_emitCountedLoop(iTmp, nTmp, body) {
|
|
3160
|
+
this.pushInstruction({ op: "block", blockType: "void" });
|
|
3161
|
+
this.pushInstruction({ op: "loop", blockType: "void" });
|
|
3162
|
+
this.pushInstruction({ op: "local.get", index: iTmp });
|
|
3163
|
+
this.pushInstruction({ op: "local.get", index: nTmp });
|
|
3164
|
+
this.pushInstruction("i32.ge_s");
|
|
3165
|
+
this.pushInstruction({ op: "br_if", depth: 1 });
|
|
3166
|
+
body();
|
|
3167
|
+
this.pushInstruction({ op: "br", depth: 0 });
|
|
3168
|
+
this.pushInstruction("end");
|
|
3169
|
+
this.pushInstruction("end");
|
|
3170
|
+
}
|
|
3171
|
+
|
|
3172
|
+
// []rune(s): one pass counts the code points, a second fills an exactly
|
|
3173
|
+
// sized array (cap == len, as for every slice GoFront creates).
|
|
3174
|
+
_emitStringToRuneSlice(targetType) {
|
|
3175
|
+
const sliceInfo = this.mod.getSliceType(this._getSliceElemType(targetType));
|
|
3176
|
+
const arrInfo = sliceInfo.arrInfo;
|
|
3177
|
+
const arrW = { kind: "ref", nullable: true, typeIndex: arrInfo.typeIndex };
|
|
3178
|
+
const strTmp = this.acquireTemp("externref");
|
|
3179
|
+
const unitsTmp = this.acquireTemp("i32");
|
|
3180
|
+
const iTmp = this.acquireTemp("i32");
|
|
3181
|
+
const kTmp = this.acquireTemp("i32");
|
|
3182
|
+
const cpTmp = this.acquireTemp("i32");
|
|
3183
|
+
const arrTmp = this.acquireTemp(arrW);
|
|
3184
|
+
this.pushInstruction({ op: "local.tee", index: strTmp });
|
|
3185
|
+
this.pushInstruction({
|
|
3186
|
+
op: "call",
|
|
3187
|
+
funcIndex: this.mod.getStringLenImportIndex(),
|
|
3188
|
+
});
|
|
3189
|
+
this.pushInstruction({ op: "local.set", index: unitsTmp });
|
|
3190
|
+
const resetIndex = () => {
|
|
3191
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
3192
|
+
this.pushInstruction({ op: "local.set", index: iTmp });
|
|
3193
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
3194
|
+
this.pushInstruction({ op: "local.set", index: kTmp });
|
|
3195
|
+
};
|
|
3196
|
+
const bumpK = () => {
|
|
3197
|
+
this.pushInstruction({ op: "local.get", index: kTmp });
|
|
3198
|
+
this.pushInstruction({ op: "i32.const", value: 1 });
|
|
3199
|
+
this.pushInstruction("i32.add");
|
|
3200
|
+
this.pushInstruction({ op: "local.set", index: kTmp });
|
|
3201
|
+
};
|
|
3202
|
+
resetIndex();
|
|
3203
|
+
this._emitCountedLoop(iTmp, unitsTmp, () => {
|
|
3204
|
+
this._emitNextCodePoint(strTmp, iTmp, cpTmp);
|
|
3205
|
+
bumpK();
|
|
3206
|
+
});
|
|
3207
|
+
this.pushInstruction({ op: "local.get", index: kTmp });
|
|
3208
|
+
this.pushInstruction({
|
|
3209
|
+
op: "array.new_default",
|
|
3210
|
+
typeIndex: arrInfo.typeIndex,
|
|
3211
|
+
});
|
|
3212
|
+
this.pushInstruction({ op: "local.set", index: arrTmp });
|
|
3213
|
+
resetIndex();
|
|
3214
|
+
this._emitCountedLoop(iTmp, unitsTmp, () => {
|
|
3215
|
+
this._emitNextCodePoint(strTmp, iTmp, cpTmp);
|
|
3216
|
+
this.pushInstruction({ op: "local.get", index: arrTmp });
|
|
3217
|
+
this.pushInstruction({ op: "local.get", index: kTmp });
|
|
3218
|
+
this.pushInstruction({ op: "local.get", index: cpTmp });
|
|
3219
|
+
this.pushInstruction({ op: "array.set", typeIndex: arrInfo.typeIndex });
|
|
3220
|
+
bumpK();
|
|
3221
|
+
});
|
|
3222
|
+
this.pushInstruction({ op: "local.get", index: arrTmp });
|
|
3223
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
3224
|
+
this.pushInstruction({ op: "local.get", index: kTmp });
|
|
3225
|
+
this.pushInstruction({ op: "local.get", index: kTmp });
|
|
3226
|
+
this.pushInstruction({ op: "struct.new", typeIndex: sliceInfo.typeIndex });
|
|
3227
|
+
this.releaseTemp(arrTmp, arrW);
|
|
3228
|
+
this.releaseTemp(cpTmp, "i32");
|
|
3229
|
+
this.releaseTemp(kTmp, "i32");
|
|
3230
|
+
this.releaseTemp(iTmp, "i32");
|
|
3231
|
+
this.releaseTemp(unitsTmp, "i32");
|
|
3232
|
+
this.releaseTemp(strTmp, "externref");
|
|
3233
|
+
}
|
|
3234
|
+
|
|
3235
|
+
// string(runes): concatenates `str_from_code_point` of each element.
|
|
3236
|
+
_emitRuneSliceToString(fromType) {
|
|
3237
|
+
const sliceInfo = this.mod.getSliceType(this._getSliceElemType(fromType));
|
|
3238
|
+
const arrInfo = sliceInfo.arrInfo;
|
|
3239
|
+
const sliceW = {
|
|
3240
|
+
kind: "ref",
|
|
3241
|
+
nullable: true,
|
|
3242
|
+
typeIndex: sliceInfo.typeIndex,
|
|
3243
|
+
};
|
|
3244
|
+
const arrW = { kind: "ref", nullable: true, typeIndex: arrInfo.typeIndex };
|
|
3245
|
+
const sTmp = this.acquireTemp(sliceW);
|
|
3246
|
+
this.pushInstruction({ op: "local.set", index: sTmp });
|
|
3247
|
+
const arrTmp = this.acquireTemp(arrW);
|
|
3248
|
+
const offTmp = this.acquireTemp("i32");
|
|
3249
|
+
const lenTmp = this.acquireTemp("i32");
|
|
3250
|
+
const capTmp = this.acquireTemp("i32");
|
|
3251
|
+
this._emitSliceUnpack(sTmp, sliceInfo, {
|
|
3252
|
+
arr: arrTmp,
|
|
3253
|
+
off: offTmp,
|
|
3254
|
+
len: lenTmp,
|
|
3255
|
+
cap: capTmp,
|
|
3256
|
+
});
|
|
3257
|
+
this.releaseTemp(sTmp, sliceW);
|
|
3258
|
+
this.releaseTemp(capTmp, "i32");
|
|
3259
|
+
const accTmp = this.acquireTemp("externref");
|
|
3260
|
+
this.emitBasicLit({ kind: "BasicLit", litKind: "STRING", value: "" });
|
|
3261
|
+
this.pushInstruction({ op: "local.set", index: accTmp });
|
|
3262
|
+
const iTmp = this.acquireTemp("i32");
|
|
3263
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
3264
|
+
this.pushInstruction({ op: "local.set", index: iTmp });
|
|
3265
|
+
this._emitCountedLoop(iTmp, lenTmp, () => {
|
|
3266
|
+
this.pushInstruction({ op: "local.get", index: accTmp });
|
|
3267
|
+
this.pushInstruction({ op: "local.get", index: arrTmp });
|
|
3268
|
+
this.pushInstruction({ op: "local.get", index: offTmp });
|
|
3269
|
+
this.pushInstruction({ op: "local.get", index: iTmp });
|
|
3270
|
+
this.pushInstruction("i32.add");
|
|
3271
|
+
this.pushInstruction({ op: "array.get", typeIndex: arrInfo.typeIndex });
|
|
3272
|
+
this.pushInstruction({
|
|
3273
|
+
op: "call",
|
|
3274
|
+
funcIndex: this.mod.getStringFromCodePointImportIndex(),
|
|
3275
|
+
});
|
|
3276
|
+
this.pushInstruction({
|
|
3277
|
+
op: "call",
|
|
3278
|
+
funcIndex: this.mod.getStringConcatImportIndex(),
|
|
3279
|
+
});
|
|
3280
|
+
this.pushInstruction({ op: "local.set", index: accTmp });
|
|
3281
|
+
this.pushInstruction({ op: "local.get", index: iTmp });
|
|
3282
|
+
this.pushInstruction({ op: "i32.const", value: 1 });
|
|
3283
|
+
this.pushInstruction("i32.add");
|
|
3284
|
+
this.pushInstruction({ op: "local.set", index: iTmp });
|
|
3285
|
+
});
|
|
3286
|
+
this.pushInstruction({ op: "local.get", index: accTmp });
|
|
3287
|
+
this.releaseTemp(iTmp, "i32");
|
|
3288
|
+
this.releaseTemp(accTmp, "externref");
|
|
3289
|
+
this.releaseTemp(lenTmp, "i32");
|
|
3290
|
+
this.releaseTemp(offTmp, "i32");
|
|
3291
|
+
this.releaseTemp(arrTmp, arrW);
|
|
3292
|
+
}
|
|
3293
|
+
|
|
2033
3294
|
_emitByteSliceToString(fromType) {
|
|
2034
3295
|
const elemGo = fromType.elem;
|
|
2035
3296
|
const sliceInfo = this.mod.getSliceType(elemGo);
|