gofront 1.6.1 → 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 +45 -49
- package/package.json +12 -8
- package/src/backend/js/expressions.js +1627 -148
- package/src/backend/js/fmt-runtime.js +573 -0
- package/src/backend/js/index.js +508 -102
- package/src/backend/js/runtime.js +1997 -106
- package/src/backend/js/statements.js +931 -87
- 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 +7 -1
- package/src/backend/js/stdlib/fmt.js +349 -10
- package/src/backend/js/stdlib/index.js +19 -2
- package/src/backend/js/stdlib/json.js +1167 -0
- 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 +34 -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 +446 -72
- package/src/backend/wasm/emit-builtins.js +320 -13
- package/src/backend/wasm/emit-exprs.js +1404 -199
- package/src/backend/wasm/emit-maps.js +61 -13
- package/src/backend/wasm/emit-stmts.js +1359 -164
- package/src/backend/wasm/emit.js +518 -74
- package/src/backend/wasm/encode.js +128 -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/optimize.js +4 -2
- 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 +3 -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 +499 -0
- package/src/backend/wasm/stdlib/time.js +1041 -12
- package/src/backend/wasm/types.js +221 -15
- package/src/backend/wasm/variadic.js +93 -0
- package/src/backend/wasm/wat.js +84 -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} +89 -15
- package/src/compiler.js +107 -269
- package/src/dts-parser.js +71 -14
- 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 -6
- package/src/lower/captures.js +176 -32
- package/src/lower/embedding.js +2 -1
- 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/parser/struct-tag.js +144 -0
- package/src/{templ-parser.js → parser/templ.js} +138 -15
- package/src/parser/types.js +130 -13
- package/src/resolver.js +22 -1
- package/src/typechecker/assignability.js +215 -11
- package/src/typechecker/const-fold.js +356 -0
- package/src/typechecker/constants.js +256 -0
- package/src/typechecker/expressions.js +960 -99
- package/src/typechecker/index.js +491 -72
- package/src/typechecker/init-order.js +330 -0
- package/src/typechecker/resolve.js +596 -52
- package/src/typechecker/statements.js +157 -17
- package/src/typechecker/stdlib/core.js +333 -55
- package/src/typechecker/stdlib/excluded.js +898 -0
- package/src/typechecker/stdlib/extended.js +359 -26
- package/src/typechecker/{stdlib.js → stdlib/index.js} +6 -4
- package/src/typechecker/stdlib/simd.js +123 -0
- package/src/typechecker/termination.js +1 -1
- package/src/typechecker/types.js +165 -26
- 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
|
@@ -354,6 +354,7 @@ const OPCODES = {
|
|
|
354
354
|
"ref.func": 0xd2,
|
|
355
355
|
"ref.eq": 0xd3,
|
|
356
356
|
"ref.as_non_null": 0xd4,
|
|
357
|
+
br_on_null: 0xd5,
|
|
357
358
|
};
|
|
358
359
|
|
|
359
360
|
// 0xFC prefixed saturating truncations
|
|
@@ -393,6 +394,64 @@ const GC_OPCODES = {
|
|
|
393
394
|
"extern.convert_any": 0x1b,
|
|
394
395
|
};
|
|
395
396
|
|
|
397
|
+
// 0xFD prefixed SIMD instructions (Wasm SIMD / v128)
|
|
398
|
+
const SIMD_OPCODES = {
|
|
399
|
+
"v128.load": 0,
|
|
400
|
+
"v128.store": 11,
|
|
401
|
+
"v128.const": 12,
|
|
402
|
+
"i8x16.shuffle": 13,
|
|
403
|
+
"i8x16.swizzle": 14,
|
|
404
|
+
"i8x16.splat": 15,
|
|
405
|
+
"i16x8.splat": 16,
|
|
406
|
+
"i32x4.splat": 17,
|
|
407
|
+
"i64x2.splat": 18,
|
|
408
|
+
"f32x4.splat": 19,
|
|
409
|
+
"f64x2.splat": 20,
|
|
410
|
+
"i8x16.extract_lane_s": 21,
|
|
411
|
+
"i8x16.extract_lane_u": 22,
|
|
412
|
+
"i8x16.replace_lane": 23,
|
|
413
|
+
"i16x8.extract_lane_s": 24,
|
|
414
|
+
"i16x8.extract_lane_u": 25,
|
|
415
|
+
"i16x8.replace_lane": 26,
|
|
416
|
+
"i32x4.extract_lane": 27,
|
|
417
|
+
"i32x4.replace_lane": 28,
|
|
418
|
+
"i64x2.extract_lane": 29,
|
|
419
|
+
"i64x2.replace_lane": 30,
|
|
420
|
+
"f32x4.extract_lane": 31,
|
|
421
|
+
"f32x4.replace_lane": 32,
|
|
422
|
+
"i32x4.eq": 55,
|
|
423
|
+
"f32x4.eq": 65,
|
|
424
|
+
"f32x4.ne": 66,
|
|
425
|
+
"f32x4.lt": 67,
|
|
426
|
+
"f32x4.gt": 68,
|
|
427
|
+
"f32x4.le": 69,
|
|
428
|
+
"f32x4.ge": 70,
|
|
429
|
+
"v128.not": 77,
|
|
430
|
+
"v128.and": 78,
|
|
431
|
+
"v128.andnot": 79,
|
|
432
|
+
"v128.or": 80,
|
|
433
|
+
"v128.xor": 81,
|
|
434
|
+
"v128.bitselect": 82,
|
|
435
|
+
"v128.any_true": 83,
|
|
436
|
+
"i32x4.all_true": 163,
|
|
437
|
+
"i32x4.bitmask": 164,
|
|
438
|
+
"f32x4.ceil": 103,
|
|
439
|
+
"f32x4.floor": 104,
|
|
440
|
+
"f32x4.trunc": 105,
|
|
441
|
+
"f32x4.nearest": 106,
|
|
442
|
+
"f32x4.abs": 224,
|
|
443
|
+
"f32x4.neg": 225,
|
|
444
|
+
"f32x4.sqrt": 227,
|
|
445
|
+
"f32x4.add": 228,
|
|
446
|
+
"f32x4.sub": 229,
|
|
447
|
+
"f32x4.mul": 230,
|
|
448
|
+
"f32x4.div": 231,
|
|
449
|
+
"f32x4.min": 232,
|
|
450
|
+
"f32x4.max": 233,
|
|
451
|
+
"f32x4.pmin": 234,
|
|
452
|
+
"f32x4.pmax": 235,
|
|
453
|
+
};
|
|
454
|
+
|
|
396
455
|
// Natural alignment (log2) per load/store opcode; memarg = align, offset.
|
|
397
456
|
const MEM_ALIGN = {
|
|
398
457
|
"i32.load": 2,
|
|
@@ -433,6 +492,48 @@ function encodeInstruction(inst) {
|
|
|
433
492
|
return [0xfc, ...encodeU32LEB(SAT_TRUNC_OPCODES[op])];
|
|
434
493
|
}
|
|
435
494
|
|
|
495
|
+
// 2. Check SIMD opcodes (0xFD prefix)
|
|
496
|
+
if (SIMD_OPCODES[op] !== undefined) {
|
|
497
|
+
const subOp = SIMD_OPCODES[op];
|
|
498
|
+
const bytes = [0xfd, ...encodeU32LEB(subOp)];
|
|
499
|
+
if (op === "v128.load" || op === "v128.store") {
|
|
500
|
+
bytes.push(...encodeU32LEB(inst.align ?? 2));
|
|
501
|
+
bytes.push(...encodeU32LEB(inst.offset ?? 0));
|
|
502
|
+
return bytes;
|
|
503
|
+
}
|
|
504
|
+
if (op === "v128.const") {
|
|
505
|
+
const prefixLen = bytes.length;
|
|
506
|
+
if (inst.bytes) {
|
|
507
|
+
for (let i = 0; i < 16; i++) {
|
|
508
|
+
bytes.push(inst.bytes[i] ?? 0);
|
|
509
|
+
}
|
|
510
|
+
} else if (inst.values) {
|
|
511
|
+
const vals = inst.values.slice(0, 4);
|
|
512
|
+
for (const val of vals) {
|
|
513
|
+
bytes.push(...encodeF32(val));
|
|
514
|
+
}
|
|
515
|
+
while (bytes.length < prefixLen + 16) {
|
|
516
|
+
bytes.push(0);
|
|
517
|
+
}
|
|
518
|
+
} else {
|
|
519
|
+
for (let i = 0; i < 16; i++) bytes.push(0);
|
|
520
|
+
}
|
|
521
|
+
return bytes;
|
|
522
|
+
}
|
|
523
|
+
if (op === "i8x16.shuffle") {
|
|
524
|
+
const lanes = inst.lanes ?? inst.indices ?? [];
|
|
525
|
+
for (let i = 0; i < 16; i++) {
|
|
526
|
+
bytes.push(lanes[i] ?? 0);
|
|
527
|
+
}
|
|
528
|
+
return bytes;
|
|
529
|
+
}
|
|
530
|
+
if (op.includes("extract_lane") || op.includes("replace_lane")) {
|
|
531
|
+
bytes.push((inst.lane ?? inst.index ?? 0) & 0xff);
|
|
532
|
+
return bytes;
|
|
533
|
+
}
|
|
534
|
+
return bytes;
|
|
535
|
+
}
|
|
536
|
+
|
|
436
537
|
// 2. Check WasmGC opcodes (0xFB prefix)
|
|
437
538
|
if (GC_OPCODES[op] !== undefined) {
|
|
438
539
|
const subOp = GC_OPCODES[op];
|
|
@@ -494,6 +595,7 @@ function encodeInstruction(inst) {
|
|
|
494
595
|
|
|
495
596
|
case "br":
|
|
496
597
|
case "br_if":
|
|
598
|
+
case "br_on_null":
|
|
497
599
|
return [byte, ...encodeU32LEB(inst.depth ?? inst.index ?? 0)];
|
|
498
600
|
|
|
499
601
|
case "br_table": {
|
|
@@ -618,35 +720,48 @@ function encodeTypeEntry(t) {
|
|
|
618
720
|
return [0x4e, ...count, ...innerBytes];
|
|
619
721
|
}
|
|
620
722
|
|
|
723
|
+
let compositeBytes;
|
|
621
724
|
if (t.form === "struct") {
|
|
622
725
|
// Struct type: 0x5F, vector of fields [valtype, mutability]
|
|
623
726
|
const fields = t.fields ?? [];
|
|
624
|
-
|
|
727
|
+
compositeBytes = [
|
|
625
728
|
0x5f,
|
|
626
729
|
...encodeVector(fields, (f) => [
|
|
627
730
|
...encodeValType(f.type),
|
|
628
731
|
f.mutable ? 0x01 : 0x00,
|
|
629
732
|
]),
|
|
630
733
|
];
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
if (t.form === "array") {
|
|
734
|
+
} else if (t.form === "array") {
|
|
634
735
|
// Array type: 0x5E, field [valtype, mutability]
|
|
635
|
-
|
|
736
|
+
compositeBytes = [
|
|
636
737
|
0x5e,
|
|
637
738
|
...encodeValType(t.elemType ?? t.type),
|
|
638
739
|
t.mutable ? 0x01 : 0x00,
|
|
639
740
|
];
|
|
741
|
+
} else {
|
|
742
|
+
// Function type: 0x60, params vector, results vector
|
|
743
|
+
const params = t.params ?? [];
|
|
744
|
+
const results = t.results ?? [];
|
|
745
|
+
compositeBytes = [
|
|
746
|
+
0x60,
|
|
747
|
+
...encodeVector(params, encodeValType),
|
|
748
|
+
...encodeVector(results, encodeValType),
|
|
749
|
+
];
|
|
640
750
|
}
|
|
641
751
|
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
752
|
+
if (t.sub) {
|
|
753
|
+
const superIndices =
|
|
754
|
+
t.superTypeIndex !== undefined
|
|
755
|
+
? [t.superTypeIndex]
|
|
756
|
+
: (t.superTypeIndices ?? []);
|
|
757
|
+
return [
|
|
758
|
+
0x50,
|
|
759
|
+
...encodeVector(superIndices, encodeU32LEB),
|
|
760
|
+
...compositeBytes,
|
|
761
|
+
];
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
return compositeBytes;
|
|
650
765
|
}
|
|
651
766
|
|
|
652
767
|
function encodeTypeSection(types) {
|
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
// src/backend/wasm/globals.js
|
|
2
|
+
// Package-level constants and variables: constant folding, zero values and the
|
|
3
|
+
// synthesised `__init_globals` start function; plus interface dispatcher synthesis.
|
|
4
|
+
|
|
5
|
+
import { foldConstExpr } from "../../typechecker/const-fold.js";
|
|
6
|
+
import { orderPackageInits } from "../../typechecker/init-order.js";
|
|
7
|
+
import { _someNode } from "./prescan.js";
|
|
8
|
+
import { isStructType, toWasmType, variadicAsSlice } from "./types.js";
|
|
9
|
+
|
|
10
|
+
// Package-level `const` declarations (iota is already substituted by the
|
|
11
|
+
// parser). Literals and expressions the folder can reduce become BasicLits in
|
|
12
|
+
// `constCache`; whatever is left is recorded in `nonLiteralConsts` and later
|
|
13
|
+
// becomes a global initialised by the start function.
|
|
14
|
+
export function _collectPackageConsts(progs, mod, checker = null) {
|
|
15
|
+
const entries = [];
|
|
16
|
+
for (const p of progs) {
|
|
17
|
+
for (const d of p.decls ?? []) {
|
|
18
|
+
if (d.kind !== "ConstDecl") continue;
|
|
19
|
+
for (const spec of d.decls ?? []) {
|
|
20
|
+
for (let i = 0; i < spec.names.length; i++) {
|
|
21
|
+
if (spec.value?.[i])
|
|
22
|
+
entries.push({
|
|
23
|
+
name: spec.names[i],
|
|
24
|
+
expr: spec.value[i],
|
|
25
|
+
filename: p._filename,
|
|
26
|
+
source: p._source,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const resolve = (name) => mod.constCache.get(name) ?? null;
|
|
33
|
+
// Iterate to a fixpoint so a constant may refer to one declared later.
|
|
34
|
+
let pending = entries;
|
|
35
|
+
let progress = true;
|
|
36
|
+
while (progress && pending.length > 0) {
|
|
37
|
+
progress = false;
|
|
38
|
+
const rest = [];
|
|
39
|
+
for (const entry of pending) {
|
|
40
|
+
let lit = null;
|
|
41
|
+
try {
|
|
42
|
+
lit = foldConstExpr(entry.expr, resolve);
|
|
43
|
+
} catch (err) {
|
|
44
|
+
if (checker?.err) {
|
|
45
|
+
const savedFile = checker._currentFile;
|
|
46
|
+
const savedSource = checker._currentSource;
|
|
47
|
+
if (entry.filename) checker._currentFile = entry.filename;
|
|
48
|
+
if (entry.source) checker._currentSource = entry.source;
|
|
49
|
+
checker.err(err.message, entry.expr);
|
|
50
|
+
checker._currentFile = savedFile;
|
|
51
|
+
checker._currentSource = savedSource;
|
|
52
|
+
} else {
|
|
53
|
+
throw new Error(
|
|
54
|
+
`Constant expression error for '${entry.name}': ${err.message}`,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (lit) {
|
|
60
|
+
mod.constCache.set(entry.name, lit);
|
|
61
|
+
progress = true;
|
|
62
|
+
} else {
|
|
63
|
+
rest.push(entry);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
pending = rest;
|
|
67
|
+
}
|
|
68
|
+
for (const entry of pending) mod.nonLiteralConsts.add(entry.name);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function _isPointerGoType(t, mod) {
|
|
72
|
+
if (!t) return false;
|
|
73
|
+
if (t.kind === "pointer" || t.kind === "PointerType" || t.kind === "StarExpr")
|
|
74
|
+
return true;
|
|
75
|
+
// `type P *T` used as a field type: look through the name.
|
|
76
|
+
const named =
|
|
77
|
+
t.kind === "named" || t.kind === "TypeName" || t.kind === "Ident";
|
|
78
|
+
const under = named
|
|
79
|
+
? (t.underlying ?? mod.checker?.types?.get(t.name)?.underlying)
|
|
80
|
+
: null;
|
|
81
|
+
return under ? _isPointerGoType(under, mod) : false;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Constant-expression zero value of a struct global. Nested struct values are
|
|
85
|
+
// built in place: a null ref would trap on the first field access.
|
|
86
|
+
// Null when a nested array is too large to build as a constant expression.
|
|
87
|
+
function _zeroStructConst(structInfo, mod) {
|
|
88
|
+
const out = [];
|
|
89
|
+
for (const f of structInfo.fields) {
|
|
90
|
+
const zero = _zeroFieldConst(f, mod);
|
|
91
|
+
if (!zero) return null;
|
|
92
|
+
out.push(...zero);
|
|
93
|
+
}
|
|
94
|
+
out.push({ op: "struct.new", typeIndex: structInfo.typeIndex });
|
|
95
|
+
return out;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function _zeroFieldConst(f, mod) {
|
|
99
|
+
const w = f.wType;
|
|
100
|
+
if (w === "i64") return [{ op: "i64.const", value: 0n }];
|
|
101
|
+
if (w === "f32" || w === "f64") return [{ op: `${w}.const`, value: 0 }];
|
|
102
|
+
if (w === "externref") return [{ op: "ref.null", heapType: "extern" }];
|
|
103
|
+
if (w === "anyref") return [{ op: "ref.null", heapType: "any" }];
|
|
104
|
+
if (typeof w !== "object" || w === null)
|
|
105
|
+
return [{ op: "i32.const", value: 0 }];
|
|
106
|
+
const ti = w.typeIndex;
|
|
107
|
+
const slice = typeof ti === "number" ? mod.getSliceTypeByIndex(ti) : null;
|
|
108
|
+
if (slice) return [{ op: "global.get", index: slice.emptyGlobalIndex }];
|
|
109
|
+
if (typeof ti === "number" && !_isPointerGoType(f.goType, mod)) {
|
|
110
|
+
for (const s of mod.structTypes.values())
|
|
111
|
+
if (s.typeIndex === ti) return _zeroStructConst(s, mod);
|
|
112
|
+
// An array field is a value: a null ref would trap on first use.
|
|
113
|
+
if (_arrayShape(f.goType, mod.checker))
|
|
114
|
+
return _zeroArrayConst(f.goType, mod);
|
|
115
|
+
}
|
|
116
|
+
return [{ op: "ref.null", heapType: ti ?? "any" }];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Length and element type of an array type (checker type, AST node or name).
|
|
120
|
+
function _arrayShape(t, checker) {
|
|
121
|
+
if (!t) return null;
|
|
122
|
+
if (t.kind === "array" || t.kind === "ArrayType") {
|
|
123
|
+
const size = typeof t.size === "number" ? t.size : Number(t.size?.value);
|
|
124
|
+
return Number.isInteger(size) ? { size, elem: t.elem } : null;
|
|
125
|
+
}
|
|
126
|
+
if (t.kind === "named" && t.underlying)
|
|
127
|
+
return _arrayShape(t.underlying, checker);
|
|
128
|
+
if (t.kind === "TypeName" || t.kind === "Ident")
|
|
129
|
+
return _arrayShape(checker?.types?.get(t.name), checker);
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Constant-expression zero value of a package-level array global (a null ref
|
|
134
|
+
// would trap on the first element access); null when it cannot be built.
|
|
135
|
+
function _zeroArrayConst(goType, mod, depth = 0) {
|
|
136
|
+
const checker = mod.checker;
|
|
137
|
+
if (!goType || goType.kind === "pointer" || depth > 4) return null;
|
|
138
|
+
const shape = _arrayShape(goType, checker);
|
|
139
|
+
if (!shape) return null;
|
|
140
|
+
const { size, elem } = shape;
|
|
141
|
+
const arrInfo = mod.getArrayType(elem);
|
|
142
|
+
const elemW = toWasmType(elem, checker, mod);
|
|
143
|
+
let one = null;
|
|
144
|
+
if (_arrayShape(elem, checker)) {
|
|
145
|
+
one = _zeroArrayConst(elem, mod, depth + 1);
|
|
146
|
+
if (!one) return null;
|
|
147
|
+
} else if (
|
|
148
|
+
elem?.kind !== "pointer" &&
|
|
149
|
+
elem?.kind !== "StarExpr" &&
|
|
150
|
+
typeof elemW === "object" &&
|
|
151
|
+
isStructType(elem, checker, mod)
|
|
152
|
+
) {
|
|
153
|
+
const info = Array.from(mod.structTypes.values()).find(
|
|
154
|
+
(x) => x.typeIndex === elemW.typeIndex,
|
|
155
|
+
);
|
|
156
|
+
if (!info) return null;
|
|
157
|
+
one = _zeroStructConst(info, mod);
|
|
158
|
+
if (!one) return null;
|
|
159
|
+
}
|
|
160
|
+
if (!one)
|
|
161
|
+
return [
|
|
162
|
+
{ op: "i32.const", value: size },
|
|
163
|
+
{ op: "array.new_default", typeIndex: arrInfo.typeIndex },
|
|
164
|
+
];
|
|
165
|
+
if (size > 64) return null;
|
|
166
|
+
const out = [];
|
|
167
|
+
for (let i = 0; i < size; i++) out.push(...one);
|
|
168
|
+
out.push({ op: "array.new_fixed", typeIndex: arrInfo.typeIndex, size });
|
|
169
|
+
return out;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function _collectPackageGlobals(progs, mod, checker) {
|
|
173
|
+
const pending = []; // non-constant initializers, run by the start function
|
|
174
|
+
const pendingConsts = []; // unfoldable consts; they run before any var
|
|
175
|
+
for (const p of progs) {
|
|
176
|
+
for (const d of p.decls ?? []) {
|
|
177
|
+
const isConst = d.kind === "ConstDecl";
|
|
178
|
+
if (d.kind !== "VarDecl" && !isConst) continue;
|
|
179
|
+
for (const spec of d.decls ?? d.specs ?? [d]) {
|
|
180
|
+
const names = spec.names ?? (spec.name ? [spec.name] : []);
|
|
181
|
+
const values = spec.value ?? (spec.init ? [spec.init] : []);
|
|
182
|
+
for (let i = 0; i < names.length; i++) {
|
|
183
|
+
const name = names[i];
|
|
184
|
+
// Foldable consts live in `constCache`, not in a global.
|
|
185
|
+
if (isConst && !mod.isNonLiteralConst(name)) continue;
|
|
186
|
+
const rawType =
|
|
187
|
+
spec.type ??
|
|
188
|
+
(values.length === 1 && names.length > 1
|
|
189
|
+
? values[0]?._type?.types?.[i]
|
|
190
|
+
: values[i]?._type);
|
|
191
|
+
const wType = toWasmType(rawType, checker, mod);
|
|
192
|
+
const gIdx = mod.globals.length;
|
|
193
|
+
let needsRuntimeZero = false;
|
|
194
|
+
let initInsts = [{ op: `${wType}.const`, value: 0 }];
|
|
195
|
+
if (wType === "i64") initInsts = [{ op: "i64.const", value: 0n }];
|
|
196
|
+
else if (wType === "f32" || wType === "f64")
|
|
197
|
+
initInsts = [{ op: `${wType}.const`, value: 0.0 }];
|
|
198
|
+
else if (
|
|
199
|
+
wType === "externref" ||
|
|
200
|
+
wType === "anyref" ||
|
|
201
|
+
typeof wType === "object"
|
|
202
|
+
) {
|
|
203
|
+
const sliceInfo =
|
|
204
|
+
typeof wType === "object" && typeof wType.typeIndex === "number"
|
|
205
|
+
? mod.getSliceTypeByIndex(wType.typeIndex)
|
|
206
|
+
: null;
|
|
207
|
+
if (sliceInfo) {
|
|
208
|
+
initInsts = [
|
|
209
|
+
{ op: "global.get", index: sliceInfo.emptyGlobalIndex },
|
|
210
|
+
];
|
|
211
|
+
} else if (
|
|
212
|
+
typeof wType === "object" &&
|
|
213
|
+
typeof wType.typeIndex === "number" &&
|
|
214
|
+
rawType?.kind !== "pointer"
|
|
215
|
+
) {
|
|
216
|
+
const structInfo = Array.from(mod.structTypes.values()).find(
|
|
217
|
+
(s) => s.typeIndex === wType.typeIndex,
|
|
218
|
+
);
|
|
219
|
+
const arrZero = structInfo ? null : _zeroArrayConst(rawType, mod);
|
|
220
|
+
const structZero = structInfo
|
|
221
|
+
? _zeroStructConst(structInfo, mod)
|
|
222
|
+
: null;
|
|
223
|
+
if (structZero) {
|
|
224
|
+
initInsts = structZero;
|
|
225
|
+
} else if (arrZero) {
|
|
226
|
+
initInsts = arrZero;
|
|
227
|
+
} else if (
|
|
228
|
+
(structInfo || _arrayShape(rawType, checker)) &&
|
|
229
|
+
!values[i]
|
|
230
|
+
) {
|
|
231
|
+
// Too large for a constant expression: the start
|
|
232
|
+
// function builds the zero value.
|
|
233
|
+
needsRuntimeZero = true;
|
|
234
|
+
initInsts = [
|
|
235
|
+
{ op: "ref.null", heapType: wType.typeIndex ?? "any" },
|
|
236
|
+
];
|
|
237
|
+
} else {
|
|
238
|
+
initInsts = [
|
|
239
|
+
{ op: "ref.null", heapType: wType.typeIndex ?? "any" },
|
|
240
|
+
];
|
|
241
|
+
}
|
|
242
|
+
} else {
|
|
243
|
+
initInsts = [
|
|
244
|
+
{
|
|
245
|
+
op: "ref.null",
|
|
246
|
+
heapType:
|
|
247
|
+
typeof wType === "object"
|
|
248
|
+
? (wType.heapType ?? wType.typeIndex ?? "any")
|
|
249
|
+
: wType.replace("ref", ""),
|
|
250
|
+
},
|
|
251
|
+
];
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
let litNode = values?.[i];
|
|
256
|
+
let sign = 1;
|
|
257
|
+
if (
|
|
258
|
+
litNode &&
|
|
259
|
+
litNode.kind === "UnaryExpr" &&
|
|
260
|
+
litNode.op === "-" &&
|
|
261
|
+
litNode.operand?.kind === "BasicLit"
|
|
262
|
+
) {
|
|
263
|
+
litNode = litNode.operand;
|
|
264
|
+
sign = -1;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
let isConstInit = false;
|
|
268
|
+
if (litNode && litNode.kind === "BasicLit") {
|
|
269
|
+
if (litNode.litKind === "INT") {
|
|
270
|
+
isConstInit = true;
|
|
271
|
+
const s = String(litNode.value);
|
|
272
|
+
const clean = /^0[0-7]+$/.test(s) ? `0o${s.slice(1)}` : s;
|
|
273
|
+
initInsts = [
|
|
274
|
+
wType === "i64"
|
|
275
|
+
? {
|
|
276
|
+
op: "i64.const",
|
|
277
|
+
value: BigInt(sign) * BigInt(clean),
|
|
278
|
+
}
|
|
279
|
+
: {
|
|
280
|
+
op: "i32.const",
|
|
281
|
+
value: (sign * Number(clean)) | 0,
|
|
282
|
+
},
|
|
283
|
+
];
|
|
284
|
+
} else if (litNode.litKind === "FLOAT") {
|
|
285
|
+
isConstInit = true;
|
|
286
|
+
initInsts = [
|
|
287
|
+
{
|
|
288
|
+
op: `${wType}.const`,
|
|
289
|
+
value: sign * Number(litNode.value),
|
|
290
|
+
},
|
|
291
|
+
];
|
|
292
|
+
} else if (litNode.litKind === "BOOL") {
|
|
293
|
+
isConstInit = true;
|
|
294
|
+
initInsts = [
|
|
295
|
+
{
|
|
296
|
+
op: "i32.const",
|
|
297
|
+
value: litNode.value === "true" ? 1 : 0,
|
|
298
|
+
},
|
|
299
|
+
];
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
if (needsRuntimeZero) pending.push({ name, zeroType: rawType });
|
|
303
|
+
if (!isConst && i === 0 && values.length === 1 && names.length > 1) {
|
|
304
|
+
// `var q, r = pair()`: one call fills every name.
|
|
305
|
+
pending.push({ name, names, expr: values[0] });
|
|
306
|
+
}
|
|
307
|
+
if (values[i] && !isConstInit && names.length === values.length) {
|
|
308
|
+
(isConst ? pendingConsts : pending).push({
|
|
309
|
+
name,
|
|
310
|
+
expr: values[i],
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
mod.globals.push({
|
|
314
|
+
type: wType,
|
|
315
|
+
mutable: true,
|
|
316
|
+
init: initInsts,
|
|
317
|
+
});
|
|
318
|
+
mod.globalCache.set(name, {
|
|
319
|
+
index: gIdx,
|
|
320
|
+
type: wType,
|
|
321
|
+
goType: rawType,
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
// Initialisers run in dependency order (Go spec), not declaration order.
|
|
328
|
+
const rank = new Map();
|
|
329
|
+
for (const e of orderPackageInits(progs).vars)
|
|
330
|
+
for (const n of e.names) rank.set(n, rank.size);
|
|
331
|
+
pending.sort((a, b) => (rank.get(a.name) ?? 0) - (rank.get(b.name) ?? 0));
|
|
332
|
+
return [..._orderConstInits(pendingConsts), ...pending];
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Orders runtime-initialised consts so each runs after the consts it
|
|
336
|
+
// references (Go allows a constant to use one declared later).
|
|
337
|
+
export function _orderConstInits(entries) {
|
|
338
|
+
const byName = new Map(entries.map((e) => [e.name, e]));
|
|
339
|
+
const ordered = [];
|
|
340
|
+
const state = new Map();
|
|
341
|
+
const visit = (entry) => {
|
|
342
|
+
if (state.has(entry.name)) return;
|
|
343
|
+
state.set(entry.name, true);
|
|
344
|
+
_someNode(entry.expr, (n) => {
|
|
345
|
+
if (n.kind === "Ident" && byName.has(n.name)) visit(byName.get(n.name));
|
|
346
|
+
return false;
|
|
347
|
+
});
|
|
348
|
+
ordered.push(entry);
|
|
349
|
+
};
|
|
350
|
+
for (const e of entries) visit(e);
|
|
351
|
+
return ordered;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Synthesises `__init_globals()` assigning every non-constant package-level
|
|
355
|
+
// initializer in declaration order and invoking package `init()` functions;
|
|
356
|
+
// registered as the module start function.
|
|
357
|
+
export function _makeGlobalInitFunc(pending, initNames = []) {
|
|
358
|
+
const stmts = pending.map(({ name, names, expr, zeroType }) =>
|
|
359
|
+
zeroType
|
|
360
|
+
? { kind: "GlobalZeroInit", name, goType: zeroType }
|
|
361
|
+
: {
|
|
362
|
+
kind: "AssignStmt",
|
|
363
|
+
lhs: names
|
|
364
|
+
? names.map((n, i) => ({
|
|
365
|
+
kind: "Ident",
|
|
366
|
+
name: n,
|
|
367
|
+
_type: expr._type?.types?.[i],
|
|
368
|
+
}))
|
|
369
|
+
: [{ kind: "Ident", name, _type: expr._type }],
|
|
370
|
+
op: "=",
|
|
371
|
+
rhs: [expr],
|
|
372
|
+
},
|
|
373
|
+
);
|
|
374
|
+
for (const initName of initNames) {
|
|
375
|
+
stmts.push({
|
|
376
|
+
kind: "ExprStmt",
|
|
377
|
+
expr: {
|
|
378
|
+
kind: "CallExpr",
|
|
379
|
+
func: { kind: "Ident", name: initName },
|
|
380
|
+
args: [],
|
|
381
|
+
},
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
stmts.push({ kind: "ReturnStmt", values: [] });
|
|
385
|
+
return {
|
|
386
|
+
kind: "FuncDecl",
|
|
387
|
+
name: "__init_globals",
|
|
388
|
+
params: [],
|
|
389
|
+
returnType: null,
|
|
390
|
+
body: { kind: "Block", stmts, list: stmts },
|
|
391
|
+
_isGlobalInit: true,
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
export function _makeInterfaceDispatcher(info, checker) {
|
|
396
|
+
let methodSig = info.methodSig;
|
|
397
|
+
if (!methodSig) {
|
|
398
|
+
const raw =
|
|
399
|
+
info.ifaceType?.kind === "named" && info.ifaceType.underlying
|
|
400
|
+
? info.ifaceType.underlying
|
|
401
|
+
: info.ifaceType;
|
|
402
|
+
const ifaceResolved = checker?.resolveType?.(raw) ?? raw;
|
|
403
|
+
const methods =
|
|
404
|
+
ifaceResolved?.methods ?? ifaceResolved?.underlying?.methods;
|
|
405
|
+
if (methods instanceof Map) {
|
|
406
|
+
methodSig = methods.get(info.methodName);
|
|
407
|
+
} else if (Array.isArray(methods)) {
|
|
408
|
+
methodSig = methods.find((m) => (m.name ?? m) === info.methodName);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const params = [{ name: "__recv", type: { kind: "basic", name: "any" } }];
|
|
413
|
+
if (methodSig?.params) {
|
|
414
|
+
const sigParams = variadicAsSlice(methodSig.params, methodSig.variadic);
|
|
415
|
+
for (let p = 0; p < sigParams.length; p++) {
|
|
416
|
+
params.push({ name: `__arg${p}`, type: sigParams[p] });
|
|
417
|
+
}
|
|
418
|
+
} else if (info.callNode?.args) {
|
|
419
|
+
for (let p = 0; p < info.callNode.args.length; p++) {
|
|
420
|
+
params.push({
|
|
421
|
+
name: `__arg${p}`,
|
|
422
|
+
type: info.callNode.args[p]._type ?? {
|
|
423
|
+
kind: "basic",
|
|
424
|
+
name: "any",
|
|
425
|
+
},
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
let returnType = info.callNode?._type ?? null;
|
|
431
|
+
if (methodSig?.returns) {
|
|
432
|
+
returnType =
|
|
433
|
+
methodSig.returns.length === 1
|
|
434
|
+
? methodSig.returns[0]
|
|
435
|
+
: methodSig.returns.length > 1
|
|
436
|
+
? { kind: "tuple", types: methodSig.returns }
|
|
437
|
+
: null;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
return {
|
|
441
|
+
kind: "FuncDecl",
|
|
442
|
+
name: `__dispatch_${info.ifaceName}_${info.methodName}`,
|
|
443
|
+
params,
|
|
444
|
+
returnType,
|
|
445
|
+
body: { kind: "Block", stmts: [], list: [] },
|
|
446
|
+
_isInterfaceDispatcher: true,
|
|
447
|
+
_ifaceName: info.ifaceName,
|
|
448
|
+
_ifaceType: info.ifaceType,
|
|
449
|
+
_methodName: info.methodName,
|
|
450
|
+
_rootFuncDecl: null,
|
|
451
|
+
};
|
|
452
|
+
}
|