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
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
// Maps GoFront types to WasmGC types and manages type registry.
|
|
3
3
|
|
|
4
4
|
import { SHARED_KINDS } from "../../typechecker/stdlib/shared.js";
|
|
5
|
+
import { isComplex, isComplex64 } from "../../typechecker/types.js";
|
|
6
|
+
|
|
7
|
+
export { isComplex, isComplex64 };
|
|
5
8
|
|
|
6
9
|
// Name of the runtime's error struct (`errors.New`, `fmt.Errorf`, strconv).
|
|
7
10
|
export const ERROR_STRUCT = "__errorString";
|
|
@@ -15,8 +18,14 @@ export function isTestingT(goType) {
|
|
|
15
18
|
? goType.base
|
|
16
19
|
: goType.kind === "StarExpr"
|
|
17
20
|
? (goType.expr ?? goType.operand)
|
|
18
|
-
:
|
|
19
|
-
|
|
21
|
+
: goType;
|
|
22
|
+
const name = base?.name ?? (typeof base === "string" ? base : null);
|
|
23
|
+
return (
|
|
24
|
+
name === "testing.T" ||
|
|
25
|
+
name === "testing.B" ||
|
|
26
|
+
name === "testing.M" ||
|
|
27
|
+
name === "testing.TB"
|
|
28
|
+
);
|
|
20
29
|
}
|
|
21
30
|
|
|
22
31
|
export function toWasmType(goType, checker = null, mod = null) {
|
|
@@ -27,6 +36,7 @@ export function toWasmType(goType, checker = null, mod = null) {
|
|
|
27
36
|
goType === "i64" ||
|
|
28
37
|
goType === "f32" ||
|
|
29
38
|
goType === "f64" ||
|
|
39
|
+
goType === "v128" ||
|
|
30
40
|
goType === "anyref" ||
|
|
31
41
|
goType === "externref" ||
|
|
32
42
|
goType === "funcref"
|
|
@@ -39,6 +49,16 @@ export function toWasmType(goType, checker = null, mod = null) {
|
|
|
39
49
|
}
|
|
40
50
|
if (isTestingT(goType)) return "externref";
|
|
41
51
|
|
|
52
|
+
if (
|
|
53
|
+
goType.kind === "simd" ||
|
|
54
|
+
(goType.kind === "named" &&
|
|
55
|
+
(goType.name === "simd.F32x4" ||
|
|
56
|
+
goType.name === "F32x4" ||
|
|
57
|
+
goType.underlying?.kind === "simd"))
|
|
58
|
+
) {
|
|
59
|
+
return "v128";
|
|
60
|
+
}
|
|
61
|
+
|
|
42
62
|
// Pointer AST nodes
|
|
43
63
|
if (goType.kind === "PointerType") {
|
|
44
64
|
return toWasmType({ kind: "pointer", base: goType.base }, checker, mod);
|
|
@@ -68,6 +88,9 @@ export function toWasmType(goType, checker = null, mod = null) {
|
|
|
68
88
|
|
|
69
89
|
// TypeName AST node
|
|
70
90
|
if (goType.kind === "TypeName" || goType.kind === "Ident") {
|
|
91
|
+
if (goType.name === "simd.F32x4" || goType.name === "F32x4") {
|
|
92
|
+
return "v128";
|
|
93
|
+
}
|
|
71
94
|
if (mod?.getStructType(goType.name)) {
|
|
72
95
|
return {
|
|
73
96
|
kind: "ref",
|
|
@@ -84,16 +107,32 @@ export function toWasmType(goType, checker = null, mod = null) {
|
|
|
84
107
|
|
|
85
108
|
// Unwrap named types
|
|
86
109
|
if (goType.kind === "named") {
|
|
87
|
-
if (
|
|
110
|
+
if (
|
|
111
|
+
goType.name === "simd.F32x4" ||
|
|
112
|
+
goType.name === "F32x4" ||
|
|
113
|
+
goType.underlying?.kind === "simd"
|
|
114
|
+
) {
|
|
115
|
+
return "v128";
|
|
116
|
+
}
|
|
117
|
+
// An instance written `Box[int]` is the monomorphised struct `Box$int`.
|
|
118
|
+
const instance = goType.typeArgs?.length
|
|
119
|
+
? mod?.getStructType(`${goType.name}$${instanceArgsKey(goType.typeArgs)}`)
|
|
120
|
+
: null;
|
|
121
|
+
const struct = instance ?? mod?.getStructType(goType.name);
|
|
122
|
+
if (struct) {
|
|
88
123
|
return {
|
|
89
124
|
kind: "ref",
|
|
90
125
|
nullable: true,
|
|
91
|
-
typeIndex:
|
|
126
|
+
typeIndex: struct.typeIndex,
|
|
92
127
|
};
|
|
93
128
|
}
|
|
94
129
|
if (goType.underlying) {
|
|
95
130
|
return toWasmType(goType.underlying, checker, mod);
|
|
96
131
|
}
|
|
132
|
+
// A bare `{ kind: "named", name }` (method receivers): look it up.
|
|
133
|
+
const resolved = checker?.types?.get(goType.name);
|
|
134
|
+
if (resolved?.underlying)
|
|
135
|
+
return toWasmType(resolved.underlying, checker, mod);
|
|
97
136
|
}
|
|
98
137
|
|
|
99
138
|
// Untyped constants
|
|
@@ -107,6 +146,17 @@ export function toWasmType(goType, checker = null, mod = null) {
|
|
|
107
146
|
return "externref";
|
|
108
147
|
case "bool":
|
|
109
148
|
return "i32";
|
|
149
|
+
case "complex128":
|
|
150
|
+
if (mod?.getComplexType) {
|
|
151
|
+
const info = mod.getComplexType("complex128");
|
|
152
|
+
return {
|
|
153
|
+
kind: "ref",
|
|
154
|
+
nullable: true,
|
|
155
|
+
typeIndex: info.typeIndex,
|
|
156
|
+
typeName: info.name,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
return { kind: "ref", nullable: true, heapType: "struct" };
|
|
110
160
|
default:
|
|
111
161
|
return "i64";
|
|
112
162
|
}
|
|
@@ -138,6 +188,32 @@ export function toWasmType(goType, checker = null, mod = null) {
|
|
|
138
188
|
case "float64":
|
|
139
189
|
return "f64";
|
|
140
190
|
|
|
191
|
+
case "complex64": {
|
|
192
|
+
if (mod?.getComplexType) {
|
|
193
|
+
const info = mod.getComplexType("complex64");
|
|
194
|
+
return {
|
|
195
|
+
kind: "ref",
|
|
196
|
+
nullable: true,
|
|
197
|
+
typeIndex: info.typeIndex,
|
|
198
|
+
typeName: info.name,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
return { kind: "ref", nullable: true, heapType: "struct" };
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
case "complex128": {
|
|
205
|
+
if (mod?.getComplexType) {
|
|
206
|
+
const info = mod.getComplexType("complex128");
|
|
207
|
+
return {
|
|
208
|
+
kind: "ref",
|
|
209
|
+
nullable: true,
|
|
210
|
+
typeIndex: info.typeIndex,
|
|
211
|
+
typeName: info.name,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
return { kind: "ref", nullable: true, heapType: "struct" };
|
|
215
|
+
}
|
|
216
|
+
|
|
141
217
|
case "string":
|
|
142
218
|
return "externref";
|
|
143
219
|
|
|
@@ -160,14 +236,32 @@ export function toWasmType(goType, checker = null, mod = null) {
|
|
|
160
236
|
if (
|
|
161
237
|
typeof baseWType === "object" &&
|
|
162
238
|
baseWType !== null &&
|
|
163
|
-
baseWType.kind === "ref"
|
|
239
|
+
baseWType.kind === "ref" &&
|
|
240
|
+
// A pointer to a slice or map goes through the scalar box:
|
|
241
|
+
// the pointee is a replaceable reference value.
|
|
242
|
+
!isReplaceableRef(goType.base, checker)
|
|
164
243
|
) {
|
|
165
244
|
return baseWType;
|
|
166
245
|
}
|
|
167
|
-
if (mod?.
|
|
246
|
+
if (mod?.getPtrType) {
|
|
247
|
+
const ptr = mod.getPtrType(goType.base);
|
|
248
|
+
if (mod.getBoxType) mod.getBoxType(goType.base);
|
|
249
|
+
if (ptr)
|
|
250
|
+
return {
|
|
251
|
+
kind: "ref",
|
|
252
|
+
nullable: true,
|
|
253
|
+
typeIndex: ptr.typeIndex,
|
|
254
|
+
typeName: ptr.name,
|
|
255
|
+
};
|
|
256
|
+
} else if (mod?.getBoxType) {
|
|
168
257
|
const box = mod.getBoxType(goType.base);
|
|
169
258
|
if (box)
|
|
170
|
-
return {
|
|
259
|
+
return {
|
|
260
|
+
kind: "ref",
|
|
261
|
+
nullable: true,
|
|
262
|
+
typeIndex: box.typeIndex,
|
|
263
|
+
typeName: box.name,
|
|
264
|
+
};
|
|
171
265
|
}
|
|
172
266
|
}
|
|
173
267
|
return { kind: "ref", nullable: true, heapType: "any" };
|
|
@@ -241,10 +335,8 @@ export function toWasmType(goType, checker = null, mod = null) {
|
|
|
241
335
|
for (const n of names) rawFields.push({ name: n, goType: f.type });
|
|
242
336
|
}
|
|
243
337
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
return { kind: "ref", nullable: true, typeIndex: info.typeIndex };
|
|
247
|
-
}
|
|
338
|
+
const info = mod.getAnonStructType(rawFields);
|
|
339
|
+
return { kind: "ref", nullable: true, typeIndex: info.typeIndex };
|
|
248
340
|
}
|
|
249
341
|
return { kind: "ref", nullable: true, heapType: "struct" };
|
|
250
342
|
}
|
|
@@ -273,14 +365,19 @@ export function getInterfaceMethodList(iface) {
|
|
|
273
365
|
if (iface.methods instanceof Map) {
|
|
274
366
|
return Array.from(iface.methods.entries()).map(([name, sig]) => ({
|
|
275
367
|
name,
|
|
276
|
-
params: (
|
|
368
|
+
params: variadicAsSlice(
|
|
369
|
+
(sig.params ?? []).map((p) => p.type ?? p),
|
|
370
|
+
sig.variadic,
|
|
371
|
+
),
|
|
277
372
|
returns: (sig.returns ?? []).map((r) => r.type ?? r),
|
|
278
373
|
}));
|
|
279
374
|
}
|
|
280
375
|
if (Array.isArray(iface.methods)) {
|
|
281
376
|
return iface.methods.map((m) => ({
|
|
282
377
|
name: m.name?.value ?? m.name ?? m,
|
|
283
|
-
params: (m.params ?? []).map((p) =>
|
|
378
|
+
params: (m.params ?? []).map((p) =>
|
|
379
|
+
p.variadic ? { kind: "SliceType", elem: p.type } : (p.type ?? p),
|
|
380
|
+
),
|
|
284
381
|
returns: m.returnType
|
|
285
382
|
? m.returnType.kind === "TupleType" || m.returnType.kind === "tuple"
|
|
286
383
|
? m.returnType.types
|
|
@@ -291,6 +388,12 @@ export function getInterfaceMethodList(iface) {
|
|
|
291
388
|
return [];
|
|
292
389
|
}
|
|
293
390
|
|
|
391
|
+
// The type-argument part of a monomorphised name (see monomorph.js typeKey):
|
|
392
|
+
// the dot of a qualified argument is replaced to keep the name an identifier.
|
|
393
|
+
function instanceArgsKey(typeArgs) {
|
|
394
|
+
return typeArgs.map(goTypeKey).join("$").replaceAll(".", "_");
|
|
395
|
+
}
|
|
396
|
+
|
|
294
397
|
export function goTypeKey(t) {
|
|
295
398
|
if (!t) return "any";
|
|
296
399
|
if (typeof t === "string") {
|
|
@@ -340,6 +443,9 @@ export function goTypeKey(t) {
|
|
|
340
443
|
return `struct_${rawFields.join("_")}`;
|
|
341
444
|
}
|
|
342
445
|
if (t.kind === "named") {
|
|
446
|
+
// An instance of a generic type is the monomorphised `Box$int`.
|
|
447
|
+
if (t.name && t.typeArgs?.length && !t.name.includes("$"))
|
|
448
|
+
return `${t.name}$${instanceArgsKey(t.typeArgs)}`;
|
|
343
449
|
return t.name ?? (t.underlying ? goTypeKey(t.underlying) : "any");
|
|
344
450
|
}
|
|
345
451
|
if (t.name) return t.name;
|
|
@@ -383,6 +489,43 @@ export function isPointerToStruct(goType, checker = null, mod = null) {
|
|
|
383
489
|
return false;
|
|
384
490
|
}
|
|
385
491
|
|
|
492
|
+
// Slices, maps and pointers are reference values that an assignment through a
|
|
493
|
+
// pointer replaces (a nil map becomes a new one, `*pp = q` repoints `p`),
|
|
494
|
+
// unlike structs and arrays, whose pointer is the value's own reference.
|
|
495
|
+
function isReplaceableRef(goType, checker) {
|
|
496
|
+
let t = goType;
|
|
497
|
+
if (t?.kind === "TypeName" && checker?.types?.has(t.name))
|
|
498
|
+
t = checker.types.get(t.name);
|
|
499
|
+
if (t?.kind === "named" && t.underlying) t = t.underlying;
|
|
500
|
+
return (
|
|
501
|
+
isSliceType(goType, checker) ||
|
|
502
|
+
isMapType(goType, checker) ||
|
|
503
|
+
t?.kind === "pointer" ||
|
|
504
|
+
t?.kind === "PointerType"
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
export function isScalarType(goType, checker = null, mod = null) {
|
|
509
|
+
if (!goType) return false;
|
|
510
|
+
// A slice or map is a pointee "scalar": the pointer to it is a
|
|
511
|
+
// box, so the pointee can be replaced (`*p = make(...)`).
|
|
512
|
+
if (isReplaceableRef(goType, checker)) return true;
|
|
513
|
+
if (
|
|
514
|
+
isStructType(goType, checker, mod) ||
|
|
515
|
+
isArrayType(goType, checker) ||
|
|
516
|
+
isMapType(goType, checker) ||
|
|
517
|
+
isFuncType(goType, checker) ||
|
|
518
|
+
isComplex(goType)
|
|
519
|
+
) {
|
|
520
|
+
return false;
|
|
521
|
+
}
|
|
522
|
+
const wType = toWasmType(goType, checker, mod);
|
|
523
|
+
if (typeof wType === "object" && wType !== null && wType.kind === "ref") {
|
|
524
|
+
return false;
|
|
525
|
+
}
|
|
526
|
+
return true;
|
|
527
|
+
}
|
|
528
|
+
|
|
386
529
|
export function isSliceType(goType, checker = null) {
|
|
387
530
|
if (!goType) return false;
|
|
388
531
|
if (goType.kind === "TypeName" || goType.kind === "Ident") {
|
|
@@ -392,9 +535,38 @@ export function isSliceType(goType, checker = null) {
|
|
|
392
535
|
if (goType.kind === "named" && goType.underlying) {
|
|
393
536
|
return isSliceType(goType.underlying, checker);
|
|
394
537
|
}
|
|
538
|
+
if (goType.kind === "named" && !goType.underlying) {
|
|
539
|
+
const resolved = checker?.types?.get(goType.name);
|
|
540
|
+
if (resolved && resolved !== goType) return isSliceType(resolved, checker);
|
|
541
|
+
}
|
|
395
542
|
return goType.kind === "slice" || goType.kind === "SliceType";
|
|
396
543
|
}
|
|
397
544
|
|
|
545
|
+
export function getSliceElemType(t, checker = null) {
|
|
546
|
+
if (!t) return null;
|
|
547
|
+
if (t.elem) return t.elem;
|
|
548
|
+
if (t.underlying?.elem) return t.underlying.elem;
|
|
549
|
+
if (t.name && checker?.types?.has(t.name)) {
|
|
550
|
+
const resolved = checker.types.get(t.name);
|
|
551
|
+
return getSliceElemType(resolved, checker);
|
|
552
|
+
}
|
|
553
|
+
return null;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// `[]rune` / `[]int32` (also through named types): the target of `[]rune(s)`.
|
|
557
|
+
export function isRuneSlice(goType, checker = null) {
|
|
558
|
+
if (!isSliceType(goType, checker)) return false;
|
|
559
|
+
let t = goType;
|
|
560
|
+
while (t && !t.elem) {
|
|
561
|
+
if (t.kind === "named") t = t.underlying;
|
|
562
|
+
else if (t.kind === "TypeName" || t.kind === "Ident")
|
|
563
|
+
t = checker?.types?.get(t.name);
|
|
564
|
+
else return false;
|
|
565
|
+
}
|
|
566
|
+
const name = t?.elem?.name;
|
|
567
|
+
return name === "rune" || name === "int32";
|
|
568
|
+
}
|
|
569
|
+
|
|
398
570
|
// `gofront/shared` linear-memory buffer: returns the SHARED_KINDS entry
|
|
399
571
|
// (elem, ctor, bytes, key) or null.
|
|
400
572
|
export function getSharedInfo(goType, checker = null) {
|
|
@@ -430,6 +602,10 @@ export function isMapType(goType, checker = null) {
|
|
|
430
602
|
if (goType.kind === "named" && goType.underlying) {
|
|
431
603
|
return isMapType(goType.underlying, checker);
|
|
432
604
|
}
|
|
605
|
+
if (goType.kind === "named" && !goType.underlying) {
|
|
606
|
+
const resolved = checker?.types?.get(goType.name);
|
|
607
|
+
if (resolved && resolved !== goType) return isMapType(resolved, checker);
|
|
608
|
+
}
|
|
433
609
|
return goType.kind === "map" || goType.kind === "MapType";
|
|
434
610
|
}
|
|
435
611
|
|
|
@@ -443,6 +619,11 @@ export function getMapKeyValTypes(goType, checker = null) {
|
|
|
443
619
|
if (goType.kind === "named" && goType.underlying) {
|
|
444
620
|
return getMapKeyValTypes(goType.underlying, checker);
|
|
445
621
|
}
|
|
622
|
+
if (goType.kind === "named" && !goType.underlying) {
|
|
623
|
+
const resolved = checker?.types?.get(goType.name);
|
|
624
|
+
if (resolved && resolved !== goType)
|
|
625
|
+
return getMapKeyValTypes(resolved, checker);
|
|
626
|
+
}
|
|
446
627
|
if (goType.kind === "map" || goType.kind === "MapType") {
|
|
447
628
|
const key = goType.key ?? goType.keyType;
|
|
448
629
|
const val =
|
|
@@ -462,6 +643,10 @@ export function isStringType(goType, checker = null) {
|
|
|
462
643
|
if (goType.kind === "named") {
|
|
463
644
|
if (goType.name === "string") return true;
|
|
464
645
|
if (goType.underlying) return isStringType(goType.underlying, checker);
|
|
646
|
+
// A bare `{ kind: "named", name }` (interface candidates) resolves by name.
|
|
647
|
+
const resolved = checker?.types?.get(goType.name);
|
|
648
|
+
if (resolved && resolved !== goType && resolved.kind === "named")
|
|
649
|
+
return isStringType(resolved, checker);
|
|
465
650
|
}
|
|
466
651
|
if (goType.kind === "basic") return goType.name === "string";
|
|
467
652
|
if (goType.kind === "untyped") return goType.base === "string";
|
|
@@ -507,6 +692,14 @@ export function isInterfaceType(goType, checker = null) {
|
|
|
507
692
|
return false;
|
|
508
693
|
}
|
|
509
694
|
|
|
695
|
+
// `bool`, including the untyped boolean of a constant (`const ok = true`).
|
|
696
|
+
export function isBoolGoType(goType) {
|
|
697
|
+
return (
|
|
698
|
+
goType?.name === "bool" ||
|
|
699
|
+
(goType?.kind === "untyped" && goType.base === "bool")
|
|
700
|
+
);
|
|
701
|
+
}
|
|
702
|
+
|
|
510
703
|
// The built-in `error` interface (checker ERROR type or its name).
|
|
511
704
|
export function isErrorType(goType) {
|
|
512
705
|
if (!goType) return false;
|
|
@@ -618,7 +811,9 @@ export function getFuncSignature(goType, checker = null) {
|
|
|
618
811
|
goType.kind === "FuncLit" ||
|
|
619
812
|
goType.kind === "FuncDecl"
|
|
620
813
|
) {
|
|
621
|
-
const params = (goType.params ?? []).map((p) =>
|
|
814
|
+
const params = (goType.params ?? []).map((p) =>
|
|
815
|
+
p.variadic ? { kind: "SliceType", elem: p.type } : (p.type ?? p),
|
|
816
|
+
);
|
|
622
817
|
const returns = [];
|
|
623
818
|
if (goType.returnType) {
|
|
624
819
|
if (
|
|
@@ -651,11 +846,22 @@ export function getFuncSignature(goType, checker = null) {
|
|
|
651
846
|
returns.push(r);
|
|
652
847
|
}
|
|
653
848
|
}
|
|
654
|
-
return {
|
|
849
|
+
return {
|
|
850
|
+
params: variadicAsSlice(goType.params ?? [], goType.variadic),
|
|
851
|
+
returns,
|
|
852
|
+
};
|
|
655
853
|
}
|
|
656
854
|
return { params: [], returns: [] };
|
|
657
855
|
}
|
|
658
856
|
|
|
857
|
+
// Checker func types keep a variadic parameter's element type; in WASM the
|
|
858
|
+
// parameter is a slice (see variadic.js).
|
|
859
|
+
export function variadicAsSlice(params, variadic) {
|
|
860
|
+
if (!variadic || params.length === 0) return params;
|
|
861
|
+
const last = params[params.length - 1];
|
|
862
|
+
return [...params.slice(0, -1), { kind: "slice", elem: last }];
|
|
863
|
+
}
|
|
864
|
+
|
|
659
865
|
export function isUnhashableGoType(goType, checker = null, seen = new Set()) {
|
|
660
866
|
if (!goType) return false;
|
|
661
867
|
if (seen.has(goType)) return false;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// src/backend/wasm/variadic.js
|
|
2
|
+
// WASM has no rest parameters. A variadic `xs ...T` becomes an ordinary
|
|
3
|
+
// `[]T` parameter, and every call to a GoFront function, method or closure
|
|
4
|
+
// packs its trailing arguments into a `[]T` literal (`f(s...)` passes `s`
|
|
5
|
+
// through). Emitter-backed stdlib calls keep their raw arguments. Runs
|
|
6
|
+
// after monomorphisation, so callee types are concrete, on the WASM
|
|
7
|
+
// pipeline's own copy of the programs.
|
|
8
|
+
|
|
9
|
+
import { typeToTypeNode } from "./monomorph.js";
|
|
10
|
+
|
|
11
|
+
function variadicFuncType(t) {
|
|
12
|
+
const fn = t?.kind === "named" ? t.underlying : t;
|
|
13
|
+
return fn?.kind === "func" && fn.variadic ? fn : null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// `fmt.Sprintf(...)` or a method of an emitter-backed type (`t.Errorf(...)`
|
|
17
|
+
// on `*testing.T`): those emitters take the arguments unpacked. Emitter
|
|
18
|
+
// types are the dotted names; GoFront and Go-source types are not.
|
|
19
|
+
function isStdlibCall(call) {
|
|
20
|
+
const f = call.func;
|
|
21
|
+
if (f?.kind !== "SelectorExpr") return false;
|
|
22
|
+
let recv = f.expr?._type;
|
|
23
|
+
if (recv?.kind === "namespace") return true;
|
|
24
|
+
if (recv?.kind === "pointer") recv = recv.base;
|
|
25
|
+
return typeof recv?.name === "string" && recv.name.includes(".");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function packCallArgs(call) {
|
|
29
|
+
if (call._multiForward || isStdlibCall(call)) return false;
|
|
30
|
+
const fn = variadicFuncType(call.func?._type);
|
|
31
|
+
if (!fn) return false;
|
|
32
|
+
const n = fn.params.length;
|
|
33
|
+
const args = call.args ?? [];
|
|
34
|
+
if (args.length === n && args[n - 1]?._spread) {
|
|
35
|
+
args[n - 1]._spread = false;
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
if (args.length < n - 1) return false;
|
|
39
|
+
const elem = fn.params[n - 1];
|
|
40
|
+
const sliceType = { kind: "slice", elem };
|
|
41
|
+
// No variadic arguments: the parameter is the nil slice, as in Go.
|
|
42
|
+
const rest = args.slice(n - 1);
|
|
43
|
+
const packed =
|
|
44
|
+
rest.length === 0
|
|
45
|
+
? {
|
|
46
|
+
kind: "TypeConversion",
|
|
47
|
+
targetType: sliceType,
|
|
48
|
+
expr: { kind: "Ident", name: "nil" },
|
|
49
|
+
_type: sliceType,
|
|
50
|
+
_line: call._line,
|
|
51
|
+
_col: call._col,
|
|
52
|
+
}
|
|
53
|
+
: null;
|
|
54
|
+
call.args = [
|
|
55
|
+
...args.slice(0, n - 1),
|
|
56
|
+
packed ?? {
|
|
57
|
+
kind: "CompositeLit",
|
|
58
|
+
typeExpr: { kind: "SliceType", elem: typeToTypeNode(elem) },
|
|
59
|
+
elems: args.slice(n - 1),
|
|
60
|
+
_type: sliceType,
|
|
61
|
+
_line: call._line,
|
|
62
|
+
_col: call._col,
|
|
63
|
+
},
|
|
64
|
+
];
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function sliceVariadicParam(params) {
|
|
69
|
+
const last = params?.[params.length - 1];
|
|
70
|
+
if (!last?.variadic) return false;
|
|
71
|
+
last.type = { kind: "SliceType", elem: last.type };
|
|
72
|
+
last.variadic = false;
|
|
73
|
+
last._variadic = true; // the JS facade keeps a rest parameter
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Rewrites variadic declarations and calls in place; true if anything changed. */
|
|
78
|
+
export function desugarVariadics(node) {
|
|
79
|
+
if (!node || typeof node !== "object") return false;
|
|
80
|
+
if (Array.isArray(node)) {
|
|
81
|
+
let changed = false;
|
|
82
|
+
for (const n of node) changed = desugarVariadics(n) || changed;
|
|
83
|
+
return changed;
|
|
84
|
+
}
|
|
85
|
+
let changed = false;
|
|
86
|
+
if (Array.isArray(node.params)) changed = sliceVariadicParam(node.params);
|
|
87
|
+
if (node.kind === "CallExpr") changed = packCallArgs(node) || changed;
|
|
88
|
+
for (const key of Object.keys(node)) {
|
|
89
|
+
if (key.charCodeAt(0) === 95) continue; // "_" side tables
|
|
90
|
+
changed = desugarVariadics(node[key]) || changed;
|
|
91
|
+
}
|
|
92
|
+
return changed;
|
|
93
|
+
}
|
package/src/backend/wasm/wat.js
CHANGED
|
@@ -8,7 +8,13 @@ function formatValType(t) {
|
|
|
8
8
|
if (t.kind === "ref") {
|
|
9
9
|
const nullStr = t.nullable ? "null " : "";
|
|
10
10
|
const target =
|
|
11
|
-
t.
|
|
11
|
+
t.typeName !== undefined
|
|
12
|
+
? t.typeName.startsWith("$")
|
|
13
|
+
? t.typeName
|
|
14
|
+
: `$${t.typeName}`
|
|
15
|
+
: t.typeIndex !== undefined
|
|
16
|
+
? `$t${t.typeIndex}`
|
|
17
|
+
: (t.heapType ?? "any");
|
|
12
18
|
return `(ref ${nullStr}${target})`;
|
|
13
19
|
}
|
|
14
20
|
}
|
|
@@ -16,7 +22,11 @@ function formatValType(t) {
|
|
|
16
22
|
}
|
|
17
23
|
|
|
18
24
|
function formatTypeEntry(t, id) {
|
|
19
|
-
const typeId =
|
|
25
|
+
const typeId = t.name
|
|
26
|
+
? ` ${t.name.startsWith("$") ? t.name : `$${t.name}`}`
|
|
27
|
+
: id !== undefined
|
|
28
|
+
? ` $t${id}`
|
|
29
|
+
: "";
|
|
20
30
|
if (t.form === "rec") {
|
|
21
31
|
const inners = t.types
|
|
22
32
|
.map(
|
|
@@ -26,6 +36,7 @@ function formatTypeEntry(t, id) {
|
|
|
26
36
|
.join("\n");
|
|
27
37
|
return `(rec\n${inners}\n )`;
|
|
28
38
|
}
|
|
39
|
+
let body;
|
|
29
40
|
if (t.form === "struct") {
|
|
30
41
|
const fields = (t.fields ?? [])
|
|
31
42
|
.map((f) => {
|
|
@@ -33,19 +44,29 @@ function formatTypeEntry(t, id) {
|
|
|
33
44
|
return f.mutable ? `(field (mut ${inner}))` : `(field ${inner})`;
|
|
34
45
|
})
|
|
35
46
|
.join(" ");
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
if (t.form === "array") {
|
|
47
|
+
body = fields ? `(struct ${fields})` : `(struct)`;
|
|
48
|
+
} else if (t.form === "array") {
|
|
39
49
|
const inner = formatValType(t.elemType ?? t.type);
|
|
40
50
|
const field = t.mutable ? `(mut ${inner})` : inner;
|
|
41
|
-
|
|
51
|
+
body = `(array ${field})`;
|
|
52
|
+
} else {
|
|
53
|
+
// func
|
|
54
|
+
const params = (t.params ?? []).map(formatValType).join(" ");
|
|
55
|
+
const results = (t.results ?? []).map(formatValType).join(" ");
|
|
56
|
+
const paramStr = params ? ` (param ${params})` : "";
|
|
57
|
+
const resultStr = results ? ` (result ${results})` : "";
|
|
58
|
+
body = `(func${paramStr}${resultStr})`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (t.sub) {
|
|
62
|
+
const superStr = t.superType
|
|
63
|
+
? ` ${t.superType.startsWith("$") ? t.superType : `$${t.superType}`}`
|
|
64
|
+
: t.superTypeIndex !== undefined
|
|
65
|
+
? ` $t${t.superTypeIndex}`
|
|
66
|
+
: "";
|
|
67
|
+
return `(type${typeId} (sub${superStr} ${body}))`;
|
|
42
68
|
}
|
|
43
|
-
|
|
44
|
-
const params = (t.params ?? []).map(formatValType).join(" ");
|
|
45
|
-
const results = (t.results ?? []).map(formatValType).join(" ");
|
|
46
|
-
const paramStr = params ? ` (param ${params})` : "";
|
|
47
|
-
const resultStr = results ? ` (result ${results})` : "";
|
|
48
|
-
return `(type${typeId} (func${paramStr}${resultStr}))`;
|
|
69
|
+
return `(type${typeId} ${body})`;
|
|
49
70
|
}
|
|
50
71
|
|
|
51
72
|
function formatInstruction(inst) {
|
|
@@ -56,11 +77,13 @@ function formatInstruction(inst) {
|
|
|
56
77
|
case "loop":
|
|
57
78
|
case "if": {
|
|
58
79
|
const bt = inst.blockType ?? inst.resultType;
|
|
80
|
+
if (typeof bt === "number") return `${op} (type $t${bt})`;
|
|
59
81
|
const res = bt && bt !== "void" ? ` (result ${formatValType(bt)})` : "";
|
|
60
82
|
return `${op}${res}`;
|
|
61
83
|
}
|
|
62
84
|
case "br":
|
|
63
85
|
case "br_if":
|
|
86
|
+
case "br_on_null":
|
|
64
87
|
return `${op} ${inst.depth ?? inst.index ?? 0}`;
|
|
65
88
|
case "br_table": {
|
|
66
89
|
const targets = (inst.targets ?? []).join(" ");
|
|
@@ -73,7 +96,7 @@ function formatInstruction(inst) {
|
|
|
73
96
|
return `${op} (type $t${inst.typeIndex ?? 0})`;
|
|
74
97
|
case "call_ref":
|
|
75
98
|
case "return_call_ref":
|
|
76
|
-
return `${op} (
|
|
99
|
+
return `${op} ${inst.typeName ? (inst.typeName.startsWith("$") ? inst.typeName : `$${inst.typeName}`) : `$t${inst.typeIndex ?? 0}`}`;
|
|
77
100
|
case "ref.func":
|
|
78
101
|
return `ref.func ${inst.funcIndex ?? inst.index ?? 0}`;
|
|
79
102
|
case "local.get":
|
|
@@ -98,6 +121,43 @@ function formatInstruction(inst) {
|
|
|
98
121
|
case "i32.store8":
|
|
99
122
|
case "i32.store16":
|
|
100
123
|
return inst.offset ? `${op} offset=${inst.offset}` : op;
|
|
124
|
+
case "v128.load":
|
|
125
|
+
case "v128.store": {
|
|
126
|
+
const parts = [op];
|
|
127
|
+
if (inst.offset) parts.push(`offset=${inst.offset}`);
|
|
128
|
+
if (inst.align !== undefined) parts.push(`align=${1 << inst.align}`);
|
|
129
|
+
return parts.join(" ");
|
|
130
|
+
}
|
|
131
|
+
case "v128.const": {
|
|
132
|
+
if (inst.values && inst.values.length === 4) {
|
|
133
|
+
return `${op} f32x4 ${inst.values.join(" ")}`;
|
|
134
|
+
}
|
|
135
|
+
if (inst.bytes) {
|
|
136
|
+
const b = new Uint8Array(16);
|
|
137
|
+
b.set(inst.bytes.slice(0, 16));
|
|
138
|
+
const dv = new DataView(b.buffer);
|
|
139
|
+
return `${op} i32x4 ${dv.getInt32(0, true)} ${dv.getInt32(4, true)} ${dv.getInt32(8, true)} ${dv.getInt32(12, true)}`;
|
|
140
|
+
}
|
|
141
|
+
return `${op} i32x4 0 0 0 0`;
|
|
142
|
+
}
|
|
143
|
+
case "i8x16.shuffle": {
|
|
144
|
+
const lanes = (inst.lanes ?? inst.indices ?? []).slice(0, 16);
|
|
145
|
+
while (lanes.length < 16) lanes.push(0);
|
|
146
|
+
return `${op} ${lanes.join(" ")}`;
|
|
147
|
+
}
|
|
148
|
+
case "i8x16.extract_lane_s":
|
|
149
|
+
case "i8x16.extract_lane_u":
|
|
150
|
+
case "i8x16.replace_lane":
|
|
151
|
+
case "i16x8.extract_lane_s":
|
|
152
|
+
case "i16x8.extract_lane_u":
|
|
153
|
+
case "i16x8.replace_lane":
|
|
154
|
+
case "i32x4.extract_lane":
|
|
155
|
+
case "i32x4.replace_lane":
|
|
156
|
+
case "i64x2.extract_lane":
|
|
157
|
+
case "i64x2.replace_lane":
|
|
158
|
+
case "f32x4.extract_lane":
|
|
159
|
+
case "f32x4.replace_lane":
|
|
160
|
+
return `${op} ${inst.lane ?? inst.index ?? 0}`;
|
|
101
161
|
case "i32.const":
|
|
102
162
|
case "i64.const":
|
|
103
163
|
return `${op} ${inst.value ?? 0}`;
|
|
@@ -132,12 +192,12 @@ function formatInstruction(inst) {
|
|
|
132
192
|
return `${op}${res}${catches ? ` ${catches}` : ""}`;
|
|
133
193
|
}
|
|
134
194
|
case "struct.new":
|
|
135
|
-
return `struct.new ${inst.typeIndex !== undefined ? `$t${inst.typeIndex}` : 0}`;
|
|
195
|
+
return `struct.new ${inst.typeName ? (inst.typeName.startsWith("$") ? inst.typeName : `$${inst.typeName}`) : inst.typeIndex !== undefined ? `$t${inst.typeIndex}` : 0}`;
|
|
136
196
|
case "struct.get":
|
|
137
197
|
case "struct.get_s":
|
|
138
198
|
case "struct.get_u":
|
|
139
199
|
case "struct.set":
|
|
140
|
-
return `${op} ${inst.typeIndex !== undefined ? `$t${inst.typeIndex}` : 0} ${inst.fieldIndex ?? 0}`;
|
|
200
|
+
return `${op} ${inst.typeName ? (inst.typeName.startsWith("$") ? inst.typeName : `$${inst.typeName}`) : inst.typeIndex !== undefined ? `$t${inst.typeIndex}` : 0} ${inst.fieldIndex ?? 0}`;
|
|
141
201
|
case "array.new":
|
|
142
202
|
case "array.new_default":
|
|
143
203
|
case "array.get":
|
|
@@ -156,11 +216,15 @@ function formatInstruction(inst) {
|
|
|
156
216
|
case "ref.cast":
|
|
157
217
|
case "ref.cast_null": {
|
|
158
218
|
const ht =
|
|
159
|
-
inst.
|
|
160
|
-
?
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
219
|
+
inst.typeName !== undefined
|
|
220
|
+
? inst.typeName.startsWith("$")
|
|
221
|
+
? inst.typeName
|
|
222
|
+
: `$${inst.typeName}`
|
|
223
|
+
: inst.typeIndex !== undefined
|
|
224
|
+
? `$t${inst.typeIndex}`
|
|
225
|
+
: inst.heapType !== undefined
|
|
226
|
+
? String(inst.heapType)
|
|
227
|
+
: 0;
|
|
164
228
|
return `${op} ${ht}`;
|
|
165
229
|
}
|
|
166
230
|
case "any.convert_extern":
|