gofront 1.8.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/package.json +5 -4
- package/src/backend/js/expressions.js +849 -371
- package/src/backend/js/index.js +241 -147
- package/src/backend/js/natives.js +383 -0
- package/src/backend/js/runtime.js +512 -2547
- package/src/backend/js/statements.js +506 -103
- package/src/backend/js/stdlib/index.js +45 -93
- package/src/backend/js/templ.js +32 -45
- package/src/backend/js/type-info.js +177 -13
- package/src/backend/shared/fixed-float.js +20 -0
- package/src/backend/shared/fmt-types.js +63 -171
- package/src/backend/shared/host-unicode.js +36 -0
- package/src/backend/wasm/boundary.js +490 -965
- package/src/backend/wasm/emit-builtins.js +643 -256
- package/src/backend/wasm/emit-exprs.js +124 -57
- package/src/backend/wasm/emit-maps.js +17 -367
- package/src/backend/wasm/emit-stmts.js +325 -75
- package/src/backend/wasm/emit.js +104 -13
- package/src/backend/wasm/globals.js +47 -8
- package/src/backend/wasm/glue.js +2 -4
- package/src/backend/wasm/index.js +36 -530
- package/src/backend/wasm/method-expr.js +12 -1
- package/src/backend/wasm/module-emitter.js +92 -717
- package/src/backend/wasm/monomorph.js +103 -6
- package/src/backend/wasm/natives.js +596 -0
- package/src/backend/wasm/prescan.js +5 -153
- package/src/backend/wasm/prune-funcs.js +89 -0
- package/src/backend/wasm/stdlib/index.js +2 -32
- package/src/backend/wasm/types.js +68 -20
- package/src/backend/wasm/variadic.js +3 -3
- package/src/cli/cli-core.js +5 -1
- package/src/cli/test-runner.js +115 -342
- package/src/compiler.js +108 -130
- package/src/lexer/lexer.js +105 -11
- package/src/lexer/templ-lexer.js +61 -18
- package/src/lexer/tokens.js +1 -0
- package/src/link/stdlib.js +354 -132
- package/src/link/type-names.js +148 -0
- package/src/link/wasm.js +30 -6
- package/src/lower/bounds.js +153 -0
- package/src/lower/captures.js +92 -33
- package/src/lower/embedding.js +32 -3
- package/src/lower/errors-as.js +196 -0
- package/src/lower/fmt-formatters.js +905 -0
- package/src/lower/functions.js +2 -0
- package/src/lower/gen-types.js +274 -0
- package/src/lower/index.js +7 -1
- package/src/lower/json-codecs.js +228 -247
- package/src/lower/ownership.js +25 -7
- package/src/lower/reachability.js +69 -51
- package/src/lower/walk.js +12 -0
- package/src/parser/declarations.js +28 -5
- package/src/parser/expressions.js +86 -16
- package/src/parser/statements.js +1 -0
- package/src/parser/templ.js +44 -5
- package/src/parser/types.js +1 -0
- package/src/resolver.js +1 -2
- package/src/typechecker/assignability.js +124 -11
- package/src/typechecker/const-fold.js +70 -27
- package/src/typechecker/constants.js +32 -6
- package/src/typechecker/expressions.js +326 -98
- package/src/typechecker/index.js +208 -33
- package/src/typechecker/init-order.js +136 -40
- package/src/typechecker/operand-text.js +40 -0
- package/src/typechecker/resolve.js +192 -36
- package/src/typechecker/statements.js +122 -11
- package/src/typechecker/stdlib/excluded.js +9 -5
- package/src/typechecker/stdlib/index.js +25 -9
- package/src/typechecker/termination.js +10 -1
- package/src/typechecker/types.js +203 -573
- package/stdlib/bytes/buffer.go +479 -0
- package/stdlib/bytes/bytes.go +1010 -0
- package/stdlib/bytes/reader.go +161 -0
- package/stdlib/context/context.go +115 -0
- package/stdlib/crypto/rand/rand.go +34 -0
- package/stdlib/crypto/sha256/sha256.go +249 -0
- package/stdlib/encoding/base64/base64.go +331 -0
- package/stdlib/encoding/binary/binary.go +332 -0
- package/stdlib/encoding/hex/hex.go +140 -0
- package/stdlib/encoding/json/json.go +62 -0
- package/stdlib/errors/errors.go +174 -0
- package/stdlib/fmt/errors.go +110 -0
- package/stdlib/fmt/format.go +732 -0
- package/stdlib/fmt/print.go +1232 -0
- package/stdlib/gom/gom.go +293 -0
- package/stdlib/html/html.go +251 -0
- package/stdlib/internal/fmtrt/fmtrt.go +163 -0
- package/stdlib/internal/jsonrt/dec.go +171 -38
- package/stdlib/internal/jsonrt/enc.go +186 -36
- package/stdlib/internal/jsonrt/scanner.go +17 -8
- package/stdlib/io/io.go +157 -1
- package/stdlib/maps/maps.go +131 -0
- package/stdlib/math/bits/bits.go +507 -0
- package/stdlib/math/extra.go +16 -14
- package/stdlib/math/math.go +439 -0
- package/stdlib/math/rand/rand.go +297 -0
- package/stdlib/os/os.go +93 -0
- package/stdlib/path/filepath/filepath.go +47 -0
- package/stdlib/path/path.go +394 -0
- package/stdlib/regexp/regexp.go +231 -0
- package/stdlib/slices/slices.go +526 -0
- package/stdlib/slices/sort.go +209 -0
- package/stdlib/slices/zsort_cmp.go +480 -0
- package/stdlib/slices/zsort_ordered.go +482 -0
- package/stdlib/sort/sort.go +391 -452
- package/stdlib/sort/zsort_func.go +480 -0
- package/stdlib/sort/zsort_iface.go +480 -0
- package/stdlib/strconv/float.go +1699 -0
- package/stdlib/strconv/isprint.go +752 -0
- package/stdlib/strconv/quote.go +235 -50
- package/stdlib/strconv/strconv.go +616 -0
- package/stdlib/strings/builder.go +95 -0
- package/stdlib/strings/reader.go +163 -0
- package/stdlib/strings/replacer.go +102 -0
- package/stdlib/strings/strings.go +807 -0
- package/stdlib/sync/sync.go +268 -0
- package/stdlib/testing/async_js.go +61 -0
- package/stdlib/testing/benchmark.go +297 -0
- package/stdlib/testing/testing.go +523 -0
- package/stdlib/time/duration.go +233 -0
- package/stdlib/time/format.go +745 -0
- package/stdlib/time/parse.go +790 -0
- package/stdlib/time/sleep_js.go +63 -0
- package/stdlib/time/time.go +834 -0
- package/stdlib/unicode/unicode.go +141 -3
- package/stdlib/unicode/utf8/utf8.go +464 -0
- package/src/backend/js/fmt-runtime.js +0 -573
- package/src/backend/js/stdlib/base64.js +0 -12
- package/src/backend/js/stdlib/binary.js +0 -102
- package/src/backend/js/stdlib/bits.js +0 -119
- package/src/backend/js/stdlib/builder.js +0 -98
- package/src/backend/js/stdlib/bytes.js +0 -368
- package/src/backend/js/stdlib/errors.js +0 -45
- package/src/backend/js/stdlib/fmt.js +0 -517
- package/src/backend/js/stdlib/gom.js +0 -155
- package/src/backend/js/stdlib/hex.js +0 -17
- package/src/backend/js/stdlib/html.js +0 -20
- package/src/backend/js/stdlib/io.js +0 -137
- package/src/backend/js/stdlib/json.js +0 -1167
- package/src/backend/js/stdlib/maps.js +0 -37
- package/src/backend/js/stdlib/math.js +0 -85
- package/src/backend/js/stdlib/os.js +0 -13
- package/src/backend/js/stdlib/path.js +0 -48
- package/src/backend/js/stdlib/rand.js +0 -32
- package/src/backend/js/stdlib/regexp.js +0 -54
- package/src/backend/js/stdlib/sha256.js +0 -12
- package/src/backend/js/stdlib/shared.js +0 -17
- package/src/backend/js/stdlib/simd.js +0 -34
- package/src/backend/js/stdlib/slices.js +0 -107
- package/src/backend/js/stdlib/sort.js +0 -30
- package/src/backend/js/stdlib/strconv.js +0 -79
- package/src/backend/js/stdlib/strings.js +0 -301
- package/src/backend/js/stdlib/sync.js +0 -13
- package/src/backend/js/stdlib/testing.js +0 -41
- package/src/backend/js/stdlib/time.js +0 -152
- package/src/backend/js/stdlib/unicode.js +0 -25
- package/src/backend/js/stdlib/utf8.js +0 -26
- package/src/backend/wasm/stdlib/bytes.js +0 -6716
- package/src/backend/wasm/stdlib/core.js +0 -10278
- package/src/backend/wasm/stdlib/io.js +0 -3578
- package/src/backend/wasm/stdlib/packages.js +0 -2402
- package/src/backend/wasm/stdlib/pure.js +0 -720
- package/src/backend/wasm/stdlib/time.js +0 -2229
- package/src/typechecker/stdlib/core.js +0 -1079
- package/src/typechecker/stdlib/extended.js +0 -1210
- package/stdlib/bytes/iter.go +0 -192
- package/stdlib/bytes/search.go +0 -240
- package/stdlib/bytes/split.go +0 -218
- package/stdlib/errors/overlay.go +0 -12
- package/stdlib/maps/iter.go +0 -65
- package/stdlib/slices/extra.go +0 -73
- package/stdlib/slices/iter.go +0 -113
- package/stdlib/strings/extra.go +0 -35
- package/stdlib/strings/iter.go +0 -122
- package/stdlib/sync/extra.go +0 -105
- package/stdlib/unicode/utf8/bytes.go +0 -239
|
@@ -2,11 +2,14 @@
|
|
|
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 { typeStr } from "../../typechecker/types.js";
|
|
7
|
+
import { goTypeName } from "../shared/fmt-types.js";
|
|
5
8
|
import {
|
|
6
9
|
anonIfaceName,
|
|
10
|
+
ERROR_STRUCT,
|
|
7
11
|
getMapKeyValTypes,
|
|
8
12
|
isArrayType,
|
|
9
|
-
isBoolGoType,
|
|
10
13
|
isComplex,
|
|
11
14
|
isComplex64,
|
|
12
15
|
isMapType,
|
|
@@ -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
|
-
|
|
33
|
+
this.emitRuntimePanicThrow();
|
|
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");
|
|
31
53
|
}
|
|
32
54
|
|
|
33
|
-
// Consumes the anyref
|
|
34
|
-
// `env.panic
|
|
35
|
-
|
|
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,60 @@ export class BuiltinsEmitter {
|
|
|
1303
1257
|
};
|
|
1304
1258
|
}
|
|
1305
1259
|
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
this.
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
return true;
|
|
1324
|
-
}
|
|
1325
|
-
if (recvTypeName === "strings.Reader") {
|
|
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;
|
|
1390
|
-
}
|
|
1391
|
-
if (
|
|
1392
|
-
recvTypeName === "base64.Encoding" ||
|
|
1393
|
-
recvTypeName === "encoding/base64.Encoding"
|
|
1394
|
-
) {
|
|
1395
|
-
this.emitBase64EncodingMethodCall(field, expr, args, targetWasmType);
|
|
1396
|
-
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
|
+
if (this.mod.resolveFuncIndex(methodName) !== null)
|
|
1269
|
+
return { chain: [{ structInfo, embed }], methodName };
|
|
1270
|
+
const inner = this.mod.getStructType(typeName);
|
|
1271
|
+
const deeper = inner && this._findEmbeddedMethod(inner, method, seen);
|
|
1272
|
+
if (deeper)
|
|
1273
|
+
return {
|
|
1274
|
+
chain: [{ structInfo, embed }, ...deeper.chain],
|
|
1275
|
+
methodName: deeper.methodName,
|
|
1276
|
+
};
|
|
1397
1277
|
}
|
|
1398
|
-
return
|
|
1278
|
+
return null;
|
|
1399
1279
|
}
|
|
1400
1280
|
|
|
1401
1281
|
_emitEmbeddedMethodCall(recvTypeName, func, args, recvType) {
|
|
1402
1282
|
const structInfo = this.mod.getStructType(recvTypeName);
|
|
1403
1283
|
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
|
-
return true;
|
|
1425
|
-
}
|
|
1284
|
+
const found = this._findEmbeddedMethod(structInfo, func.field);
|
|
1285
|
+
if (!found) return false;
|
|
1286
|
+
const { chain, methodName } = found;
|
|
1287
|
+
const subIdx = this.mod.resolveFuncIndex(methodName);
|
|
1288
|
+
const targetParamTypes = this.mod.getFuncParamTypes(methodName);
|
|
1289
|
+
const valueRecv =
|
|
1290
|
+
this.mod.getFuncParamGoTypes(methodName)[0]?.kind !== "pointer";
|
|
1291
|
+
this.emitExpr(func.expr, this.toWasmType(recvType));
|
|
1292
|
+
this._emitNilCheck(func.expr._type);
|
|
1293
|
+
for (const [i, { structInfo: owner, embed }] of chain.entries()) {
|
|
1294
|
+
this.pushInstruction({
|
|
1295
|
+
op: "struct.get",
|
|
1296
|
+
typeIndex: owner.typeIndex,
|
|
1297
|
+
fieldIndex: embed.fieldIndex,
|
|
1298
|
+
});
|
|
1299
|
+
// Reading through an embedded nil pointer (to the next embedded
|
|
1300
|
+
// struct, or as a value receiver) is a recoverable nil dereference.
|
|
1301
|
+
const ft = owner.fields[embed.fieldIndex]?.goType;
|
|
1302
|
+
const isPtr = ft?.kind === "pointer" || ft?.kind === "PointerType";
|
|
1303
|
+
if (isPtr && (i < chain.length - 1 || valueRecv)) this._emitNilBranch();
|
|
1426
1304
|
}
|
|
1427
|
-
|
|
1305
|
+
for (let i = 0; i < args.length; i++) {
|
|
1306
|
+
const pType = targetParamTypes[i + 1] ?? null;
|
|
1307
|
+
this.emitExpr(args[i], pType);
|
|
1308
|
+
}
|
|
1309
|
+
this.pushInstruction({
|
|
1310
|
+
op: "call",
|
|
1311
|
+
funcIndex: subIdx,
|
|
1312
|
+
});
|
|
1313
|
+
return true;
|
|
1428
1314
|
}
|
|
1429
1315
|
|
|
1430
1316
|
_emitStaticCall(call) {
|
|
@@ -1437,6 +1323,12 @@ export class BuiltinsEmitter {
|
|
|
1437
1323
|
if (targetFuncIdx !== null) {
|
|
1438
1324
|
const paramTypes = this.mod.getFuncParamTypes(func.name);
|
|
1439
1325
|
const paramGoTypes = this.mod.getFuncParamGoTypes(func.name);
|
|
1326
|
+
if (call._multiForward) {
|
|
1327
|
+
// `f(g())`: g's results are f's arguments as they come.
|
|
1328
|
+
this.emitExpr(args[0], null);
|
|
1329
|
+
this.pushInstruction({ op: "call", funcIndex: targetFuncIdx });
|
|
1330
|
+
return true;
|
|
1331
|
+
}
|
|
1440
1332
|
for (let i = 0; i < args.length; i++) {
|
|
1441
1333
|
const arg = args[i];
|
|
1442
1334
|
const pType = paramTypes[i] ?? null;
|
|
@@ -1534,4 +1426,499 @@ export class BuiltinsEmitter {
|
|
|
1534
1426
|
|
|
1535
1427
|
this._emitClosureCall(call);
|
|
1536
1428
|
}
|
|
1429
|
+
|
|
1430
|
+
emitSliceClear(arg, argType) {
|
|
1431
|
+
const elemGoType = this._getSliceElemType(argType);
|
|
1432
|
+
const sliceInfo = this.mod.getSliceType(elemGoType);
|
|
1433
|
+
const arrInfo = sliceInfo.arrInfo;
|
|
1434
|
+
|
|
1435
|
+
const sliceTmp = this.acquireTemp({
|
|
1436
|
+
kind: "ref",
|
|
1437
|
+
nullable: true,
|
|
1438
|
+
typeIndex: sliceInfo.typeIndex,
|
|
1439
|
+
});
|
|
1440
|
+
this.emitExpr(arg, {
|
|
1441
|
+
kind: "ref",
|
|
1442
|
+
nullable: true,
|
|
1443
|
+
typeIndex: sliceInfo.typeIndex,
|
|
1444
|
+
});
|
|
1445
|
+
this.pushInstruction({ op: "local.set", index: sliceTmp });
|
|
1446
|
+
|
|
1447
|
+
this.pushInstruction({ op: "block", blockType: "void" });
|
|
1448
|
+
this.pushInstruction({ op: "local.get", index: sliceTmp });
|
|
1449
|
+
this.pushInstruction("ref.is_null");
|
|
1450
|
+
this.pushInstruction({ op: "br_if", depth: 0 });
|
|
1451
|
+
|
|
1452
|
+
const arrTmp = this.acquireTemp({
|
|
1453
|
+
kind: "ref",
|
|
1454
|
+
nullable: true,
|
|
1455
|
+
typeIndex: arrInfo.typeIndex,
|
|
1456
|
+
});
|
|
1457
|
+
const offTmp = this.acquireTemp("i32");
|
|
1458
|
+
const lenTmp = this.acquireTemp("i32");
|
|
1459
|
+
const iLoc = this.acquireTemp("i32");
|
|
1460
|
+
|
|
1461
|
+
this._emitSliceUnpack(sliceTmp, sliceInfo, {
|
|
1462
|
+
arr: arrTmp,
|
|
1463
|
+
off: offTmp,
|
|
1464
|
+
len: lenTmp,
|
|
1465
|
+
});
|
|
1466
|
+
|
|
1467
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
1468
|
+
this.pushInstruction({ op: "local.set", index: iLoc });
|
|
1469
|
+
|
|
1470
|
+
this.pushInstruction({ op: "block", blockType: "void" });
|
|
1471
|
+
this.pushInstruction({ op: "loop", blockType: "void" });
|
|
1472
|
+
this.pushInstruction({ op: "local.get", index: iLoc });
|
|
1473
|
+
this.pushInstruction({ op: "local.get", index: lenTmp });
|
|
1474
|
+
this.pushInstruction("i32.ge_s");
|
|
1475
|
+
this.pushInstruction({ op: "br_if", depth: 1 });
|
|
1476
|
+
|
|
1477
|
+
this.pushInstruction({ op: "local.get", index: arrTmp });
|
|
1478
|
+
this.pushInstruction({ op: "local.get", index: offTmp });
|
|
1479
|
+
this.pushInstruction({ op: "local.get", index: iLoc });
|
|
1480
|
+
this.pushInstruction("i32.add");
|
|
1481
|
+
this.emitZeroValue(elemGoType, arrInfo.elemWType);
|
|
1482
|
+
this.pushInstruction({ op: "array.set", typeIndex: arrInfo.typeIndex });
|
|
1483
|
+
|
|
1484
|
+
this.pushInstruction({ op: "local.get", index: iLoc });
|
|
1485
|
+
this.pushInstruction({ op: "i32.const", value: 1 });
|
|
1486
|
+
this.pushInstruction("i32.add");
|
|
1487
|
+
this.pushInstruction({ op: "local.set", index: iLoc });
|
|
1488
|
+
this.pushInstruction({ op: "br", depth: 0 });
|
|
1489
|
+
this.pushInstruction("end");
|
|
1490
|
+
this.pushInstruction("end");
|
|
1491
|
+
|
|
1492
|
+
this.releaseTemp(iLoc, "i32");
|
|
1493
|
+
this.releaseTemp(lenTmp, "i32");
|
|
1494
|
+
this.releaseTemp(offTmp, "i32");
|
|
1495
|
+
this.releaseTemp(arrTmp, {
|
|
1496
|
+
kind: "ref",
|
|
1497
|
+
nullable: true,
|
|
1498
|
+
typeIndex: arrInfo.typeIndex,
|
|
1499
|
+
});
|
|
1500
|
+
|
|
1501
|
+
this.pushInstruction("end");
|
|
1502
|
+
this.releaseTemp(sliceTmp, {
|
|
1503
|
+
kind: "ref",
|
|
1504
|
+
nullable: true,
|
|
1505
|
+
typeIndex: sliceInfo.typeIndex,
|
|
1506
|
+
});
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
// Pushes an integer-ish argument as i32 (Go ints are i64 in this backend).
|
|
1510
|
+
_emitI32Arg(arg) {
|
|
1511
|
+
const wType = toWasmType(arg?._type, this.mod.checker);
|
|
1512
|
+
if (wType === "i64") {
|
|
1513
|
+
this.emitExpr(arg, "i64");
|
|
1514
|
+
this.pushInstruction("i32.wrap_i64");
|
|
1515
|
+
} else {
|
|
1516
|
+
this.emitExpr(arg, "i32");
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
// `a == b` for two anyref operands (interface values). `ref.eq` only
|
|
1521
|
+
// accepts eqref, so host strings (internalised externs) are compared via
|
|
1522
|
+
// `str_eq` and anything else by identity.
|
|
1523
|
+
_emitAnyEq() {
|
|
1524
|
+
const idx = this.mod.resolveFuncIndex("__any_eq");
|
|
1525
|
+
if (idx === null) {
|
|
1526
|
+
throw new Error(
|
|
1527
|
+
"internal: anyref equality emitted but __any_eq was not declared (usesAnyEq scan missed a site)",
|
|
1528
|
+
);
|
|
1529
|
+
}
|
|
1530
|
+
this.pushInstruction({ op: "call", funcIndex: idx });
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
// Every `[wasm type index, Go type name]` an `any` can hold in this module,
|
|
1534
|
+
// more specific types last so they overwrite their supertypes.
|
|
1535
|
+
_dynamicTypeNames() {
|
|
1536
|
+
const named = (name) => {
|
|
1537
|
+
const i = name.indexOf("$$");
|
|
1538
|
+
return i < 0
|
|
1539
|
+
? goTypeName({ kind: "named", name })
|
|
1540
|
+
: `${name.slice(i + 2)}.${name.slice(0, i)}`;
|
|
1541
|
+
};
|
|
1542
|
+
const mod = this.mod;
|
|
1543
|
+
const names = [];
|
|
1544
|
+
for (const s of mod.sliceTypes.values())
|
|
1545
|
+
names.push([
|
|
1546
|
+
s.typeIndex,
|
|
1547
|
+
goTypeName({ kind: "slice", elem: s.elemGoType }),
|
|
1548
|
+
]);
|
|
1549
|
+
for (const m of mod.mapTypes.values())
|
|
1550
|
+
names.push([
|
|
1551
|
+
m.typeIndex,
|
|
1552
|
+
goTypeName({ kind: "map", key: m.keyGoType, value: m.valGoType }),
|
|
1553
|
+
]);
|
|
1554
|
+
for (const info of new Set(mod.structTypes.values())) {
|
|
1555
|
+
if (info.isAnon) continue;
|
|
1556
|
+
names.push([
|
|
1557
|
+
info.typeIndex,
|
|
1558
|
+
info.name === ERROR_STRUCT
|
|
1559
|
+
? "*errors.errorString"
|
|
1560
|
+
: `*${named(info.name)}`,
|
|
1561
|
+
]);
|
|
1562
|
+
}
|
|
1563
|
+
for (const box of mod.ifaceBoxTypes.values())
|
|
1564
|
+
names.push([box.typeIndex, goTypeName(box.goType)]);
|
|
1565
|
+
for (const info of mod.taggedSliceTypes.values())
|
|
1566
|
+
names.push([info.typeIndex, goTypeName(info.goType)]);
|
|
1567
|
+
for (const info of mod.nilBoxTypes.values())
|
|
1568
|
+
names.push([info.typeIndex, goTypeName(info.goType)]);
|
|
1569
|
+
return names;
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
// Body of `__ta_fail(v any, src string, target string, iface int32)`: panics
|
|
1573
|
+
// with Go's failed type assertion message, naming the dynamic type of `v`
|
|
1574
|
+
// from the types the module knows (`src` is the static interface type, or
|
|
1575
|
+
// the first method the target interface needs when `iface` is 1).
|
|
1576
|
+
_emitTypeAssertFail() {
|
|
1577
|
+
const strConst = (text) => {
|
|
1578
|
+
this.pushInstruction({
|
|
1579
|
+
op: "i32.const",
|
|
1580
|
+
value: this.mod.internString(text),
|
|
1581
|
+
});
|
|
1582
|
+
this.pushInstruction({
|
|
1583
|
+
op: "call",
|
|
1584
|
+
funcIndex: this.mod.getStringImportIndex(),
|
|
1585
|
+
});
|
|
1586
|
+
};
|
|
1587
|
+
const concat = () =>
|
|
1588
|
+
this.pushInstruction({
|
|
1589
|
+
op: "call",
|
|
1590
|
+
funcIndex: this.mod.getStringConcatImportIndex(),
|
|
1591
|
+
});
|
|
1592
|
+
const dyn = this.allocLocal(null, "externref");
|
|
1593
|
+
strConst("interface {}");
|
|
1594
|
+
this.pushInstruction({ op: "local.set", index: dyn });
|
|
1595
|
+
const when = (typeIndex, text) => {
|
|
1596
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1597
|
+
this.pushInstruction({ op: "ref.test", typeIndex });
|
|
1598
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1599
|
+
strConst(text);
|
|
1600
|
+
this.pushInstruction({ op: "local.set", index: dyn });
|
|
1601
|
+
this.pushInstruction("end");
|
|
1602
|
+
};
|
|
1603
|
+
for (const [typeIndex, text] of this._dynamicTypeNames())
|
|
1604
|
+
when(typeIndex, text);
|
|
1605
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1606
|
+
this.pushInstruction({
|
|
1607
|
+
op: "call",
|
|
1608
|
+
funcIndex: this.mod.getIsStringImportIndex(),
|
|
1609
|
+
});
|
|
1610
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1611
|
+
strConst("string");
|
|
1612
|
+
this.pushInstruction({ op: "local.set", index: dyn });
|
|
1613
|
+
this.pushInstruction("end");
|
|
1614
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1615
|
+
this.pushInstruction("ref.is_null");
|
|
1616
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1617
|
+
strConst("nil");
|
|
1618
|
+
this.pushInstruction({ op: "local.set", index: dyn });
|
|
1619
|
+
this.pushInstruction("end");
|
|
1620
|
+
|
|
1621
|
+
// An interface target: `<dyn> is not <target>: missing method <M>`.
|
|
1622
|
+
this.pushInstruction({ op: "local.get", index: 3 });
|
|
1623
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1624
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1625
|
+
this.pushInstruction("ref.is_null");
|
|
1626
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1627
|
+
strConst("interface conversion: interface is nil, not ");
|
|
1628
|
+
this.pushInstruction({ op: "local.get", index: 2 });
|
|
1629
|
+
concat();
|
|
1630
|
+
this.emitRuntimePanicThrow();
|
|
1631
|
+
this.pushInstruction("end");
|
|
1632
|
+
strConst("interface conversion: ");
|
|
1633
|
+
this.pushInstruction({ op: "local.get", index: dyn });
|
|
1634
|
+
concat();
|
|
1635
|
+
strConst(" is not ");
|
|
1636
|
+
concat();
|
|
1637
|
+
this.pushInstruction({ op: "local.get", index: 2 });
|
|
1638
|
+
concat();
|
|
1639
|
+
strConst(": missing method ");
|
|
1640
|
+
concat();
|
|
1641
|
+
this.pushInstruction({ op: "local.get", index: 1 });
|
|
1642
|
+
concat();
|
|
1643
|
+
this.emitRuntimePanicThrow();
|
|
1644
|
+
this.pushInstruction("end");
|
|
1645
|
+
|
|
1646
|
+
strConst("interface conversion: ");
|
|
1647
|
+
this.pushInstruction({ op: "local.get", index: 1 });
|
|
1648
|
+
concat();
|
|
1649
|
+
strConst(" is ");
|
|
1650
|
+
concat();
|
|
1651
|
+
this.pushInstruction({ op: "local.get", index: dyn });
|
|
1652
|
+
concat();
|
|
1653
|
+
strConst(", not ");
|
|
1654
|
+
concat();
|
|
1655
|
+
this.pushInstruction({ op: "local.get", index: 2 });
|
|
1656
|
+
concat();
|
|
1657
|
+
this.emitRuntimePanicThrow();
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
emitRuntimeHelper(fn) {
|
|
1661
|
+
const errInfo = this.mod.getStructType(ERROR_STRUCT);
|
|
1662
|
+
switch (fn._isRuntimeHelper) {
|
|
1663
|
+
case "any_eq": {
|
|
1664
|
+
const bothNull = () => {
|
|
1665
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1666
|
+
this.pushInstruction("ref.is_null");
|
|
1667
|
+
this.pushInstruction({ op: "local.get", index: 1 });
|
|
1668
|
+
this.pushInstruction("ref.is_null");
|
|
1669
|
+
};
|
|
1670
|
+
bothNull();
|
|
1671
|
+
this.pushInstruction("i32.and");
|
|
1672
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1673
|
+
this.pushInstruction({ op: "i32.const", value: 1 });
|
|
1674
|
+
this.pushInstruction("return");
|
|
1675
|
+
this.pushInstruction("end");
|
|
1676
|
+
bothNull();
|
|
1677
|
+
this.pushInstruction("i32.or");
|
|
1678
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1679
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
1680
|
+
this.pushInstruction("return");
|
|
1681
|
+
this.pushInstruction("end");
|
|
1682
|
+
|
|
1683
|
+
// Check unhashable / uncomparable types
|
|
1684
|
+
const checkUncomparable = (typeIndex) => {
|
|
1685
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1686
|
+
this.pushInstruction({ op: "ref.test", typeIndex });
|
|
1687
|
+
this.pushInstruction({ op: "local.get", index: 1 });
|
|
1688
|
+
this.pushInstruction({ op: "ref.test", typeIndex });
|
|
1689
|
+
this.pushInstruction("i32.or");
|
|
1690
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1691
|
+
this.emitPanic("runtime error: comparing uncomparable type");
|
|
1692
|
+
this.pushInstruction("end");
|
|
1693
|
+
};
|
|
1694
|
+
for (const s of this.mod.sliceTypes.values())
|
|
1695
|
+
checkUncomparable(s.typeIndex);
|
|
1696
|
+
for (const m of this.mod.mapTypes.values())
|
|
1697
|
+
checkUncomparable(m.typeIndex);
|
|
1698
|
+
for (const c of this.mod.closureTypes.values())
|
|
1699
|
+
checkUncomparable(c.typeIndex);
|
|
1700
|
+
for (const b of this.mod.ifaceBoxTypes.values()) {
|
|
1701
|
+
if (isUnhashableGoType(b.goType, this.mod.checker))
|
|
1702
|
+
checkUncomparable(b.typeIndex);
|
|
1703
|
+
}
|
|
1704
|
+
for (const a of this.mod.arrayTypes.values()) {
|
|
1705
|
+
if (isUnhashableGoType(a.elemGoType, this.mod.checker))
|
|
1706
|
+
checkUncomparable(a.typeIndex);
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
// Boxed values compare by value (Go: any(3) == any(3), struct values, etc.).
|
|
1710
|
+
for (const box of this.mod.ifaceBoxTypes.values()) {
|
|
1711
|
+
if (isUnhashableGoType(box.goType, this.mod.checker)) continue;
|
|
1712
|
+
for (const idx of [0, 1]) {
|
|
1713
|
+
this.pushInstruction({ op: "local.get", index: idx });
|
|
1714
|
+
this.pushInstruction({ op: "ref.test", typeIndex: box.typeIndex });
|
|
1715
|
+
}
|
|
1716
|
+
this.pushInstruction("i32.and");
|
|
1717
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1718
|
+
const f1 = this.allocLocal(null, box.wType);
|
|
1719
|
+
const f2 = this.allocLocal(null, box.wType);
|
|
1720
|
+
for (const [src, dst] of [
|
|
1721
|
+
[0, f1],
|
|
1722
|
+
[1, f2],
|
|
1723
|
+
]) {
|
|
1724
|
+
this.pushInstruction({ op: "local.get", index: src });
|
|
1725
|
+
this.pushInstruction({ op: "ref.cast", typeIndex: box.typeIndex });
|
|
1726
|
+
this.pushInstruction({
|
|
1727
|
+
op: "struct.get",
|
|
1728
|
+
typeIndex: box.typeIndex,
|
|
1729
|
+
fieldIndex: 0,
|
|
1730
|
+
});
|
|
1731
|
+
this.pushInstruction({ op: "local.set", index: dst });
|
|
1732
|
+
}
|
|
1733
|
+
this.emitKeyEq(f1, f2, box.goType);
|
|
1734
|
+
this.pushInstruction("return");
|
|
1735
|
+
this.pushInstruction("end");
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
// Strings
|
|
1739
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1740
|
+
this.pushInstruction({
|
|
1741
|
+
op: "call",
|
|
1742
|
+
funcIndex: this.mod.getIsStringImportIndex(),
|
|
1743
|
+
});
|
|
1744
|
+
this.pushInstruction({ op: "local.get", index: 1 });
|
|
1745
|
+
this.pushInstruction({
|
|
1746
|
+
op: "call",
|
|
1747
|
+
funcIndex: this.mod.getIsStringImportIndex(),
|
|
1748
|
+
});
|
|
1749
|
+
this.pushInstruction("i32.and");
|
|
1750
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1751
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1752
|
+
this.pushInstruction("extern.convert_any");
|
|
1753
|
+
this.pushInstruction({ op: "local.get", index: 1 });
|
|
1754
|
+
this.pushInstruction("extern.convert_any");
|
|
1755
|
+
this.pushInstruction({
|
|
1756
|
+
op: "call",
|
|
1757
|
+
funcIndex: this.mod.getStringCmpImportIndex("=="),
|
|
1758
|
+
});
|
|
1759
|
+
this.pushInstruction("return");
|
|
1760
|
+
this.pushInstruction("end");
|
|
1761
|
+
|
|
1762
|
+
// Heap reference equality
|
|
1763
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1764
|
+
this.pushInstruction({ op: "ref.test", heapType: "eq" });
|
|
1765
|
+
this.pushInstruction({ op: "local.get", index: 1 });
|
|
1766
|
+
this.pushInstruction({ op: "ref.test", heapType: "eq" });
|
|
1767
|
+
this.pushInstruction("i32.and");
|
|
1768
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1769
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1770
|
+
this.pushInstruction({ op: "ref.cast", heapType: "eq" });
|
|
1771
|
+
this.pushInstruction({ op: "local.get", index: 1 });
|
|
1772
|
+
this.pushInstruction({ op: "ref.cast", heapType: "eq" });
|
|
1773
|
+
this.pushInstruction("ref.eq");
|
|
1774
|
+
this.pushInstruction("return");
|
|
1775
|
+
this.pushInstruction("end");
|
|
1776
|
+
|
|
1777
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
1778
|
+
this.pushInstruction("return");
|
|
1779
|
+
return;
|
|
1780
|
+
}
|
|
1781
|
+
case "any_hash": {
|
|
1782
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1783
|
+
this.pushInstruction("ref.is_null");
|
|
1784
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1785
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
1786
|
+
this.pushInstruction("return");
|
|
1787
|
+
this.pushInstruction("end");
|
|
1788
|
+
|
|
1789
|
+
// Check unhashable types
|
|
1790
|
+
const checkUnhashable = (typeIndex, goType) => {
|
|
1791
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1792
|
+
this.pushInstruction({ op: "ref.test", typeIndex });
|
|
1793
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1794
|
+
this.emitPanic(
|
|
1795
|
+
goType
|
|
1796
|
+
? `runtime error: hash of unhashable type ${typeStr(goType)}`
|
|
1797
|
+
: "runtime error: hash of unhashable type",
|
|
1798
|
+
);
|
|
1799
|
+
this.pushInstruction("end");
|
|
1800
|
+
};
|
|
1801
|
+
// A slice type first built from source syntax (a `TypeName`) names
|
|
1802
|
+
// its element as written.
|
|
1803
|
+
const spelled = (t) =>
|
|
1804
|
+
t?.kind === "TypeName" ? { kind: "basic", name: t.name } : t;
|
|
1805
|
+
for (const sliceInfo of this.mod.sliceTypes.values())
|
|
1806
|
+
checkUnhashable(sliceInfo.typeIndex, {
|
|
1807
|
+
kind: "slice",
|
|
1808
|
+
elem: spelled(sliceInfo.elemGoType),
|
|
1809
|
+
});
|
|
1810
|
+
for (const mapInfo of this.mod.mapTypes.values())
|
|
1811
|
+
checkUnhashable(mapInfo.typeIndex, {
|
|
1812
|
+
kind: "map",
|
|
1813
|
+
key: mapInfo.keyGoType,
|
|
1814
|
+
value: mapInfo.valGoType,
|
|
1815
|
+
});
|
|
1816
|
+
for (const closureInfo of this.mod.closureTypes.values())
|
|
1817
|
+
checkUnhashable(closureInfo.typeIndex);
|
|
1818
|
+
for (const box of this.mod.ifaceBoxTypes.values()) {
|
|
1819
|
+
if (isUnhashableGoType(box.goType, this.mod.checker))
|
|
1820
|
+
checkUnhashable(box.typeIndex);
|
|
1821
|
+
}
|
|
1822
|
+
for (const arrInfo of this.mod.arrayTypes.values()) {
|
|
1823
|
+
if (isUnhashableGoType(arrInfo.elemGoType, this.mod.checker))
|
|
1824
|
+
checkUnhashable(arrInfo.typeIndex);
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
// Check boxed values (scalars, struct values)
|
|
1828
|
+
for (const box of this.mod.ifaceBoxTypes.values()) {
|
|
1829
|
+
if (isUnhashableGoType(box.goType, this.mod.checker)) continue;
|
|
1830
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1831
|
+
this.pushInstruction({ op: "ref.test", typeIndex: box.typeIndex });
|
|
1832
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1833
|
+
const valLoc = this.allocLocal(null, box.wType);
|
|
1834
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1835
|
+
this.pushInstruction({ op: "ref.cast", typeIndex: box.typeIndex });
|
|
1836
|
+
this.pushInstruction({
|
|
1837
|
+
op: "struct.get",
|
|
1838
|
+
typeIndex: box.typeIndex,
|
|
1839
|
+
fieldIndex: 0,
|
|
1840
|
+
});
|
|
1841
|
+
this.pushInstruction({ op: "local.set", index: valLoc });
|
|
1842
|
+
this.pushInstruction({ op: "i32.const", value: box.typeIndex });
|
|
1843
|
+
this.pushInstruction({ op: "i32.const", value: 31 });
|
|
1844
|
+
this.pushInstruction("i32.mul");
|
|
1845
|
+
this.emitKeyHash(valLoc, box.goType);
|
|
1846
|
+
this.pushInstruction("i32.add");
|
|
1847
|
+
this.pushInstruction("return");
|
|
1848
|
+
this.pushInstruction("end");
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
// Check string
|
|
1852
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1853
|
+
this.pushInstruction({
|
|
1854
|
+
op: "call",
|
|
1855
|
+
funcIndex: this.mod.getIsStringImportIndex(),
|
|
1856
|
+
});
|
|
1857
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1858
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1859
|
+
this.pushInstruction("extern.convert_any");
|
|
1860
|
+
this.pushInstruction({
|
|
1861
|
+
op: "call",
|
|
1862
|
+
funcIndex: this.mod.getStringHashImportIndex(),
|
|
1863
|
+
});
|
|
1864
|
+
this.pushInstruction("return");
|
|
1865
|
+
this.pushInstruction("end");
|
|
1866
|
+
|
|
1867
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
1868
|
+
this.pushInstruction("return");
|
|
1869
|
+
return;
|
|
1870
|
+
}
|
|
1871
|
+
case "ta_fail":
|
|
1872
|
+
this._emitTypeAssertFail();
|
|
1873
|
+
return;
|
|
1874
|
+
case "error_msg":
|
|
1875
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1876
|
+
this.pushInstruction({
|
|
1877
|
+
op: "struct.get",
|
|
1878
|
+
typeIndex: errInfo.typeIndex,
|
|
1879
|
+
fieldIndex: 0,
|
|
1880
|
+
});
|
|
1881
|
+
this.pushInstruction("return");
|
|
1882
|
+
return;
|
|
1883
|
+
case "mk_error":
|
|
1884
|
+
// nil message (strconv success) → nil error
|
|
1885
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1886
|
+
this.pushInstruction("any.convert_extern");
|
|
1887
|
+
this.pushInstruction("ref.is_null");
|
|
1888
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1889
|
+
this.pushInstruction({ op: "ref.null", heapType: "any" });
|
|
1890
|
+
this.pushInstruction("return");
|
|
1891
|
+
this.pushInstruction("end");
|
|
1892
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1893
|
+
this.pushInstruction({
|
|
1894
|
+
op: "struct.new",
|
|
1895
|
+
typeIndex: errInfo.typeIndex,
|
|
1896
|
+
});
|
|
1897
|
+
this.pushInstruction("return");
|
|
1898
|
+
return;
|
|
1899
|
+
case "error_str":
|
|
1900
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1901
|
+
this.pushInstruction("ref.is_null");
|
|
1902
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1903
|
+
this.pushInstruction({
|
|
1904
|
+
op: "i32.const",
|
|
1905
|
+
value: this.mod.internString("<nil>"),
|
|
1906
|
+
});
|
|
1907
|
+
this.pushInstruction({
|
|
1908
|
+
op: "call",
|
|
1909
|
+
funcIndex: this.mod.getStringImportIndex(),
|
|
1910
|
+
});
|
|
1911
|
+
this.pushInstruction("return");
|
|
1912
|
+
this.pushInstruction("end");
|
|
1913
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
1914
|
+
this.pushInstruction({
|
|
1915
|
+
op: "call",
|
|
1916
|
+
funcIndex: this.mod.resolveFuncIndex("__dispatch_error_Error"),
|
|
1917
|
+
});
|
|
1918
|
+
this.pushInstruction("return");
|
|
1919
|
+
return;
|
|
1920
|
+
default:
|
|
1921
|
+
throw new Error(`Unknown runtime helper: ${fn._isRuntimeHelper}`);
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1537
1924
|
}
|