gofront 1.7.0 → 1.8.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 +13 -13
- package/package.json +8 -37
- package/src/backend/js/expressions.js +1341 -148
- package/src/backend/js/fmt-runtime.js +573 -0
- package/src/backend/js/index.js +375 -57
- package/src/backend/js/runtime.js +1460 -133
- package/src/backend/js/statements.js +900 -86
- package/src/backend/js/stdlib/bits.js +87 -14
- package/src/backend/js/stdlib/builder.js +41 -6
- package/src/backend/js/stdlib/bytes.js +253 -7
- package/src/backend/js/stdlib/errors.js +2 -1
- package/src/backend/js/stdlib/fmt.js +349 -10
- package/src/backend/js/stdlib/index.js +8 -3
- package/src/backend/js/stdlib/json.js +247 -52
- package/src/backend/js/stdlib/maps.js +0 -8
- package/src/backend/js/stdlib/math.js +17 -1
- package/src/backend/js/stdlib/simd.js +10 -0
- package/src/backend/js/stdlib/slices.js +36 -6
- package/src/backend/js/stdlib/strconv.js +10 -5
- package/src/backend/js/stdlib/strings.js +83 -4
- package/src/backend/js/stdlib/testing.js +7 -0
- package/src/backend/js/stdlib/time.js +79 -55
- package/src/backend/js/stdlib/unicode.js +1 -1
- package/src/backend/js/stdlib/utf8.js +1 -1
- package/src/backend/js/templ.js +38 -76
- package/src/backend/js/type-info.js +272 -0
- package/src/backend/shared/fmt-types.js +188 -0
- package/src/backend/wasm/boundary.js +436 -80
- package/src/backend/wasm/emit-builtins.js +303 -13
- package/src/backend/wasm/emit-exprs.js +1397 -203
- package/src/backend/wasm/emit-maps.js +61 -13
- package/src/backend/wasm/emit-stmts.js +1359 -164
- package/src/backend/wasm/emit.js +512 -77
- package/src/backend/wasm/encode.js +43 -13
- package/src/backend/wasm/globals.js +452 -0
- package/src/backend/wasm/index.js +194 -2489
- package/src/backend/wasm/method-expr.js +381 -0
- package/src/backend/wasm/module-emitter.js +2240 -0
- package/src/backend/wasm/monomorph.js +62 -4
- package/src/backend/wasm/prescan.js +397 -0
- package/src/backend/wasm/stdlib/bytes.js +1804 -127
- package/src/backend/wasm/stdlib/core.js +1574 -199
- package/src/backend/wasm/stdlib/index.js +0 -5
- package/src/backend/wasm/stdlib/io.js +1567 -150
- package/src/backend/wasm/stdlib/packages.js +2 -0
- package/src/backend/wasm/stdlib/simd.js +293 -28
- package/src/backend/wasm/stdlib/time.js +1041 -12
- package/src/backend/wasm/types.js +200 -15
- package/src/backend/wasm/variadic.js +93 -0
- package/src/backend/wasm/wat.js +47 -20
- package/src/{cli-core.js → cli/cli-core.js} +236 -10
- package/src/{test-runner.js → cli/test-runner.js} +332 -39
- package/src/{vendor.js → cli/vendor.js} +3 -4
- package/src/compiler.js +107 -269
- package/src/index.js +15 -169
- package/src/{lexer.js → lexer/lexer.js} +102 -55
- package/src/{tokens.js → lexer/tokens.js} +1 -0
- package/src/link/sourcemap.js +97 -0
- package/src/link/stdlib.js +515 -0
- package/src/link/wasm.js +294 -0
- package/src/lower/bounds.js +394 -0
- package/src/lower/boxing.js +1 -1
- package/src/lower/captures.js +176 -32
- package/src/lower/index.js +26 -18
- package/src/lower/json-codecs.js +1398 -0
- package/src/lower/ownership.js +22 -1
- package/src/lower/range.js +86 -1
- package/src/lower/reachability.js +132 -0
- package/src/parser/declarations.js +58 -13
- package/src/parser/expressions.js +29 -7
- package/src/parser/index.js +2 -1
- package/src/parser/statements.js +37 -22
- package/src/{templ-parser.js → parser/templ.js} +138 -15
- package/src/parser/types.js +69 -9
- package/src/resolver.js +22 -1
- package/src/typechecker/assignability.js +176 -17
- package/src/typechecker/const-fold.js +356 -0
- package/src/typechecker/constants.js +256 -0
- package/src/typechecker/expressions.js +931 -122
- package/src/typechecker/index.js +459 -67
- package/src/typechecker/init-order.js +330 -0
- package/src/typechecker/resolve.js +451 -56
- package/src/typechecker/statements.js +90 -9
- package/src/typechecker/stdlib/core.js +333 -55
- package/src/typechecker/stdlib/excluded.js +898 -0
- package/src/typechecker/stdlib/extended.js +322 -26
- package/src/typechecker/{stdlib.js → stdlib/index.js} +5 -5
- package/src/typechecker/stdlib/simd.js +65 -15
- package/src/typechecker/termination.js +1 -1
- package/src/typechecker/types.js +149 -25
- package/stdlib/bytes/iter.go +192 -0
- package/stdlib/bytes/search.go +240 -0
- package/stdlib/bytes/split.go +218 -0
- package/stdlib/cmp/cmp.go +65 -0
- package/stdlib/encoding/json/errors.go +31 -0
- package/stdlib/encoding/json/indent.go +71 -0
- package/stdlib/encoding/json/number.go +12 -0
- package/stdlib/encoding/json/stream.go +173 -0
- package/stdlib/errors/overlay.go +12 -0
- package/stdlib/hash/hash.go +53 -0
- package/stdlib/internal/jsonrt/dec.go +738 -0
- package/stdlib/internal/jsonrt/enc.go +366 -0
- package/stdlib/internal/jsonrt/indent.go +135 -0
- package/stdlib/internal/jsonrt/number.go +27 -0
- package/stdlib/internal/jsonrt/reader.go +73 -0
- package/stdlib/internal/jsonrt/scanner.go +649 -0
- package/stdlib/io/io.go +346 -0
- package/stdlib/iter/iter.go +18 -0
- package/stdlib/maps/iter.go +65 -0
- package/stdlib/math/extra.go +270 -0
- package/stdlib/slices/extra.go +73 -0
- package/stdlib/slices/iter.go +113 -0
- package/stdlib/sort/sort.go +561 -0
- package/stdlib/strconv/quote.go +341 -0
- package/stdlib/strings/extra.go +35 -0
- package/stdlib/strings/iter.go +122 -0
- package/stdlib/sync/extra.go +105 -0
- package/stdlib/unicode/unicode.go +112 -0
- package/stdlib/unicode/utf8/bytes.go +239 -0
- package/src/backend/wasm/const-fold.js +0 -205
- package/src/backend/wasm/emit-stdlib.js +0 -13
- /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
- /package/src/{colors.js → cli/colors.js} +0 -0
- /package/src/{dev-server.js → cli/dev-server.js} +0 -0
- /package/src/{minifier.js → cli/minifier.js} +0 -0
- /package/src/{project-config.js → cli/project-config.js} +0 -0
- /package/src/{pwa.js → cli/pwa.js} +0 -0
- /package/src/{templ-lexer.js → lexer/templ-lexer.js} +0 -0
|
@@ -3,12 +3,24 @@
|
|
|
3
3
|
// math/bits, maps, slices, strings, strconv, fmt, errors, sort, unicode/utf8,
|
|
4
4
|
// testing) plus the runtime helper bodies they rely on.
|
|
5
5
|
|
|
6
|
+
import {
|
|
7
|
+
fmtDescHasFloat,
|
|
8
|
+
fmtDescIsSeq,
|
|
9
|
+
fmtTypeDesc,
|
|
10
|
+
fmtWantsTypes,
|
|
11
|
+
} from "../../shared/fmt-types.js";
|
|
6
12
|
import {
|
|
7
13
|
ERROR_STRUCT,
|
|
8
14
|
getMapKeyValTypes,
|
|
15
|
+
getSliceElemType,
|
|
9
16
|
goTypeKey,
|
|
17
|
+
isAnyType,
|
|
10
18
|
isArrayType,
|
|
19
|
+
isBoolGoType,
|
|
20
|
+
isComplex,
|
|
21
|
+
isComplex64,
|
|
11
22
|
isErrorType,
|
|
23
|
+
isFuncType,
|
|
12
24
|
isMapType,
|
|
13
25
|
isPointerToStruct,
|
|
14
26
|
isSigned,
|
|
@@ -18,6 +30,45 @@ import {
|
|
|
18
30
|
toWasmType,
|
|
19
31
|
} from "../types.js";
|
|
20
32
|
|
|
33
|
+
function _isPointerKind(t) {
|
|
34
|
+
return (
|
|
35
|
+
t?.kind === "pointer" || t?.kind === "PointerType" || t?.kind === "StarExpr"
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Whether `sig` is `Error() string` / `String() string` and in the method set
|
|
40
|
+
// of the operand (a pointer receiver needs a pointer operand).
|
|
41
|
+
function _isFmtMethodSig(sig, isPtr) {
|
|
42
|
+
if (!sig || sig.params?.length || sig.variadic) return false;
|
|
43
|
+
if (sig._ptrRecv && !isPtr) return false;
|
|
44
|
+
const ret = sig.returns ?? [];
|
|
45
|
+
return ret.length === 1 && ret[0]?.name === "string";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function _isUint32(goType) {
|
|
49
|
+
let t = goType;
|
|
50
|
+
while (t && (t.kind === "named" || t.kind === "TypeName") && t.underlying) {
|
|
51
|
+
t = t.underlying;
|
|
52
|
+
}
|
|
53
|
+
return t?.name === "uint32";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function _isSimplePrimitive(t) {
|
|
57
|
+
const n = t?.name ?? t;
|
|
58
|
+
return n === "string" || n === "bool";
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function _getStructOrComplexInfo(mod, emitter, goType) {
|
|
62
|
+
if (isComplex(goType)) {
|
|
63
|
+
const is64 = isComplex64(goType);
|
|
64
|
+
return mod.getComplexType(is64 ? "complex64" : "complex128");
|
|
65
|
+
}
|
|
66
|
+
return (
|
|
67
|
+
emitter._resolveStructInfo({ _type: goType }) ??
|
|
68
|
+
(goType?.name ? mod.getStructType(goType.name) : null)
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
21
72
|
// `unicode/utf8` package constants.
|
|
22
73
|
const UTF8_CONSTS = {
|
|
23
74
|
RuneError: 0xfffd,
|
|
@@ -86,6 +137,20 @@ export class StdlibCoreEmitter {
|
|
|
86
137
|
);
|
|
87
138
|
}
|
|
88
139
|
|
|
140
|
+
_isBitsConst(expr) {
|
|
141
|
+
return this._isPkgExpr(expr, "bits") && expr.field === "UintSize";
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
emitBitsConst(name, targetWasmType) {
|
|
145
|
+
if (name === "UintSize") {
|
|
146
|
+
if (targetWasmType === "i32") {
|
|
147
|
+
this.pushInstruction({ op: "i32.const", value: 64 });
|
|
148
|
+
} else {
|
|
149
|
+
this.pushInstruction({ op: "i64.const", value: 64n });
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
89
154
|
emitUtf8Const(name, targetWasmType) {
|
|
90
155
|
const v = UTF8_CONSTS[name];
|
|
91
156
|
if ((targetWasmType ?? "i32") === "i64") {
|
|
@@ -337,8 +402,10 @@ export class StdlibCoreEmitter {
|
|
|
337
402
|
Cbrt: "cbrt",
|
|
338
403
|
Exp: "exp",
|
|
339
404
|
Log: "log",
|
|
405
|
+
Log1p: "log1p",
|
|
340
406
|
Log2: "log2",
|
|
341
407
|
Log10: "log10",
|
|
408
|
+
Expm1: "expm1",
|
|
342
409
|
Round: "round",
|
|
343
410
|
Sinh: "sinh",
|
|
344
411
|
Cosh: "cosh",
|
|
@@ -364,7 +431,7 @@ export class StdlibCoreEmitter {
|
|
|
364
431
|
emitTestingCall(func, args) {
|
|
365
432
|
const method = func.field;
|
|
366
433
|
const nameIdx = this.mod.internString(method);
|
|
367
|
-
if (method === "Run") {
|
|
434
|
+
if (method === "Run" && args.length > 0) {
|
|
368
435
|
this.emitExpr(func.expr, "externref");
|
|
369
436
|
this.emitExpr(args[0], "externref");
|
|
370
437
|
this.emitExpr(args[1], "anyref");
|
|
@@ -374,6 +441,15 @@ export class StdlibCoreEmitter {
|
|
|
374
441
|
});
|
|
375
442
|
return;
|
|
376
443
|
}
|
|
444
|
+
if (method === "Cleanup") {
|
|
445
|
+
this.emitExpr(func.expr, "externref");
|
|
446
|
+
this.emitExpr(args[0], "anyref");
|
|
447
|
+
this.pushInstruction({
|
|
448
|
+
op: "call",
|
|
449
|
+
funcIndex: this.mod.getStdlibImportIndex("testing_cleanup"),
|
|
450
|
+
});
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
377
453
|
if (method === "Name") {
|
|
378
454
|
this.emitExpr(func.expr, "externref");
|
|
379
455
|
this.pushInstruction({
|
|
@@ -382,6 +458,60 @@ export class StdlibCoreEmitter {
|
|
|
382
458
|
});
|
|
383
459
|
return;
|
|
384
460
|
}
|
|
461
|
+
if (method === "Loop") {
|
|
462
|
+
this.emitExpr(func.expr, "externref");
|
|
463
|
+
this.pushInstruction({ op: "i32.const", value: nameIdx });
|
|
464
|
+
this.pushInstruction({
|
|
465
|
+
op: "call",
|
|
466
|
+
funcIndex: this.mod.getTestingFlagImportIndex(),
|
|
467
|
+
});
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
if (method === "Run" && args.length === 0) {
|
|
471
|
+
this.emitExpr(func.expr, "externref");
|
|
472
|
+
this.pushInstruction({ op: "i32.const", value: nameIdx });
|
|
473
|
+
this.pushInstruction({
|
|
474
|
+
op: "call",
|
|
475
|
+
funcIndex: this.mod.getTestingFlagImportIndex(),
|
|
476
|
+
});
|
|
477
|
+
this.pushInstruction("i64.extend_i32_s");
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
if (method === "Elapsed") {
|
|
481
|
+
this.emitExpr(func.expr, "externref");
|
|
482
|
+
this.pushInstruction({
|
|
483
|
+
op: "call",
|
|
484
|
+
funcIndex: this.mod.getStdlibImportIndex("testing_elapsed"),
|
|
485
|
+
});
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
if (method === "Context") {
|
|
489
|
+
this.emitExpr(func.expr, "externref");
|
|
490
|
+
this.pushInstruction({
|
|
491
|
+
op: "call",
|
|
492
|
+
funcIndex: this.mod.getStdlibImportIndex("testing_context"),
|
|
493
|
+
});
|
|
494
|
+
this.pushInstruction("any.convert_extern");
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
if (method === "Output") {
|
|
498
|
+
this.emitExpr(func.expr, "externref");
|
|
499
|
+
this.pushInstruction({
|
|
500
|
+
op: "call",
|
|
501
|
+
funcIndex: this.mod.getStdlibImportIndex("testing_output"),
|
|
502
|
+
});
|
|
503
|
+
this.pushInstruction("any.convert_extern");
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
if (method === "Deadline") {
|
|
507
|
+
this.emitExpr(func.expr, "externref");
|
|
508
|
+
this.pushInstruction("drop");
|
|
509
|
+
const timeType = this.mod.checker?.types?.get("time.Time");
|
|
510
|
+
const timeWType = this.toWasmType(timeType);
|
|
511
|
+
this.emitZeroValue(timeType, timeWType);
|
|
512
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
385
515
|
if (method === "Failed" || method === "Skipped") {
|
|
386
516
|
this.emitExpr(func.expr, "externref");
|
|
387
517
|
this.pushInstruction({ op: "i32.const", value: nameIdx });
|
|
@@ -406,29 +536,133 @@ export class StdlibCoreEmitter {
|
|
|
406
536
|
// argument itself, or `Error()` on an `error` operand) happens in the first
|
|
407
537
|
// phase: such code can call Sprintf and reset the JS-side argument buffer,
|
|
408
538
|
_isCompositeGoType(goType) {
|
|
409
|
-
if (!goType) return false;
|
|
539
|
+
if (!goType || _isSimplePrimitive(goType)) return false;
|
|
410
540
|
if (isErrorType(goType) && this.mod.hasErrorType) return false;
|
|
411
|
-
if (goType.name === "string" || goType === "string") return false;
|
|
412
|
-
if (goType.name === "bool" || goType === "bool") return false;
|
|
413
541
|
return (
|
|
542
|
+
isComplex(goType) ||
|
|
543
|
+
toWasmType(goType, this.mod.checker, this.mod) === "v128" ||
|
|
414
544
|
isSliceType(goType, this.mod.checker) ||
|
|
415
545
|
isArrayType(goType, this.mod.checker) ||
|
|
416
546
|
isMapType(goType, this.mod.checker) ||
|
|
417
547
|
isStructType(goType, this.mod.checker, this.mod) ||
|
|
418
548
|
isPointerToStruct(goType, this.mod.checker, this.mod) ||
|
|
419
|
-
goType
|
|
420
|
-
|
|
421
|
-
|
|
549
|
+
_isPointerKind(goType)
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
_isFmtExternOperand(goType) {
|
|
554
|
+
const name = goType?.name;
|
|
555
|
+
if (name === "time.Duration") {
|
|
556
|
+
return true;
|
|
557
|
+
}
|
|
558
|
+
return (
|
|
559
|
+
isFuncType(goType, this.mod.checker) ||
|
|
560
|
+
isAnyType(goType, this.mod.checker) ||
|
|
561
|
+
this._isCompositeGoType(goType) ||
|
|
562
|
+
this._fmtMethodOf(goType) !== null
|
|
422
563
|
);
|
|
423
564
|
}
|
|
424
565
|
|
|
566
|
+
// Pushes `arg` as the externref the fmt host helpers format.
|
|
567
|
+
_emitOperandExternref(arg, goType, allowNil = false) {
|
|
568
|
+
if (goType?.name === "time.Duration") {
|
|
569
|
+
// The raw count stays available for %d; %v and %s use the string.
|
|
570
|
+
const durTmp = this.acquireTemp("i64");
|
|
571
|
+
this.emitExpr(arg, "i64");
|
|
572
|
+
this.pushInstruction({ op: "local.set", index: durTmp });
|
|
573
|
+
this.pushInstruction({ op: "local.get", index: durTmp });
|
|
574
|
+
this._emitToExternrefPlain({ kind: "basic", name: "int64" });
|
|
575
|
+
this.pushInstruction({ op: "local.get", index: durTmp });
|
|
576
|
+
this._callStdlib("time_duration_string");
|
|
577
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
578
|
+
this.pushInstruction({
|
|
579
|
+
op: "call",
|
|
580
|
+
funcIndex: this.mod.getFmtNewStringerIndex(),
|
|
581
|
+
});
|
|
582
|
+
this.releaseTemp(durTmp, "i64");
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
if (isAnyType(goType, this.mod.checker)) {
|
|
586
|
+
this.emitExpr(arg, "anyref");
|
|
587
|
+
this._emitAnyToExternref();
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
if (isFuncType(goType, this.mod.checker)) {
|
|
591
|
+
// A func value has no address in WebAssembly: a nil one prints
|
|
592
|
+
// <nil>, any other prints a fixed address-style text.
|
|
593
|
+
this.emitExpr(arg, toWasmType(goType, this.mod.checker, this.mod));
|
|
594
|
+
this.pushInstruction("ref.is_null");
|
|
595
|
+
this.pushInstruction({ op: "if", blockType: "externref" });
|
|
596
|
+
this.pushInstruction({ op: "ref.null", heapType: "extern" });
|
|
597
|
+
this.pushInstruction("else");
|
|
598
|
+
this.pushInstruction({
|
|
599
|
+
op: "i32.const",
|
|
600
|
+
value: this.mod.internString("0x1"),
|
|
601
|
+
});
|
|
602
|
+
this.pushInstruction({
|
|
603
|
+
op: "call",
|
|
604
|
+
funcIndex: this.mod.getStringImportIndex(),
|
|
605
|
+
});
|
|
606
|
+
this.pushInstruction("end");
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
this.emitExpr(arg, toWasmType(goType, this.mod.checker, this.mod));
|
|
610
|
+
this.emitToExternref(goType, false, new Set(), allowNil);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// Turns the `any` on the stack into the externref the fmt host helpers
|
|
614
|
+
// format: strings pass through, scalars are boxed by their dynamic type,
|
|
615
|
+
// anything else (and nil) becomes a null externref.
|
|
616
|
+
_emitAnyToExternref() {
|
|
617
|
+
const helper = this.mod.resolveFuncIndex("__any_fmt");
|
|
618
|
+
if (helper !== null && helper !== undefined) {
|
|
619
|
+
this.pushInstruction({ op: "call", funcIndex: helper });
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
this._emitAnyScalarsToExternref();
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
_emitAnyScalarsToExternref() {
|
|
626
|
+
const tmp = this.acquireTemp("anyref");
|
|
627
|
+
this.pushInstruction({ op: "local.set", index: tmp });
|
|
628
|
+
const kinds = [
|
|
629
|
+
"string",
|
|
630
|
+
"bool",
|
|
631
|
+
"int",
|
|
632
|
+
"int8",
|
|
633
|
+
"int16",
|
|
634
|
+
"int32",
|
|
635
|
+
"int64",
|
|
636
|
+
"uint",
|
|
637
|
+
"uint8",
|
|
638
|
+
"uint16",
|
|
639
|
+
"uint32",
|
|
640
|
+
"uint64",
|
|
641
|
+
"float32",
|
|
642
|
+
"float64",
|
|
643
|
+
];
|
|
644
|
+
for (const name of kinds) {
|
|
645
|
+
const t = { kind: "basic", name };
|
|
646
|
+
this.pushInstruction({ op: "local.get", index: tmp });
|
|
647
|
+
this._emitTypeTest(t);
|
|
648
|
+
this.pushInstruction({ op: "if", blockType: "externref" });
|
|
649
|
+
this.pushInstruction({ op: "local.get", index: tmp });
|
|
650
|
+
this._emitTypeCast(t, toWasmType(t, this.mod.checker, this.mod));
|
|
651
|
+
this.emitToExternref(t, false);
|
|
652
|
+
this.pushInstruction("else");
|
|
653
|
+
}
|
|
654
|
+
this.pushInstruction({ op: "ref.null", heapType: "extern" });
|
|
655
|
+
for (let i = 0; i < kinds.length; i++) this.pushInstruction("end");
|
|
656
|
+
this.releaseTemp(tmp, "anyref");
|
|
657
|
+
}
|
|
658
|
+
|
|
425
659
|
_emitScalarToExternref(goType, valTmp, valW) {
|
|
426
660
|
if (goType?.name === "string" || goType === "string") {
|
|
427
661
|
this.pushInstruction({ op: "local.get", index: valTmp });
|
|
428
662
|
this.releaseTemp(valTmp, valW);
|
|
429
663
|
return true;
|
|
430
664
|
}
|
|
431
|
-
if (goType
|
|
665
|
+
if (isBoolGoType(goType) || goType === "bool") {
|
|
432
666
|
this.pushInstruction({ op: "local.get", index: valTmp });
|
|
433
667
|
this.pushInstruction({
|
|
434
668
|
op: "call",
|
|
@@ -446,6 +680,32 @@ export class StdlibCoreEmitter {
|
|
|
446
680
|
this.releaseTemp(valTmp, valW);
|
|
447
681
|
return true;
|
|
448
682
|
}
|
|
683
|
+
if ((valW === "i32" || valW === "i64") && !isSigned(goType)) {
|
|
684
|
+
// Unsigned operands are widened (uint32) or reinterpreted by the
|
|
685
|
+
// host (uint64), so the top bit is not read as a sign.
|
|
686
|
+
this.pushInstruction({ op: "local.get", index: valTmp });
|
|
687
|
+
if (valW === "i32") {
|
|
688
|
+
if (_isUint32(goType)) {
|
|
689
|
+
this.pushInstruction("i64.extend_i32_u");
|
|
690
|
+
this.pushInstruction({
|
|
691
|
+
op: "call",
|
|
692
|
+
funcIndex: this.mod.getFmtBoxI64Index(),
|
|
693
|
+
});
|
|
694
|
+
} else {
|
|
695
|
+
this.pushInstruction({
|
|
696
|
+
op: "call",
|
|
697
|
+
funcIndex: this.mod.getFmtBoxI32Index(),
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
} else {
|
|
701
|
+
this.pushInstruction({
|
|
702
|
+
op: "call",
|
|
703
|
+
funcIndex: this.mod.getFmtBoxU64Index(),
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
this.releaseTemp(valTmp, valW);
|
|
707
|
+
return true;
|
|
708
|
+
}
|
|
449
709
|
if (valW === "i32" || valW === "i64") {
|
|
450
710
|
this.pushInstruction({ op: "local.get", index: valTmp });
|
|
451
711
|
const funcIndex =
|
|
@@ -469,8 +729,15 @@ export class StdlibCoreEmitter {
|
|
|
469
729
|
return false;
|
|
470
730
|
}
|
|
471
731
|
|
|
472
|
-
_emitSliceToExternref(
|
|
473
|
-
|
|
732
|
+
_emitSliceToExternref(
|
|
733
|
+
goType,
|
|
734
|
+
valTmp,
|
|
735
|
+
valW,
|
|
736
|
+
inCycle,
|
|
737
|
+
nextSeen,
|
|
738
|
+
allowNil = false,
|
|
739
|
+
) {
|
|
740
|
+
const elemGoType = this._getSliceElemType(goType);
|
|
474
741
|
const sliceInfo = this.mod.getSliceType(elemGoType);
|
|
475
742
|
const arrInfo = this.mod.getArrayType(elemGoType);
|
|
476
743
|
const arrWType = {
|
|
@@ -504,6 +771,10 @@ export class StdlibCoreEmitter {
|
|
|
504
771
|
this.pushInstruction({ op: "local.get", index: valTmp });
|
|
505
772
|
this.pushInstruction("ref.is_null");
|
|
506
773
|
this.pushInstruction({ op: "if", blockType: "void" });
|
|
774
|
+
if (allowNil) {
|
|
775
|
+
this.pushInstruction({ op: "ref.null", heapType: "extern" });
|
|
776
|
+
this.pushInstruction({ op: "local.set", index: resTmp });
|
|
777
|
+
}
|
|
507
778
|
this.pushInstruction("else");
|
|
508
779
|
|
|
509
780
|
this._emitSliceUnpack(valTmp, sliceInfo, {
|
|
@@ -512,6 +783,15 @@ export class StdlibCoreEmitter {
|
|
|
512
783
|
len: lenTmp,
|
|
513
784
|
});
|
|
514
785
|
|
|
786
|
+
if (allowNil) {
|
|
787
|
+
this.pushInstruction({ op: "local.get", index: arrTmp });
|
|
788
|
+
this.pushInstruction("ref.is_null");
|
|
789
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
790
|
+
this.pushInstruction({ op: "ref.null", heapType: "extern" });
|
|
791
|
+
this.pushInstruction({ op: "local.set", index: resTmp });
|
|
792
|
+
this.pushInstruction("end");
|
|
793
|
+
}
|
|
794
|
+
|
|
515
795
|
this.pushInstruction({ op: "local.get", index: arrTmp });
|
|
516
796
|
this.pushInstruction("ref.is_null");
|
|
517
797
|
this.pushInstruction({ op: "local.get", index: lenTmp });
|
|
@@ -572,8 +852,49 @@ export class StdlibCoreEmitter {
|
|
|
572
852
|
this.releaseTemp(valTmp, valW);
|
|
573
853
|
}
|
|
574
854
|
|
|
855
|
+
// F32x4 and Mask32x4 print like a [4]float32 / [4]bool array.
|
|
856
|
+
_emitSimdToExternref(goType, valTmp, valW) {
|
|
857
|
+
const isMask =
|
|
858
|
+
goType?.name === "simd.Mask32x4" ||
|
|
859
|
+
goType?.simd === "Mask32x4" ||
|
|
860
|
+
goType?.underlying?.simd === "Mask32x4";
|
|
861
|
+
const resTmp = this.acquireTemp("externref");
|
|
862
|
+
this.pushInstruction({
|
|
863
|
+
op: "call",
|
|
864
|
+
funcIndex: this.mod.getFmtNewArrayIndex(),
|
|
865
|
+
});
|
|
866
|
+
this.pushInstruction({ op: "local.set", index: resTmp });
|
|
867
|
+
for (let lane = 0; lane < 4; lane++) {
|
|
868
|
+
this.pushInstruction({ op: "local.get", index: resTmp });
|
|
869
|
+
this.pushInstruction({ op: "local.get", index: valTmp });
|
|
870
|
+
if (isMask) {
|
|
871
|
+
this.pushInstruction({ op: "i32x4.extract_lane", lane });
|
|
872
|
+
this.pushInstruction({ op: "i32.const", value: 0 });
|
|
873
|
+
this.pushInstruction("i32.ne");
|
|
874
|
+
this.pushInstruction({
|
|
875
|
+
op: "call",
|
|
876
|
+
funcIndex: this.mod.getFmtBoxBoolIndex(),
|
|
877
|
+
});
|
|
878
|
+
} else {
|
|
879
|
+
this.pushInstruction({ op: "f32x4.extract_lane", lane });
|
|
880
|
+
this.pushInstruction("f64.promote_f32");
|
|
881
|
+
this.pushInstruction({
|
|
882
|
+
op: "call",
|
|
883
|
+
funcIndex: this.mod.getFmtBoxF64Index(),
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
this.pushInstruction({
|
|
887
|
+
op: "call",
|
|
888
|
+
funcIndex: this.mod.getFmtArrayPushIndex(),
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
this.pushInstruction({ op: "local.get", index: resTmp });
|
|
892
|
+
this.releaseTemp(resTmp, "externref");
|
|
893
|
+
this.releaseTemp(valTmp, valW);
|
|
894
|
+
}
|
|
895
|
+
|
|
575
896
|
_emitArrayToExternref(goType, valTmp, valW, inCycle, nextSeen) {
|
|
576
|
-
const elemGoType =
|
|
897
|
+
const elemGoType = this._getArrayElemType(goType);
|
|
577
898
|
const arrInfo = this.mod.getArrayType(elemGoType);
|
|
578
899
|
const resTmp = this.acquireTemp("externref");
|
|
579
900
|
const lenTmp = this.acquireTemp("i32");
|
|
@@ -597,6 +918,8 @@ export class StdlibCoreEmitter {
|
|
|
597
918
|
this.pushInstruction({ op: "local.get", index: valTmp });
|
|
598
919
|
this.pushInstruction("ref.is_null");
|
|
599
920
|
this.pushInstruction({ op: "if", blockType: "void" });
|
|
921
|
+
this.pushInstruction({ op: "ref.null", heapType: "extern" });
|
|
922
|
+
this.pushInstruction({ op: "local.set", index: resTmp });
|
|
600
923
|
this.pushInstruction("else");
|
|
601
924
|
|
|
602
925
|
this.pushInstruction({ op: "local.get", index: valTmp });
|
|
@@ -649,7 +972,14 @@ export class StdlibCoreEmitter {
|
|
|
649
972
|
this.releaseTemp(valTmp, valW);
|
|
650
973
|
}
|
|
651
974
|
|
|
652
|
-
_emitMapToExternref(
|
|
975
|
+
_emitMapToExternref(
|
|
976
|
+
goType,
|
|
977
|
+
valTmp,
|
|
978
|
+
valW,
|
|
979
|
+
inCycle,
|
|
980
|
+
nextSeen,
|
|
981
|
+
allowNil = false,
|
|
982
|
+
) {
|
|
653
983
|
const { keyType, valType } = getMapKeyValTypes(goType, this.mod.checker);
|
|
654
984
|
const mapInfo = this.mod.getMapType(keyType, valType);
|
|
655
985
|
const resTmp = this.acquireTemp("externref");
|
|
@@ -682,6 +1012,10 @@ export class StdlibCoreEmitter {
|
|
|
682
1012
|
this.pushInstruction({ op: "local.get", index: valTmp });
|
|
683
1013
|
this.pushInstruction("ref.is_null");
|
|
684
1014
|
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1015
|
+
if (allowNil) {
|
|
1016
|
+
this.pushInstruction({ op: "ref.null", heapType: "extern" });
|
|
1017
|
+
this.pushInstruction({ op: "local.set", index: resTmp });
|
|
1018
|
+
}
|
|
685
1019
|
this.pushInstruction("else");
|
|
686
1020
|
|
|
687
1021
|
// currIdx = map.head (field 5)
|
|
@@ -781,9 +1115,13 @@ export class StdlibCoreEmitter {
|
|
|
781
1115
|
this.pushInstruction({ op: "ref.null", heapType: "extern" });
|
|
782
1116
|
this.pushInstruction({ op: "local.set", index: resTmp });
|
|
783
1117
|
this.pushInstruction("else");
|
|
1118
|
+
// Kind 2 is a pointer to a scalar: the host keeps one box per target,
|
|
1119
|
+
// so the same pointer formatted twice (%p) gets the same id.
|
|
1120
|
+
this.pushInstruction({ op: "local.get", index: valTmp });
|
|
1121
|
+
this.pushInstruction({ op: "i32.const", value: 2 });
|
|
784
1122
|
this.pushInstruction({
|
|
785
1123
|
op: "call",
|
|
786
|
-
funcIndex: this.mod.
|
|
1124
|
+
funcIndex: this.mod.getFmtNewStructIndex(),
|
|
787
1125
|
});
|
|
788
1126
|
this.pushInstruction({ op: "local.set", index: resTmp });
|
|
789
1127
|
this.pushInstruction("end");
|
|
@@ -802,9 +1140,7 @@ export class StdlibCoreEmitter {
|
|
|
802
1140
|
inCycle,
|
|
803
1141
|
nextSeen,
|
|
804
1142
|
) {
|
|
805
|
-
const structInfo =
|
|
806
|
-
this._resolveStructInfo({ _type: goType }) ??
|
|
807
|
-
(goType?.name ? this.mod.getStructType(goType.name) : null);
|
|
1143
|
+
const structInfo = _getStructOrComplexInfo(this.mod, this, goType);
|
|
808
1144
|
const resTmp = this.acquireTemp("externref");
|
|
809
1145
|
|
|
810
1146
|
this.pushInstruction({ op: "local.get", index: valTmp });
|
|
@@ -831,6 +1167,22 @@ export class StdlibCoreEmitter {
|
|
|
831
1167
|
funcIndex: this.mod.getFmtNewStructIndex(),
|
|
832
1168
|
});
|
|
833
1169
|
this.pushInstruction({ op: "local.set", index: resTmp });
|
|
1170
|
+
// The Go type name, for `%T` and `%#v` of an `any` holding the struct.
|
|
1171
|
+
const tdesc = fmtTypeDesc(
|
|
1172
|
+
isPtr ? (goType.base ?? goType.elem) : goType,
|
|
1173
|
+
(t) => this.mod.checker?.resolveType?.(t) ?? t,
|
|
1174
|
+
);
|
|
1175
|
+
if (tdesc?.k === "struct" && tdesc.n) {
|
|
1176
|
+
this.pushInstruction({ op: "local.get", index: resTmp });
|
|
1177
|
+
this.pushInstruction({
|
|
1178
|
+
op: "i32.const",
|
|
1179
|
+
value: this.mod.internString(tdesc.n),
|
|
1180
|
+
});
|
|
1181
|
+
this.pushInstruction({
|
|
1182
|
+
op: "call",
|
|
1183
|
+
funcIndex: this.mod.getFmtStructTypeImportIndex(),
|
|
1184
|
+
});
|
|
1185
|
+
}
|
|
834
1186
|
|
|
835
1187
|
if ((!isPtr || !isNested) && !inCycle && structInfo?.fields) {
|
|
836
1188
|
for (let i = 0; i < structInfo.fields.length; i++) {
|
|
@@ -865,47 +1217,259 @@ export class StdlibCoreEmitter {
|
|
|
865
1217
|
this.releaseTemp(valTmp, valW);
|
|
866
1218
|
}
|
|
867
1219
|
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
if (
|
|
1220
|
+
// The `String() string` / `Error() string` method fmt calls on a value of
|
|
1221
|
+
// static type `goType` (Error first, as in Go), or null. A pointer-receiver
|
|
1222
|
+
// method applies to pointers only; interfaces are dispatched on the dynamic
|
|
1223
|
+
// type (`__any_fmt`).
|
|
1224
|
+
_fmtMethodOf(goType) {
|
|
1225
|
+
if (!goType || isAnyType(goType, this.mod.checker)) return null;
|
|
1226
|
+
const checker = this.mod.checker;
|
|
1227
|
+
const isPtr = _isPointerKind(goType);
|
|
1228
|
+
const named = this._fmtNamed(isPtr ? (goType.base ?? goType.elem) : goType);
|
|
1229
|
+
if (!named || named.name === ERROR_STRUCT) return null;
|
|
1230
|
+
// Elements of an `any`-held container share a wasm type across Go types
|
|
1231
|
+
// (`[]Color` and `[]int`), so scalar-backed named types are not dispatched.
|
|
1232
|
+
if (
|
|
1233
|
+
this._fmtNoScalarMethods &&
|
|
1234
|
+
!isPtr &&
|
|
1235
|
+
!isStructType(goType, checker, this.mod)
|
|
1236
|
+
)
|
|
1237
|
+
return null;
|
|
1238
|
+
// Linked projects use a stub checker (no `implements`), so look the
|
|
1239
|
+
// method up on the named type directly.
|
|
1240
|
+
const methodMap = this._fmtMethodMap(named);
|
|
1241
|
+
for (const method of ["Error", "String"]) {
|
|
1242
|
+
const sig = methodMap?.get(method);
|
|
1243
|
+
if (!_isFmtMethodSig(sig, isPtr)) continue;
|
|
1244
|
+
const found = this._fmtMethodFunc(named, method);
|
|
1245
|
+
if (found) return { method, isPtr, ...found };
|
|
1246
|
+
}
|
|
1247
|
+
return null;
|
|
1248
|
+
}
|
|
874
1249
|
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
1250
|
+
// The named type behind `t` (type names resolve through the checker), or null.
|
|
1251
|
+
_fmtNamed(t) {
|
|
1252
|
+
if (t?.kind === "TypeName" || t?.kind === "Ident")
|
|
1253
|
+
t = this.mod.checker?.types?.get(t.name) ?? t;
|
|
1254
|
+
return t?.kind === "named" && t.name ? t : null;
|
|
1255
|
+
}
|
|
878
1256
|
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
1257
|
+
_fmtMethodMap(named) {
|
|
1258
|
+
const resolved = this.mod.checker?.types?.get(named.name) ?? named;
|
|
1259
|
+
const under = resolved?.kind === "named" ? resolved.underlying : resolved;
|
|
1260
|
+
if (under?.kind === "interface") return null;
|
|
1261
|
+
return resolved?.methods?.size
|
|
1262
|
+
? resolved.methods
|
|
1263
|
+
: (under?.methods ?? null);
|
|
1264
|
+
}
|
|
883
1265
|
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
1266
|
+
_fmtMethodFunc(named, method) {
|
|
1267
|
+
const names = [`${named.name}.${method}`];
|
|
1268
|
+
if (named.pkg) names.unshift(`${named.pkg}.${named.name}.${method}`);
|
|
1269
|
+
for (const n of names) {
|
|
1270
|
+
const funcIndex = this.mod.resolveFuncIndex(n);
|
|
1271
|
+
if (funcIndex === null || funcIndex === undefined) continue;
|
|
1272
|
+
const recv = this.mod.getFuncParamGoTypes(n)[0];
|
|
1273
|
+
return { funcIndex, valueRecv: !_isPointerKind(recv) };
|
|
887
1274
|
}
|
|
1275
|
+
return null;
|
|
1276
|
+
}
|
|
888
1277
|
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
1278
|
+
// A struct that embeds `error` and declares no Error/String itself prints
|
|
1279
|
+
// the embedded error's text (the checker does not promote interface
|
|
1280
|
+
// methods, so only fmt sees this).
|
|
1281
|
+
_fmtEmbeddedError(goType) {
|
|
1282
|
+
if (!this.mod.hasErrorType) return null;
|
|
1283
|
+
let t = _isPointerKind(goType) ? (goType.base ?? goType.elem) : goType;
|
|
1284
|
+
if (t?.kind === "TypeName" || t?.kind === "Ident")
|
|
1285
|
+
t = this.mod.checker?.types?.get(t.name) ?? t;
|
|
1286
|
+
if (t?.kind !== "named" || t.name === ERROR_STRUCT) return null;
|
|
1287
|
+
const sInfo = this.mod.getStructType(t.name);
|
|
1288
|
+
const field = sInfo?.fields?.find(
|
|
1289
|
+
(f) => f.embedded && isErrorType(f.goType),
|
|
1290
|
+
);
|
|
1291
|
+
if (!field) return null;
|
|
1292
|
+
return {
|
|
1293
|
+
typeIndex: sInfo.typeIndex,
|
|
1294
|
+
fieldIndex: sInfo.fieldIndexMap.get(field.name),
|
|
1295
|
+
};
|
|
1296
|
+
}
|
|
893
1297
|
|
|
894
|
-
|
|
895
|
-
const
|
|
896
|
-
|
|
897
|
-
const
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
1298
|
+
_emitEmbeddedErrorToExternref(goType, emb, isNested, seen) {
|
|
1299
|
+
const valW = toWasmType(goType, this.mod.checker, this.mod);
|
|
1300
|
+
const valTmp = this.acquireTemp(valW);
|
|
1301
|
+
const rawTmp = this.acquireTemp("externref");
|
|
1302
|
+
const errTmp = this.acquireTemp("anyref");
|
|
1303
|
+
this.pushInstruction({ op: "local.tee", index: valTmp });
|
|
1304
|
+
this._emitToExternrefPlain(goType, isNested, seen);
|
|
1305
|
+
this.pushInstruction({ op: "local.set", index: rawTmp });
|
|
1306
|
+
// nil struct pointer or nil embedded error: print the struct as is.
|
|
1307
|
+
this.pushInstruction({ op: "local.get", index: valTmp });
|
|
1308
|
+
this.pushInstruction("ref.is_null");
|
|
1309
|
+
this.pushInstruction({ op: "if", blockType: "externref" });
|
|
1310
|
+
this.pushInstruction({ op: "local.get", index: rawTmp });
|
|
1311
|
+
this.pushInstruction("else");
|
|
1312
|
+
this.pushInstruction({ op: "local.get", index: valTmp });
|
|
1313
|
+
this.pushInstruction({
|
|
1314
|
+
op: "struct.get",
|
|
1315
|
+
typeIndex: emb.typeIndex,
|
|
1316
|
+
fieldIndex: emb.fieldIndex,
|
|
1317
|
+
});
|
|
1318
|
+
this.pushInstruction({ op: "local.tee", index: errTmp });
|
|
1319
|
+
this.pushInstruction("ref.is_null");
|
|
1320
|
+
this.pushInstruction({ op: "if", blockType: "externref" });
|
|
1321
|
+
this.pushInstruction({ op: "local.get", index: rawTmp });
|
|
1322
|
+
this.pushInstruction("else");
|
|
1323
|
+
this.pushInstruction({ op: "local.get", index: rawTmp });
|
|
1324
|
+
this.pushInstruction({ op: "local.get", index: errTmp });
|
|
1325
|
+
this.pushInstruction({
|
|
1326
|
+
op: "call",
|
|
1327
|
+
funcIndex: this.mod.resolveFuncIndex("__error_str"),
|
|
1328
|
+
});
|
|
1329
|
+
this.pushInstruction({ op: "i32.const", value: 1 });
|
|
1330
|
+
this.pushInstruction({
|
|
1331
|
+
op: "call",
|
|
1332
|
+
funcIndex: this.mod.getFmtNewStringerIndex(),
|
|
1333
|
+
});
|
|
1334
|
+
this.pushInstruction("end");
|
|
1335
|
+
this.pushInstruction("end");
|
|
1336
|
+
this.releaseTemp(errTmp, "anyref");
|
|
1337
|
+
this.releaseTemp(rawTmp, "externref");
|
|
1338
|
+
this.releaseTemp(valTmp, valW);
|
|
1339
|
+
}
|
|
902
1340
|
|
|
903
|
-
|
|
904
|
-
|
|
1341
|
+
emitToExternref(
|
|
1342
|
+
goType,
|
|
1343
|
+
isNested = false,
|
|
1344
|
+
seen = new Set(),
|
|
1345
|
+
allowNil = false,
|
|
1346
|
+
) {
|
|
1347
|
+
const m = this._fmtMethodOf(goType);
|
|
1348
|
+
if (!m) {
|
|
1349
|
+
const emb = this._fmtEmbeddedError(goType);
|
|
1350
|
+
if (emb) {
|
|
1351
|
+
this._emitEmbeddedErrorToExternref(goType, emb, isNested, seen);
|
|
1352
|
+
return;
|
|
1353
|
+
}
|
|
1354
|
+
this._emitToExternrefPlain(goType, isNested, seen, allowNil);
|
|
905
1355
|
return;
|
|
906
1356
|
}
|
|
907
|
-
|
|
908
|
-
|
|
1357
|
+
const valW = toWasmType(goType, this.mod.checker, this.mod);
|
|
1358
|
+
const valTmp = this.acquireTemp(valW);
|
|
1359
|
+
const rawTmp = this.acquireTemp("externref");
|
|
1360
|
+
this.pushInstruction({ op: "local.tee", index: valTmp });
|
|
1361
|
+
this._emitToExternrefPlain(goType, isNested, seen, allowNil);
|
|
1362
|
+
this.pushInstruction({ op: "local.set", index: rawTmp });
|
|
1363
|
+
const nilGuard = m.isPtr && m.valueRecv;
|
|
1364
|
+
if (nilGuard) {
|
|
1365
|
+
// A nil pointer with a value receiver prints <nil>.
|
|
1366
|
+
this.pushInstruction({ op: "local.get", index: valTmp });
|
|
1367
|
+
this.pushInstruction("ref.is_null");
|
|
1368
|
+
this.pushInstruction({ op: "if", blockType: "externref" });
|
|
1369
|
+
this.pushInstruction({ op: "ref.null", heapType: "extern" });
|
|
1370
|
+
this.pushInstruction("else");
|
|
1371
|
+
}
|
|
1372
|
+
this.pushInstruction({ op: "local.get", index: rawTmp });
|
|
1373
|
+
this.pushInstruction({ op: "local.get", index: valTmp });
|
|
1374
|
+
if (nilGuard && !isPointerToStruct(goType, this.mod.checker, this.mod)) {
|
|
1375
|
+
// A pointer to a scalar is a box; the value receiver takes the scalar.
|
|
1376
|
+
const box = this.mod.getBoxType(goType.base ?? goType.elem);
|
|
1377
|
+
this.pushInstruction({
|
|
1378
|
+
op: "ref.cast",
|
|
1379
|
+
typeIndex: box.typeIndex,
|
|
1380
|
+
typeName: box.name,
|
|
1381
|
+
});
|
|
1382
|
+
this.pushInstruction({
|
|
1383
|
+
op: "struct.get",
|
|
1384
|
+
typeIndex: box.typeIndex,
|
|
1385
|
+
typeName: box.name,
|
|
1386
|
+
fieldIndex: 0,
|
|
1387
|
+
});
|
|
1388
|
+
}
|
|
1389
|
+
this.pushInstruction({ op: "call", funcIndex: m.funcIndex });
|
|
1390
|
+
this.pushInstruction({
|
|
1391
|
+
op: "i32.const",
|
|
1392
|
+
value: m.method === "Error" ? 1 : 0,
|
|
1393
|
+
});
|
|
1394
|
+
this.pushInstruction({
|
|
1395
|
+
op: "call",
|
|
1396
|
+
funcIndex: this.mod.getFmtNewStringerIndex(),
|
|
1397
|
+
});
|
|
1398
|
+
if (nilGuard) this.pushInstruction("end");
|
|
1399
|
+
this.releaseTemp(rawTmp, "externref");
|
|
1400
|
+
this.releaseTemp(valTmp, valW);
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
_emitToExternrefPlain(
|
|
1404
|
+
goType,
|
|
1405
|
+
isNested = false,
|
|
1406
|
+
seen = new Set(),
|
|
1407
|
+
allowNil = false,
|
|
1408
|
+
) {
|
|
1409
|
+
const valW = toWasmType(goType, this.mod.checker, this.mod);
|
|
1410
|
+
const valTmp = this.acquireTemp(valW);
|
|
1411
|
+
this.pushInstruction({ op: "local.set", index: valTmp });
|
|
1412
|
+
|
|
1413
|
+
if (isAnyType(goType, this.mod.checker)) {
|
|
1414
|
+
this.pushInstruction({ op: "local.get", index: valTmp });
|
|
1415
|
+
this.releaseTemp(valTmp, valW);
|
|
1416
|
+
this._emitAnyToExternref();
|
|
1417
|
+
return;
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
if (this._emitScalarToExternref(goType, valTmp, valW)) return;
|
|
1421
|
+
|
|
1422
|
+
if (valW === "v128") {
|
|
1423
|
+
this._emitSimdToExternref(goType, valTmp, valW);
|
|
1424
|
+
return;
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
const tKey = goType ? goTypeKey(goType) : null;
|
|
1428
|
+
const inCycle = tKey !== null && seen.has(tKey);
|
|
1429
|
+
const nextSeen = tKey !== null ? new Set(seen).add(tKey) : seen;
|
|
1430
|
+
|
|
1431
|
+
if (isSliceType(goType, this.mod.checker)) {
|
|
1432
|
+
this._emitSliceToExternref(
|
|
1433
|
+
goType,
|
|
1434
|
+
valTmp,
|
|
1435
|
+
valW,
|
|
1436
|
+
inCycle,
|
|
1437
|
+
nextSeen,
|
|
1438
|
+
allowNil,
|
|
1439
|
+
);
|
|
1440
|
+
return;
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
if (isArrayType(goType, this.mod.checker)) {
|
|
1444
|
+
this._emitArrayToExternref(goType, valTmp, valW, inCycle, nextSeen);
|
|
1445
|
+
return;
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
if (isMapType(goType, this.mod.checker)) {
|
|
1449
|
+
this._emitMapToExternref(
|
|
1450
|
+
goType,
|
|
1451
|
+
valTmp,
|
|
1452
|
+
valW,
|
|
1453
|
+
inCycle,
|
|
1454
|
+
nextSeen,
|
|
1455
|
+
allowNil,
|
|
1456
|
+
);
|
|
1457
|
+
return;
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
const isStructPtr = isPointerToStruct(goType, this.mod.checker, this.mod);
|
|
1461
|
+
const isStruct =
|
|
1462
|
+
isStructType(goType, this.mod.checker, this.mod) ||
|
|
1463
|
+
isStructPtr ||
|
|
1464
|
+
isComplex(goType);
|
|
1465
|
+
const isScalarPtr = !isStructPtr && _isPointerKind(goType);
|
|
1466
|
+
|
|
1467
|
+
if (isScalarPtr) {
|
|
1468
|
+
this._emitScalarPtrToExternref(valTmp, valW);
|
|
1469
|
+
return;
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
if (isStruct) {
|
|
909
1473
|
this._emitStructToExternref(
|
|
910
1474
|
goType,
|
|
911
1475
|
valTmp,
|
|
@@ -918,10 +1482,34 @@ export class StdlibCoreEmitter {
|
|
|
918
1482
|
return;
|
|
919
1483
|
}
|
|
920
1484
|
|
|
921
|
-
|
|
1485
|
+
if (valW !== "externref") {
|
|
1486
|
+
this.pushInstruction({ op: "ref.null", heapType: "extern" });
|
|
1487
|
+
} else {
|
|
1488
|
+
this.pushInstruction({ op: "local.get", index: valTmp });
|
|
1489
|
+
}
|
|
922
1490
|
this.releaseTemp(valTmp, valW);
|
|
923
1491
|
}
|
|
924
1492
|
|
|
1493
|
+
// `xs...` (xs []any: the host appends each element) and a bare `nil`
|
|
1494
|
+
// operand; returns the buffer entry, or null for an ordinary operand.
|
|
1495
|
+
_emitSpecialFmtOperand(arg, goType) {
|
|
1496
|
+
const spread = arg._spread && isSliceType(goType, this.mod.checker);
|
|
1497
|
+
const isNil =
|
|
1498
|
+
(arg.kind === "Ident" && arg.name === "nil") ||
|
|
1499
|
+
goType?.name === "nil" ||
|
|
1500
|
+
goType?.kind === "nil";
|
|
1501
|
+
if (!spread && !isNil) return null;
|
|
1502
|
+
const tmp = this.acquireTemp("externref");
|
|
1503
|
+
if (spread) {
|
|
1504
|
+
this.emitExpr(arg, toWasmType(goType, this.mod.checker, this.mod));
|
|
1505
|
+
this.emitToExternref(goType, false);
|
|
1506
|
+
} else {
|
|
1507
|
+
this.pushInstruction({ op: "ref.null", heapType: "extern" });
|
|
1508
|
+
}
|
|
1509
|
+
this.pushInstruction({ op: "local.set", index: tmp });
|
|
1510
|
+
return { tmp, wType: "externref", isBool: false, errTmp: null, spread };
|
|
1511
|
+
}
|
|
1512
|
+
|
|
925
1513
|
// Evaluates every argument into a temp first, then pushes them in order via
|
|
926
1514
|
// the typed `testing_arg_*` imports. Anything that may run user code (the
|
|
927
1515
|
// argument itself, or `Error()` on an `error` operand) happens in the first
|
|
@@ -929,12 +1517,17 @@ export class StdlibCoreEmitter {
|
|
|
929
1517
|
// so nothing may be pushed until every operand is fully evaluated.
|
|
930
1518
|
// Returns the temps so the caller can still read an argument (fmt.Errorf's
|
|
931
1519
|
// %w cause keeps the original error value in `errTmp`).
|
|
932
|
-
|
|
1520
|
+
// `markErrors` (Sprint/Print/Append) pushes error operands through
|
|
1521
|
+
// `testing_arg_err`, so the host sees a non-string like Go does.
|
|
1522
|
+
// `typed` (a Printf-style call whose format needs operand types, see
|
|
1523
|
+
// fmtWantsTypes) tags each operand of a concrete static type with that
|
|
1524
|
+
// type through `fmt_arg_type`, so the host can print `%T` and `%#v`.
|
|
1525
|
+
_emitArgBuffer(args, markErrors = false, typed = false) {
|
|
933
1526
|
const temps = [];
|
|
934
1527
|
for (const arg of args) {
|
|
935
1528
|
const goType = arg._type ?? this._resolveExprGoType(arg);
|
|
936
1529
|
const isBool =
|
|
937
|
-
goType
|
|
1530
|
+
isBoolGoType(goType) ||
|
|
938
1531
|
(arg.kind === "BasicLit" && arg.litKind === "BOOL");
|
|
939
1532
|
const isErr =
|
|
940
1533
|
this.mod.hasErrorType &&
|
|
@@ -945,6 +1538,11 @@ export class StdlibCoreEmitter {
|
|
|
945
1538
|
this.mod.checker?.types?.get("error"),
|
|
946
1539
|
),
|
|
947
1540
|
));
|
|
1541
|
+
const special = this._emitSpecialFmtOperand(arg, goType);
|
|
1542
|
+
if (special) {
|
|
1543
|
+
temps.push(special);
|
|
1544
|
+
continue;
|
|
1545
|
+
}
|
|
948
1546
|
const valW = toWasmType(goType, this.mod.checker, this.mod);
|
|
949
1547
|
if (isErr) {
|
|
950
1548
|
const errTmp = this.acquireTemp("anyref");
|
|
@@ -956,47 +1554,112 @@ export class StdlibCoreEmitter {
|
|
|
956
1554
|
funcIndex: this.mod.resolveFuncIndex("__error_str"),
|
|
957
1555
|
});
|
|
958
1556
|
this.pushInstruction({ op: "local.set", index: tmp });
|
|
959
|
-
temps.push({
|
|
1557
|
+
temps.push({
|
|
1558
|
+
tmp,
|
|
1559
|
+
wType: "externref",
|
|
1560
|
+
isBool: false,
|
|
1561
|
+
errTmp,
|
|
1562
|
+
markErr: markErrors,
|
|
1563
|
+
});
|
|
960
1564
|
continue;
|
|
961
1565
|
}
|
|
962
|
-
if (this.
|
|
1566
|
+
if (this._isFmtExternOperand(goType)) {
|
|
963
1567
|
const tmp = this.acquireTemp("externref");
|
|
964
|
-
this.
|
|
965
|
-
this.emitToExternref(goType, false);
|
|
1568
|
+
this._emitOperandExternref(arg, goType, typed);
|
|
966
1569
|
this.pushInstruction({ op: "local.set", index: tmp });
|
|
967
1570
|
temps.push({ tmp, wType: "externref", isBool: false, errTmp: null });
|
|
968
1571
|
continue;
|
|
969
1572
|
}
|
|
1573
|
+
// An unsigned 32-bit operand widens to i64; an unsigned 64-bit one
|
|
1574
|
+
// stays i64 and is reinterpreted by the host (`testing_arg_u64`), as
|
|
1575
|
+
// f64 cannot hold every uint64 exactly.
|
|
970
1576
|
let wType = valW;
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
wType = "i64";
|
|
974
|
-
} else if (wType === "i64") {
|
|
975
|
-
wType = "f64";
|
|
976
|
-
}
|
|
977
|
-
}
|
|
1577
|
+
const u64 = !isSigned(goType) && valW === "i64";
|
|
1578
|
+
if (!isSigned(goType) && valW === "i32") wType = "i64";
|
|
978
1579
|
const tmp = this.acquireTemp(wType);
|
|
979
1580
|
this.emitExpr(arg, valW);
|
|
980
|
-
if (!isSigned(goType)) {
|
|
981
|
-
|
|
982
|
-
this.pushInstruction("i64.extend_i32_u");
|
|
983
|
-
} else if (valW === "i64") {
|
|
984
|
-
this.pushInstruction("f64.convert_i64_u");
|
|
985
|
-
}
|
|
1581
|
+
if (!isSigned(goType) && valW === "i32") {
|
|
1582
|
+
this.pushInstruction("i64.extend_i32_u");
|
|
986
1583
|
}
|
|
987
1584
|
this.pushInstruction({ op: "local.set", index: tmp });
|
|
988
|
-
temps.push({ tmp, wType, isBool, errTmp: null });
|
|
1585
|
+
temps.push({ tmp, wType, isBool, errTmp: null, u64 });
|
|
989
1586
|
}
|
|
990
|
-
for (const
|
|
1587
|
+
for (const [i, temp] of temps.entries()) {
|
|
1588
|
+
const { tmp, wType, isBool, markErr, spread, u64 } = temp;
|
|
991
1589
|
this.pushInstruction({ op: "local.get", index: tmp });
|
|
992
|
-
this.
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
1590
|
+
let funcIndex = this.mod.getTestingArgImportIndex(wType, isBool);
|
|
1591
|
+
if (u64) funcIndex = this.mod.getTestingArgU64ImportIndex();
|
|
1592
|
+
if (markErr) funcIndex = this.mod.getTestingArgErrImportIndex();
|
|
1593
|
+
if (spread) funcIndex = this.mod.getTestingArgSpreadImportIndex();
|
|
1594
|
+
this.pushInstruction({ op: "call", funcIndex });
|
|
1595
|
+
if (!spread && (typed || this._fmtOperandHasFloat(args[i])))
|
|
1596
|
+
this._emitFmtArgType(args[i]);
|
|
996
1597
|
}
|
|
997
1598
|
return temps;
|
|
998
1599
|
}
|
|
999
1600
|
|
|
1601
|
+
// Operands the per-argument Println imports cannot print: spreads, a bare
|
|
1602
|
+
// nil, floats (static type), and unsigned 32/64-bit integers, whose
|
|
1603
|
+
// signedness only the argument buffer preserves.
|
|
1604
|
+
_fmtOperandNeedsBuffer(arg) {
|
|
1605
|
+
if (arg._spread || this._fmtOperandHasFloat(arg)) return true;
|
|
1606
|
+
const goType = arg._type ?? this._resolveExprGoType(arg);
|
|
1607
|
+
if (
|
|
1608
|
+
(arg.kind === "Ident" && arg.name === "nil") ||
|
|
1609
|
+
goType?.name === "nil" ||
|
|
1610
|
+
goType?.kind === "nil"
|
|
1611
|
+
) {
|
|
1612
|
+
return true;
|
|
1613
|
+
}
|
|
1614
|
+
if (isSigned(goType)) return false;
|
|
1615
|
+
const valW = toWasmType(goType, this.mod.checker, this.mod);
|
|
1616
|
+
return valW === "i64" || (valW === "i32" && _isUint32(goType));
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
// Whether the operand's static type mentions a float: those travel with
|
|
1620
|
+
// their type so the host prints float32 and Go's %v exponent forms.
|
|
1621
|
+
_fmtOperandHasFloat(arg) {
|
|
1622
|
+
const goType = arg._type ?? this._resolveExprGoType(arg);
|
|
1623
|
+
return fmtDescHasFloat(
|
|
1624
|
+
fmtTypeDesc(goType, (t) => this.mod.checker?.resolveType?.(t) ?? t),
|
|
1625
|
+
);
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
// Tags the operand just pushed with its static type (see _emitArgBuffer).
|
|
1629
|
+
// Interface-typed operands carry their dynamic type and stay untagged.
|
|
1630
|
+
_emitFmtArgType(arg) {
|
|
1631
|
+
const goType = arg._type ?? this._resolveExprGoType(arg);
|
|
1632
|
+
const desc = fmtTypeDesc(
|
|
1633
|
+
goType,
|
|
1634
|
+
(t) => this.mod.checker?.resolveType?.(t) ?? t,
|
|
1635
|
+
);
|
|
1636
|
+
if (!desc || desc.k === "i") return;
|
|
1637
|
+
this.pushInstruction({
|
|
1638
|
+
op: "i32.const",
|
|
1639
|
+
value: this.mod.internString(JSON.stringify(desc)),
|
|
1640
|
+
});
|
|
1641
|
+
this.pushInstruction({
|
|
1642
|
+
op: "call",
|
|
1643
|
+
funcIndex: this.mod.getFmtArgTypeImportIndex(),
|
|
1644
|
+
});
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
// Whether a Printf-style call's operands need their static types: the
|
|
1648
|
+
// format must be a literal that uses `%T`, `%#v` or has surplus operands.
|
|
1649
|
+
_fmtNeedsTypes(format, operands) {
|
|
1650
|
+
if (format?.kind !== "BasicLit") return false;
|
|
1651
|
+
const spread = operands.some((a) => a._spread);
|
|
1652
|
+
const seqArgs = operands.some((a) =>
|
|
1653
|
+
fmtDescIsSeq(
|
|
1654
|
+
fmtTypeDesc(
|
|
1655
|
+
a._type ?? this._resolveExprGoType(a),
|
|
1656
|
+
(t) => this.mod.checker?.resolveType?.(t) ?? t,
|
|
1657
|
+
),
|
|
1658
|
+
),
|
|
1659
|
+
);
|
|
1660
|
+
return fmtWantsTypes(format.value, spread ? -1 : operands.length, seqArgs);
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1000
1663
|
_releaseArgBuffer(temps) {
|
|
1001
1664
|
for (let i = temps.length - 1; i >= 0; i--) {
|
|
1002
1665
|
this.releaseTemp(temps[i].tmp, temps[i].wType);
|
|
@@ -1004,23 +1667,443 @@ export class StdlibCoreEmitter {
|
|
|
1004
1667
|
}
|
|
1005
1668
|
}
|
|
1006
1669
|
|
|
1007
|
-
|
|
1670
|
+
_emitBitsArith(name, args) {
|
|
1008
1671
|
switch (name) {
|
|
1672
|
+
case "Add32": {
|
|
1673
|
+
const sumTmp = this.acquireTemp("i64");
|
|
1674
|
+
this.emitExpr(args[0], "i32");
|
|
1675
|
+
this.pushInstruction("i64.extend_i32_u");
|
|
1676
|
+
this.emitExpr(args[1], "i32");
|
|
1677
|
+
this.pushInstruction("i64.extend_i32_u");
|
|
1678
|
+
this.pushInstruction("i64.add");
|
|
1679
|
+
this.emitExpr(args[2], "i32");
|
|
1680
|
+
this.pushInstruction("i64.extend_i32_u");
|
|
1681
|
+
this.pushInstruction("i64.add");
|
|
1682
|
+
this.pushInstruction({ op: "local.set", index: sumTmp });
|
|
1683
|
+
this.pushInstruction({ op: "local.get", index: sumTmp });
|
|
1684
|
+
this.pushInstruction("i32.wrap_i64");
|
|
1685
|
+
this.pushInstruction({ op: "local.get", index: sumTmp });
|
|
1686
|
+
this.pushInstruction({ op: "i64.const", value: 32n });
|
|
1687
|
+
this.pushInstruction("i64.shr_u");
|
|
1688
|
+
this.pushInstruction("i32.wrap_i64");
|
|
1689
|
+
this.releaseTemp(sumTmp, "i64");
|
|
1690
|
+
return true;
|
|
1691
|
+
}
|
|
1692
|
+
case "Add":
|
|
1693
|
+
case "Add64": {
|
|
1694
|
+
const xTmp = this.acquireTemp("i64");
|
|
1695
|
+
const yTmp = this.acquireTemp("i64");
|
|
1696
|
+
const cTmp = this.acquireTemp("i64");
|
|
1697
|
+
const s1Tmp = this.acquireTemp("i64");
|
|
1698
|
+
const sTmp = this.acquireTemp("i64");
|
|
1699
|
+
this.emitExpr(args[0], "i64");
|
|
1700
|
+
this.pushInstruction({ op: "local.set", index: xTmp });
|
|
1701
|
+
this.emitExpr(args[1], "i64");
|
|
1702
|
+
this.pushInstruction({ op: "local.set", index: yTmp });
|
|
1703
|
+
this.emitExpr(args[2], "i64");
|
|
1704
|
+
this.pushInstruction({ op: "local.set", index: cTmp });
|
|
1705
|
+
this.pushInstruction({ op: "local.get", index: xTmp });
|
|
1706
|
+
this.pushInstruction({ op: "local.get", index: yTmp });
|
|
1707
|
+
this.pushInstruction("i64.add");
|
|
1708
|
+
this.pushInstruction({ op: "local.set", index: s1Tmp });
|
|
1709
|
+
this.pushInstruction({ op: "local.get", index: s1Tmp });
|
|
1710
|
+
this.pushInstruction({ op: "local.get", index: cTmp });
|
|
1711
|
+
this.pushInstruction("i64.add");
|
|
1712
|
+
this.pushInstruction({ op: "local.set", index: sTmp });
|
|
1713
|
+
this.pushInstruction({ op: "local.get", index: sTmp });
|
|
1714
|
+
this.pushInstruction({ op: "local.get", index: s1Tmp });
|
|
1715
|
+
this.pushInstruction({ op: "local.get", index: xTmp });
|
|
1716
|
+
this.pushInstruction("i64.lt_u");
|
|
1717
|
+
this.pushInstruction({ op: "local.get", index: sTmp });
|
|
1718
|
+
this.pushInstruction({ op: "local.get", index: s1Tmp });
|
|
1719
|
+
this.pushInstruction("i64.lt_u");
|
|
1720
|
+
this.pushInstruction("i32.or");
|
|
1721
|
+
this.pushInstruction("i64.extend_i32_u");
|
|
1722
|
+
this.releaseTemp(xTmp, "i64");
|
|
1723
|
+
this.releaseTemp(yTmp, "i64");
|
|
1724
|
+
this.releaseTemp(cTmp, "i64");
|
|
1725
|
+
this.releaseTemp(s1Tmp, "i64");
|
|
1726
|
+
this.releaseTemp(sTmp, "i64");
|
|
1727
|
+
return true;
|
|
1728
|
+
}
|
|
1729
|
+
case "Sub32": {
|
|
1730
|
+
const diffTmp = this.acquireTemp("i64");
|
|
1731
|
+
this.emitExpr(args[0], "i32");
|
|
1732
|
+
this.pushInstruction("i64.extend_i32_u");
|
|
1733
|
+
this.emitExpr(args[1], "i32");
|
|
1734
|
+
this.pushInstruction("i64.extend_i32_u");
|
|
1735
|
+
this.pushInstruction("i64.sub");
|
|
1736
|
+
this.emitExpr(args[2], "i32");
|
|
1737
|
+
this.pushInstruction("i64.extend_i32_u");
|
|
1738
|
+
this.pushInstruction("i64.sub");
|
|
1739
|
+
this.pushInstruction({ op: "local.set", index: diffTmp });
|
|
1740
|
+
this.pushInstruction({ op: "local.get", index: diffTmp });
|
|
1741
|
+
this.pushInstruction("i32.wrap_i64");
|
|
1742
|
+
this.pushInstruction({ op: "local.get", index: diffTmp });
|
|
1743
|
+
this.pushInstruction({ op: "i64.const", value: 63n });
|
|
1744
|
+
this.pushInstruction("i64.shr_u");
|
|
1745
|
+
this.pushInstruction("i32.wrap_i64");
|
|
1746
|
+
this.releaseTemp(diffTmp, "i64");
|
|
1747
|
+
return true;
|
|
1748
|
+
}
|
|
1749
|
+
case "Sub":
|
|
1750
|
+
case "Sub64": {
|
|
1751
|
+
const xTmp = this.acquireTemp("i64");
|
|
1752
|
+
const yTmp = this.acquireTemp("i64");
|
|
1753
|
+
const bTmp = this.acquireTemp("i64");
|
|
1754
|
+
const d1Tmp = this.acquireTemp("i64");
|
|
1755
|
+
const diffTmp = this.acquireTemp("i64");
|
|
1756
|
+
this.emitExpr(args[0], "i64");
|
|
1757
|
+
this.pushInstruction({ op: "local.set", index: xTmp });
|
|
1758
|
+
this.emitExpr(args[1], "i64");
|
|
1759
|
+
this.pushInstruction({ op: "local.set", index: yTmp });
|
|
1760
|
+
this.emitExpr(args[2], "i64");
|
|
1761
|
+
this.pushInstruction({ op: "local.set", index: bTmp });
|
|
1762
|
+
this.pushInstruction({ op: "local.get", index: xTmp });
|
|
1763
|
+
this.pushInstruction({ op: "local.get", index: yTmp });
|
|
1764
|
+
this.pushInstruction("i64.sub");
|
|
1765
|
+
this.pushInstruction({ op: "local.set", index: d1Tmp });
|
|
1766
|
+
this.pushInstruction({ op: "local.get", index: d1Tmp });
|
|
1767
|
+
this.pushInstruction({ op: "local.get", index: bTmp });
|
|
1768
|
+
this.pushInstruction("i64.sub");
|
|
1769
|
+
this.pushInstruction({ op: "local.set", index: diffTmp });
|
|
1770
|
+
this.pushInstruction({ op: "local.get", index: diffTmp });
|
|
1771
|
+
this.pushInstruction({ op: "local.get", index: xTmp });
|
|
1772
|
+
this.pushInstruction({ op: "local.get", index: yTmp });
|
|
1773
|
+
this.pushInstruction("i64.lt_u");
|
|
1774
|
+
this.pushInstruction({ op: "local.get", index: d1Tmp });
|
|
1775
|
+
this.pushInstruction({ op: "local.get", index: bTmp });
|
|
1776
|
+
this.pushInstruction("i64.lt_u");
|
|
1777
|
+
this.pushInstruction("i32.or");
|
|
1778
|
+
this.pushInstruction("i64.extend_i32_u");
|
|
1779
|
+
this.releaseTemp(xTmp, "i64");
|
|
1780
|
+
this.releaseTemp(yTmp, "i64");
|
|
1781
|
+
this.releaseTemp(bTmp, "i64");
|
|
1782
|
+
this.releaseTemp(d1Tmp, "i64");
|
|
1783
|
+
this.releaseTemp(diffTmp, "i64");
|
|
1784
|
+
return true;
|
|
1785
|
+
}
|
|
1786
|
+
case "Mul32": {
|
|
1787
|
+
const pTmp = this.acquireTemp("i64");
|
|
1788
|
+
this.emitExpr(args[0], "i32");
|
|
1789
|
+
this.pushInstruction("i64.extend_i32_u");
|
|
1790
|
+
this.emitExpr(args[1], "i32");
|
|
1791
|
+
this.pushInstruction("i64.extend_i32_u");
|
|
1792
|
+
this.pushInstruction("i64.mul");
|
|
1793
|
+
this.pushInstruction({ op: "local.set", index: pTmp });
|
|
1794
|
+
this.pushInstruction({ op: "local.get", index: pTmp });
|
|
1795
|
+
this.pushInstruction({ op: "i64.const", value: 32n });
|
|
1796
|
+
this.pushInstruction("i64.shr_u");
|
|
1797
|
+
this.pushInstruction("i32.wrap_i64");
|
|
1798
|
+
this.pushInstruction({ op: "local.get", index: pTmp });
|
|
1799
|
+
this.pushInstruction("i32.wrap_i64");
|
|
1800
|
+
this.releaseTemp(pTmp, "i64");
|
|
1801
|
+
return true;
|
|
1802
|
+
}
|
|
1803
|
+
case "Mul":
|
|
1804
|
+
case "Mul64": {
|
|
1805
|
+
const xTmp = this.acquireTemp("i64");
|
|
1806
|
+
const yTmp = this.acquireTemp("i64");
|
|
1807
|
+
const x0Tmp = this.acquireTemp("i64");
|
|
1808
|
+
const x1Tmp = this.acquireTemp("i64");
|
|
1809
|
+
const y0Tmp = this.acquireTemp("i64");
|
|
1810
|
+
const y1Tmp = this.acquireTemp("i64");
|
|
1811
|
+
const w0Tmp = this.acquireTemp("i64");
|
|
1812
|
+
const tTmp = this.acquireTemp("i64");
|
|
1813
|
+
const w1Tmp = this.acquireTemp("i64");
|
|
1814
|
+
this.emitExpr(args[0], "i64");
|
|
1815
|
+
this.pushInstruction({ op: "local.set", index: xTmp });
|
|
1816
|
+
this.emitExpr(args[1], "i64");
|
|
1817
|
+
this.pushInstruction({ op: "local.set", index: yTmp });
|
|
1818
|
+
this.pushInstruction({ op: "local.get", index: xTmp });
|
|
1819
|
+
this.pushInstruction({ op: "i64.const", value: 0xffffffffn });
|
|
1820
|
+
this.pushInstruction("i64.and");
|
|
1821
|
+
this.pushInstruction({ op: "local.set", index: x0Tmp });
|
|
1822
|
+
this.pushInstruction({ op: "local.get", index: xTmp });
|
|
1823
|
+
this.pushInstruction({ op: "i64.const", value: 32n });
|
|
1824
|
+
this.pushInstruction("i64.shr_u");
|
|
1825
|
+
this.pushInstruction({ op: "local.set", index: x1Tmp });
|
|
1826
|
+
this.pushInstruction({ op: "local.get", index: yTmp });
|
|
1827
|
+
this.pushInstruction({ op: "i64.const", value: 0xffffffffn });
|
|
1828
|
+
this.pushInstruction("i64.and");
|
|
1829
|
+
this.pushInstruction({ op: "local.set", index: y0Tmp });
|
|
1830
|
+
this.pushInstruction({ op: "local.get", index: yTmp });
|
|
1831
|
+
this.pushInstruction({ op: "i64.const", value: 32n });
|
|
1832
|
+
this.pushInstruction("i64.shr_u");
|
|
1833
|
+
this.pushInstruction({ op: "local.set", index: y1Tmp });
|
|
1834
|
+
this.pushInstruction({ op: "local.get", index: x0Tmp });
|
|
1835
|
+
this.pushInstruction({ op: "local.get", index: y0Tmp });
|
|
1836
|
+
this.pushInstruction("i64.mul");
|
|
1837
|
+
this.pushInstruction({ op: "local.set", index: w0Tmp });
|
|
1838
|
+
this.pushInstruction({ op: "local.get", index: x1Tmp });
|
|
1839
|
+
this.pushInstruction({ op: "local.get", index: y0Tmp });
|
|
1840
|
+
this.pushInstruction("i64.mul");
|
|
1841
|
+
this.pushInstruction({ op: "local.get", index: w0Tmp });
|
|
1842
|
+
this.pushInstruction({ op: "i64.const", value: 32n });
|
|
1843
|
+
this.pushInstruction("i64.shr_u");
|
|
1844
|
+
this.pushInstruction("i64.add");
|
|
1845
|
+
this.pushInstruction({ op: "local.set", index: tTmp });
|
|
1846
|
+
this.pushInstruction({ op: "local.get", index: tTmp });
|
|
1847
|
+
this.pushInstruction({ op: "i64.const", value: 0xffffffffn });
|
|
1848
|
+
this.pushInstruction("i64.and");
|
|
1849
|
+
this.pushInstruction({ op: "local.get", index: x0Tmp });
|
|
1850
|
+
this.pushInstruction({ op: "local.get", index: y1Tmp });
|
|
1851
|
+
this.pushInstruction("i64.mul");
|
|
1852
|
+
this.pushInstruction("i64.add");
|
|
1853
|
+
this.pushInstruction({ op: "local.set", index: w1Tmp });
|
|
1854
|
+
this.pushInstruction({ op: "local.get", index: x1Tmp });
|
|
1855
|
+
this.pushInstruction({ op: "local.get", index: y1Tmp });
|
|
1856
|
+
this.pushInstruction("i64.mul");
|
|
1857
|
+
this.pushInstruction({ op: "local.get", index: tTmp });
|
|
1858
|
+
this.pushInstruction({ op: "i64.const", value: 32n });
|
|
1859
|
+
this.pushInstruction("i64.shr_u");
|
|
1860
|
+
this.pushInstruction("i64.add");
|
|
1861
|
+
this.pushInstruction({ op: "local.get", index: w1Tmp });
|
|
1862
|
+
this.pushInstruction({ op: "i64.const", value: 32n });
|
|
1863
|
+
this.pushInstruction("i64.shr_u");
|
|
1864
|
+
this.pushInstruction("i64.add");
|
|
1865
|
+
this.pushInstruction({ op: "local.get", index: xTmp });
|
|
1866
|
+
this.pushInstruction({ op: "local.get", index: yTmp });
|
|
1867
|
+
this.pushInstruction("i64.mul");
|
|
1868
|
+
this.releaseTemp(xTmp, "i64");
|
|
1869
|
+
this.releaseTemp(yTmp, "i64");
|
|
1870
|
+
this.releaseTemp(x0Tmp, "i64");
|
|
1871
|
+
this.releaseTemp(x1Tmp, "i64");
|
|
1872
|
+
this.releaseTemp(y0Tmp, "i64");
|
|
1873
|
+
this.releaseTemp(y1Tmp, "i64");
|
|
1874
|
+
this.releaseTemp(w0Tmp, "i64");
|
|
1875
|
+
this.releaseTemp(tTmp, "i64");
|
|
1876
|
+
this.releaseTemp(w1Tmp, "i64");
|
|
1877
|
+
return true;
|
|
1878
|
+
}
|
|
1879
|
+
case "Div32":
|
|
1880
|
+
this.emitExpr(args[0], "i32");
|
|
1881
|
+
this.emitExpr(args[1], "i32");
|
|
1882
|
+
this.emitExpr(args[2], "i32");
|
|
1883
|
+
this.pushInstruction({
|
|
1884
|
+
op: "call",
|
|
1885
|
+
funcIndex: this.mod.getStdlibImportIndex("bits_div32"),
|
|
1886
|
+
});
|
|
1887
|
+
this.pushInstruction({
|
|
1888
|
+
op: "call",
|
|
1889
|
+
funcIndex: this.mod.getStdlibImportIndex("bits_div32_rem"),
|
|
1890
|
+
});
|
|
1891
|
+
return true;
|
|
1892
|
+
case "Rem32":
|
|
1893
|
+
this.emitExpr(args[0], "i32");
|
|
1894
|
+
this.emitExpr(args[1], "i32");
|
|
1895
|
+
this.emitExpr(args[2], "i32");
|
|
1896
|
+
this.pushInstruction({
|
|
1897
|
+
op: "call",
|
|
1898
|
+
funcIndex: this.mod.getStdlibImportIndex("bits_rem32"),
|
|
1899
|
+
});
|
|
1900
|
+
return true;
|
|
1901
|
+
case "Div":
|
|
1902
|
+
case "Div64":
|
|
1903
|
+
this.emitExpr(args[0], "i64");
|
|
1904
|
+
this.emitExpr(args[1], "i64");
|
|
1905
|
+
this.emitExpr(args[2], "i64");
|
|
1906
|
+
this.pushInstruction({
|
|
1907
|
+
op: "call",
|
|
1908
|
+
funcIndex: this.mod.getStdlibImportIndex("bits_div64"),
|
|
1909
|
+
});
|
|
1910
|
+
this.pushInstruction({
|
|
1911
|
+
op: "call",
|
|
1912
|
+
funcIndex: this.mod.getStdlibImportIndex("bits_div64_rem"),
|
|
1913
|
+
});
|
|
1914
|
+
return true;
|
|
1915
|
+
case "Rem":
|
|
1916
|
+
case "Rem64":
|
|
1917
|
+
this.emitExpr(args[0], "i64");
|
|
1918
|
+
this.emitExpr(args[1], "i64");
|
|
1919
|
+
this.emitExpr(args[2], "i64");
|
|
1920
|
+
this.pushInstruction({
|
|
1921
|
+
op: "call",
|
|
1922
|
+
funcIndex: this.mod.getStdlibImportIndex("bits_rem64"),
|
|
1923
|
+
});
|
|
1924
|
+
return true;
|
|
1925
|
+
default:
|
|
1926
|
+
return false;
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1930
|
+
_emitBitsCount(name, args) {
|
|
1931
|
+
switch (name) {
|
|
1932
|
+
case "LeadingZeros8":
|
|
1933
|
+
this.emitExpr(args[0], "i32");
|
|
1934
|
+
this.pushInstruction({ op: "i32.const", value: 0xff });
|
|
1935
|
+
this.pushInstruction("i32.and");
|
|
1936
|
+
this.pushInstruction("i32.clz");
|
|
1937
|
+
this.pushInstruction({ op: "i32.const", value: 24 });
|
|
1938
|
+
this.pushInstruction("i32.sub");
|
|
1939
|
+
this.pushInstruction("i64.extend_i32_u");
|
|
1940
|
+
return true;
|
|
1941
|
+
case "LeadingZeros16":
|
|
1942
|
+
this.emitExpr(args[0], "i32");
|
|
1943
|
+
this.pushInstruction({ op: "i32.const", value: 0xffff });
|
|
1944
|
+
this.pushInstruction("i32.and");
|
|
1945
|
+
this.pushInstruction("i32.clz");
|
|
1946
|
+
this.pushInstruction({ op: "i32.const", value: 16 });
|
|
1947
|
+
this.pushInstruction("i32.sub");
|
|
1948
|
+
this.pushInstruction("i64.extend_i32_u");
|
|
1949
|
+
return true;
|
|
1009
1950
|
case "LeadingZeros32":
|
|
1010
1951
|
this.emitExpr(args[0], "i32");
|
|
1011
1952
|
this.pushInstruction("i32.clz");
|
|
1012
1953
|
this.pushInstruction("i64.extend_i32_u");
|
|
1013
|
-
return;
|
|
1954
|
+
return true;
|
|
1955
|
+
case "LeadingZeros":
|
|
1956
|
+
case "LeadingZeros64":
|
|
1957
|
+
this.emitExpr(args[0], "i64");
|
|
1958
|
+
this.pushInstruction("i64.clz");
|
|
1959
|
+
return true;
|
|
1960
|
+
case "TrailingZeros8":
|
|
1961
|
+
this.emitExpr(args[0], "i32");
|
|
1962
|
+
this.pushInstruction({ op: "i32.const", value: 0xff });
|
|
1963
|
+
this.pushInstruction("i32.and");
|
|
1964
|
+
this.pushInstruction({ op: "i32.const", value: 0x100 });
|
|
1965
|
+
this.pushInstruction("i32.or");
|
|
1966
|
+
this.pushInstruction("i32.ctz");
|
|
1967
|
+
this.pushInstruction("i64.extend_i32_u");
|
|
1968
|
+
return true;
|
|
1969
|
+
case "TrailingZeros16":
|
|
1970
|
+
this.emitExpr(args[0], "i32");
|
|
1971
|
+
this.pushInstruction({ op: "i32.const", value: 0xffff });
|
|
1972
|
+
this.pushInstruction("i32.and");
|
|
1973
|
+
this.pushInstruction({ op: "i32.const", value: 0x10000 });
|
|
1974
|
+
this.pushInstruction("i32.or");
|
|
1975
|
+
this.pushInstruction("i32.ctz");
|
|
1976
|
+
this.pushInstruction("i64.extend_i32_u");
|
|
1977
|
+
return true;
|
|
1014
1978
|
case "TrailingZeros32":
|
|
1015
1979
|
this.emitExpr(args[0], "i32");
|
|
1016
1980
|
this.pushInstruction("i32.ctz");
|
|
1017
1981
|
this.pushInstruction("i64.extend_i32_u");
|
|
1018
|
-
return;
|
|
1982
|
+
return true;
|
|
1983
|
+
case "TrailingZeros":
|
|
1984
|
+
case "TrailingZeros64":
|
|
1985
|
+
this.emitExpr(args[0], "i64");
|
|
1986
|
+
this.pushInstruction("i64.ctz");
|
|
1987
|
+
return true;
|
|
1988
|
+
case "OnesCount8":
|
|
1989
|
+
this.emitExpr(args[0], "i32");
|
|
1990
|
+
this.pushInstruction({ op: "i32.const", value: 0xff });
|
|
1991
|
+
this.pushInstruction("i32.and");
|
|
1992
|
+
this.pushInstruction("i32.popcnt");
|
|
1993
|
+
this.pushInstruction("i64.extend_i32_u");
|
|
1994
|
+
return true;
|
|
1995
|
+
case "OnesCount16":
|
|
1996
|
+
this.emitExpr(args[0], "i32");
|
|
1997
|
+
this.pushInstruction({ op: "i32.const", value: 0xffff });
|
|
1998
|
+
this.pushInstruction("i32.and");
|
|
1999
|
+
this.pushInstruction("i32.popcnt");
|
|
2000
|
+
this.pushInstruction("i64.extend_i32_u");
|
|
2001
|
+
return true;
|
|
1019
2002
|
case "OnesCount32":
|
|
1020
2003
|
this.emitExpr(args[0], "i32");
|
|
1021
2004
|
this.pushInstruction("i32.popcnt");
|
|
1022
2005
|
this.pushInstruction("i64.extend_i32_u");
|
|
1023
|
-
return;
|
|
2006
|
+
return true;
|
|
2007
|
+
case "OnesCount":
|
|
2008
|
+
case "OnesCount64":
|
|
2009
|
+
this.emitExpr(args[0], "i64");
|
|
2010
|
+
this.pushInstruction("i64.popcnt");
|
|
2011
|
+
return true;
|
|
2012
|
+
case "Len8":
|
|
2013
|
+
this.pushInstruction({ op: "i32.const", value: 32 });
|
|
2014
|
+
this.emitExpr(args[0], "i32");
|
|
2015
|
+
this.pushInstruction({ op: "i32.const", value: 0xff });
|
|
2016
|
+
this.pushInstruction("i32.and");
|
|
2017
|
+
this.pushInstruction("i32.clz");
|
|
2018
|
+
this.pushInstruction("i32.sub");
|
|
2019
|
+
this.pushInstruction("i64.extend_i32_u");
|
|
2020
|
+
return true;
|
|
2021
|
+
case "Len16":
|
|
2022
|
+
this.pushInstruction({ op: "i32.const", value: 32 });
|
|
2023
|
+
this.emitExpr(args[0], "i32");
|
|
2024
|
+
this.pushInstruction({ op: "i32.const", value: 0xffff });
|
|
2025
|
+
this.pushInstruction("i32.and");
|
|
2026
|
+
this.pushInstruction("i32.clz");
|
|
2027
|
+
this.pushInstruction("i32.sub");
|
|
2028
|
+
this.pushInstruction("i64.extend_i32_u");
|
|
2029
|
+
return true;
|
|
2030
|
+
case "Len32":
|
|
2031
|
+
this.pushInstruction({ op: "i32.const", value: 32 });
|
|
2032
|
+
this.emitExpr(args[0], "i32");
|
|
2033
|
+
this.pushInstruction("i32.clz");
|
|
2034
|
+
this.pushInstruction("i32.sub");
|
|
2035
|
+
this.pushInstruction("i64.extend_i32_u");
|
|
2036
|
+
return true;
|
|
2037
|
+
case "Len":
|
|
2038
|
+
case "Len64":
|
|
2039
|
+
this.pushInstruction({ op: "i64.const", value: 64n });
|
|
2040
|
+
this.emitExpr(args[0], "i64");
|
|
2041
|
+
this.pushInstruction("i64.clz");
|
|
2042
|
+
this.pushInstruction("i64.sub");
|
|
2043
|
+
return true;
|
|
2044
|
+
default:
|
|
2045
|
+
return false;
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2049
|
+
_emitBitsRotate(name, args) {
|
|
2050
|
+
switch (name) {
|
|
2051
|
+
case "RotateLeft8": {
|
|
2052
|
+
const xTmp = this.acquireTemp("i32");
|
|
2053
|
+
const sTmp = this.acquireTemp("i32");
|
|
2054
|
+
this.emitExpr(args[0], "i32");
|
|
2055
|
+
this.pushInstruction({ op: "i32.const", value: 0xff });
|
|
2056
|
+
this.pushInstruction("i32.and");
|
|
2057
|
+
this.pushInstruction({ op: "local.set", index: xTmp });
|
|
2058
|
+
this.emitExpr(args[1], "i32");
|
|
2059
|
+
this.pushInstruction({ op: "i32.const", value: 7 });
|
|
2060
|
+
this.pushInstruction("i32.and");
|
|
2061
|
+
this.pushInstruction({ op: "local.set", index: sTmp });
|
|
2062
|
+
this.pushInstruction({ op: "local.get", index: xTmp });
|
|
2063
|
+
this.pushInstruction({ op: "local.get", index: sTmp });
|
|
2064
|
+
this.pushInstruction("i32.shl");
|
|
2065
|
+
this.pushInstruction({ op: "local.get", index: xTmp });
|
|
2066
|
+
this.pushInstruction({ op: "i32.const", value: 8 });
|
|
2067
|
+
this.pushInstruction({ op: "local.get", index: sTmp });
|
|
2068
|
+
this.pushInstruction("i32.sub");
|
|
2069
|
+
this.pushInstruction({ op: "i32.const", value: 7 });
|
|
2070
|
+
this.pushInstruction("i32.and");
|
|
2071
|
+
this.pushInstruction("i32.shr_u");
|
|
2072
|
+
this.pushInstruction("i32.or");
|
|
2073
|
+
this.pushInstruction({ op: "i32.const", value: 0xff });
|
|
2074
|
+
this.pushInstruction("i32.and");
|
|
2075
|
+
this.releaseTemp(sTmp, "i32");
|
|
2076
|
+
this.releaseTemp(xTmp, "i32");
|
|
2077
|
+
return true;
|
|
2078
|
+
}
|
|
2079
|
+
case "RotateLeft16": {
|
|
2080
|
+
const xTmp = this.acquireTemp("i32");
|
|
2081
|
+
const sTmp = this.acquireTemp("i32");
|
|
2082
|
+
this.emitExpr(args[0], "i32");
|
|
2083
|
+
this.pushInstruction({ op: "i32.const", value: 0xffff });
|
|
2084
|
+
this.pushInstruction("i32.and");
|
|
2085
|
+
this.pushInstruction({ op: "local.set", index: xTmp });
|
|
2086
|
+
this.emitExpr(args[1], "i32");
|
|
2087
|
+
this.pushInstruction({ op: "i32.const", value: 15 });
|
|
2088
|
+
this.pushInstruction("i32.and");
|
|
2089
|
+
this.pushInstruction({ op: "local.set", index: sTmp });
|
|
2090
|
+
this.pushInstruction({ op: "local.get", index: xTmp });
|
|
2091
|
+
this.pushInstruction({ op: "local.get", index: sTmp });
|
|
2092
|
+
this.pushInstruction("i32.shl");
|
|
2093
|
+
this.pushInstruction({ op: "local.get", index: xTmp });
|
|
2094
|
+
this.pushInstruction({ op: "i32.const", value: 16 });
|
|
2095
|
+
this.pushInstruction({ op: "local.get", index: sTmp });
|
|
2096
|
+
this.pushInstruction("i32.sub");
|
|
2097
|
+
this.pushInstruction({ op: "i32.const", value: 15 });
|
|
2098
|
+
this.pushInstruction("i32.and");
|
|
2099
|
+
this.pushInstruction("i32.shr_u");
|
|
2100
|
+
this.pushInstruction("i32.or");
|
|
2101
|
+
this.pushInstruction({ op: "i32.const", value: 0xffff });
|
|
2102
|
+
this.pushInstruction("i32.and");
|
|
2103
|
+
this.releaseTemp(sTmp, "i32");
|
|
2104
|
+
this.releaseTemp(xTmp, "i32");
|
|
2105
|
+
return true;
|
|
2106
|
+
}
|
|
1024
2107
|
case "RotateLeft32": {
|
|
1025
2108
|
this.emitExpr(args[0], "i32");
|
|
1026
2109
|
const arg1WType = toWasmType(args[1]?._type, this.mod.checker);
|
|
@@ -1030,22 +2113,10 @@ export class StdlibCoreEmitter {
|
|
|
1030
2113
|
} else {
|
|
1031
2114
|
this.emitExpr(args[1], "i32");
|
|
1032
2115
|
}
|
|
1033
|
-
this.pushInstruction("i32.rotl");
|
|
1034
|
-
return;
|
|
1035
|
-
}
|
|
1036
|
-
|
|
1037
|
-
case "LeadingZeros64":
|
|
1038
|
-
this.emitExpr(args[0], "i64");
|
|
1039
|
-
this.pushInstruction("i64.clz");
|
|
1040
|
-
return;
|
|
1041
|
-
case "TrailingZeros64":
|
|
1042
|
-
this.emitExpr(args[0], "i64");
|
|
1043
|
-
this.pushInstruction("i64.ctz");
|
|
1044
|
-
return;
|
|
1045
|
-
case "OnesCount64":
|
|
1046
|
-
this.emitExpr(args[0], "i64");
|
|
1047
|
-
this.pushInstruction("i64.popcnt");
|
|
1048
|
-
return;
|
|
2116
|
+
this.pushInstruction("i32.rotl");
|
|
2117
|
+
return true;
|
|
2118
|
+
}
|
|
2119
|
+
case "RotateLeft":
|
|
1049
2120
|
case "RotateLeft64": {
|
|
1050
2121
|
this.emitExpr(args[0], "i64");
|
|
1051
2122
|
const arg1WType = toWasmType(args[1]?._type, this.mod.checker);
|
|
@@ -1056,111 +2127,103 @@ export class StdlibCoreEmitter {
|
|
|
1056
2127
|
this.emitExpr(args[1], "i64");
|
|
1057
2128
|
}
|
|
1058
2129
|
this.pushInstruction("i64.rotl");
|
|
1059
|
-
return;
|
|
2130
|
+
return true;
|
|
1060
2131
|
}
|
|
1061
|
-
|
|
1062
|
-
case "Len32":
|
|
1063
|
-
this.pushInstruction({ op: "i32.const", value: 32 });
|
|
1064
|
-
this.emitExpr(args[0], "i32");
|
|
1065
|
-
this.pushInstruction("i32.clz");
|
|
1066
|
-
this.pushInstruction("i32.sub");
|
|
1067
|
-
this.pushInstruction("i64.extend_i32_u");
|
|
1068
|
-
return;
|
|
1069
|
-
|
|
1070
|
-
case "Len64":
|
|
1071
|
-
this.pushInstruction({ op: "i64.const", value: 64n });
|
|
1072
|
-
this.emitExpr(args[0], "i64");
|
|
1073
|
-
this.pushInstruction("i64.clz");
|
|
1074
|
-
this.pushInstruction("i64.sub");
|
|
1075
|
-
return;
|
|
1076
|
-
|
|
1077
|
-
case "ReverseBytes16": {
|
|
1078
|
-
const xTmp = this.acquireTemp("i32");
|
|
2132
|
+
case "Reverse8":
|
|
1079
2133
|
this.emitExpr(args[0], "i32");
|
|
1080
|
-
this.pushInstruction({ op: "local.tee", index: xTmp });
|
|
1081
|
-
this.pushInstruction({ op: "i32.const", value: 8 });
|
|
1082
|
-
this.pushInstruction("i32.shr_u");
|
|
1083
2134
|
this.pushInstruction({ op: "i32.const", value: 0xff });
|
|
1084
2135
|
this.pushInstruction("i32.and");
|
|
1085
|
-
this.
|
|
1086
|
-
this.pushInstruction({ op: "i32.const", value:
|
|
2136
|
+
this._emitReverse32Sequence();
|
|
2137
|
+
this.pushInstruction({ op: "i32.const", value: 24 });
|
|
2138
|
+
this.pushInstruction("i32.shr_u");
|
|
2139
|
+
return true;
|
|
2140
|
+
case "Reverse16":
|
|
2141
|
+
this.emitExpr(args[0], "i32");
|
|
2142
|
+
this.pushInstruction({ op: "i32.const", value: 0xffff });
|
|
1087
2143
|
this.pushInstruction("i32.and");
|
|
1088
|
-
this.
|
|
1089
|
-
this.pushInstruction("i32.
|
|
1090
|
-
this.pushInstruction("i32.
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
case "ReverseBytes32": {
|
|
2144
|
+
this._emitReverse32Sequence();
|
|
2145
|
+
this.pushInstruction({ op: "i32.const", value: 16 });
|
|
2146
|
+
this.pushInstruction("i32.shr_u");
|
|
2147
|
+
return true;
|
|
2148
|
+
case "Reverse32":
|
|
1096
2149
|
this.emitExpr(args[0], "i32");
|
|
1097
|
-
this.
|
|
1098
|
-
return;
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
case "ReverseBytes64": {
|
|
2150
|
+
this._emitReverse32Sequence();
|
|
2151
|
+
return true;
|
|
2152
|
+
case "Reverse":
|
|
2153
|
+
case "Reverse64": {
|
|
1102
2154
|
const xTmp = this.acquireTemp("i64");
|
|
1103
2155
|
this.emitExpr(args[0], "i64");
|
|
1104
2156
|
this.pushInstruction({ op: "local.set", index: xTmp });
|
|
1105
|
-
|
|
1106
|
-
// lo 32 bits -> ReverseBytes32 -> extend to i64 -> shl 32
|
|
1107
2157
|
this.pushInstruction({ op: "local.get", index: xTmp });
|
|
1108
2158
|
this.pushInstruction("i32.wrap_i64");
|
|
1109
|
-
this.
|
|
2159
|
+
this._emitReverse32Sequence();
|
|
1110
2160
|
this.pushInstruction("i64.extend_i32_u");
|
|
1111
2161
|
this.pushInstruction({ op: "i64.const", value: 32n });
|
|
1112
2162
|
this.pushInstruction("i64.shl");
|
|
1113
|
-
|
|
1114
|
-
// hi 32 bits -> ReverseBytes32 -> extend to i64
|
|
1115
2163
|
this.pushInstruction({ op: "local.get", index: xTmp });
|
|
1116
2164
|
this.pushInstruction({ op: "i64.const", value: 32n });
|
|
1117
2165
|
this.pushInstruction("i64.shr_u");
|
|
1118
2166
|
this.pushInstruction("i32.wrap_i64");
|
|
1119
|
-
this.
|
|
2167
|
+
this._emitReverse32Sequence();
|
|
1120
2168
|
this.pushInstruction("i64.extend_i32_u");
|
|
1121
|
-
|
|
1122
2169
|
this.pushInstruction("i64.or");
|
|
1123
2170
|
this.releaseTemp(xTmp, "i64");
|
|
1124
|
-
return;
|
|
2171
|
+
return true;
|
|
1125
2172
|
}
|
|
1126
|
-
|
|
1127
|
-
|
|
2173
|
+
case "ReverseBytes16": {
|
|
2174
|
+
const xTmp = this.acquireTemp("i32");
|
|
1128
2175
|
this.emitExpr(args[0], "i32");
|
|
1129
|
-
this.
|
|
1130
|
-
|
|
2176
|
+
this.pushInstruction({ op: "local.tee", index: xTmp });
|
|
2177
|
+
this.pushInstruction({ op: "i32.const", value: 8 });
|
|
2178
|
+
this.pushInstruction("i32.shr_u");
|
|
2179
|
+
this.pushInstruction({ op: "i32.const", value: 0xff });
|
|
2180
|
+
this.pushInstruction("i32.and");
|
|
2181
|
+
this.pushInstruction({ op: "local.get", index: xTmp });
|
|
2182
|
+
this.pushInstruction({ op: "i32.const", value: 0xff });
|
|
2183
|
+
this.pushInstruction("i32.and");
|
|
2184
|
+
this.pushInstruction({ op: "i32.const", value: 8 });
|
|
2185
|
+
this.pushInstruction("i32.shl");
|
|
2186
|
+
this.pushInstruction("i32.or");
|
|
2187
|
+
this.releaseTemp(xTmp, "i32");
|
|
2188
|
+
return true;
|
|
1131
2189
|
}
|
|
1132
|
-
|
|
1133
|
-
|
|
2190
|
+
case "ReverseBytes32":
|
|
2191
|
+
this.emitExpr(args[0], "i32");
|
|
2192
|
+
this._emitReverseBytes32Sequence();
|
|
2193
|
+
return true;
|
|
2194
|
+
case "ReverseBytes":
|
|
2195
|
+
case "ReverseBytes64": {
|
|
1134
2196
|
const xTmp = this.acquireTemp("i64");
|
|
1135
2197
|
this.emitExpr(args[0], "i64");
|
|
1136
2198
|
this.pushInstruction({ op: "local.set", index: xTmp });
|
|
1137
|
-
|
|
1138
|
-
// lo 32 bits -> Reverse32 -> extend to i64 -> shl 32
|
|
1139
2199
|
this.pushInstruction({ op: "local.get", index: xTmp });
|
|
1140
2200
|
this.pushInstruction("i32.wrap_i64");
|
|
1141
|
-
this.
|
|
2201
|
+
this._emitReverseBytes32Sequence();
|
|
1142
2202
|
this.pushInstruction("i64.extend_i32_u");
|
|
1143
2203
|
this.pushInstruction({ op: "i64.const", value: 32n });
|
|
1144
2204
|
this.pushInstruction("i64.shl");
|
|
1145
|
-
|
|
1146
|
-
// hi 32 bits -> Reverse32 -> extend to i64
|
|
1147
2205
|
this.pushInstruction({ op: "local.get", index: xTmp });
|
|
1148
2206
|
this.pushInstruction({ op: "i64.const", value: 32n });
|
|
1149
2207
|
this.pushInstruction("i64.shr_u");
|
|
1150
2208
|
this.pushInstruction("i32.wrap_i64");
|
|
1151
|
-
this.
|
|
2209
|
+
this._emitReverseBytes32Sequence();
|
|
1152
2210
|
this.pushInstruction("i64.extend_i32_u");
|
|
1153
|
-
|
|
1154
2211
|
this.pushInstruction("i64.or");
|
|
1155
2212
|
this.releaseTemp(xTmp, "i64");
|
|
1156
|
-
return;
|
|
2213
|
+
return true;
|
|
1157
2214
|
}
|
|
1158
|
-
|
|
1159
2215
|
default:
|
|
1160
|
-
|
|
2216
|
+
return false;
|
|
1161
2217
|
}
|
|
1162
2218
|
}
|
|
1163
2219
|
|
|
2220
|
+
emitBitsCall(name, args) {
|
|
2221
|
+
if (this._emitBitsArith(name, args)) return;
|
|
2222
|
+
if (this._emitBitsCount(name, args)) return;
|
|
2223
|
+
if (this._emitBitsRotate(name, args)) return;
|
|
2224
|
+
throw new Error(`Unsupported bits function: bits.${name}`);
|
|
2225
|
+
}
|
|
2226
|
+
|
|
1164
2227
|
_emitReverseBytes32Sequence() {
|
|
1165
2228
|
const v = this.acquireTemp("i32");
|
|
1166
2229
|
this.pushInstruction({ op: "local.set", index: v });
|
|
@@ -1333,30 +2396,6 @@ export class StdlibCoreEmitter {
|
|
|
1333
2396
|
|
|
1334
2397
|
emitMapsCall(name, args, _targetWasmType) {
|
|
1335
2398
|
switch (name) {
|
|
1336
|
-
case "Keys": {
|
|
1337
|
-
const mArg = args[0];
|
|
1338
|
-
const mType = mArg._type ?? this._resolveExprGoType(mArg);
|
|
1339
|
-
const { keyType, valType } = getMapKeyValTypes(mType, this.mod.checker);
|
|
1340
|
-
const mapInfo = this.mod.getMapType(keyType, valType);
|
|
1341
|
-
this.emitExpr(mArg, mapInfo.wType);
|
|
1342
|
-
this.pushInstruction({
|
|
1343
|
-
op: "call",
|
|
1344
|
-
funcIndex: this.mod.resolveFuncIndex(mapInfo.keysFuncName),
|
|
1345
|
-
});
|
|
1346
|
-
return;
|
|
1347
|
-
}
|
|
1348
|
-
case "Values": {
|
|
1349
|
-
const mArg = args[0];
|
|
1350
|
-
const mType = mArg._type ?? this._resolveExprGoType(mArg);
|
|
1351
|
-
const { keyType, valType } = getMapKeyValTypes(mType, this.mod.checker);
|
|
1352
|
-
const mapInfo = this.mod.getMapType(keyType, valType);
|
|
1353
|
-
this.emitExpr(mArg, mapInfo.wType);
|
|
1354
|
-
this.pushInstruction({
|
|
1355
|
-
op: "call",
|
|
1356
|
-
funcIndex: this.mod.resolveFuncIndex(mapInfo.valuesFuncName),
|
|
1357
|
-
});
|
|
1358
|
-
return;
|
|
1359
|
-
}
|
|
1360
2399
|
case "Clone": {
|
|
1361
2400
|
const mArg = args[0];
|
|
1362
2401
|
const mType = mArg._type ?? this._resolveExprGoType(mArg);
|
|
@@ -4938,7 +5977,7 @@ export class StdlibCoreEmitter {
|
|
|
4938
5977
|
const vTmps = vNodes.map((vNode) => {
|
|
4939
5978
|
const tmp = this.acquireTemp(arrInfo.elemWType);
|
|
4940
5979
|
this.emitExpr(vNode, arrInfo.elemWType);
|
|
4941
|
-
this.
|
|
5980
|
+
this._emitCopyIfValue(vNode, elemGoType, arrInfo.elemWType);
|
|
4942
5981
|
this.pushInstruction({ op: "local.set", index: tmp });
|
|
4943
5982
|
return tmp;
|
|
4944
5983
|
});
|
|
@@ -5195,7 +6234,7 @@ export class StdlibCoreEmitter {
|
|
|
5195
6234
|
const vTmps = vNodes.map((vNode) => {
|
|
5196
6235
|
const tmp = this.acquireTemp(arrInfo.elemWType);
|
|
5197
6236
|
this.emitExpr(vNode, arrInfo.elemWType);
|
|
5198
|
-
this.
|
|
6237
|
+
this._emitCopyIfValue(vNode, elemGoType, arrInfo.elemWType);
|
|
5199
6238
|
this.pushInstruction({ op: "local.set", index: tmp });
|
|
5200
6239
|
return tmp;
|
|
5201
6240
|
});
|
|
@@ -5638,6 +6677,15 @@ export class StdlibCoreEmitter {
|
|
|
5638
6677
|
|
|
5639
6678
|
emitStringsCall(name, args, targetWasmType) {
|
|
5640
6679
|
switch (name) {
|
|
6680
|
+
case "ToValidUTF8": {
|
|
6681
|
+
this.emitExpr(args[0], "externref");
|
|
6682
|
+
this.emitExpr(args[1], "externref");
|
|
6683
|
+
this.pushInstruction({
|
|
6684
|
+
op: "call",
|
|
6685
|
+
funcIndex: this.mod.getStringToValidUTF8ImportIndex(),
|
|
6686
|
+
});
|
|
6687
|
+
return;
|
|
6688
|
+
}
|
|
5641
6689
|
case "ToUpper": {
|
|
5642
6690
|
this.emitExpr(args[0], "externref");
|
|
5643
6691
|
this.pushInstruction({
|
|
@@ -6204,11 +7252,14 @@ export class StdlibCoreEmitter {
|
|
|
6204
7252
|
return;
|
|
6205
7253
|
}
|
|
6206
7254
|
case "ParseUint": {
|
|
7255
|
+
const sTmp = this.acquireTemp("externref");
|
|
6207
7256
|
this.emitExpr(args[0], "externref");
|
|
7257
|
+
this.pushInstruction({ op: "local.tee", index: sTmp });
|
|
6208
7258
|
this._emitI32Arg(args[1]);
|
|
6209
7259
|
this._emitI32Arg(args[2]);
|
|
6210
7260
|
this._callStdlib("strconv_parse_uint");
|
|
6211
|
-
this._emitStrconvError();
|
|
7261
|
+
this._emitStrconvError("ParseUint", sTmp);
|
|
7262
|
+
this.releaseTemp(sTmp, "externref");
|
|
6212
7263
|
return;
|
|
6213
7264
|
}
|
|
6214
7265
|
case "Atoi":
|
|
@@ -6217,7 +7268,9 @@ export class StdlibCoreEmitter {
|
|
|
6217
7268
|
case "ParseBool": {
|
|
6218
7269
|
// (value, error): the JS side records a failure message that
|
|
6219
7270
|
// `strconv_err` returns (null on success) → runtime error value.
|
|
7271
|
+
const sTmp = this.acquireTemp("externref");
|
|
6220
7272
|
this.emitExpr(args[0], "externref");
|
|
7273
|
+
this.pushInstruction({ op: "local.tee", index: sTmp });
|
|
6221
7274
|
if (name === "Atoi") this._callStdlib("strconv_atoi");
|
|
6222
7275
|
else if (name === "ParseInt") {
|
|
6223
7276
|
this._emitI32Arg(args[1]);
|
|
@@ -6226,7 +7279,8 @@ export class StdlibCoreEmitter {
|
|
|
6226
7279
|
} else if (name === "ParseFloat")
|
|
6227
7280
|
this._callStdlib("strconv_parse_float");
|
|
6228
7281
|
else this._callStdlib("strconv_parse_bool");
|
|
6229
|
-
this._emitStrconvError();
|
|
7282
|
+
this._emitStrconvError(name, sTmp);
|
|
7283
|
+
this.releaseTemp(sTmp, "externref");
|
|
6230
7284
|
return;
|
|
6231
7285
|
}
|
|
6232
7286
|
default:
|
|
@@ -6234,7 +7288,7 @@ export class StdlibCoreEmitter {
|
|
|
6234
7288
|
}
|
|
6235
7289
|
}
|
|
6236
7290
|
|
|
6237
|
-
_emitStrconvError() {
|
|
7291
|
+
_emitStrconvError(fnName = null, sTmp = null) {
|
|
6238
7292
|
this._callStdlib("strconv_err");
|
|
6239
7293
|
const msgTmp = this.acquireTemp("externref");
|
|
6240
7294
|
this.pushInstruction({ op: "local.set", index: msgTmp });
|
|
@@ -6263,12 +7317,38 @@ export class StdlibCoreEmitter {
|
|
|
6263
7317
|
this.pushInstruction("end");
|
|
6264
7318
|
this.pushInstruction("end");
|
|
6265
7319
|
|
|
7320
|
+
const numErrInfo =
|
|
7321
|
+
this.mod.getStructType("NumError$$strconv") ??
|
|
7322
|
+
this.mod.getStructType("strconv.NumError");
|
|
7323
|
+
|
|
6266
7324
|
this.pushInstruction({ op: "local.get", index: msgTmp });
|
|
6267
|
-
this.pushInstruction(
|
|
6268
|
-
this.pushInstruction(
|
|
6269
|
-
|
|
6270
|
-
|
|
6271
|
-
});
|
|
7325
|
+
this.pushInstruction("any.convert_extern");
|
|
7326
|
+
this.pushInstruction("ref.is_null");
|
|
7327
|
+
this.pushInstruction({ op: "if", blockType: "anyref" });
|
|
7328
|
+
this.pushInstruction({ op: "ref.null", heapType: "any" });
|
|
7329
|
+
this.pushInstruction({ op: "else" });
|
|
7330
|
+
if (numErrInfo && fnName && sTmp !== null) {
|
|
7331
|
+
const fnStrIdx = this.mod.internString(fnName);
|
|
7332
|
+
this.pushInstruction({ op: "i32.const", value: fnStrIdx });
|
|
7333
|
+
this.pushInstruction({
|
|
7334
|
+
op: "call",
|
|
7335
|
+
funcIndex: this.mod.getStringImportIndex(),
|
|
7336
|
+
});
|
|
7337
|
+
this.pushInstruction({ op: "local.get", index: sTmp });
|
|
7338
|
+
this.pushInstruction({ op: "local.get", index: causeTmp });
|
|
7339
|
+
this.pushInstruction({
|
|
7340
|
+
op: "struct.new",
|
|
7341
|
+
typeIndex: numErrInfo.typeIndex,
|
|
7342
|
+
});
|
|
7343
|
+
} else {
|
|
7344
|
+
this.pushInstruction({ op: "local.get", index: msgTmp });
|
|
7345
|
+
this.pushInstruction({ op: "local.get", index: causeTmp });
|
|
7346
|
+
this.pushInstruction({
|
|
7347
|
+
op: "call",
|
|
7348
|
+
funcIndex: this.mod.resolveFuncIndex("__mk_error"),
|
|
7349
|
+
});
|
|
7350
|
+
}
|
|
7351
|
+
this.pushInstruction("end");
|
|
6272
7352
|
|
|
6273
7353
|
this.releaseTemp(causeTmp, "anyref");
|
|
6274
7354
|
this.releaseTemp(kindTmp, "i32");
|
|
@@ -7206,6 +8286,14 @@ export class StdlibCoreEmitter {
|
|
|
7206
8286
|
this.releaseTemp(rTmp, replacerW);
|
|
7207
8287
|
return;
|
|
7208
8288
|
}
|
|
8289
|
+
if (method === "WriteString") {
|
|
8290
|
+
const strTmp = this.acquireTemp("externref");
|
|
8291
|
+
this.emitReplacerMethodCall("Replace", recvExpr, [args[1]]);
|
|
8292
|
+
this.pushInstruction({ op: "local.set", index: strTmp });
|
|
8293
|
+
this._emitWriterWrite(args[0], strTmp);
|
|
8294
|
+
this.releaseTemp(strTmp, "externref");
|
|
8295
|
+
return;
|
|
8296
|
+
}
|
|
7209
8297
|
throw new Error(`Unsupported Replacer method: ${method}`);
|
|
7210
8298
|
}
|
|
7211
8299
|
|
|
@@ -7684,20 +8772,87 @@ export class StdlibCoreEmitter {
|
|
|
7684
8772
|
}
|
|
7685
8773
|
}
|
|
7686
8774
|
|
|
8775
|
+
// `fmt.Println(f())` with a multi-value `f`: every result becomes its own
|
|
8776
|
+
// operand, held in a hidden local so the usual operand paths apply.
|
|
8777
|
+
_expandTupleFmtArgs(args) {
|
|
8778
|
+
const tuple = args.length === 1 ? args[0]._type : null;
|
|
8779
|
+
if (tuple?.kind !== "tuple") return args;
|
|
8780
|
+
const types = tuple.types;
|
|
8781
|
+
this._fmtTupleSeq = (this._fmtTupleSeq ?? 0) + 1;
|
|
8782
|
+
const names = types.map((_, i) => `__fmt_tuple_${this._fmtTupleSeq}_${i}`);
|
|
8783
|
+
const idxs = types.map((t, i) =>
|
|
8784
|
+
this.allocLocal(names[i], this.toWasmType(t), t, true),
|
|
8785
|
+
);
|
|
8786
|
+
this.emitExpr(args[0]);
|
|
8787
|
+
for (let i = types.length - 1; i >= 0; i--) {
|
|
8788
|
+
this.pushInstruction({ op: "local.set", index: idxs[i] });
|
|
8789
|
+
}
|
|
8790
|
+
return types.map((t, i) => ({ kind: "Ident", name: names[i], _type: t }));
|
|
8791
|
+
}
|
|
8792
|
+
|
|
7687
8793
|
emitFmtCall(name, args) {
|
|
7688
|
-
|
|
7689
|
-
|
|
8794
|
+
args = this._expandTupleFmtArgs(args);
|
|
8795
|
+
if (name === "Print" || name === "Sprint" || name === "Sprintln") {
|
|
8796
|
+
// Operands travel through the argument buffer; the host joins them
|
|
8797
|
+
// with Go's spacing rule (a space only between two non-strings).
|
|
8798
|
+
const temps = this._emitArgBuffer(args, true);
|
|
8799
|
+
this.pushInstruction({
|
|
8800
|
+
op: "i32.const",
|
|
8801
|
+
value: name === "Sprintln" ? 1 : 0,
|
|
8802
|
+
});
|
|
8803
|
+
this._callStdlib(name === "Print" ? "fmt_print" : "fmt_sprint");
|
|
8804
|
+
this._releaseArgBuffer(temps);
|
|
8805
|
+
return;
|
|
8806
|
+
}
|
|
8807
|
+
if (name === "Append" || name === "Appendln" || name === "Appendf") {
|
|
8808
|
+
const [dst, ...rest] = args;
|
|
8809
|
+
const strTmp = this.acquireTemp("externref");
|
|
8810
|
+
if (name === "Appendf") {
|
|
8811
|
+
const [format, ...operands] = rest;
|
|
8812
|
+
const temps = this._emitArgBuffer(
|
|
8813
|
+
operands,
|
|
8814
|
+
false,
|
|
8815
|
+
this._fmtNeedsTypes(format, operands),
|
|
8816
|
+
);
|
|
8817
|
+
this.emitExpr(format, "externref");
|
|
8818
|
+
this._callStdlib("fmt_sprintf");
|
|
8819
|
+
this._releaseArgBuffer(temps);
|
|
8820
|
+
} else {
|
|
8821
|
+
const temps = this._emitArgBuffer(rest, true);
|
|
8822
|
+
this.pushInstruction({
|
|
8823
|
+
op: "i32.const",
|
|
8824
|
+
value: name === "Appendln" ? 1 : 0,
|
|
8825
|
+
});
|
|
8826
|
+
this._callStdlib("fmt_sprint");
|
|
8827
|
+
this._releaseArgBuffer(temps);
|
|
8828
|
+
}
|
|
8829
|
+
this.pushInstruction({ op: "local.set", index: strTmp });
|
|
8830
|
+
this._emitAppendStringToByteSlice(dst, strTmp);
|
|
8831
|
+
this.releaseTemp(strTmp, "externref");
|
|
8832
|
+
return;
|
|
8833
|
+
}
|
|
8834
|
+
if (name === "Println") {
|
|
8835
|
+
const isPrintln = true;
|
|
7690
8836
|
if (args.length === 0 && isPrintln) {
|
|
7691
8837
|
const logIdx = this.mod.getPrintlnEmptyIndex();
|
|
7692
8838
|
this.pushInstruction({ op: "call", funcIndex: logIdx });
|
|
7693
8839
|
return;
|
|
7694
8840
|
}
|
|
8841
|
+
// Float operands need their static type (float32, Go's exponent
|
|
8842
|
+
// forms), which only the argument buffer carries.
|
|
8843
|
+
if (args.some((a) => this._fmtOperandNeedsBuffer(a))) {
|
|
8844
|
+
const temps = this._emitArgBuffer(args, true);
|
|
8845
|
+
this.pushInstruction({ op: "i32.const", value: 1 });
|
|
8846
|
+
this._callStdlib("fmt_print");
|
|
8847
|
+
this._releaseArgBuffer(temps);
|
|
8848
|
+
return;
|
|
8849
|
+
}
|
|
7695
8850
|
for (let i = 0; i < args.length; i++) {
|
|
7696
8851
|
const arg = args[i];
|
|
7697
8852
|
const isLast = i === args.length - 1 && isPrintln;
|
|
7698
8853
|
const goType = arg._type ?? this._resolveExprGoType(arg);
|
|
7699
8854
|
const isBool =
|
|
7700
|
-
goType
|
|
8855
|
+
isBoolGoType(goType) ||
|
|
7701
8856
|
(arg.kind === "BasicLit" && arg.litKind === "BOOL");
|
|
7702
8857
|
if (isErrorType(goType) && this.mod.hasErrorType) {
|
|
7703
8858
|
this.emitExpr(arg, "anyref");
|
|
@@ -7711,10 +8866,8 @@ export class StdlibCoreEmitter {
|
|
|
7711
8866
|
});
|
|
7712
8867
|
continue;
|
|
7713
8868
|
}
|
|
7714
|
-
if (this.
|
|
7715
|
-
|
|
7716
|
-
this.emitExpr(arg, valW);
|
|
7717
|
-
this.emitToExternref(goType, false);
|
|
8869
|
+
if (this._isFmtExternOperand(goType)) {
|
|
8870
|
+
this._emitOperandExternref(arg, goType);
|
|
7718
8871
|
this._callStdlib("fmt_format_v");
|
|
7719
8872
|
this.pushInstruction({
|
|
7720
8873
|
op: "call",
|
|
@@ -7731,7 +8884,11 @@ export class StdlibCoreEmitter {
|
|
|
7731
8884
|
}
|
|
7732
8885
|
if (name === "Sprintf" || name === "Printf" || name === "Errorf") {
|
|
7733
8886
|
const [format, ...rest] = args;
|
|
7734
|
-
const temps = this._emitArgBuffer(
|
|
8887
|
+
const temps = this._emitArgBuffer(
|
|
8888
|
+
rest,
|
|
8889
|
+
false,
|
|
8890
|
+
this._fmtNeedsTypes(format, rest),
|
|
8891
|
+
);
|
|
7735
8892
|
this.emitExpr(format, "externref");
|
|
7736
8893
|
if (name === "Printf") {
|
|
7737
8894
|
this._callStdlib("fmt_printf");
|
|
@@ -8549,6 +9706,193 @@ export class StdlibCoreEmitter {
|
|
|
8549
9706
|
this.pushInstruction({ op: "call", funcIndex: idx });
|
|
8550
9707
|
}
|
|
8551
9708
|
|
|
9709
|
+
// Whether `emitToExternref` can turn a value of this type into a host value.
|
|
9710
|
+
_isExternConvertible(goType, depth = 0) {
|
|
9711
|
+
if (!goType || depth > 4) return false;
|
|
9712
|
+
const checker = this.mod.checker;
|
|
9713
|
+
if (isAnyType(goType, checker)) return true;
|
|
9714
|
+
if (isSliceType(goType, checker) || isArrayType(goType, checker)) {
|
|
9715
|
+
return this._isExternConvertible(
|
|
9716
|
+
getSliceElemType(goType, checker),
|
|
9717
|
+
depth + 1,
|
|
9718
|
+
);
|
|
9719
|
+
}
|
|
9720
|
+
if (isMapType(goType, checker)) {
|
|
9721
|
+
const { keyType, valType } = getMapKeyValTypes(goType, checker);
|
|
9722
|
+
return (
|
|
9723
|
+
this._isExternConvertible(keyType, depth + 1) &&
|
|
9724
|
+
this._isExternConvertible(valType, depth + 1)
|
|
9725
|
+
);
|
|
9726
|
+
}
|
|
9727
|
+
return this._isExternLeaf(goType);
|
|
9728
|
+
}
|
|
9729
|
+
|
|
9730
|
+
_isExternLeaf(goType) {
|
|
9731
|
+
const checker = this.mod.checker;
|
|
9732
|
+
if (
|
|
9733
|
+
isStructType(goType, checker, this.mod) ||
|
|
9734
|
+
isPointerToStruct(goType, checker, this.mod) ||
|
|
9735
|
+
isComplex(goType) ||
|
|
9736
|
+
_isPointerKind(goType)
|
|
9737
|
+
)
|
|
9738
|
+
return true;
|
|
9739
|
+
return ["i32", "i64", "f32", "f64", "externref", "v128"].includes(
|
|
9740
|
+
toWasmType(goType, checker, this.mod),
|
|
9741
|
+
);
|
|
9742
|
+
}
|
|
9743
|
+
|
|
9744
|
+
// Body of `__any_fmt(a any) externref`: the dynamic type of `a` is found
|
|
9745
|
+
// with a `ref.test` chain over every type the module knows (like
|
|
9746
|
+
// `__any_eq`), and the value goes through `emitToExternref`, so
|
|
9747
|
+
// String/Error dispatch and nesting work as for statically typed operands.
|
|
9748
|
+
_emitAnyFmtHelper() {
|
|
9749
|
+
const done = new Set();
|
|
9750
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
9751
|
+
this.pushInstruction("ref.is_null");
|
|
9752
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
9753
|
+
this.pushInstruction({ op: "ref.null", heapType: "extern" });
|
|
9754
|
+
this.pushInstruction("return");
|
|
9755
|
+
this.pushInstruction("end");
|
|
9756
|
+
|
|
9757
|
+
// Strings are host values; scalars are boxed by their dynamic type.
|
|
9758
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
9759
|
+
this._emitTypeTest({ kind: "basic", name: "string" });
|
|
9760
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
9761
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
9762
|
+
this.pushInstruction("extern.convert_any");
|
|
9763
|
+
this.pushInstruction("return");
|
|
9764
|
+
this.pushInstruction("end");
|
|
9765
|
+
|
|
9766
|
+
this._emitAnyFmtError(done);
|
|
9767
|
+
for (let round = 0; round < 3; round++) {
|
|
9768
|
+
this._emitAnyFmtBoxes(done);
|
|
9769
|
+
this._emitAnyFmtStructs(done);
|
|
9770
|
+
// Containers share a wasm type across Go element types, so their
|
|
9771
|
+
// elements are formatted without scalar-backed Stringer dispatch.
|
|
9772
|
+
this._fmtNoScalarMethods = true;
|
|
9773
|
+
this._emitAnyFmtContainers(done);
|
|
9774
|
+
this._fmtNoScalarMethods = false;
|
|
9775
|
+
}
|
|
9776
|
+
this._emitAnyFmtNilMaps();
|
|
9777
|
+
this.pushInstruction({ op: "ref.null", heapType: "extern" });
|
|
9778
|
+
this.pushInstruction("return");
|
|
9779
|
+
}
|
|
9780
|
+
|
|
9781
|
+
// A nil map in an interface prints as an empty map; nil pointers and funcs
|
|
9782
|
+
// fall through to `<nil>`.
|
|
9783
|
+
_emitAnyFmtNilMaps() {
|
|
9784
|
+
for (const nb of [...this.mod.nilBoxTypes.values()]) {
|
|
9785
|
+
if (!isMapType(nb.goType, this.mod.checker)) continue;
|
|
9786
|
+
const { keyType, valType } = getMapKeyValTypes(
|
|
9787
|
+
nb.goType,
|
|
9788
|
+
this.mod.checker,
|
|
9789
|
+
);
|
|
9790
|
+
const mapInfo = this.mod.getMapType(keyType, valType);
|
|
9791
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
9792
|
+
this.pushInstruction({ op: "ref.test", typeIndex: nb.typeIndex });
|
|
9793
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
9794
|
+
this.pushInstruction({ op: "ref.null", heapType: mapInfo.typeIndex });
|
|
9795
|
+
this.emitToExternref(nb.goType);
|
|
9796
|
+
this.pushInstruction("return");
|
|
9797
|
+
this.pushInstruction("end");
|
|
9798
|
+
}
|
|
9799
|
+
}
|
|
9800
|
+
|
|
9801
|
+
// One arm of the `__any_fmt` chain: if `a` is a `typeIndex`, format it as
|
|
9802
|
+
// `goType` (a box is unwrapped first) and return.
|
|
9803
|
+
_emitAnyFmtArm(done, typeIndex, goType, unbox) {
|
|
9804
|
+
if (done.has(typeIndex)) return;
|
|
9805
|
+
done.add(typeIndex);
|
|
9806
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
9807
|
+
this.pushInstruction({ op: "ref.test", typeIndex });
|
|
9808
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
9809
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
9810
|
+
this.pushInstruction({ op: "ref.cast", typeIndex });
|
|
9811
|
+
if (unbox) {
|
|
9812
|
+
this.pushInstruction({ op: "struct.get", typeIndex, fieldIndex: 0 });
|
|
9813
|
+
}
|
|
9814
|
+
this.emitToExternref(goType);
|
|
9815
|
+
this.pushInstruction("return");
|
|
9816
|
+
this.pushInstruction("end");
|
|
9817
|
+
}
|
|
9818
|
+
|
|
9819
|
+
// errors.New / fmt.Errorf values print their message.
|
|
9820
|
+
_emitAnyFmtError(done) {
|
|
9821
|
+
const errInfo = this.mod.hasErrorType
|
|
9822
|
+
? this.mod.getStructType(ERROR_STRUCT)
|
|
9823
|
+
: null;
|
|
9824
|
+
if (!errInfo) return;
|
|
9825
|
+
const msgTmp = this.acquireTemp("externref");
|
|
9826
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
9827
|
+
this.pushInstruction({ op: "ref.test", typeIndex: errInfo.typeIndex });
|
|
9828
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
9829
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
9830
|
+
this.pushInstruction({
|
|
9831
|
+
op: "call",
|
|
9832
|
+
funcIndex: this.mod.resolveFuncIndex("__error_str"),
|
|
9833
|
+
});
|
|
9834
|
+
this.pushInstruction({ op: "local.set", index: msgTmp });
|
|
9835
|
+
this.pushInstruction({ op: "local.get", index: msgTmp });
|
|
9836
|
+
this.pushInstruction({ op: "local.get", index: msgTmp });
|
|
9837
|
+
this.pushInstruction({ op: "i32.const", value: 1 });
|
|
9838
|
+
this.pushInstruction({
|
|
9839
|
+
op: "call",
|
|
9840
|
+
funcIndex: this.mod.getFmtNewStringerIndex(),
|
|
9841
|
+
});
|
|
9842
|
+
this.pushInstruction("return");
|
|
9843
|
+
this.pushInstruction("end");
|
|
9844
|
+
this.releaseTemp(msgTmp, "externref");
|
|
9845
|
+
done.add(errInfo.typeIndex);
|
|
9846
|
+
}
|
|
9847
|
+
|
|
9848
|
+
_emitAnyFmtBoxes(done) {
|
|
9849
|
+
for (const box of [...this.mod.ifaceBoxTypes.values()]) {
|
|
9850
|
+
// `any(5)` boxes the untyped constant type; format it as its default type.
|
|
9851
|
+
const goType =
|
|
9852
|
+
box.goType?.kind === "untyped"
|
|
9853
|
+
? {
|
|
9854
|
+
kind: "basic",
|
|
9855
|
+
name:
|
|
9856
|
+
{ float: "float64", rune: "int32" }[box.goType.base] ??
|
|
9857
|
+
box.goType.base,
|
|
9858
|
+
}
|
|
9859
|
+
: box.goType;
|
|
9860
|
+
if (this._isExternConvertible(goType))
|
|
9861
|
+
this._emitAnyFmtArm(done, box.typeIndex, goType, true);
|
|
9862
|
+
}
|
|
9863
|
+
}
|
|
9864
|
+
|
|
9865
|
+
// A bare struct ref in an `any` is a pointer.
|
|
9866
|
+
_emitAnyFmtStructs(done) {
|
|
9867
|
+
const checker = this.mod.checker;
|
|
9868
|
+
for (const [name, sInfo] of [...this.mod.structTypes.entries()]) {
|
|
9869
|
+
const named = name === ERROR_STRUCT ? null : checker?.types?.get(name);
|
|
9870
|
+
if (!named) continue;
|
|
9871
|
+
const ptrType = { kind: "pointer", base: named };
|
|
9872
|
+
const w = toWasmType(ptrType, checker, this.mod);
|
|
9873
|
+
if (w?.typeIndex === sInfo.typeIndex)
|
|
9874
|
+
this._emitAnyFmtArm(done, sInfo.typeIndex, ptrType, false);
|
|
9875
|
+
}
|
|
9876
|
+
}
|
|
9877
|
+
|
|
9878
|
+
_emitAnyFmtContainers(done) {
|
|
9879
|
+
const groups = [
|
|
9880
|
+
[this.mod.sliceTypes, (i) => ({ kind: "slice", elem: i.elemGoType })],
|
|
9881
|
+
[this.mod.arrayTypes, (i) => ({ kind: "array", elem: i.elemGoType })],
|
|
9882
|
+
[
|
|
9883
|
+
this.mod.mapTypes,
|
|
9884
|
+
(i) => ({ kind: "map", key: i.keyGoType, value: i.valGoType }),
|
|
9885
|
+
],
|
|
9886
|
+
];
|
|
9887
|
+
for (const [infos, toGoType] of groups) {
|
|
9888
|
+
for (const info of [...infos.values()]) {
|
|
9889
|
+
const goType = toGoType(info);
|
|
9890
|
+
if (this._isExternConvertible(goType))
|
|
9891
|
+
this._emitAnyFmtArm(done, info.typeIndex, goType, false);
|
|
9892
|
+
}
|
|
9893
|
+
}
|
|
9894
|
+
}
|
|
9895
|
+
|
|
8552
9896
|
// Bodies of the synthetic runtime helpers declared in compileWasmModule.
|
|
8553
9897
|
emitRuntimeHelper(fn) {
|
|
8554
9898
|
const errInfo = this.mod.getStructType(ERROR_STRUCT);
|
|
@@ -8773,6 +10117,9 @@ export class StdlibCoreEmitter {
|
|
|
8773
10117
|
});
|
|
8774
10118
|
this.pushInstruction("return");
|
|
8775
10119
|
return;
|
|
10120
|
+
case "any_fmt":
|
|
10121
|
+
this._emitAnyFmtHelper();
|
|
10122
|
+
return;
|
|
8776
10123
|
case "error_str":
|
|
8777
10124
|
this.pushInstruction({ op: "local.get", index: 0 });
|
|
8778
10125
|
this.pushInstruction("ref.is_null");
|
|
@@ -8880,6 +10227,36 @@ export class StdlibCoreEmitter {
|
|
|
8880
10227
|
this.pushInstruction("return");
|
|
8881
10228
|
return;
|
|
8882
10229
|
}
|
|
10230
|
+
case "testing_cleanup_cb": {
|
|
10231
|
+
// (fn anyref): fn is a func() closure
|
|
10232
|
+
const closureInfo = this.mod.getTestingCleanupClosureType();
|
|
10233
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
10234
|
+
this.pushInstruction({
|
|
10235
|
+
op: "ref.cast",
|
|
10236
|
+
typeIndex: closureInfo.typeIndex,
|
|
10237
|
+
});
|
|
10238
|
+
this.pushInstruction({
|
|
10239
|
+
op: "struct.get",
|
|
10240
|
+
typeIndex: closureInfo.typeIndex,
|
|
10241
|
+
fieldIndex: 1,
|
|
10242
|
+
});
|
|
10243
|
+
this.pushInstruction({ op: "local.get", index: 0 });
|
|
10244
|
+
this.pushInstruction({
|
|
10245
|
+
op: "ref.cast",
|
|
10246
|
+
typeIndex: closureInfo.typeIndex,
|
|
10247
|
+
});
|
|
10248
|
+
this.pushInstruction({
|
|
10249
|
+
op: "struct.get",
|
|
10250
|
+
typeIndex: closureInfo.typeIndex,
|
|
10251
|
+
fieldIndex: 0,
|
|
10252
|
+
});
|
|
10253
|
+
this.pushInstruction({
|
|
10254
|
+
op: "call_ref",
|
|
10255
|
+
typeIndex: closureInfo.funcTypeIndex,
|
|
10256
|
+
});
|
|
10257
|
+
this.pushInstruction("return");
|
|
10258
|
+
return;
|
|
10259
|
+
}
|
|
8883
10260
|
default:
|
|
8884
10261
|
throw new Error(`Unknown runtime helper: ${fn._isRuntimeHelper}`);
|
|
8885
10262
|
}
|
|
@@ -8899,5 +10276,3 @@ function _wrapVerbIndex(format) {
|
|
|
8899
10276
|
}
|
|
8900
10277
|
return -1;
|
|
8901
10278
|
}
|
|
8902
|
-
|
|
8903
|
-
export { StdlibCoreEmitter as StdlibEmitter };
|