gofront 1.8.0 → 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/package.json +6 -5
- package/src/backend/js/expressions.js +1157 -408
- package/src/backend/js/index.js +327 -153
- package/src/backend/js/natives.js +383 -0
- package/src/backend/js/runtime.js +559 -2552
- package/src/backend/js/statements.js +670 -158
- package/src/backend/js/stdlib/index.js +45 -93
- package/src/backend/js/templ.js +32 -45
- package/src/backend/js/type-info.js +298 -19
- package/src/backend/shared/fixed-float.js +20 -0
- package/src/backend/shared/fmt-types.js +91 -170
- package/src/backend/shared/host-unicode.js +36 -0
- package/src/backend/wasm/boundary.js +490 -965
- package/src/backend/wasm/emit-builtins.js +884 -256
- package/src/backend/wasm/emit-exprs.js +185 -77
- package/src/backend/wasm/emit-maps.js +17 -367
- package/src/backend/wasm/emit-shared.js +4 -1
- package/src/backend/wasm/emit-stmts.js +524 -240
- package/src/backend/wasm/emit.js +176 -38
- package/src/backend/wasm/globals.js +48 -8
- package/src/backend/wasm/glue.js +2 -4
- package/src/backend/wasm/index.js +44 -530
- package/src/backend/wasm/method-expr.js +12 -1
- package/src/backend/wasm/module-emitter.js +299 -770
- package/src/backend/wasm/monomorph.js +139 -8
- package/src/backend/wasm/natives.js +600 -0
- package/src/backend/wasm/prescan.js +5 -153
- package/src/backend/wasm/prune-funcs.js +89 -0
- package/src/backend/wasm/stdlib/index.js +2 -32
- package/src/backend/wasm/types.js +97 -20
- package/src/backend/wasm/variadic.js +9 -4
- package/src/cli/cli-core.js +5 -1
- package/src/cli/test-runner.js +115 -342
- package/src/compiler.js +113 -130
- package/src/lexer/lexer.js +106 -11
- package/src/lexer/templ-lexer.js +61 -18
- package/src/lexer/tokens.js +1 -0
- package/src/link/stdlib.js +355 -132
- package/src/link/type-names.js +204 -0
- package/src/link/wasm.js +30 -6
- package/src/lower/bounds.js +153 -0
- package/src/lower/captures.js +92 -33
- package/src/lower/embedding.js +62 -5
- package/src/lower/errors-as.js +196 -0
- package/src/lower/fmt-formatters.js +929 -0
- package/src/lower/functions.js +225 -7
- package/src/lower/gen-types.js +293 -0
- package/src/lower/index.js +14 -2
- package/src/lower/json-codecs.js +334 -245
- package/src/lower/ownership.js +25 -7
- package/src/lower/reachability.js +69 -51
- package/src/lower/walk.js +12 -0
- package/src/parser/declarations.js +30 -5
- package/src/parser/expressions.js +111 -57
- package/src/parser/statements.js +108 -0
- package/src/parser/templ.js +44 -5
- package/src/parser/types.js +12 -0
- package/src/resolver.js +1 -2
- package/src/typechecker/assignability.js +214 -19
- package/src/typechecker/const-fold.js +70 -27
- package/src/typechecker/constants.js +156 -8
- package/src/typechecker/expressions.js +490 -108
- package/src/typechecker/index.js +346 -42
- package/src/typechecker/init-order.js +136 -40
- package/src/typechecker/operand-text.js +41 -0
- package/src/typechecker/resolve.js +490 -65
- package/src/typechecker/statements.js +288 -27
- package/src/typechecker/stdlib/excluded.js +9 -5
- package/src/typechecker/stdlib/index.js +25 -9
- package/src/typechecker/termination.js +10 -1
- package/src/typechecker/types.js +287 -575
- package/stdlib/bytes/buffer.go +479 -0
- package/stdlib/bytes/bytes.go +1010 -0
- package/stdlib/bytes/reader.go +161 -0
- package/stdlib/context/context.go +115 -0
- package/stdlib/crypto/rand/rand.go +34 -0
- package/stdlib/crypto/sha256/sha256.go +249 -0
- package/stdlib/encoding/base64/base64.go +331 -0
- package/stdlib/encoding/binary/binary.go +332 -0
- package/stdlib/encoding/hex/hex.go +140 -0
- package/stdlib/encoding/json/json.go +62 -0
- package/stdlib/errors/errors.go +174 -0
- package/stdlib/fmt/errors.go +110 -0
- package/stdlib/fmt/format.go +732 -0
- package/stdlib/fmt/print.go +1232 -0
- package/stdlib/gom/gom.go +293 -0
- package/stdlib/html/html.go +251 -0
- package/stdlib/internal/fmtrt/fmtrt.go +163 -0
- package/stdlib/internal/jsonrt/dec.go +171 -38
- package/stdlib/internal/jsonrt/enc.go +186 -36
- package/stdlib/internal/jsonrt/scanner.go +17 -8
- package/stdlib/io/io.go +157 -1
- package/stdlib/maps/maps.go +131 -0
- package/stdlib/math/bits/bits.go +507 -0
- package/stdlib/math/extra.go +16 -14
- package/stdlib/math/math.go +439 -0
- package/stdlib/math/rand/rand.go +297 -0
- package/stdlib/os/os.go +93 -0
- package/stdlib/path/filepath/filepath.go +47 -0
- package/stdlib/path/path.go +394 -0
- package/stdlib/regexp/regexp.go +231 -0
- package/stdlib/slices/slices.go +526 -0
- package/stdlib/slices/sort.go +209 -0
- package/stdlib/slices/zsort_cmp.go +480 -0
- package/stdlib/slices/zsort_ordered.go +482 -0
- package/stdlib/sort/sort.go +391 -452
- package/stdlib/sort/zsort_func.go +480 -0
- package/stdlib/sort/zsort_iface.go +480 -0
- package/stdlib/strconv/float.go +1699 -0
- package/stdlib/strconv/isprint.go +752 -0
- package/stdlib/strconv/quote.go +235 -50
- package/stdlib/strconv/strconv.go +616 -0
- package/stdlib/strings/builder.go +95 -0
- package/stdlib/strings/reader.go +163 -0
- package/stdlib/strings/replacer.go +102 -0
- package/stdlib/strings/strings.go +807 -0
- package/stdlib/sync/sync.go +268 -0
- package/stdlib/testing/async_js.go +61 -0
- package/stdlib/testing/benchmark.go +297 -0
- package/stdlib/testing/testing.go +523 -0
- package/stdlib/time/duration.go +233 -0
- package/stdlib/time/format.go +745 -0
- package/stdlib/time/parse.go +790 -0
- package/stdlib/time/sleep_js.go +63 -0
- package/stdlib/time/time.go +834 -0
- package/stdlib/unicode/unicode.go +141 -3
- package/stdlib/unicode/utf8/utf8.go +464 -0
- package/src/backend/js/fmt-runtime.js +0 -573
- package/src/backend/js/stdlib/base64.js +0 -12
- package/src/backend/js/stdlib/binary.js +0 -102
- package/src/backend/js/stdlib/bits.js +0 -119
- package/src/backend/js/stdlib/builder.js +0 -98
- package/src/backend/js/stdlib/bytes.js +0 -368
- package/src/backend/js/stdlib/errors.js +0 -45
- package/src/backend/js/stdlib/fmt.js +0 -517
- package/src/backend/js/stdlib/gom.js +0 -155
- package/src/backend/js/stdlib/hex.js +0 -17
- package/src/backend/js/stdlib/html.js +0 -20
- package/src/backend/js/stdlib/io.js +0 -137
- package/src/backend/js/stdlib/json.js +0 -1167
- package/src/backend/js/stdlib/maps.js +0 -37
- package/src/backend/js/stdlib/math.js +0 -85
- package/src/backend/js/stdlib/os.js +0 -13
- package/src/backend/js/stdlib/path.js +0 -48
- package/src/backend/js/stdlib/rand.js +0 -32
- package/src/backend/js/stdlib/regexp.js +0 -54
- package/src/backend/js/stdlib/sha256.js +0 -12
- package/src/backend/js/stdlib/shared.js +0 -17
- package/src/backend/js/stdlib/simd.js +0 -34
- package/src/backend/js/stdlib/slices.js +0 -107
- package/src/backend/js/stdlib/sort.js +0 -30
- package/src/backend/js/stdlib/strconv.js +0 -79
- package/src/backend/js/stdlib/strings.js +0 -301
- package/src/backend/js/stdlib/sync.js +0 -13
- package/src/backend/js/stdlib/testing.js +0 -41
- package/src/backend/js/stdlib/time.js +0 -152
- package/src/backend/js/stdlib/unicode.js +0 -25
- package/src/backend/js/stdlib/utf8.js +0 -26
- package/src/backend/wasm/stdlib/bytes.js +0 -6716
- package/src/backend/wasm/stdlib/core.js +0 -10278
- package/src/backend/wasm/stdlib/io.js +0 -3578
- package/src/backend/wasm/stdlib/packages.js +0 -2402
- package/src/backend/wasm/stdlib/pure.js +0 -720
- package/src/backend/wasm/stdlib/time.js +0 -2229
- package/src/typechecker/stdlib/core.js +0 -1079
- package/src/typechecker/stdlib/extended.js +0 -1210
- package/stdlib/bytes/iter.go +0 -192
- package/stdlib/bytes/search.go +0 -240
- package/stdlib/bytes/split.go +0 -218
- package/stdlib/errors/overlay.go +0 -12
- package/stdlib/maps/iter.go +0 -65
- package/stdlib/slices/extra.go +0 -73
- package/stdlib/slices/iter.go +0 -113
- package/stdlib/strings/extra.go +0 -35
- package/stdlib/strings/iter.go +0 -122
- package/stdlib/sync/extra.go +0 -105
- package/stdlib/unicode/utf8/bytes.go +0 -239
|
@@ -2,13 +2,16 @@
|
|
|
2
2
|
// FunctionEmitter mixin: panic/recover, Go builtins (len, append, make, …) and
|
|
3
3
|
// call expression dispatch (static, method, closure, package).
|
|
4
4
|
|
|
5
|
+
import { hasCallOrReceive } from "../../lower/walk.js";
|
|
6
|
+
import { goTypeName } from "../shared/fmt-types.js";
|
|
5
7
|
import {
|
|
6
8
|
anonIfaceName,
|
|
9
|
+
ERROR_STRUCT,
|
|
7
10
|
getMapKeyValTypes,
|
|
8
11
|
isArrayType,
|
|
9
|
-
isBoolGoType,
|
|
10
12
|
isComplex,
|
|
11
13
|
isComplex64,
|
|
14
|
+
isFuncType,
|
|
12
15
|
isMapType,
|
|
13
16
|
isNonEmptyInterface,
|
|
14
17
|
isScalarType,
|
|
@@ -16,7 +19,8 @@ import {
|
|
|
16
19
|
isSliceType,
|
|
17
20
|
isStringType,
|
|
18
21
|
isStructType,
|
|
19
|
-
|
|
22
|
+
isUnhashableGoType,
|
|
23
|
+
printOperandKind,
|
|
20
24
|
toWasmType,
|
|
21
25
|
} from "./types.js";
|
|
22
26
|
|
|
@@ -26,13 +30,32 @@ export class BuiltinsEmitter {
|
|
|
26
30
|
const funcIdx = this.mod.getStringImportIndex();
|
|
27
31
|
this.pushInstruction({ op: "i32.const", value: strIdx });
|
|
28
32
|
this.pushInstruction({ op: "call", funcIndex: funcIdx });
|
|
29
|
-
this.
|
|
30
|
-
this.emitPanicThrow();
|
|
33
|
+
this.emitRuntimePanicThrow();
|
|
31
34
|
}
|
|
32
35
|
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
+
// Raises a Go runtime error from the externref message on the stack. A
|
|
37
|
+
// program that uses `error` recovers it as an `error` value (as Go's
|
|
38
|
+
// `runtime.Error`); the host still reports the message text.
|
|
39
|
+
emitRuntimePanicThrow() {
|
|
40
|
+
if (!this.mod.hasErrorType) {
|
|
41
|
+
this.pushInstruction("any.convert_extern");
|
|
42
|
+
this.emitPanicThrow();
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const msgTmp = this.acquireTemp("externref");
|
|
46
|
+
this.pushInstruction({ op: "local.tee", index: msgTmp });
|
|
47
|
+
this.pushInstruction({
|
|
48
|
+
op: "call",
|
|
49
|
+
funcIndex: this.mod.resolveFuncIndex("__mk_error"),
|
|
50
|
+
});
|
|
51
|
+
this.emitPanicThrow(msgTmp);
|
|
52
|
+
this.releaseTemp(msgTmp, "externref");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Consumes the anyref value on the stack, pushes a PanicNode, and raises the
|
|
56
|
+
// panic via `env.panic` (with the value, or the externref message in
|
|
57
|
+
// `hostMsgTmp`); `unreachable` tells the validator control never returns.
|
|
58
|
+
emitPanicThrow(hostMsgTmp = null) {
|
|
36
59
|
const panicNodeTypeIndex = this.mod.getPanicNodeTypeIndex();
|
|
37
60
|
const panicGlobal = this.mod.getPanicGlobalIndex();
|
|
38
61
|
const tmpVal = this.acquireTemp("anyref");
|
|
@@ -49,8 +72,12 @@ export class BuiltinsEmitter {
|
|
|
49
72
|
this.pushInstruction({ op: "global.set", index: panicGlobal });
|
|
50
73
|
|
|
51
74
|
// Prepare externref for env.panic
|
|
52
|
-
|
|
53
|
-
|
|
75
|
+
if (hostMsgTmp !== null) {
|
|
76
|
+
this.pushInstruction({ op: "local.get", index: hostMsgTmp });
|
|
77
|
+
} else {
|
|
78
|
+
this.pushInstruction({ op: "local.get", index: tmpVal });
|
|
79
|
+
this.pushInstruction("extern.convert_any");
|
|
80
|
+
}
|
|
54
81
|
this.pushInstruction({
|
|
55
82
|
op: "call",
|
|
56
83
|
funcIndex: this.mod.getPanicImportIndex(),
|
|
@@ -71,9 +98,32 @@ export class BuiltinsEmitter {
|
|
|
71
98
|
return null;
|
|
72
99
|
}
|
|
73
100
|
|
|
101
|
+
// `len`/`cap` of an array or array pointer: the constant length. An
|
|
102
|
+
// operand containing a call or receive is still evaluated, and its value
|
|
103
|
+
// (a nil array pointer included) is dropped without being dereferenced.
|
|
104
|
+
_emitConstArrayLen(call, targetWasmType) {
|
|
105
|
+
const arg = call.args[0];
|
|
106
|
+
if (hasCallOrReceive(arg)) {
|
|
107
|
+
this.emitExpr(
|
|
108
|
+
arg,
|
|
109
|
+
this.toWasmType(arg._type ?? this._resolveExprGoType(arg)),
|
|
110
|
+
);
|
|
111
|
+
this.pushInstruction("drop");
|
|
112
|
+
}
|
|
113
|
+
this.pushInstruction({ op: "i32.const", value: call._constLen });
|
|
114
|
+
if ((targetWasmType ?? "i64") === "i64")
|
|
115
|
+
this.pushInstruction("i64.extend_i32_u");
|
|
116
|
+
}
|
|
117
|
+
|
|
74
118
|
_emitBuiltinLen(call, targetWasmType) {
|
|
75
119
|
const arg = call.args[0];
|
|
76
120
|
const argType = arg._type ?? this._resolveExprGoType(arg);
|
|
121
|
+
// `len` of an array (or a pointer to one) is a constant, and the
|
|
122
|
+
// operand is not evaluated unless it contains a call or a receive.
|
|
123
|
+
if (call._constLen != null) {
|
|
124
|
+
this._emitConstArrayLen(call, targetWasmType);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
77
127
|
if (this._sharedInfo(argType)) {
|
|
78
128
|
this.emitSharedLen(arg, targetWasmType);
|
|
79
129
|
return;
|
|
@@ -141,6 +191,10 @@ export class BuiltinsEmitter {
|
|
|
141
191
|
_emitBuiltinCap(call, targetWasmType) {
|
|
142
192
|
const arg = call.args[0];
|
|
143
193
|
const argType = arg._type;
|
|
194
|
+
if (call._constLen != null) {
|
|
195
|
+
this._emitConstArrayLen(call, targetWasmType);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
144
198
|
if (isSliceType(argType, this.mod.checker)) {
|
|
145
199
|
const elemType = this._getSliceElemType(argType);
|
|
146
200
|
const sliceInfo = this.mod.getSliceType(elemType);
|
|
@@ -223,16 +277,19 @@ export class BuiltinsEmitter {
|
|
|
223
277
|
this.pushInstruction({ op: "local.set", index: capTmp });
|
|
224
278
|
}
|
|
225
279
|
|
|
280
|
+
// Go: a negative len is a len panic; otherwise cap < len is a cap panic.
|
|
226
281
|
this.pushInstruction({ op: "local.get", index: lenTmp });
|
|
227
|
-
this.pushInstruction({ op: "local.get", index: capTmp });
|
|
228
|
-
this.pushInstruction("i32.gt_u");
|
|
229
|
-
this.pushInstruction({ op: "local.get", index: capTmp });
|
|
230
282
|
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
231
283
|
this.pushInstruction("i32.lt_s");
|
|
232
|
-
this.pushInstruction("i32.or");
|
|
233
284
|
this.pushInstruction({ op: "if", blockType: "void" });
|
|
234
285
|
this.emitPanic("runtime error: makeslice: len out of range");
|
|
235
286
|
this.pushInstruction("end");
|
|
287
|
+
this.pushInstruction({ op: "local.get", index: capTmp });
|
|
288
|
+
this.pushInstruction({ op: "local.get", index: lenTmp });
|
|
289
|
+
this.pushInstruction("i32.lt_s");
|
|
290
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
291
|
+
this.emitPanic("runtime error: makeslice: cap out of range");
|
|
292
|
+
this.pushInstruction("end");
|
|
236
293
|
|
|
237
294
|
if (
|
|
238
295
|
isStructType(elemGoType, this.mod.checker, this.mod) ||
|
|
@@ -294,11 +351,9 @@ export class BuiltinsEmitter {
|
|
|
294
351
|
typeIndex: arrInfo.typeIndex,
|
|
295
352
|
});
|
|
296
353
|
} else {
|
|
297
|
-
this.
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
typeIndex: arrInfo.typeIndex,
|
|
301
|
-
});
|
|
354
|
+
this._emitNewArray(elemGoType, arrInfo, () =>
|
|
355
|
+
this.pushInstruction({ op: "local.get", index: capTmp }),
|
|
356
|
+
);
|
|
302
357
|
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
303
358
|
this.pushInstruction({ op: "local.get", index: lenTmp });
|
|
304
359
|
this.pushInstruction({ op: "local.get", index: capTmp });
|
|
@@ -397,11 +452,9 @@ export class BuiltinsEmitter {
|
|
|
397
452
|
this.pushInstruction({ op: "local.set", index: newCapTmp });
|
|
398
453
|
this.pushInstruction("end");
|
|
399
454
|
|
|
400
|
-
this.
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
typeIndex: arrInfo.typeIndex,
|
|
404
|
-
});
|
|
455
|
+
this._emitNewArray(elemGoType, arrInfo, () =>
|
|
456
|
+
this.pushInstruction({ op: "local.get", index: newCapTmp }),
|
|
457
|
+
);
|
|
405
458
|
this.pushInstruction({ op: "local.set", index: newArrTmp });
|
|
406
459
|
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
407
460
|
this.pushInstruction({ op: "local.set", index: newOffTmp });
|
|
@@ -731,13 +784,21 @@ export class BuiltinsEmitter {
|
|
|
731
784
|
for (let i = 0; i < args.length; i++) {
|
|
732
785
|
const arg = args[i];
|
|
733
786
|
const isLast = i === args.length - 1 && isPrintln;
|
|
734
|
-
const isBool =
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
787
|
+
const { wType, isBool, widen } = printOperandKind(arg, this.mod.checker);
|
|
788
|
+
this.emitExpr(
|
|
789
|
+
arg,
|
|
790
|
+
widen ? "i32" : toWasmType(arg._type, this.mod.checker),
|
|
791
|
+
);
|
|
792
|
+
if (widen) this.pushInstruction("i64.extend_i32_u");
|
|
739
793
|
const logFuncIdx = this.mod.getLogImportIndex(wType, isLast, isBool);
|
|
740
794
|
this.pushInstruction({ op: "call", funcIndex: logFuncIdx });
|
|
795
|
+
// `print` never separates operands; `println` always does.
|
|
796
|
+
if (!isPrintln) {
|
|
797
|
+
this.pushInstruction({
|
|
798
|
+
op: "call",
|
|
799
|
+
funcIndex: this.mod.getPrintResetIndex(),
|
|
800
|
+
});
|
|
801
|
+
}
|
|
741
802
|
}
|
|
742
803
|
}
|
|
743
804
|
|
|
@@ -753,7 +814,11 @@ export class BuiltinsEmitter {
|
|
|
753
814
|
} else {
|
|
754
815
|
const goType = this._resolveExprGoType(arg);
|
|
755
816
|
const wType = toWasmType(goType, this.mod.checker);
|
|
756
|
-
if (
|
|
817
|
+
if (goType?.kind === "named" && wType === "externref") {
|
|
818
|
+
// A defined string type keeps its identity (and methods) in the
|
|
819
|
+
// panic value, so `recover()` returns it, not the bare string.
|
|
820
|
+
this.emitExpr(arg, "anyref");
|
|
821
|
+
} else if (
|
|
757
822
|
isStringType(goType, this.mod.checker) ||
|
|
758
823
|
(arg.kind === "BasicLit" && arg.litKind === "STRING") ||
|
|
759
824
|
wType === "externref"
|
|
@@ -868,7 +933,7 @@ export class BuiltinsEmitter {
|
|
|
868
933
|
callType?.name === "complex64" ||
|
|
869
934
|
arg0._type?.name === "float32" ||
|
|
870
935
|
arg1._type?.name === "float32" ||
|
|
871
|
-
targetWasmType
|
|
936
|
+
this._isComplex64Target(targetWasmType);
|
|
872
937
|
const complexName = is64 ? "complex64" : "complex128";
|
|
873
938
|
const info = this.mod.getComplexType(complexName);
|
|
874
939
|
this.emitExpr(arg0, info.elemWType);
|
|
@@ -1052,10 +1117,6 @@ export class BuiltinsEmitter {
|
|
|
1052
1117
|
_emitPackageCall(call, targetWasmType) {
|
|
1053
1118
|
const { func, args } = call;
|
|
1054
1119
|
if (func.kind !== "SelectorExpr") return false;
|
|
1055
|
-
if (isTestingT(func.expr?._type)) {
|
|
1056
|
-
this.emitTestingCall(func, args);
|
|
1057
|
-
return true;
|
|
1058
|
-
}
|
|
1059
1120
|
if (
|
|
1060
1121
|
func.expr?.name &&
|
|
1061
1122
|
(this.resolveLocal(func.expr.name) ||
|
|
@@ -1064,97 +1125,9 @@ export class BuiltinsEmitter {
|
|
|
1064
1125
|
return false;
|
|
1065
1126
|
const pkgName = func.expr?.name;
|
|
1066
1127
|
switch (pkgName) {
|
|
1067
|
-
case "math":
|
|
1068
|
-
this.emitMathCall(func.field, args);
|
|
1069
|
-
return true;
|
|
1070
|
-
case "bits":
|
|
1071
|
-
this.emitBitsCall(func.field, args);
|
|
1072
|
-
return true;
|
|
1073
|
-
case "maps":
|
|
1074
|
-
this.emitMapsCall(func.field, args, targetWasmType);
|
|
1075
|
-
return true;
|
|
1076
|
-
case "slices":
|
|
1077
|
-
this.emitSlicesCall(func.field, args, targetWasmType);
|
|
1078
|
-
return true;
|
|
1079
|
-
case "strings":
|
|
1080
|
-
if (func.field === "NewReader") {
|
|
1081
|
-
this.emitStringsNewReader(call, targetWasmType);
|
|
1082
|
-
return true;
|
|
1083
|
-
}
|
|
1084
|
-
this.emitStringsCall(func.field, args, targetWasmType);
|
|
1085
|
-
return true;
|
|
1086
|
-
case "strconv":
|
|
1087
|
-
this.emitStrconvCall(func.field, args);
|
|
1088
|
-
return true;
|
|
1089
|
-
case "fmt":
|
|
1090
|
-
if (func.field.startsWith("Fprint")) {
|
|
1091
|
-
this.emitFprintCall(func.field, call, targetWasmType);
|
|
1092
|
-
return true;
|
|
1093
|
-
}
|
|
1094
|
-
if (func.field.startsWith("Sscan")) {
|
|
1095
|
-
this.emitSscanCall(func.field, call, targetWasmType);
|
|
1096
|
-
return true;
|
|
1097
|
-
}
|
|
1098
|
-
this.emitFmtCall(func.field, args);
|
|
1099
|
-
return true;
|
|
1100
|
-
case "errors":
|
|
1101
|
-
this.emitErrorsCall(func.field, args);
|
|
1102
|
-
return true;
|
|
1103
|
-
case "utf8":
|
|
1104
|
-
this.emitUtf8Call(func.field, args, targetWasmType);
|
|
1105
|
-
return true;
|
|
1106
|
-
case "sort":
|
|
1107
|
-
this.emitSortCall(func.field, args, targetWasmType);
|
|
1108
|
-
return true;
|
|
1109
1128
|
case "shared":
|
|
1110
1129
|
this.emitSharedNew(func.field, args);
|
|
1111
1130
|
return true;
|
|
1112
|
-
case "unicode":
|
|
1113
|
-
this.emitUnicodeCall(func.field, args, targetWasmType);
|
|
1114
|
-
return true;
|
|
1115
|
-
case "rand":
|
|
1116
|
-
this.emitRandCall(func.field, args, targetWasmType);
|
|
1117
|
-
return true;
|
|
1118
|
-
case "bytes":
|
|
1119
|
-
if (func.field === "NewReader") {
|
|
1120
|
-
this.emitBytesNewReader(call, targetWasmType);
|
|
1121
|
-
return true;
|
|
1122
|
-
}
|
|
1123
|
-
if (func.field === "NewBuffer") {
|
|
1124
|
-
this.emitBytesNewBuffer(call, targetWasmType);
|
|
1125
|
-
return true;
|
|
1126
|
-
}
|
|
1127
|
-
if (func.field === "NewBufferString") {
|
|
1128
|
-
this.emitBytesNewBufferString(call, targetWasmType);
|
|
1129
|
-
return true;
|
|
1130
|
-
}
|
|
1131
|
-
this.emitBytesCall(func.field, args, targetWasmType);
|
|
1132
|
-
return true;
|
|
1133
|
-
case "path":
|
|
1134
|
-
case "filepath":
|
|
1135
|
-
this.emitPathCall(func.field, args);
|
|
1136
|
-
return true;
|
|
1137
|
-
case "html":
|
|
1138
|
-
this.emitHtmlCall(func.field, args);
|
|
1139
|
-
return true;
|
|
1140
|
-
case "io":
|
|
1141
|
-
this.emitIoCall(func.field, call, targetWasmType);
|
|
1142
|
-
return true;
|
|
1143
|
-
case "time":
|
|
1144
|
-
this.emitTimeCall(func.field, call, targetWasmType);
|
|
1145
|
-
return true;
|
|
1146
|
-
case "binary":
|
|
1147
|
-
this.emitBinaryCall(func.field, call, targetWasmType);
|
|
1148
|
-
return true;
|
|
1149
|
-
case "sha256":
|
|
1150
|
-
this.emitSha256Call(func.field, call, targetWasmType);
|
|
1151
|
-
return true;
|
|
1152
|
-
case "hex":
|
|
1153
|
-
this.emitHexCall(func.field, call, targetWasmType);
|
|
1154
|
-
return true;
|
|
1155
|
-
case "base64":
|
|
1156
|
-
this.emitBase64Call(func.field, call, targetWasmType);
|
|
1157
|
-
return true;
|
|
1158
1131
|
case "simd":
|
|
1159
1132
|
this.emitSimdPackageCall(func.field, args, targetWasmType);
|
|
1160
1133
|
return true;
|
|
@@ -1191,25 +1164,6 @@ export class BuiltinsEmitter {
|
|
|
1191
1164
|
recvType?.simd === "Mask32x4",
|
|
1192
1165
|
);
|
|
1193
1166
|
}
|
|
1194
|
-
if (
|
|
1195
|
-
this._emitStdlibReceiverMethod(
|
|
1196
|
-
recvTypeName,
|
|
1197
|
-
func.field,
|
|
1198
|
-
func.expr,
|
|
1199
|
-
args,
|
|
1200
|
-
targetWasmType,
|
|
1201
|
-
)
|
|
1202
|
-
) {
|
|
1203
|
-
return true;
|
|
1204
|
-
}
|
|
1205
|
-
if (recvTypeName === "strings.Builder") {
|
|
1206
|
-
this.emitBuilderMethodCall(func.field, func.expr, args);
|
|
1207
|
-
return true;
|
|
1208
|
-
}
|
|
1209
|
-
if (recvTypeName === "strings.Replacer") {
|
|
1210
|
-
this.emitReplacerMethodCall(func.field, func.expr, args);
|
|
1211
|
-
return true;
|
|
1212
|
-
}
|
|
1213
1167
|
if (isNonEmptyInterface(recvType, this.mod.checker)) {
|
|
1214
1168
|
const ifaceName = recvTypeName ?? anonIfaceName(recvType);
|
|
1215
1169
|
const dispatchName = `__dispatch_${ifaceName}_${func.field}`;
|
|
@@ -1303,128 +1257,72 @@ export class BuiltinsEmitter {
|
|
|
1303
1257
|
};
|
|
1304
1258
|
}
|
|
1305
1259
|
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
this.emitStringsReaderMethodCall(field, expr, args, targetWasmType);
|
|
1327
|
-
return true;
|
|
1328
|
-
}
|
|
1329
|
-
if (recvTypeName === "bytes.Reader") {
|
|
1330
|
-
this.emitBytesReaderMethodCall(field, expr, args, targetWasmType);
|
|
1331
|
-
return true;
|
|
1332
|
-
}
|
|
1333
|
-
if (recvTypeName === "time.Time") {
|
|
1334
|
-
this.emitTimeMethodCall(field, expr, args, targetWasmType);
|
|
1335
|
-
return true;
|
|
1336
|
-
}
|
|
1337
|
-
if (recvTypeName === "time.Duration") {
|
|
1338
|
-
this.emitDurationMethodCall(field, expr, args, targetWasmType);
|
|
1339
|
-
return true;
|
|
1340
|
-
}
|
|
1341
|
-
if (
|
|
1342
|
-
recvTypeName === "time.Month" ||
|
|
1343
|
-
recvTypeName === "time.Weekday" ||
|
|
1344
|
-
recvTypeName === "time.Location"
|
|
1345
|
-
) {
|
|
1346
|
-
this.emitTimeAuxMethodCall(
|
|
1347
|
-
recvTypeName,
|
|
1348
|
-
field,
|
|
1349
|
-
expr,
|
|
1350
|
-
args,
|
|
1351
|
-
targetWasmType,
|
|
1352
|
-
);
|
|
1353
|
-
return true;
|
|
1354
|
-
}
|
|
1355
|
-
if (recvTypeName === "io.LimitedReader") {
|
|
1356
|
-
this.emitIoLimitedReaderMethodCall(field, expr, args, targetWasmType);
|
|
1357
|
-
return true;
|
|
1358
|
-
}
|
|
1359
|
-
if (
|
|
1360
|
-
(recvTypeName === "sync.Mutex" || recvTypeName === "sync.RWMutex") &&
|
|
1361
|
-
field !== "RLocker"
|
|
1362
|
-
) {
|
|
1363
|
-
this.emitSyncMutexMethodCall(field, expr, args, targetWasmType);
|
|
1364
|
-
return true;
|
|
1365
|
-
}
|
|
1366
|
-
if (recvTypeName === "sync.Once") {
|
|
1367
|
-
this.emitSyncOnceMethodCall(field, expr, args, targetWasmType);
|
|
1368
|
-
return true;
|
|
1369
|
-
}
|
|
1370
|
-
if (recvTypeName === "sync.WaitGroup") {
|
|
1371
|
-
this.emitSyncWaitGroupMethodCall(field, expr, args, targetWasmType);
|
|
1372
|
-
return true;
|
|
1373
|
-
}
|
|
1374
|
-
if (
|
|
1375
|
-
recvTypeName === "binary.LittleEndian" ||
|
|
1376
|
-
recvTypeName === "binary.BigEndian" ||
|
|
1377
|
-
recvTypeName === "encoding/binary.LittleEndian" ||
|
|
1378
|
-
recvTypeName === "encoding/binary.BigEndian" ||
|
|
1379
|
-
recvTypeName === "binary.ByteOrder" ||
|
|
1380
|
-
recvTypeName === "encoding/binary.ByteOrder"
|
|
1381
|
-
) {
|
|
1382
|
-
this.emitBinaryOrderMethodCall(
|
|
1383
|
-
recvTypeName,
|
|
1384
|
-
field,
|
|
1385
|
-
expr,
|
|
1386
|
-
args,
|
|
1387
|
-
targetWasmType,
|
|
1388
|
-
);
|
|
1389
|
-
return true;
|
|
1260
|
+
// The embedded struct (possibly several levels down) that declares or
|
|
1261
|
+
// forwards `method`: the chain of embeds to follow and the method's name.
|
|
1262
|
+
_findEmbeddedMethod(structInfo, method, seen = new Set()) {
|
|
1263
|
+
if (seen.has(structInfo)) return null;
|
|
1264
|
+
seen.add(structInfo);
|
|
1265
|
+
for (const embed of structInfo.embeds ?? []) {
|
|
1266
|
+
const typeName = embed.typeName ?? embed.name;
|
|
1267
|
+
const methodName = `${typeName}.${method}`;
|
|
1268
|
+
const ifaceType = structInfo.fields[embed.fieldIndex]?.goType;
|
|
1269
|
+
if (this.mod.embeddedInterfaceHas(ifaceType, method))
|
|
1270
|
+
return { chain: [{ structInfo, embed }], ifaceType };
|
|
1271
|
+
if (this.mod.resolveFuncIndex(methodName) !== null)
|
|
1272
|
+
return { chain: [{ structInfo, embed }], methodName };
|
|
1273
|
+
const inner = this.mod.getStructType(typeName);
|
|
1274
|
+
const deeper = inner && this._findEmbeddedMethod(inner, method, seen);
|
|
1275
|
+
if (deeper)
|
|
1276
|
+
return {
|
|
1277
|
+
...deeper,
|
|
1278
|
+
chain: [{ structInfo, embed }, ...deeper.chain],
|
|
1279
|
+
};
|
|
1390
1280
|
}
|
|
1391
|
-
|
|
1392
|
-
recvTypeName === "base64.Encoding" ||
|
|
1393
|
-
recvTypeName === "encoding/base64.Encoding"
|
|
1394
|
-
) {
|
|
1395
|
-
this.emitBase64EncodingMethodCall(field, expr, args, targetWasmType);
|
|
1396
|
-
return true;
|
|
1397
|
-
}
|
|
1398
|
-
return false;
|
|
1281
|
+
return null;
|
|
1399
1282
|
}
|
|
1400
1283
|
|
|
1401
1284
|
_emitEmbeddedMethodCall(recvTypeName, func, args, recvType) {
|
|
1402
1285
|
const structInfo = this.mod.getStructType(recvTypeName);
|
|
1403
1286
|
if (!structInfo) return false;
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1287
|
+
const found = this._findEmbeddedMethod(structInfo, func.field);
|
|
1288
|
+
if (!found) return false;
|
|
1289
|
+
const { chain, methodName, ifaceType } = found;
|
|
1290
|
+
// A method promoted from an embedded interface is dispatched on the
|
|
1291
|
+
// interface value the last embed holds.
|
|
1292
|
+
const dispatchName = ifaceType
|
|
1293
|
+
? `__dispatch_${this._getReceiverTypeName(ifaceType)}_${func.field}`
|
|
1294
|
+
: null;
|
|
1295
|
+
const subName = dispatchName ?? methodName;
|
|
1296
|
+
const subIdx = this.mod.resolveFuncIndex(subName);
|
|
1297
|
+
if (subIdx === null)
|
|
1298
|
+
throw new Error(`internal: no interface dispatcher ${subName}`);
|
|
1299
|
+
const targetParamTypes = this.mod.getFuncParamTypes(subName);
|
|
1300
|
+
const valueRecv =
|
|
1301
|
+
!ifaceType &&
|
|
1302
|
+
this.mod.getFuncParamGoTypes(methodName)[0]?.kind !== "pointer";
|
|
1303
|
+
this.emitExpr(func.expr, this.toWasmType(recvType));
|
|
1304
|
+
this._emitNilCheck(func.expr._type);
|
|
1305
|
+
for (const [i, { structInfo: owner, embed }] of chain.entries()) {
|
|
1306
|
+
this.pushInstruction({
|
|
1307
|
+
op: "struct.get",
|
|
1308
|
+
typeIndex: owner.typeIndex,
|
|
1309
|
+
fieldIndex: embed.fieldIndex,
|
|
1310
|
+
});
|
|
1311
|
+
// Reading through an embedded nil pointer (to the next embedded
|
|
1312
|
+
// struct, or as a value receiver) is a recoverable nil dereference.
|
|
1313
|
+
const ft = owner.fields[embed.fieldIndex]?.goType;
|
|
1314
|
+
const isPtr = ft?.kind === "pointer" || ft?.kind === "PointerType";
|
|
1315
|
+
if (isPtr && (i < chain.length - 1 || valueRecv)) this._emitNilBranch();
|
|
1426
1316
|
}
|
|
1427
|
-
|
|
1317
|
+
for (let i = 0; i < args.length; i++) {
|
|
1318
|
+
const pType = targetParamTypes[i + 1] ?? null;
|
|
1319
|
+
this.emitExpr(args[i], pType);
|
|
1320
|
+
}
|
|
1321
|
+
this.pushInstruction({
|
|
1322
|
+
op: "call",
|
|
1323
|
+
funcIndex: subIdx,
|
|
1324
|
+
});
|
|
1325
|
+
return true;
|
|
1428
1326
|
}
|
|
1429
1327
|
|
|
1430
1328
|
_emitStaticCall(call) {
|
|
@@ -1437,6 +1335,12 @@ export class BuiltinsEmitter {
|
|
|
1437
1335
|
if (targetFuncIdx !== null) {
|
|
1438
1336
|
const paramTypes = this.mod.getFuncParamTypes(func.name);
|
|
1439
1337
|
const paramGoTypes = this.mod.getFuncParamGoTypes(func.name);
|
|
1338
|
+
if (call._multiForward) {
|
|
1339
|
+
// `f(g())`: g's results are f's arguments as they come.
|
|
1340
|
+
this.emitExpr(args[0], null);
|
|
1341
|
+
this.pushInstruction({ op: "call", funcIndex: targetFuncIdx });
|
|
1342
|
+
return true;
|
|
1343
|
+
}
|
|
1440
1344
|
for (let i = 0; i < args.length; i++) {
|
|
1441
1345
|
const arg = args[i];
|
|
1442
1346
|
const pType = paramTypes[i] ?? null;
|
|
@@ -1501,6 +1405,41 @@ export class BuiltinsEmitter {
|
|
|
1501
1405
|
this.releaseTemp(closureTmp, closureWType);
|
|
1502
1406
|
}
|
|
1503
1407
|
|
|
1408
|
+
// `g(f())` that lowering left unsplit (it splits only statement operands):
|
|
1409
|
+
// f's results go into locals, and g is called with idents carrying each
|
|
1410
|
+
// element's interface-box marks, so each is boxed like any argument. Also
|
|
1411
|
+
// used for method calls, whose argument path expects one value per
|
|
1412
|
+
// parameter. Only when g's callee evaluates no call, so f() still runs
|
|
1413
|
+
// before anything else the call evaluates.
|
|
1414
|
+
_splitForwardedArgs(call) {
|
|
1415
|
+
if (!call._multiForward || call.args?.length !== 1) return null;
|
|
1416
|
+
const inner = call.args[0];
|
|
1417
|
+
const types = inner?._type?.types;
|
|
1418
|
+
if (!Array.isArray(types) || types.length === 0) return null;
|
|
1419
|
+
const holders =
|
|
1420
|
+
inner._fwdIface?.length === types.length ? inner._fwdIface : null;
|
|
1421
|
+
const isMethod =
|
|
1422
|
+
call.func?.kind === "SelectorExpr" &&
|
|
1423
|
+
call.func.expr?._type?.kind !== "namespace";
|
|
1424
|
+
if (!holders && !isMethod) return null;
|
|
1425
|
+
if (hasCallOrReceive(call.func)) return null;
|
|
1426
|
+
this.emitExpr(inner, null);
|
|
1427
|
+
this._fwdArgCounter = (this._fwdArgCounter ?? 0) + 1;
|
|
1428
|
+
const names = types.map((_, i) => `__fwdarg$${this._fwdArgCounter}$${i}`);
|
|
1429
|
+
const locals = types.map((t, i) =>
|
|
1430
|
+
this.allocLocal(names[i], this.toWasmType(t), t, true),
|
|
1431
|
+
);
|
|
1432
|
+
for (let i = locals.length - 1; i >= 0; i--)
|
|
1433
|
+
this.pushInstruction({ op: "local.set", index: locals[i] });
|
|
1434
|
+
const args = names.map((name, i) => ({
|
|
1435
|
+
kind: "Ident",
|
|
1436
|
+
name,
|
|
1437
|
+
_type: types[i],
|
|
1438
|
+
...(holders?.[i] ?? {}),
|
|
1439
|
+
}));
|
|
1440
|
+
return { ...call, args, _multiForward: false };
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1504
1443
|
emitCallExpr(call, targetWasmType = null) {
|
|
1505
1444
|
if (call._isTypeConversion) {
|
|
1506
1445
|
this.emitTypeConversion({
|
|
@@ -1520,6 +1459,12 @@ export class BuiltinsEmitter {
|
|
|
1520
1459
|
return;
|
|
1521
1460
|
}
|
|
1522
1461
|
|
|
1462
|
+
const forwarded = this._splitForwardedArgs(call);
|
|
1463
|
+
if (forwarded) {
|
|
1464
|
+
this.emitCallExpr(forwarded, targetWasmType);
|
|
1465
|
+
return;
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1523
1468
|
if (this._emitPackageCall(call, targetWasmType)) {
|
|
1524
1469
|
return;
|
|
1525
1470
|
}
|
|
@@ -1534,4 +1479,687 @@ export class BuiltinsEmitter {
|
|
|
1534
1479
|
|
|
1535
1480
|
this._emitClosureCall(call);
|
|
1536
1481
|
}
|
|
1482
|
+
|
|
1483
|
+
emitSliceClear(arg, argType) {
|
|
1484
|
+
const elemGoType = this._getSliceElemType(argType);
|
|
1485
|
+
const sliceInfo = this.mod.getSliceType(elemGoType);
|
|
1486
|
+
const arrInfo = sliceInfo.arrInfo;
|
|
1487
|
+
|
|
1488
|
+
const sliceTmp = this.acquireTemp({
|
|
1489
|
+
kind: "ref",
|
|
1490
|
+
nullable: true,
|
|
1491
|
+
typeIndex: sliceInfo.typeIndex,
|
|
1492
|
+
});
|
|
1493
|
+
this.emitExpr(arg, {
|
|
1494
|
+
kind: "ref",
|
|
1495
|
+
nullable: true,
|
|
1496
|
+
typeIndex: sliceInfo.typeIndex,
|
|
1497
|
+
});
|
|
1498
|
+
this.pushInstruction({ op: "local.set", index: sliceTmp });
|
|
1499
|
+
|
|
1500
|
+
this.pushInstruction({ op: "block", blockType: "void" });
|
|
1501
|
+
this.pushInstruction({ op: "local.get", index: sliceTmp });
|
|
1502
|
+
this.pushInstruction("ref.is_null");
|
|
1503
|
+
this.pushInstruction({ op: "br_if", depth: 0 });
|
|
1504
|
+
|
|
1505
|
+
const arrTmp = this.acquireTemp({
|
|
1506
|
+
kind: "ref",
|
|
1507
|
+
nullable: true,
|
|
1508
|
+
typeIndex: arrInfo.typeIndex,
|
|
1509
|
+
});
|
|
1510
|
+
const offTmp = this.acquireTemp("i32");
|
|
1511
|
+
const lenTmp = this.acquireTemp("i32");
|
|
1512
|
+
const iLoc = this.acquireTemp("i32");
|
|
1513
|
+
|
|
1514
|
+
this._emitSliceUnpack(sliceTmp, sliceInfo, {
|
|
1515
|
+
arr: arrTmp,
|
|
1516
|
+
off: offTmp,
|
|
1517
|
+
len: lenTmp,
|
|
1518
|
+
});
|
|
1519
|
+
|
|
1520
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
1521
|
+
this.pushInstruction({ op: "local.set", index: iLoc });
|
|
1522
|
+
|
|
1523
|
+
this.pushInstruction({ op: "block", blockType: "void" });
|
|
1524
|
+
this.pushInstruction({ op: "loop", blockType: "void" });
|
|
1525
|
+
this.pushInstruction({ op: "local.get", index: iLoc });
|
|
1526
|
+
this.pushInstruction({ op: "local.get", index: lenTmp });
|
|
1527
|
+
this.pushInstruction("i32.ge_s");
|
|
1528
|
+
this.pushInstruction({ op: "br_if", depth: 1 });
|
|
1529
|
+
|
|
1530
|
+
this.pushInstruction({ op: "local.get", index: arrTmp });
|
|
1531
|
+
this.pushInstruction({ op: "local.get", index: offTmp });
|
|
1532
|
+
this.pushInstruction({ op: "local.get", index: iLoc });
|
|
1533
|
+
this.pushInstruction("i32.add");
|
|
1534
|
+
this.emitZeroValue(elemGoType, arrInfo.elemWType);
|
|
1535
|
+
this.pushInstruction({ op: "array.set", typeIndex: arrInfo.typeIndex });
|
|
1536
|
+
|
|
1537
|
+
this.pushInstruction({ op: "local.get", index: iLoc });
|
|
1538
|
+
this.pushInstruction({ op: "i32.const", value: 1 });
|
|
1539
|
+
this.pushInstruction("i32.add");
|
|
1540
|
+
this.pushInstruction({ op: "local.set", index: iLoc });
|
|
1541
|
+
this.pushInstruction({ op: "br", depth: 0 });
|
|
1542
|
+
this.pushInstruction("end");
|
|
1543
|
+
this.pushInstruction("end");
|
|
1544
|
+
|
|
1545
|
+
this.releaseTemp(iLoc, "i32");
|
|
1546
|
+
this.releaseTemp(lenTmp, "i32");
|
|
1547
|
+
this.releaseTemp(offTmp, "i32");
|
|
1548
|
+
this.releaseTemp(arrTmp, {
|
|
1549
|
+
kind: "ref",
|
|
1550
|
+
nullable: true,
|
|
1551
|
+
typeIndex: arrInfo.typeIndex,
|
|
1552
|
+
});
|
|
1553
|
+
|
|
1554
|
+
this.pushInstruction("end");
|
|
1555
|
+
this.releaseTemp(sliceTmp, {
|
|
1556
|
+
kind: "ref",
|
|
1557
|
+
nullable: true,
|
|
1558
|
+
typeIndex: sliceInfo.typeIndex,
|
|
1559
|
+
});
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
// Pushes an integer-ish argument as i32 (Go ints are i64 in this backend).
|
|
1563
|
+
_emitI32Arg(arg) {
|
|
1564
|
+
const wType = toWasmType(arg?._type, this.mod.checker);
|
|
1565
|
+
if (wType === "i64") {
|
|
1566
|
+
this.emitExpr(arg, "i64");
|
|
1567
|
+
this.pushInstruction("i32.wrap_i64");
|
|
1568
|
+
} else {
|
|
1569
|
+
this.emitExpr(arg, "i32");
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
// `a == b` for two anyref operands (interface values). `ref.eq` only
|
|
1574
|
+
// accepts eqref, so host strings (internalised externs) are compared via
|
|
1575
|
+
// `str_eq` and anything else by identity.
|
|
1576
|
+
_emitAnyEq() {
|
|
1577
|
+
const idx = this.mod.resolveFuncIndex("__any_eq");
|
|
1578
|
+
if (idx === null) {
|
|
1579
|
+
throw new Error(
|
|
1580
|
+
"internal: anyref equality emitted but __any_eq was not declared (usesAnyEq scan missed a site)",
|
|
1581
|
+
);
|
|
1582
|
+
}
|
|
1583
|
+
this.pushInstruction({ op: "call", funcIndex: idx });
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
// Every `[wasm type index, Go type name]` an `any` can hold in this module,
|
|
1587
|
+
// more specific types last so they overwrite their supertypes.
|
|
1588
|
+
_dynamicTypeNames() {
|
|
1589
|
+
const named = (name) => {
|
|
1590
|
+
const i = name.indexOf("$$");
|
|
1591
|
+
return i < 0
|
|
1592
|
+
? goTypeName({ kind: "named", name })
|
|
1593
|
+
: `${name.slice(i + 2)}.${name.slice(0, i)}`;
|
|
1594
|
+
};
|
|
1595
|
+
const mod = this.mod;
|
|
1596
|
+
const names = [];
|
|
1597
|
+
for (const s of mod.sliceTypes.values())
|
|
1598
|
+
names.push([
|
|
1599
|
+
s.typeIndex,
|
|
1600
|
+
goTypeName({ kind: "slice", elem: s.elemGoType }),
|
|
1601
|
+
]);
|
|
1602
|
+
for (const m of mod.mapTypes.values())
|
|
1603
|
+
names.push([
|
|
1604
|
+
m.typeIndex,
|
|
1605
|
+
goTypeName({ kind: "map", key: m.keyGoType, value: m.valGoType }),
|
|
1606
|
+
]);
|
|
1607
|
+
for (const info of new Set(mod.structTypes.values())) {
|
|
1608
|
+
if (info.isAnon) continue;
|
|
1609
|
+
names.push([
|
|
1610
|
+
info.typeIndex,
|
|
1611
|
+
info.name === ERROR_STRUCT
|
|
1612
|
+
? "*errors.errorString"
|
|
1613
|
+
: `*${named(info.name)}`,
|
|
1614
|
+
]);
|
|
1615
|
+
}
|
|
1616
|
+
for (const c of mod.closureTypes.values())
|
|
1617
|
+
names.push([
|
|
1618
|
+
c.typeIndex,
|
|
1619
|
+
goTypeName({
|
|
1620
|
+
kind: "func",
|
|
1621
|
+
params: c.sig.params,
|
|
1622
|
+
returns: c.sig.returns,
|
|
1623
|
+
}),
|
|
1624
|
+
]);
|
|
1625
|
+
for (const box of mod.ifaceBoxTypes.values())
|
|
1626
|
+
names.push([box.typeIndex, goTypeName(box.goType)]);
|
|
1627
|
+
for (const info of mod.taggedSliceTypes.values())
|
|
1628
|
+
names.push([info.typeIndex, goTypeName(info.goType)]);
|
|
1629
|
+
for (const info of mod.nilBoxTypes.values())
|
|
1630
|
+
names.push([info.typeIndex, goTypeName(info.goType)]);
|
|
1631
|
+
return names;
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
// Body of `__ta_fail(v any, src string, target string, iface int32)`: panics
|
|
1635
|
+
// with Go's failed type assertion message, naming the dynamic type of `v`
|
|
1636
|
+
// from the types the module knows (`src` is the static interface type, or
|
|
1637
|
+
// the first method the target interface needs when `iface` is 1).
|
|
1638
|
+
_emitTypeAssertFail() {
|
|
1639
|
+
const strConst = (text) => {
|
|
1640
|
+
this.pushInstruction({
|
|
1641
|
+
op: "i32.const",
|
|
1642
|
+
value: this.mod.internString(text),
|
|
1643
|
+
});
|
|
1644
|
+
this.pushInstruction({
|
|
1645
|
+
op: "call",
|
|
1646
|
+
funcIndex: this.mod.getStringImportIndex(),
|
|
1647
|
+
});
|
|
1648
|
+
};
|
|
1649
|
+
const concat = () =>
|
|
1650
|
+
this.pushInstruction({
|
|
1651
|
+
op: "call",
|
|
1652
|
+
funcIndex: this.mod.getStringConcatImportIndex(),
|
|
1653
|
+
});
|
|
1654
|
+
const dyn = this.allocLocal(null, "externref");
|
|
1655
|
+
strConst("interface {}");
|
|
1656
|
+
this.pushInstruction({ op: "local.set", index: dyn });
|
|
1657
|
+
const when = (typeIndex, text) => {
|
|
1658
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1659
|
+
this.pushInstruction({ op: "ref.test", typeIndex });
|
|
1660
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1661
|
+
strConst(text);
|
|
1662
|
+
this.pushInstruction({ op: "local.set", index: dyn });
|
|
1663
|
+
this.pushInstruction("end");
|
|
1664
|
+
};
|
|
1665
|
+
for (const [typeIndex, text] of this._dynamicTypeNames())
|
|
1666
|
+
when(typeIndex, text);
|
|
1667
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1668
|
+
this.pushInstruction({
|
|
1669
|
+
op: "call",
|
|
1670
|
+
funcIndex: this.mod.getIsStringImportIndex(),
|
|
1671
|
+
});
|
|
1672
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1673
|
+
strConst("string");
|
|
1674
|
+
this.pushInstruction({ op: "local.set", index: dyn });
|
|
1675
|
+
this.pushInstruction("end");
|
|
1676
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1677
|
+
this.pushInstruction("ref.is_null");
|
|
1678
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1679
|
+
strConst("nil");
|
|
1680
|
+
this.pushInstruction({ op: "local.set", index: dyn });
|
|
1681
|
+
this.pushInstruction("end");
|
|
1682
|
+
|
|
1683
|
+
// An interface target: `<dyn> is not <target>: missing method <M>`.
|
|
1684
|
+
this.pushInstruction({ op: "local.get", index: 3 });
|
|
1685
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1686
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1687
|
+
this.pushInstruction("ref.is_null");
|
|
1688
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1689
|
+
strConst("interface conversion: interface is nil, not ");
|
|
1690
|
+
this.pushInstruction({ op: "local.get", index: 2 });
|
|
1691
|
+
concat();
|
|
1692
|
+
this.emitRuntimePanicThrow();
|
|
1693
|
+
this.pushInstruction("end");
|
|
1694
|
+
strConst("interface conversion: ");
|
|
1695
|
+
this.pushInstruction({ op: "local.get", index: dyn });
|
|
1696
|
+
concat();
|
|
1697
|
+
strConst(" is not ");
|
|
1698
|
+
concat();
|
|
1699
|
+
this.pushInstruction({ op: "local.get", index: 2 });
|
|
1700
|
+
concat();
|
|
1701
|
+
strConst(": missing method ");
|
|
1702
|
+
concat();
|
|
1703
|
+
this.pushInstruction({ op: "local.get", index: 1 });
|
|
1704
|
+
concat();
|
|
1705
|
+
this.emitRuntimePanicThrow();
|
|
1706
|
+
this.pushInstruction("end");
|
|
1707
|
+
|
|
1708
|
+
strConst("interface conversion: ");
|
|
1709
|
+
this.pushInstruction({ op: "local.get", index: 1 });
|
|
1710
|
+
concat();
|
|
1711
|
+
strConst(" is ");
|
|
1712
|
+
concat();
|
|
1713
|
+
this.pushInstruction({ op: "local.get", index: dyn });
|
|
1714
|
+
concat();
|
|
1715
|
+
strConst(", not ");
|
|
1716
|
+
concat();
|
|
1717
|
+
this.pushInstruction({ op: "local.get", index: 2 });
|
|
1718
|
+
concat();
|
|
1719
|
+
this.emitRuntimePanicThrow();
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
// `any_eq` arm for two arrays of one wasm array type (an array stored in
|
|
1723
|
+
// an interface is the array ref itself): equal lengths and elements.
|
|
1724
|
+
_emitAnyArrayEqArm(arrInfo) {
|
|
1725
|
+
const ref = { kind: "ref", nullable: true, typeIndex: arrInfo.typeIndex };
|
|
1726
|
+
for (const idx of [0, 1]) {
|
|
1727
|
+
this.pushInstruction({ op: "local.get", index: idx });
|
|
1728
|
+
this.pushInstruction({ op: "ref.test", typeIndex: arrInfo.typeIndex });
|
|
1729
|
+
}
|
|
1730
|
+
this.pushInstruction("i32.and");
|
|
1731
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1732
|
+
const a1 = this.allocLocal(null, ref);
|
|
1733
|
+
const a2 = this.allocLocal(null, ref);
|
|
1734
|
+
for (const [src, dst] of [
|
|
1735
|
+
[0, a1],
|
|
1736
|
+
[1, a2],
|
|
1737
|
+
]) {
|
|
1738
|
+
this.pushInstruction({ op: "local.get", index: src });
|
|
1739
|
+
this.pushInstruction({ op: "ref.cast", typeIndex: arrInfo.typeIndex });
|
|
1740
|
+
this.pushInstruction({ op: "local.set", index: dst });
|
|
1741
|
+
}
|
|
1742
|
+
const len = this.allocLocal(null, "i32");
|
|
1743
|
+
this.pushInstruction({ op: "local.get", index: a1 });
|
|
1744
|
+
this.pushInstruction("array.len");
|
|
1745
|
+
this.pushInstruction({ op: "local.tee", index: len });
|
|
1746
|
+
this.pushInstruction({ op: "local.get", index: a2 });
|
|
1747
|
+
this.pushInstruction("array.len");
|
|
1748
|
+
this.pushInstruction("i32.ne");
|
|
1749
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1750
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
1751
|
+
this.pushInstruction("return");
|
|
1752
|
+
this.pushInstruction("end");
|
|
1753
|
+
const i = this.allocLocal(null, "i32");
|
|
1754
|
+
const e1 = this.allocLocal(null, arrInfo.elemWType);
|
|
1755
|
+
const e2 = this.allocLocal(null, arrInfo.elemWType);
|
|
1756
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
1757
|
+
this.pushInstruction({ op: "local.set", index: i });
|
|
1758
|
+
this.pushInstruction({ op: "block", blockType: "void" });
|
|
1759
|
+
this.pushInstruction({ op: "loop", blockType: "void" });
|
|
1760
|
+
this.pushInstruction({ op: "local.get", index: i });
|
|
1761
|
+
this.pushInstruction({ op: "local.get", index: len });
|
|
1762
|
+
this.pushInstruction("i32.ge_u");
|
|
1763
|
+
this.pushInstruction({ op: "br_if", depth: 1 });
|
|
1764
|
+
for (const [arr, dst] of [
|
|
1765
|
+
[a1, e1],
|
|
1766
|
+
[a2, e2],
|
|
1767
|
+
]) {
|
|
1768
|
+
this.pushInstruction({ op: "local.get", index: arr });
|
|
1769
|
+
this.pushInstruction({ op: "local.get", index: i });
|
|
1770
|
+
this.pushInstruction({ op: "array.get", typeIndex: arrInfo.typeIndex });
|
|
1771
|
+
this.pushInstruction({ op: "local.set", index: dst });
|
|
1772
|
+
}
|
|
1773
|
+
this.emitKeyEq(e1, e2, arrInfo.elemGoType);
|
|
1774
|
+
this.pushInstruction("i32.eqz");
|
|
1775
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1776
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
1777
|
+
this.pushInstruction("return");
|
|
1778
|
+
this.pushInstruction("end");
|
|
1779
|
+
this.pushInstruction({ op: "local.get", index: i });
|
|
1780
|
+
this.pushInstruction({ op: "i32.const", value: 1 });
|
|
1781
|
+
this.pushInstruction("i32.add");
|
|
1782
|
+
this.pushInstruction({ op: "local.set", index: i });
|
|
1783
|
+
this.pushInstruction({ op: "br", depth: 0 });
|
|
1784
|
+
this.pushInstruction("end");
|
|
1785
|
+
this.pushInstruction("end");
|
|
1786
|
+
this.pushInstruction({ op: "i32.const", value: 1 });
|
|
1787
|
+
this.pushInstruction("return");
|
|
1788
|
+
this.pushInstruction("end");
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
// `any_hash` arm for an array: the wasm array type, then each element,
|
|
1792
|
+
// as `emitKeyHash` combines a static array key (so equal arrays under
|
|
1793
|
+
// `any_eq` hash equal).
|
|
1794
|
+
_emitAnyArrayHashArm(arrInfo) {
|
|
1795
|
+
const ref = { kind: "ref", nullable: true, typeIndex: arrInfo.typeIndex };
|
|
1796
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1797
|
+
this.pushInstruction({ op: "ref.test", typeIndex: arrInfo.typeIndex });
|
|
1798
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1799
|
+
const arr = this.allocLocal(null, ref);
|
|
1800
|
+
const h = this.allocLocal(null, "i32");
|
|
1801
|
+
const i = this.allocLocal(null, "i32");
|
|
1802
|
+
const e = this.allocLocal(null, arrInfo.elemWType);
|
|
1803
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1804
|
+
this.pushInstruction({ op: "ref.cast", typeIndex: arrInfo.typeIndex });
|
|
1805
|
+
this.pushInstruction({ op: "local.set", index: arr });
|
|
1806
|
+
this.pushInstruction({ op: "i32.const", value: arrInfo.typeIndex });
|
|
1807
|
+
this.pushInstruction({ op: "local.set", index: h });
|
|
1808
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
1809
|
+
this.pushInstruction({ op: "local.set", index: i });
|
|
1810
|
+
this.pushInstruction({ op: "block", blockType: "void" });
|
|
1811
|
+
this.pushInstruction({ op: "loop", blockType: "void" });
|
|
1812
|
+
this.pushInstruction({ op: "local.get", index: i });
|
|
1813
|
+
this.pushInstruction({ op: "local.get", index: arr });
|
|
1814
|
+
this.pushInstruction("array.len");
|
|
1815
|
+
this.pushInstruction("i32.ge_u");
|
|
1816
|
+
this.pushInstruction({ op: "br_if", depth: 1 });
|
|
1817
|
+
this.pushInstruction({ op: "local.get", index: arr });
|
|
1818
|
+
this.pushInstruction({ op: "local.get", index: i });
|
|
1819
|
+
this.pushInstruction({ op: "array.get", typeIndex: arrInfo.typeIndex });
|
|
1820
|
+
this.pushInstruction({ op: "local.set", index: e });
|
|
1821
|
+
this.pushInstruction({ op: "local.get", index: h });
|
|
1822
|
+
this.pushInstruction({ op: "i32.const", value: 31 });
|
|
1823
|
+
this.pushInstruction("i32.mul");
|
|
1824
|
+
this.emitKeyHash(e, arrInfo.elemGoType);
|
|
1825
|
+
this.pushInstruction("i32.add");
|
|
1826
|
+
this.pushInstruction({ op: "local.set", index: h });
|
|
1827
|
+
this.pushInstruction({ op: "local.get", index: i });
|
|
1828
|
+
this.pushInstruction({ op: "i32.const", value: 1 });
|
|
1829
|
+
this.pushInstruction("i32.add");
|
|
1830
|
+
this.pushInstruction({ op: "local.set", index: i });
|
|
1831
|
+
this.pushInstruction({ op: "br", depth: 0 });
|
|
1832
|
+
this.pushInstruction("end");
|
|
1833
|
+
this.pushInstruction("end");
|
|
1834
|
+
this.pushInstruction({ op: "local.get", index: h });
|
|
1835
|
+
this.pushInstruction("return");
|
|
1836
|
+
this.pushInstruction("end");
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
emitRuntimeHelper(fn) {
|
|
1840
|
+
const errInfo = this.mod.getStructType(ERROR_STRUCT);
|
|
1841
|
+
switch (fn._isRuntimeHelper) {
|
|
1842
|
+
case "any_eq": {
|
|
1843
|
+
const bothNull = () => {
|
|
1844
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1845
|
+
this.pushInstruction("ref.is_null");
|
|
1846
|
+
this.pushInstruction({ op: "local.get", index: 1 });
|
|
1847
|
+
this.pushInstruction("ref.is_null");
|
|
1848
|
+
};
|
|
1849
|
+
bothNull();
|
|
1850
|
+
this.pushInstruction("i32.and");
|
|
1851
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1852
|
+
this.pushInstruction({ op: "i32.const", value: 1 });
|
|
1853
|
+
this.pushInstruction("return");
|
|
1854
|
+
this.pushInstruction("end");
|
|
1855
|
+
bothNull();
|
|
1856
|
+
this.pushInstruction("i32.or");
|
|
1857
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1858
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
1859
|
+
this.pushInstruction("return");
|
|
1860
|
+
this.pushInstruction("end");
|
|
1861
|
+
|
|
1862
|
+
// Check unhashable / uncomparable types
|
|
1863
|
+
const checkUncomparable = (typeIndex) => {
|
|
1864
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1865
|
+
this.pushInstruction({ op: "ref.test", typeIndex });
|
|
1866
|
+
this.pushInstruction({ op: "local.get", index: 1 });
|
|
1867
|
+
this.pushInstruction({ op: "ref.test", typeIndex });
|
|
1868
|
+
this.pushInstruction("i32.or");
|
|
1869
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1870
|
+
this.emitPanic("runtime error: comparing uncomparable type");
|
|
1871
|
+
this.pushInstruction("end");
|
|
1872
|
+
};
|
|
1873
|
+
for (const s of this.mod.sliceTypes.values())
|
|
1874
|
+
checkUncomparable(s.typeIndex);
|
|
1875
|
+
for (const m of this.mod.mapTypes.values())
|
|
1876
|
+
checkUncomparable(m.typeIndex);
|
|
1877
|
+
for (const c of this.mod.closureTypes.values())
|
|
1878
|
+
checkUncomparable(c.typeIndex);
|
|
1879
|
+
for (const b of this.mod.ifaceBoxTypes.values()) {
|
|
1880
|
+
if (isUnhashableGoType(b.goType, this.mod.checker))
|
|
1881
|
+
checkUncomparable(b.typeIndex);
|
|
1882
|
+
}
|
|
1883
|
+
for (const a of this.mod.arrayTypes.values()) {
|
|
1884
|
+
if (isUnhashableGoType(a.elemGoType, this.mod.checker))
|
|
1885
|
+
checkUncomparable(a.typeIndex);
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
// Boxed values compare by value (Go: any(3) == any(3), struct values, etc.).
|
|
1889
|
+
for (const box of this.mod.ifaceBoxTypes.values()) {
|
|
1890
|
+
if (isUnhashableGoType(box.goType, this.mod.checker)) continue;
|
|
1891
|
+
for (const idx of [0, 1]) {
|
|
1892
|
+
this.pushInstruction({ op: "local.get", index: idx });
|
|
1893
|
+
this.pushInstruction({ op: "ref.test", typeIndex: box.typeIndex });
|
|
1894
|
+
}
|
|
1895
|
+
this.pushInstruction("i32.and");
|
|
1896
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1897
|
+
const f1 = this.allocLocal(null, box.wType);
|
|
1898
|
+
const f2 = this.allocLocal(null, box.wType);
|
|
1899
|
+
for (const [src, dst] of [
|
|
1900
|
+
[0, f1],
|
|
1901
|
+
[1, f2],
|
|
1902
|
+
]) {
|
|
1903
|
+
this.pushInstruction({ op: "local.get", index: src });
|
|
1904
|
+
this.pushInstruction({ op: "ref.cast", typeIndex: box.typeIndex });
|
|
1905
|
+
this.pushInstruction({
|
|
1906
|
+
op: "struct.get",
|
|
1907
|
+
typeIndex: box.typeIndex,
|
|
1908
|
+
fieldIndex: 0,
|
|
1909
|
+
});
|
|
1910
|
+
this.pushInstruction({ op: "local.set", index: dst });
|
|
1911
|
+
}
|
|
1912
|
+
this.emitKeyEq(f1, f2, box.goType);
|
|
1913
|
+
this.pushInstruction("return");
|
|
1914
|
+
this.pushInstruction("end");
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1917
|
+
// Arrays compare element by element.
|
|
1918
|
+
for (const a of [...this.mod.arrayTypes.values()]) {
|
|
1919
|
+
if (!isUnhashableGoType(a.elemGoType, this.mod.checker))
|
|
1920
|
+
this._emitAnyArrayEqArm(a);
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1923
|
+
// Strings
|
|
1924
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1925
|
+
this.pushInstruction({
|
|
1926
|
+
op: "call",
|
|
1927
|
+
funcIndex: this.mod.getIsStringImportIndex(),
|
|
1928
|
+
});
|
|
1929
|
+
this.pushInstruction({ op: "local.get", index: 1 });
|
|
1930
|
+
this.pushInstruction({
|
|
1931
|
+
op: "call",
|
|
1932
|
+
funcIndex: this.mod.getIsStringImportIndex(),
|
|
1933
|
+
});
|
|
1934
|
+
this.pushInstruction("i32.and");
|
|
1935
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1936
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1937
|
+
this.pushInstruction("extern.convert_any");
|
|
1938
|
+
this.pushInstruction({ op: "local.get", index: 1 });
|
|
1939
|
+
this.pushInstruction("extern.convert_any");
|
|
1940
|
+
this.pushInstruction({
|
|
1941
|
+
op: "call",
|
|
1942
|
+
funcIndex: this.mod.getStringCmpImportIndex("=="),
|
|
1943
|
+
});
|
|
1944
|
+
this.pushInstruction("return");
|
|
1945
|
+
this.pushInstruction("end");
|
|
1946
|
+
|
|
1947
|
+
// Heap reference equality
|
|
1948
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1949
|
+
this.pushInstruction({ op: "ref.test", heapType: "eq" });
|
|
1950
|
+
this.pushInstruction({ op: "local.get", index: 1 });
|
|
1951
|
+
this.pushInstruction({ op: "ref.test", heapType: "eq" });
|
|
1952
|
+
this.pushInstruction("i32.and");
|
|
1953
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1954
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1955
|
+
this.pushInstruction({ op: "ref.cast", heapType: "eq" });
|
|
1956
|
+
this.pushInstruction({ op: "local.get", index: 1 });
|
|
1957
|
+
this.pushInstruction({ op: "ref.cast", heapType: "eq" });
|
|
1958
|
+
this.pushInstruction("ref.eq");
|
|
1959
|
+
this.pushInstruction("return");
|
|
1960
|
+
this.pushInstruction("end");
|
|
1961
|
+
|
|
1962
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
1963
|
+
this.pushInstruction("return");
|
|
1964
|
+
return;
|
|
1965
|
+
}
|
|
1966
|
+
case "any_ref": {
|
|
1967
|
+
// A tagged slice is a fresh header on each conversion to an `any`:
|
|
1968
|
+
// its backing array is the identity every copy shares.
|
|
1969
|
+
for (const info of this.mod.taggedSliceTypes.values()) {
|
|
1970
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1971
|
+
this.pushInstruction({ op: "ref.test", typeIndex: info.typeIndex });
|
|
1972
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1973
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1974
|
+
this.pushInstruction({ op: "ref.cast", typeIndex: info.typeIndex });
|
|
1975
|
+
this.pushInstruction({
|
|
1976
|
+
op: "struct.get",
|
|
1977
|
+
typeIndex: info.typeIndex,
|
|
1978
|
+
fieldIndex: 0,
|
|
1979
|
+
});
|
|
1980
|
+
this.pushInstruction("return");
|
|
1981
|
+
this.pushInstruction("end");
|
|
1982
|
+
}
|
|
1983
|
+
// A boxed defined map or func value: the reference inside the box.
|
|
1984
|
+
for (const box of this.mod.ifaceBoxTypes.values()) {
|
|
1985
|
+
if (
|
|
1986
|
+
!isMapType(box.goType, this.mod.checker) &&
|
|
1987
|
+
!isFuncType(box.goType, this.mod.checker)
|
|
1988
|
+
)
|
|
1989
|
+
continue;
|
|
1990
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1991
|
+
this.pushInstruction({ op: "ref.test", typeIndex: box.typeIndex });
|
|
1992
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1993
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1994
|
+
this.pushInstruction({ op: "ref.cast", typeIndex: box.typeIndex });
|
|
1995
|
+
this.pushInstruction({
|
|
1996
|
+
op: "struct.get",
|
|
1997
|
+
typeIndex: box.typeIndex,
|
|
1998
|
+
fieldIndex: 0,
|
|
1999
|
+
});
|
|
2000
|
+
this.pushInstruction("return");
|
|
2001
|
+
this.pushInstruction("end");
|
|
2002
|
+
}
|
|
2003
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
2004
|
+
this.pushInstruction("return");
|
|
2005
|
+
return;
|
|
2006
|
+
}
|
|
2007
|
+
case "any_hash": {
|
|
2008
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
2009
|
+
this.pushInstruction("ref.is_null");
|
|
2010
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
2011
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
2012
|
+
this.pushInstruction("return");
|
|
2013
|
+
this.pushInstruction("end");
|
|
2014
|
+
|
|
2015
|
+
// Check unhashable types. `goType` names the dynamic type in the
|
|
2016
|
+
// panic, as Go does (`main.S`, `main.L`, `func()`).
|
|
2017
|
+
const checkUnhashable = (typeIndex, goType) => {
|
|
2018
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
2019
|
+
this.pushInstruction({ op: "ref.test", typeIndex });
|
|
2020
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
2021
|
+
this.emitPanic(
|
|
2022
|
+
goType
|
|
2023
|
+
? `runtime error: hash of unhashable type ${goTypeName(goType)}`
|
|
2024
|
+
: "runtime error: hash of unhashable type",
|
|
2025
|
+
);
|
|
2026
|
+
this.pushInstruction("end");
|
|
2027
|
+
};
|
|
2028
|
+
// A slice type first built from source syntax (a `TypeName`) names
|
|
2029
|
+
// its element as written.
|
|
2030
|
+
const spelled = (t) =>
|
|
2031
|
+
t?.kind === "TypeName" ? { kind: "basic", name: t.name } : t;
|
|
2032
|
+
// Tagged slices (a defined slice type in an interface) are subtypes
|
|
2033
|
+
// of the plain slice type, so they are tested first.
|
|
2034
|
+
for (const info of this.mod.taggedSliceTypes.values())
|
|
2035
|
+
checkUnhashable(info.typeIndex, info.goType);
|
|
2036
|
+
for (const sliceInfo of this.mod.sliceTypes.values())
|
|
2037
|
+
checkUnhashable(sliceInfo.typeIndex, {
|
|
2038
|
+
kind: "slice",
|
|
2039
|
+
elem: spelled(sliceInfo.elemGoType),
|
|
2040
|
+
});
|
|
2041
|
+
for (const mapInfo of this.mod.mapTypes.values())
|
|
2042
|
+
checkUnhashable(mapInfo.typeIndex, {
|
|
2043
|
+
kind: "map",
|
|
2044
|
+
key: mapInfo.keyGoType,
|
|
2045
|
+
value: mapInfo.valGoType,
|
|
2046
|
+
});
|
|
2047
|
+
for (const closureInfo of this.mod.closureTypes.values())
|
|
2048
|
+
checkUnhashable(closureInfo.typeIndex, {
|
|
2049
|
+
kind: "func",
|
|
2050
|
+
params: closureInfo.sig.params,
|
|
2051
|
+
returns: closureInfo.sig.returns,
|
|
2052
|
+
});
|
|
2053
|
+
for (const box of this.mod.ifaceBoxTypes.values()) {
|
|
2054
|
+
if (isUnhashableGoType(box.goType, this.mod.checker))
|
|
2055
|
+
checkUnhashable(box.typeIndex, box.goType);
|
|
2056
|
+
}
|
|
2057
|
+
for (const arrInfo of this.mod.arrayTypes.values()) {
|
|
2058
|
+
if (isUnhashableGoType(arrInfo.elemGoType, this.mod.checker))
|
|
2059
|
+
checkUnhashable(arrInfo.typeIndex);
|
|
2060
|
+
}
|
|
2061
|
+
|
|
2062
|
+
// Check boxed values (scalars, struct values)
|
|
2063
|
+
for (const box of this.mod.ifaceBoxTypes.values()) {
|
|
2064
|
+
if (isUnhashableGoType(box.goType, this.mod.checker)) continue;
|
|
2065
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
2066
|
+
this.pushInstruction({ op: "ref.test", typeIndex: box.typeIndex });
|
|
2067
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
2068
|
+
const valLoc = this.allocLocal(null, box.wType);
|
|
2069
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
2070
|
+
this.pushInstruction({ op: "ref.cast", typeIndex: box.typeIndex });
|
|
2071
|
+
this.pushInstruction({
|
|
2072
|
+
op: "struct.get",
|
|
2073
|
+
typeIndex: box.typeIndex,
|
|
2074
|
+
fieldIndex: 0,
|
|
2075
|
+
});
|
|
2076
|
+
this.pushInstruction({ op: "local.set", index: valLoc });
|
|
2077
|
+
this.pushInstruction({ op: "i32.const", value: box.typeIndex });
|
|
2078
|
+
this.pushInstruction({ op: "i32.const", value: 31 });
|
|
2079
|
+
this.pushInstruction("i32.mul");
|
|
2080
|
+
this.emitKeyHash(valLoc, box.goType);
|
|
2081
|
+
this.pushInstruction("i32.add");
|
|
2082
|
+
this.pushInstruction("return");
|
|
2083
|
+
this.pushInstruction("end");
|
|
2084
|
+
}
|
|
2085
|
+
|
|
2086
|
+
// Arrays hash element by element.
|
|
2087
|
+
for (const a of [...this.mod.arrayTypes.values()]) {
|
|
2088
|
+
if (!isUnhashableGoType(a.elemGoType, this.mod.checker))
|
|
2089
|
+
this._emitAnyArrayHashArm(a);
|
|
2090
|
+
}
|
|
2091
|
+
|
|
2092
|
+
// Check string
|
|
2093
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
2094
|
+
this.pushInstruction({
|
|
2095
|
+
op: "call",
|
|
2096
|
+
funcIndex: this.mod.getIsStringImportIndex(),
|
|
2097
|
+
});
|
|
2098
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
2099
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
2100
|
+
this.pushInstruction("extern.convert_any");
|
|
2101
|
+
this.pushInstruction({
|
|
2102
|
+
op: "call",
|
|
2103
|
+
funcIndex: this.mod.getStringHashImportIndex(),
|
|
2104
|
+
});
|
|
2105
|
+
this.pushInstruction("return");
|
|
2106
|
+
this.pushInstruction("end");
|
|
2107
|
+
|
|
2108
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
2109
|
+
this.pushInstruction("return");
|
|
2110
|
+
return;
|
|
2111
|
+
}
|
|
2112
|
+
case "ta_fail":
|
|
2113
|
+
this._emitTypeAssertFail();
|
|
2114
|
+
return;
|
|
2115
|
+
case "error_msg":
|
|
2116
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
2117
|
+
this.pushInstruction({
|
|
2118
|
+
op: "struct.get",
|
|
2119
|
+
typeIndex: errInfo.typeIndex,
|
|
2120
|
+
fieldIndex: 0,
|
|
2121
|
+
});
|
|
2122
|
+
this.pushInstruction("return");
|
|
2123
|
+
return;
|
|
2124
|
+
case "mk_error":
|
|
2125
|
+
// nil message (strconv success) → nil error
|
|
2126
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
2127
|
+
this.pushInstruction("any.convert_extern");
|
|
2128
|
+
this.pushInstruction("ref.is_null");
|
|
2129
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
2130
|
+
this.pushInstruction({ op: "ref.null", heapType: "any" });
|
|
2131
|
+
this.pushInstruction("return");
|
|
2132
|
+
this.pushInstruction("end");
|
|
2133
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
2134
|
+
this.pushInstruction({
|
|
2135
|
+
op: "struct.new",
|
|
2136
|
+
typeIndex: errInfo.typeIndex,
|
|
2137
|
+
});
|
|
2138
|
+
this.pushInstruction("return");
|
|
2139
|
+
return;
|
|
2140
|
+
case "error_str":
|
|
2141
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
2142
|
+
this.pushInstruction("ref.is_null");
|
|
2143
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
2144
|
+
this.pushInstruction({
|
|
2145
|
+
op: "i32.const",
|
|
2146
|
+
value: this.mod.internString("<nil>"),
|
|
2147
|
+
});
|
|
2148
|
+
this.pushInstruction({
|
|
2149
|
+
op: "call",
|
|
2150
|
+
funcIndex: this.mod.getStringImportIndex(),
|
|
2151
|
+
});
|
|
2152
|
+
this.pushInstruction("return");
|
|
2153
|
+
this.pushInstruction("end");
|
|
2154
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
2155
|
+
this.pushInstruction({
|
|
2156
|
+
op: "call",
|
|
2157
|
+
funcIndex: this.mod.resolveFuncIndex("__dispatch_error_Error"),
|
|
2158
|
+
});
|
|
2159
|
+
this.pushInstruction("return");
|
|
2160
|
+
return;
|
|
2161
|
+
default:
|
|
2162
|
+
throw new Error(`Unknown runtime helper: ${fn._isRuntimeHelper}`);
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
1537
2165
|
}
|