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
|
@@ -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
|
|
@@ -418,11 +419,26 @@ const SIMD_OPCODES = {
|
|
|
418
419
|
"i64x2.replace_lane": 30,
|
|
419
420
|
"f32x4.extract_lane": 31,
|
|
420
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,
|
|
421
429
|
"v128.not": 77,
|
|
422
430
|
"v128.and": 78,
|
|
423
431
|
"v128.andnot": 79,
|
|
424
432
|
"v128.or": 80,
|
|
425
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,
|
|
426
442
|
"f32x4.abs": 224,
|
|
427
443
|
"f32x4.neg": 225,
|
|
428
444
|
"f32x4.sqrt": 227,
|
|
@@ -579,6 +595,7 @@ function encodeInstruction(inst) {
|
|
|
579
595
|
|
|
580
596
|
case "br":
|
|
581
597
|
case "br_if":
|
|
598
|
+
case "br_on_null":
|
|
582
599
|
return [byte, ...encodeU32LEB(inst.depth ?? inst.index ?? 0)];
|
|
583
600
|
|
|
584
601
|
case "br_table": {
|
|
@@ -703,35 +720,48 @@ function encodeTypeEntry(t) {
|
|
|
703
720
|
return [0x4e, ...count, ...innerBytes];
|
|
704
721
|
}
|
|
705
722
|
|
|
723
|
+
let compositeBytes;
|
|
706
724
|
if (t.form === "struct") {
|
|
707
725
|
// Struct type: 0x5F, vector of fields [valtype, mutability]
|
|
708
726
|
const fields = t.fields ?? [];
|
|
709
|
-
|
|
727
|
+
compositeBytes = [
|
|
710
728
|
0x5f,
|
|
711
729
|
...encodeVector(fields, (f) => [
|
|
712
730
|
...encodeValType(f.type),
|
|
713
731
|
f.mutable ? 0x01 : 0x00,
|
|
714
732
|
]),
|
|
715
733
|
];
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
if (t.form === "array") {
|
|
734
|
+
} else if (t.form === "array") {
|
|
719
735
|
// Array type: 0x5E, field [valtype, mutability]
|
|
720
|
-
|
|
736
|
+
compositeBytes = [
|
|
721
737
|
0x5e,
|
|
722
738
|
...encodeValType(t.elemType ?? t.type),
|
|
723
739
|
t.mutable ? 0x01 : 0x00,
|
|
724
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
|
+
];
|
|
725
750
|
}
|
|
726
751
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
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;
|
|
735
765
|
}
|
|
736
766
|
|
|
737
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
|
+
}
|