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
|
@@ -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) {
|
|
@@ -105,16 +114,25 @@ export function toWasmType(goType, checker = null, mod = null) {
|
|
|
105
114
|
) {
|
|
106
115
|
return "v128";
|
|
107
116
|
}
|
|
108
|
-
|
|
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) {
|
|
109
123
|
return {
|
|
110
124
|
kind: "ref",
|
|
111
125
|
nullable: true,
|
|
112
|
-
typeIndex:
|
|
126
|
+
typeIndex: struct.typeIndex,
|
|
113
127
|
};
|
|
114
128
|
}
|
|
115
129
|
if (goType.underlying) {
|
|
116
130
|
return toWasmType(goType.underlying, checker, mod);
|
|
117
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);
|
|
118
136
|
}
|
|
119
137
|
|
|
120
138
|
// Untyped constants
|
|
@@ -128,6 +146,17 @@ export function toWasmType(goType, checker = null, mod = null) {
|
|
|
128
146
|
return "externref";
|
|
129
147
|
case "bool":
|
|
130
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" };
|
|
131
160
|
default:
|
|
132
161
|
return "i64";
|
|
133
162
|
}
|
|
@@ -159,6 +188,32 @@ export function toWasmType(goType, checker = null, mod = null) {
|
|
|
159
188
|
case "float64":
|
|
160
189
|
return "f64";
|
|
161
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
|
+
|
|
162
217
|
case "string":
|
|
163
218
|
return "externref";
|
|
164
219
|
|
|
@@ -181,14 +236,32 @@ export function toWasmType(goType, checker = null, mod = null) {
|
|
|
181
236
|
if (
|
|
182
237
|
typeof baseWType === "object" &&
|
|
183
238
|
baseWType !== null &&
|
|
184
|
-
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)
|
|
185
243
|
) {
|
|
186
244
|
return baseWType;
|
|
187
245
|
}
|
|
188
|
-
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) {
|
|
189
257
|
const box = mod.getBoxType(goType.base);
|
|
190
258
|
if (box)
|
|
191
|
-
return {
|
|
259
|
+
return {
|
|
260
|
+
kind: "ref",
|
|
261
|
+
nullable: true,
|
|
262
|
+
typeIndex: box.typeIndex,
|
|
263
|
+
typeName: box.name,
|
|
264
|
+
};
|
|
192
265
|
}
|
|
193
266
|
}
|
|
194
267
|
return { kind: "ref", nullable: true, heapType: "any" };
|
|
@@ -262,10 +335,8 @@ export function toWasmType(goType, checker = null, mod = null) {
|
|
|
262
335
|
for (const n of names) rawFields.push({ name: n, goType: f.type });
|
|
263
336
|
}
|
|
264
337
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
return { kind: "ref", nullable: true, typeIndex: info.typeIndex };
|
|
268
|
-
}
|
|
338
|
+
const info = mod.getAnonStructType(rawFields);
|
|
339
|
+
return { kind: "ref", nullable: true, typeIndex: info.typeIndex };
|
|
269
340
|
}
|
|
270
341
|
return { kind: "ref", nullable: true, heapType: "struct" };
|
|
271
342
|
}
|
|
@@ -294,14 +365,19 @@ export function getInterfaceMethodList(iface) {
|
|
|
294
365
|
if (iface.methods instanceof Map) {
|
|
295
366
|
return Array.from(iface.methods.entries()).map(([name, sig]) => ({
|
|
296
367
|
name,
|
|
297
|
-
params: (
|
|
368
|
+
params: variadicAsSlice(
|
|
369
|
+
(sig.params ?? []).map((p) => p.type ?? p),
|
|
370
|
+
sig.variadic,
|
|
371
|
+
),
|
|
298
372
|
returns: (sig.returns ?? []).map((r) => r.type ?? r),
|
|
299
373
|
}));
|
|
300
374
|
}
|
|
301
375
|
if (Array.isArray(iface.methods)) {
|
|
302
376
|
return iface.methods.map((m) => ({
|
|
303
377
|
name: m.name?.value ?? m.name ?? m,
|
|
304
|
-
params: (m.params ?? []).map((p) =>
|
|
378
|
+
params: (m.params ?? []).map((p) =>
|
|
379
|
+
p.variadic ? { kind: "SliceType", elem: p.type } : (p.type ?? p),
|
|
380
|
+
),
|
|
305
381
|
returns: m.returnType
|
|
306
382
|
? m.returnType.kind === "TupleType" || m.returnType.kind === "tuple"
|
|
307
383
|
? m.returnType.types
|
|
@@ -312,6 +388,12 @@ export function getInterfaceMethodList(iface) {
|
|
|
312
388
|
return [];
|
|
313
389
|
}
|
|
314
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
|
+
|
|
315
397
|
export function goTypeKey(t) {
|
|
316
398
|
if (!t) return "any";
|
|
317
399
|
if (typeof t === "string") {
|
|
@@ -361,6 +443,9 @@ export function goTypeKey(t) {
|
|
|
361
443
|
return `struct_${rawFields.join("_")}`;
|
|
362
444
|
}
|
|
363
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)}`;
|
|
364
449
|
return t.name ?? (t.underlying ? goTypeKey(t.underlying) : "any");
|
|
365
450
|
}
|
|
366
451
|
if (t.name) return t.name;
|
|
@@ -404,6 +489,43 @@ export function isPointerToStruct(goType, checker = null, mod = null) {
|
|
|
404
489
|
return false;
|
|
405
490
|
}
|
|
406
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
|
+
|
|
407
529
|
export function isSliceType(goType, checker = null) {
|
|
408
530
|
if (!goType) return false;
|
|
409
531
|
if (goType.kind === "TypeName" || goType.kind === "Ident") {
|
|
@@ -413,9 +535,38 @@ export function isSliceType(goType, checker = null) {
|
|
|
413
535
|
if (goType.kind === "named" && goType.underlying) {
|
|
414
536
|
return isSliceType(goType.underlying, checker);
|
|
415
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
|
+
}
|
|
416
542
|
return goType.kind === "slice" || goType.kind === "SliceType";
|
|
417
543
|
}
|
|
418
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
|
+
|
|
419
570
|
// `gofront/shared` linear-memory buffer: returns the SHARED_KINDS entry
|
|
420
571
|
// (elem, ctor, bytes, key) or null.
|
|
421
572
|
export function getSharedInfo(goType, checker = null) {
|
|
@@ -451,6 +602,10 @@ export function isMapType(goType, checker = null) {
|
|
|
451
602
|
if (goType.kind === "named" && goType.underlying) {
|
|
452
603
|
return isMapType(goType.underlying, checker);
|
|
453
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
|
+
}
|
|
454
609
|
return goType.kind === "map" || goType.kind === "MapType";
|
|
455
610
|
}
|
|
456
611
|
|
|
@@ -464,6 +619,11 @@ export function getMapKeyValTypes(goType, checker = null) {
|
|
|
464
619
|
if (goType.kind === "named" && goType.underlying) {
|
|
465
620
|
return getMapKeyValTypes(goType.underlying, checker);
|
|
466
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
|
+
}
|
|
467
627
|
if (goType.kind === "map" || goType.kind === "MapType") {
|
|
468
628
|
const key = goType.key ?? goType.keyType;
|
|
469
629
|
const val =
|
|
@@ -483,6 +643,10 @@ export function isStringType(goType, checker = null) {
|
|
|
483
643
|
if (goType.kind === "named") {
|
|
484
644
|
if (goType.name === "string") return true;
|
|
485
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);
|
|
486
650
|
}
|
|
487
651
|
if (goType.kind === "basic") return goType.name === "string";
|
|
488
652
|
if (goType.kind === "untyped") return goType.base === "string";
|
|
@@ -528,6 +692,14 @@ export function isInterfaceType(goType, checker = null) {
|
|
|
528
692
|
return false;
|
|
529
693
|
}
|
|
530
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
|
+
|
|
531
703
|
// The built-in `error` interface (checker ERROR type or its name).
|
|
532
704
|
export function isErrorType(goType) {
|
|
533
705
|
if (!goType) return false;
|
|
@@ -639,7 +811,9 @@ export function getFuncSignature(goType, checker = null) {
|
|
|
639
811
|
goType.kind === "FuncLit" ||
|
|
640
812
|
goType.kind === "FuncDecl"
|
|
641
813
|
) {
|
|
642
|
-
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
|
+
);
|
|
643
817
|
const returns = [];
|
|
644
818
|
if (goType.returnType) {
|
|
645
819
|
if (
|
|
@@ -672,11 +846,22 @@ export function getFuncSignature(goType, checker = null) {
|
|
|
672
846
|
returns.push(r);
|
|
673
847
|
}
|
|
674
848
|
}
|
|
675
|
-
return {
|
|
849
|
+
return {
|
|
850
|
+
params: variadicAsSlice(goType.params ?? [], goType.variadic),
|
|
851
|
+
returns,
|
|
852
|
+
};
|
|
676
853
|
}
|
|
677
854
|
return { params: [], returns: [] };
|
|
678
855
|
}
|
|
679
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
|
+
|
|
680
865
|
export function isUnhashableGoType(goType, checker = null, seen = new Set()) {
|
|
681
866
|
if (!goType) return false;
|
|
682
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":
|
|
@@ -169,12 +192,12 @@ function formatInstruction(inst) {
|
|
|
169
192
|
return `${op}${res}${catches ? ` ${catches}` : ""}`;
|
|
170
193
|
}
|
|
171
194
|
case "struct.new":
|
|
172
|
-
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}`;
|
|
173
196
|
case "struct.get":
|
|
174
197
|
case "struct.get_s":
|
|
175
198
|
case "struct.get_u":
|
|
176
199
|
case "struct.set":
|
|
177
|
-
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}`;
|
|
178
201
|
case "array.new":
|
|
179
202
|
case "array.new_default":
|
|
180
203
|
case "array.get":
|
|
@@ -193,11 +216,15 @@ function formatInstruction(inst) {
|
|
|
193
216
|
case "ref.cast":
|
|
194
217
|
case "ref.cast_null": {
|
|
195
218
|
const ht =
|
|
196
|
-
inst.
|
|
197
|
-
?
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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;
|
|
201
228
|
return `${op} ${ht}`;
|
|
202
229
|
}
|
|
203
230
|
case "any.convert_extern":
|