gofront 1.8.0 → 1.9.1
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 +3 -3
- package/package.json +6 -5
- package/src/backend/js/expressions.js +1157 -408
- package/src/backend/js/index.js +327 -153
- package/src/backend/js/natives.js +383 -0
- package/src/backend/js/runtime.js +559 -2552
- package/src/backend/js/statements.js +670 -158
- package/src/backend/js/stdlib/index.js +45 -93
- package/src/backend/js/templ.js +32 -45
- package/src/backend/js/type-info.js +298 -19
- package/src/backend/shared/fixed-float.js +20 -0
- package/src/backend/shared/fmt-types.js +91 -170
- package/src/backend/shared/host-unicode.js +36 -0
- package/src/backend/wasm/boundary.js +490 -965
- package/src/backend/wasm/emit-builtins.js +884 -256
- package/src/backend/wasm/emit-exprs.js +185 -77
- package/src/backend/wasm/emit-maps.js +17 -367
- package/src/backend/wasm/emit-shared.js +4 -1
- package/src/backend/wasm/emit-stmts.js +524 -240
- package/src/backend/wasm/emit.js +176 -38
- package/src/backend/wasm/globals.js +48 -8
- package/src/backend/wasm/glue.js +2 -4
- package/src/backend/wasm/index.js +44 -530
- package/src/backend/wasm/method-expr.js +12 -1
- package/src/backend/wasm/module-emitter.js +299 -770
- package/src/backend/wasm/monomorph.js +139 -8
- package/src/backend/wasm/natives.js +600 -0
- package/src/backend/wasm/prescan.js +5 -153
- package/src/backend/wasm/prune-funcs.js +89 -0
- package/src/backend/wasm/stdlib/index.js +2 -32
- package/src/backend/wasm/types.js +97 -20
- package/src/backend/wasm/variadic.js +9 -4
- package/src/cli/cli-core.js +5 -1
- package/src/cli/test-runner.js +115 -342
- package/src/compiler.js +113 -130
- package/src/lexer/lexer.js +106 -11
- package/src/lexer/templ-lexer.js +61 -18
- package/src/lexer/tokens.js +1 -0
- package/src/link/stdlib.js +355 -132
- package/src/link/type-names.js +204 -0
- package/src/link/wasm.js +30 -6
- package/src/lower/bounds.js +153 -0
- package/src/lower/captures.js +92 -33
- package/src/lower/embedding.js +62 -5
- package/src/lower/errors-as.js +196 -0
- package/src/lower/fmt-formatters.js +929 -0
- package/src/lower/functions.js +225 -7
- package/src/lower/gen-types.js +293 -0
- package/src/lower/index.js +14 -2
- package/src/lower/json-codecs.js +334 -245
- package/src/lower/ownership.js +25 -7
- package/src/lower/reachability.js +69 -51
- package/src/lower/walk.js +12 -0
- package/src/parser/declarations.js +30 -5
- package/src/parser/expressions.js +111 -57
- package/src/parser/statements.js +108 -0
- package/src/parser/templ.js +44 -5
- package/src/parser/types.js +12 -0
- package/src/resolver.js +1 -2
- package/src/typechecker/assignability.js +214 -19
- package/src/typechecker/const-fold.js +70 -27
- package/src/typechecker/constants.js +156 -8
- package/src/typechecker/expressions.js +490 -108
- package/src/typechecker/index.js +346 -42
- package/src/typechecker/init-order.js +136 -40
- package/src/typechecker/operand-text.js +41 -0
- package/src/typechecker/resolve.js +490 -65
- package/src/typechecker/statements.js +288 -27
- package/src/typechecker/stdlib/excluded.js +9 -5
- package/src/typechecker/stdlib/index.js +25 -9
- package/src/typechecker/termination.js +10 -1
- package/src/typechecker/types.js +287 -575
- package/stdlib/bytes/buffer.go +479 -0
- package/stdlib/bytes/bytes.go +1010 -0
- package/stdlib/bytes/reader.go +161 -0
- package/stdlib/context/context.go +115 -0
- package/stdlib/crypto/rand/rand.go +34 -0
- package/stdlib/crypto/sha256/sha256.go +249 -0
- package/stdlib/encoding/base64/base64.go +331 -0
- package/stdlib/encoding/binary/binary.go +332 -0
- package/stdlib/encoding/hex/hex.go +140 -0
- package/stdlib/encoding/json/json.go +62 -0
- package/stdlib/errors/errors.go +174 -0
- package/stdlib/fmt/errors.go +110 -0
- package/stdlib/fmt/format.go +732 -0
- package/stdlib/fmt/print.go +1232 -0
- package/stdlib/gom/gom.go +293 -0
- package/stdlib/html/html.go +251 -0
- package/stdlib/internal/fmtrt/fmtrt.go +163 -0
- package/stdlib/internal/jsonrt/dec.go +171 -38
- package/stdlib/internal/jsonrt/enc.go +186 -36
- package/stdlib/internal/jsonrt/scanner.go +17 -8
- package/stdlib/io/io.go +157 -1
- package/stdlib/maps/maps.go +131 -0
- package/stdlib/math/bits/bits.go +507 -0
- package/stdlib/math/extra.go +16 -14
- package/stdlib/math/math.go +439 -0
- package/stdlib/math/rand/rand.go +297 -0
- package/stdlib/os/os.go +93 -0
- package/stdlib/path/filepath/filepath.go +47 -0
- package/stdlib/path/path.go +394 -0
- package/stdlib/regexp/regexp.go +231 -0
- package/stdlib/slices/slices.go +526 -0
- package/stdlib/slices/sort.go +209 -0
- package/stdlib/slices/zsort_cmp.go +480 -0
- package/stdlib/slices/zsort_ordered.go +482 -0
- package/stdlib/sort/sort.go +391 -452
- package/stdlib/sort/zsort_func.go +480 -0
- package/stdlib/sort/zsort_iface.go +480 -0
- package/stdlib/strconv/float.go +1699 -0
- package/stdlib/strconv/isprint.go +752 -0
- package/stdlib/strconv/quote.go +235 -50
- package/stdlib/strconv/strconv.go +616 -0
- package/stdlib/strings/builder.go +95 -0
- package/stdlib/strings/reader.go +163 -0
- package/stdlib/strings/replacer.go +102 -0
- package/stdlib/strings/strings.go +807 -0
- package/stdlib/sync/sync.go +268 -0
- package/stdlib/testing/async_js.go +61 -0
- package/stdlib/testing/benchmark.go +297 -0
- package/stdlib/testing/testing.go +523 -0
- package/stdlib/time/duration.go +233 -0
- package/stdlib/time/format.go +745 -0
- package/stdlib/time/parse.go +790 -0
- package/stdlib/time/sleep_js.go +63 -0
- package/stdlib/time/time.go +834 -0
- package/stdlib/unicode/unicode.go +141 -3
- package/stdlib/unicode/utf8/utf8.go +464 -0
- package/src/backend/js/fmt-runtime.js +0 -573
- package/src/backend/js/stdlib/base64.js +0 -12
- package/src/backend/js/stdlib/binary.js +0 -102
- package/src/backend/js/stdlib/bits.js +0 -119
- package/src/backend/js/stdlib/builder.js +0 -98
- package/src/backend/js/stdlib/bytes.js +0 -368
- package/src/backend/js/stdlib/errors.js +0 -45
- package/src/backend/js/stdlib/fmt.js +0 -517
- package/src/backend/js/stdlib/gom.js +0 -155
- package/src/backend/js/stdlib/hex.js +0 -17
- package/src/backend/js/stdlib/html.js +0 -20
- package/src/backend/js/stdlib/io.js +0 -137
- package/src/backend/js/stdlib/json.js +0 -1167
- package/src/backend/js/stdlib/maps.js +0 -37
- package/src/backend/js/stdlib/math.js +0 -85
- package/src/backend/js/stdlib/os.js +0 -13
- package/src/backend/js/stdlib/path.js +0 -48
- package/src/backend/js/stdlib/rand.js +0 -32
- package/src/backend/js/stdlib/regexp.js +0 -54
- package/src/backend/js/stdlib/sha256.js +0 -12
- package/src/backend/js/stdlib/shared.js +0 -17
- package/src/backend/js/stdlib/simd.js +0 -34
- package/src/backend/js/stdlib/slices.js +0 -107
- package/src/backend/js/stdlib/sort.js +0 -30
- package/src/backend/js/stdlib/strconv.js +0 -79
- package/src/backend/js/stdlib/strings.js +0 -301
- package/src/backend/js/stdlib/sync.js +0 -13
- package/src/backend/js/stdlib/testing.js +0 -41
- package/src/backend/js/stdlib/time.js +0 -152
- package/src/backend/js/stdlib/unicode.js +0 -25
- package/src/backend/js/stdlib/utf8.js +0 -26
- package/src/backend/wasm/stdlib/bytes.js +0 -6716
- package/src/backend/wasm/stdlib/core.js +0 -10278
- package/src/backend/wasm/stdlib/io.js +0 -3578
- package/src/backend/wasm/stdlib/packages.js +0 -2402
- package/src/backend/wasm/stdlib/pure.js +0 -720
- package/src/backend/wasm/stdlib/time.js +0 -2229
- package/src/typechecker/stdlib/core.js +0 -1079
- package/src/typechecker/stdlib/extended.js +0 -1210
- package/stdlib/bytes/iter.go +0 -192
- package/stdlib/bytes/search.go +0 -240
- package/stdlib/bytes/split.go +0 -218
- package/stdlib/errors/overlay.go +0 -12
- package/stdlib/maps/iter.go +0 -65
- package/stdlib/slices/extra.go +0 -73
- package/stdlib/slices/iter.go +0 -113
- package/stdlib/strings/extra.go +0 -35
- package/stdlib/strings/iter.go +0 -122
- package/stdlib/sync/extra.go +0 -105
- package/stdlib/unicode/utf8/bytes.go +0 -239
package/src/backend/wasm/emit.js
CHANGED
|
@@ -55,6 +55,18 @@ function sameSignature(c, params, paramTypes, want, got) {
|
|
|
55
55
|
return want === undefined || got === undefined || sameTypes(got, want);
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
// True when a struct type embeds a pointer, directly or through an embedded
|
|
59
|
+
// struct: promoted method calls read through it.
|
|
60
|
+
function _embedsPointer(t, seen = new Set()) {
|
|
61
|
+
const base = t?.kind === "pointer" ? t.base : t;
|
|
62
|
+
const st = base?.underlying ?? base;
|
|
63
|
+
if (!st || seen.has(st)) return false;
|
|
64
|
+
seen.add(st);
|
|
65
|
+
return (st._embeds ?? []).some(
|
|
66
|
+
(e) => e.kind === "pointer" || _embedsPointer(e, seen),
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
58
70
|
export class FunctionEmitter {
|
|
59
71
|
constructor(moduleEmitter, funcDecl, funcIndex) {
|
|
60
72
|
this.mod = moduleEmitter;
|
|
@@ -201,6 +213,35 @@ export class FunctionEmitter {
|
|
|
201
213
|
return targets;
|
|
202
214
|
}
|
|
203
215
|
|
|
216
|
+
// Outer stacked labels (`a: b: for`) alias the innermost one. Collected
|
|
217
|
+
// once per root function so the yield closures of a range-over-func loop
|
|
218
|
+
// (emitted by their own emitter) resolve them too.
|
|
219
|
+
_stackedLabelAlias(label) {
|
|
220
|
+
const root = this.rootFuncDecl;
|
|
221
|
+
if (root && !root._labelAliases) {
|
|
222
|
+
const aliases = new Map();
|
|
223
|
+
const scan = (node) => {
|
|
224
|
+
if (!node || typeof node !== "object") return;
|
|
225
|
+
if (Array.isArray(node)) {
|
|
226
|
+
for (const item of node) scan(item);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
if (node.kind === "LabeledStmt" && node.body?.kind === "LabeledStmt") {
|
|
230
|
+
let inner = node.body;
|
|
231
|
+
while (inner.body?.kind === "LabeledStmt") inner = inner.body;
|
|
232
|
+
aliases.set(node.label, inner.label);
|
|
233
|
+
}
|
|
234
|
+
for (const k of Object.keys(node)) {
|
|
235
|
+
if (k.startsWith("_")) continue;
|
|
236
|
+
scan(node[k]);
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
scan(root.body);
|
|
240
|
+
root._labelAliases = aliases;
|
|
241
|
+
}
|
|
242
|
+
return root?._labelAliases?.get(label) ?? this._labelAliases?.get(label);
|
|
243
|
+
}
|
|
244
|
+
|
|
204
245
|
_findLabelTarget(label, role) {
|
|
205
246
|
for (const target of this.rootFuncDecl?._labelTargets?.values() ?? []) {
|
|
206
247
|
if (target.label === label && target.role === role) return target;
|
|
@@ -781,7 +822,22 @@ export class FunctionEmitter {
|
|
|
781
822
|
// A pointer dereference or a call through a func value: sites whose nil
|
|
782
823
|
// operand raises Go's panic through the `nil` block.
|
|
783
824
|
_isNilTrapSite(node) {
|
|
784
|
-
if (node.kind === "CallExpr")
|
|
825
|
+
if (node.kind === "CallExpr")
|
|
826
|
+
return (
|
|
827
|
+
this._isFuncValueCall(node) ||
|
|
828
|
+
(node.func?.kind === "SelectorExpr" &&
|
|
829
|
+
_embedsPointer(node.func.expr?._type))
|
|
830
|
+
);
|
|
831
|
+
// Binding a method value on an interface checks it for nil.
|
|
832
|
+
if (node.kind === "NilCheckStmt") return true;
|
|
833
|
+
// A pointer to an array: indexing, slicing and ranging with a value
|
|
834
|
+
// read the array.
|
|
835
|
+
if (
|
|
836
|
+
node.kind === "IndexExpr" ||
|
|
837
|
+
node.kind === "SliceExpr" ||
|
|
838
|
+
node.kind === "RangeExpr"
|
|
839
|
+
)
|
|
840
|
+
return node.expr?._type?.kind === "pointer";
|
|
785
841
|
const star =
|
|
786
842
|
(node.kind === "UnaryExpr" && node.op === "*") ||
|
|
787
843
|
node.kind === "StarExpr";
|
|
@@ -893,11 +949,12 @@ export class FunctionEmitter {
|
|
|
893
949
|
return null;
|
|
894
950
|
}
|
|
895
951
|
|
|
896
|
-
_buildEnvFields(capturedNames) {
|
|
952
|
+
_buildEnvFields(capturedNames, funcLit) {
|
|
897
953
|
const envFields = [];
|
|
954
|
+
const types = this.captureAnalysis?.captureTypes?.get(funcLit);
|
|
898
955
|
for (const name of capturedNames) {
|
|
899
956
|
const isBoxed = this.mutatedCaptures.has(name);
|
|
900
|
-
const goType = this._findCapturedVarGoType(name);
|
|
957
|
+
const goType = types?.get(name) ?? this._findCapturedVarGoType(name);
|
|
901
958
|
const boxInfo = isBoxed ? this.mod.getBoxType(goType) : null;
|
|
902
959
|
envFields.push({
|
|
903
960
|
name,
|
|
@@ -913,7 +970,10 @@ export class FunctionEmitter {
|
|
|
913
970
|
}
|
|
914
971
|
|
|
915
972
|
_unpackClosureEnv() {
|
|
916
|
-
const envFields = this._buildEnvFields(
|
|
973
|
+
const envFields = this._buildEnvFields(
|
|
974
|
+
this.capturedNames,
|
|
975
|
+
this.funcDecl._funcLit,
|
|
976
|
+
);
|
|
917
977
|
const envInfo = this.mod.getEnvType(envFields);
|
|
918
978
|
this.envInfo = envInfo;
|
|
919
979
|
this.envFields = envFields;
|
|
@@ -1126,7 +1186,7 @@ export class FunctionEmitter {
|
|
|
1126
1186
|
const instName = (ft?.kind === "PointerType" ? ft.base : ft)?.name;
|
|
1127
1187
|
const embedInfo =
|
|
1128
1188
|
(instName && this.mod.structTypes.get(instName)) ??
|
|
1129
|
-
this.mod.structTypes.get(embed.name);
|
|
1189
|
+
this.mod.structTypes.get(embed.typeName ?? embed.name);
|
|
1130
1190
|
if (embedInfo) {
|
|
1131
1191
|
const sub = this._resolveFieldPath(embedInfo, fieldName);
|
|
1132
1192
|
if (sub) {
|
|
@@ -1381,6 +1441,7 @@ export class FunctionEmitter {
|
|
|
1381
1441
|
|
|
1382
1442
|
// 2. Iterate candidates
|
|
1383
1443
|
for (const cand of candidates) {
|
|
1444
|
+
this._emitNilPtrCandidate(cand, fn, recvLocal);
|
|
1384
1445
|
this.pushInstruction({ op: "block", blockType: "void" });
|
|
1385
1446
|
this.pushInstruction({ op: "local.get", index: recvLocal });
|
|
1386
1447
|
this.pushInstruction({ op: "ref.test", typeIndex: cand.testTypeIndex });
|
|
@@ -1402,31 +1463,9 @@ export class FunctionEmitter {
|
|
|
1402
1463
|
typeIndex: cand.testTypeIndex,
|
|
1403
1464
|
fieldIndex: 0,
|
|
1404
1465
|
});
|
|
1405
|
-
if (cand.embedPath)
|
|
1406
|
-
for (const step of cand.embedPath) {
|
|
1407
|
-
if (step.castTypeIndex) {
|
|
1408
|
-
this.pushInstruction({
|
|
1409
|
-
op: "ref.cast",
|
|
1410
|
-
typeIndex: step.castTypeIndex,
|
|
1411
|
-
});
|
|
1412
|
-
}
|
|
1413
|
-
this.pushInstruction({
|
|
1414
|
-
op: "struct.get",
|
|
1415
|
-
typeIndex: step.parentTypeIndex,
|
|
1416
|
-
fieldIndex: step.fieldIndex,
|
|
1417
|
-
});
|
|
1418
|
-
}
|
|
1419
|
-
}
|
|
1466
|
+
if (cand.embedPath) this._emitEmbedPath(cand);
|
|
1420
1467
|
} else {
|
|
1421
|
-
if (cand.embedPath)
|
|
1422
|
-
for (const step of cand.embedPath) {
|
|
1423
|
-
this.pushInstruction({
|
|
1424
|
-
op: "struct.get",
|
|
1425
|
-
typeIndex: step.parentTypeIndex,
|
|
1426
|
-
fieldIndex: step.fieldIndex,
|
|
1427
|
-
});
|
|
1428
|
-
}
|
|
1429
|
-
}
|
|
1468
|
+
if (cand.embedPath) this._emitEmbedPath(cand);
|
|
1430
1469
|
if (cand.needsValueDeref && cand.structInfo) {
|
|
1431
1470
|
this.emitCloneStruct(cand.structInfo, cand.targetRecvWType);
|
|
1432
1471
|
}
|
|
@@ -1437,7 +1476,10 @@ export class FunctionEmitter {
|
|
|
1437
1476
|
this.pushInstruction({ op: "local.get", index: p });
|
|
1438
1477
|
}
|
|
1439
1478
|
|
|
1440
|
-
this.pushInstruction({
|
|
1479
|
+
this.pushInstruction({
|
|
1480
|
+
op: "call",
|
|
1481
|
+
funcIndex: cand.selfDispatch ? fn._globalFuncIndex : cand.funcIndex,
|
|
1482
|
+
});
|
|
1441
1483
|
this.pushInstruction("return");
|
|
1442
1484
|
this.pushInstruction("end");
|
|
1443
1485
|
}
|
|
@@ -1445,6 +1487,73 @@ export class FunctionEmitter {
|
|
|
1445
1487
|
emitMiss("interface conversion: nil or unmatched type for method call");
|
|
1446
1488
|
}
|
|
1447
1489
|
|
|
1490
|
+
// Walks `cand.embedPath` from the embedder on the stack to the embedded
|
|
1491
|
+
// value. A step through a nil embedded pointer panics, except that the last
|
|
1492
|
+
// step may be nil for a pointer-receiver method (`nilCheckLast` is unset).
|
|
1493
|
+
_emitEmbedPath(cand) {
|
|
1494
|
+
const steps = cand.embedPath;
|
|
1495
|
+
for (const [i, step] of steps.entries()) {
|
|
1496
|
+
if (step.castTypeIndex) {
|
|
1497
|
+
this.pushInstruction({ op: "ref.cast", typeIndex: step.castTypeIndex });
|
|
1498
|
+
}
|
|
1499
|
+
this.pushInstruction({
|
|
1500
|
+
op: "struct.get",
|
|
1501
|
+
typeIndex: step.parentTypeIndex,
|
|
1502
|
+
fieldIndex: step.fieldIndex,
|
|
1503
|
+
});
|
|
1504
|
+
if (step.ptrWType && (i < steps.length - 1 || cand.nilCheckLast))
|
|
1505
|
+
this._emitNilPanicIfNull(step.ptrWType);
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
// Panics with a nil dereference when the reference on the stack is null,
|
|
1510
|
+
// leaving it on the stack otherwise.
|
|
1511
|
+
_emitNilPanicIfNull(wType) {
|
|
1512
|
+
const tmp = this.acquireTemp(wType);
|
|
1513
|
+
this.pushInstruction({ op: "local.tee", index: tmp });
|
|
1514
|
+
this.pushInstruction("ref.is_null");
|
|
1515
|
+
this.pushInstruction({ op: "if", blockType: "void" });
|
|
1516
|
+
this.emitPanic(
|
|
1517
|
+
"runtime error: invalid memory address or nil pointer dereference",
|
|
1518
|
+
);
|
|
1519
|
+
this.pushInstruction("end");
|
|
1520
|
+
this.pushInstruction({ op: "local.get", index: tmp });
|
|
1521
|
+
this.releaseTemp(tmp, wType);
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
// A typed nil `*S` inside the interface is a marker object (see
|
|
1525
|
+
// getNilBoxType): a pointer-receiver method runs with a nil receiver, as in
|
|
1526
|
+
// Go; a value-receiver or promoted method must read through the nil pointer.
|
|
1527
|
+
// A promoted candidate tests the outer struct but carries the embedded
|
|
1528
|
+
// struct's info, so it is recognised by its embed path.
|
|
1529
|
+
_emitNilPtrCandidate(cand, fn, recvLocal) {
|
|
1530
|
+
if (cand.isBoxedValue || !cand.structInfo) return;
|
|
1531
|
+
if (!cand.embedPath && cand.testTypeIndex !== cand.structInfo.typeIndex)
|
|
1532
|
+
return;
|
|
1533
|
+
const nilBox = this.mod.getNilBoxType({
|
|
1534
|
+
kind: "pointer",
|
|
1535
|
+
base: { kind: "named", name: cand.recvTypeName },
|
|
1536
|
+
});
|
|
1537
|
+
this.pushInstruction({ op: "block", blockType: "void" });
|
|
1538
|
+
this.pushInstruction({ op: "local.get", index: recvLocal });
|
|
1539
|
+
this.pushInstruction({ op: "ref.test", typeIndex: nilBox.typeIndex });
|
|
1540
|
+
this.pushInstruction("i32.eqz");
|
|
1541
|
+
this.pushInstruction({ op: "br_if", depth: 0 });
|
|
1542
|
+
if (cand.needsValueDeref || cand.embedPath) {
|
|
1543
|
+
this.emitPanic(
|
|
1544
|
+
"runtime error: invalid memory address or nil pointer dereference",
|
|
1545
|
+
);
|
|
1546
|
+
} else {
|
|
1547
|
+
this.pushInstruction({ op: "ref.null", heapType: cand.testTypeIndex });
|
|
1548
|
+
for (let p = 1; p < fn.params.length; p++) {
|
|
1549
|
+
this.pushInstruction({ op: "local.get", index: p });
|
|
1550
|
+
}
|
|
1551
|
+
this.pushInstruction({ op: "call", funcIndex: cand.funcIndex });
|
|
1552
|
+
this.pushInstruction("return");
|
|
1553
|
+
}
|
|
1554
|
+
this.pushInstruction("end");
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1448
1557
|
// A value-receiver method reached through a pointer box reads the pointee
|
|
1449
1558
|
// first, so a nil pointer inside the interface is a catchable panic.
|
|
1450
1559
|
_emitNilPtrBoxPanic(cand, recvLocal) {
|
|
@@ -1570,11 +1679,9 @@ export class FunctionEmitter {
|
|
|
1570
1679
|
size,
|
|
1571
1680
|
});
|
|
1572
1681
|
} else {
|
|
1573
|
-
this.
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
typeIndex: arrInfo.typeIndex,
|
|
1577
|
-
});
|
|
1682
|
+
this._emitNewArray(elemType, arrInfo, () =>
|
|
1683
|
+
this.pushInstruction({ op: "i32.const", value: size }),
|
|
1684
|
+
);
|
|
1578
1685
|
}
|
|
1579
1686
|
return;
|
|
1580
1687
|
}
|
|
@@ -1616,8 +1723,24 @@ export class FunctionEmitter {
|
|
|
1616
1723
|
}
|
|
1617
1724
|
}
|
|
1618
1725
|
|
|
1619
|
-
|
|
1620
|
-
|
|
1726
|
+
// Pushes a new array of `arrInfo` with the length `emitLen` pushes. Elements
|
|
1727
|
+
// start at their zero value: strings are the empty string, not a null
|
|
1728
|
+
// reference, so the array reads, compares and prints like Go's.
|
|
1729
|
+
_emitNewArray(elemType, arrInfo, emitLen) {
|
|
1730
|
+
if (
|
|
1731
|
+
arrInfo.elemWType === "externref" &&
|
|
1732
|
+
isStringType(elemType, this.mod.checker)
|
|
1733
|
+
) {
|
|
1734
|
+
this.emitZeroValue(elemType, "externref");
|
|
1735
|
+
emitLen();
|
|
1736
|
+
this.pushInstruction({ op: "array.new", typeIndex: arrInfo.typeIndex });
|
|
1737
|
+
return;
|
|
1738
|
+
}
|
|
1739
|
+
emitLen();
|
|
1740
|
+
this.pushInstruction({
|
|
1741
|
+
op: "array.new_default",
|
|
1742
|
+
typeIndex: arrInfo.typeIndex,
|
|
1743
|
+
});
|
|
1621
1744
|
}
|
|
1622
1745
|
|
|
1623
1746
|
// Temps are keyed by a canonical string so ref-typed temps (fresh object
|
|
@@ -1796,10 +1919,25 @@ export class FunctionEmitter {
|
|
|
1796
1919
|
this.emitExprStmt(stmt);
|
|
1797
1920
|
break;
|
|
1798
1921
|
|
|
1922
|
+
// Evaluates `stmt.expr` and panics (recoverably) when it is nil.
|
|
1923
|
+
case "NilCheckStmt":
|
|
1924
|
+
this.emitExpr(stmt.expr, "anyref");
|
|
1925
|
+
this._emitNilBranch();
|
|
1926
|
+
this.pushInstruction("drop");
|
|
1927
|
+
break;
|
|
1928
|
+
|
|
1799
1929
|
case "LabeledStmt":
|
|
1800
1930
|
if (stmt.body) {
|
|
1801
|
-
|
|
1931
|
+
// Stacked labels (`a: b: for`) all target the loop: outer
|
|
1932
|
+
// labels alias the innermost one.
|
|
1933
|
+
if (stmt.body.kind !== "LabeledStmt") {
|
|
1802
1934
|
stmt.body.label = stmt.label;
|
|
1935
|
+
this._labelAliases?.delete(stmt.label);
|
|
1936
|
+
} else {
|
|
1937
|
+
let inner = stmt.body;
|
|
1938
|
+
while (inner.body?.kind === "LabeledStmt") inner = inner.body;
|
|
1939
|
+
this._labelAliases ??= new Map();
|
|
1940
|
+
this._labelAliases.set(stmt.label, inner.label);
|
|
1803
1941
|
}
|
|
1804
1942
|
this.emitStmt(stmt.body);
|
|
1805
1943
|
}
|
|
@@ -5,7 +5,12 @@
|
|
|
5
5
|
import { foldConstExpr } from "../../typechecker/const-fold.js";
|
|
6
6
|
import { orderPackageInits } from "../../typechecker/init-order.js";
|
|
7
7
|
import { _someNode } from "./prescan.js";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
isStringType,
|
|
10
|
+
isStructType,
|
|
11
|
+
toWasmType,
|
|
12
|
+
variadicAsSlice,
|
|
13
|
+
} from "./types.js";
|
|
9
14
|
|
|
10
15
|
// Package-level `const` declarations (iota is already substituted by the
|
|
11
16
|
// parser). Literals and expressions the folder can reduce become BasicLits in
|
|
@@ -99,6 +104,7 @@ function _zeroFieldConst(f, mod) {
|
|
|
99
104
|
const w = f.wType;
|
|
100
105
|
if (w === "i64") return [{ op: "i64.const", value: 0n }];
|
|
101
106
|
if (w === "f32" || w === "f64") return [{ op: `${w}.const`, value: 0 }];
|
|
107
|
+
if (w === "v128") return [{ op: "v128.const", bytes: new Array(16).fill(0) }];
|
|
102
108
|
if (w === "externref") return [{ op: "ref.null", heapType: "extern" }];
|
|
103
109
|
if (w === "anyref") return [{ op: "ref.null", heapType: "any" }];
|
|
104
110
|
if (typeof w !== "object" || w === null)
|
|
@@ -169,6 +175,36 @@ function _zeroArrayConst(goType, mod, depth = 0) {
|
|
|
169
175
|
return out;
|
|
170
176
|
}
|
|
171
177
|
|
|
178
|
+
// True when the zero value of `t` holds a string (a string, or a struct or
|
|
179
|
+
// array containing one). A constant expression cannot build the empty string,
|
|
180
|
+
// so such globals get their zero value from the start function.
|
|
181
|
+
function _zeroHasString(t, mod, depth = 0) {
|
|
182
|
+
if (!t || depth > 4 || _isPointerGoType(t, mod)) return false;
|
|
183
|
+
if (isStringType(t, mod.checker)) return true;
|
|
184
|
+
const shape = _arrayShape(t, mod.checker);
|
|
185
|
+
if (shape) return _zeroHasString(shape.elem, mod, depth + 1);
|
|
186
|
+
const w = toWasmType(t, mod.checker, mod);
|
|
187
|
+
if (typeof w === "object" && typeof w?.typeIndex === "number") {
|
|
188
|
+
const info = Array.from(mod.structTypes.values()).find(
|
|
189
|
+
(x) => x.typeIndex === w.typeIndex,
|
|
190
|
+
);
|
|
191
|
+
if (info)
|
|
192
|
+
return info.fields.some((f) => _zeroHasString(f.goType, mod, depth + 1));
|
|
193
|
+
}
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Types of the names in `var a, b = f()` / `var v, ok = m[k]`: one value fills
|
|
198
|
+
// every name.
|
|
199
|
+
function _spreadValueTypes(spec, value) {
|
|
200
|
+
if (spec._commaOk)
|
|
201
|
+
return [
|
|
202
|
+
value._mapValueType ?? value._type,
|
|
203
|
+
{ kind: "basic", name: "bool" },
|
|
204
|
+
];
|
|
205
|
+
return value?._type?.types ?? [];
|
|
206
|
+
}
|
|
207
|
+
|
|
172
208
|
export function _collectPackageGlobals(progs, mod, checker) {
|
|
173
209
|
const pending = []; // non-constant initializers, run by the start function
|
|
174
210
|
const pendingConsts = []; // unfoldable consts; they run before any var
|
|
@@ -183,11 +219,12 @@ export function _collectPackageGlobals(progs, mod, checker) {
|
|
|
183
219
|
const name = names[i];
|
|
184
220
|
// Foldable consts live in `constCache`, not in a global.
|
|
185
221
|
if (isConst && !mod.isNonLiteralConst(name)) continue;
|
|
222
|
+
const spreadTypes =
|
|
223
|
+
values.length === 1 && names.length > 1
|
|
224
|
+
? _spreadValueTypes(spec, values[0])
|
|
225
|
+
: null;
|
|
186
226
|
const rawType =
|
|
187
|
-
spec.type ??
|
|
188
|
-
(values.length === 1 && names.length > 1
|
|
189
|
-
? values[0]?._type?.types?.[i]
|
|
190
|
-
: values[i]?._type);
|
|
227
|
+
spec.type ?? (spreadTypes ? spreadTypes[i] : values[i]?._type);
|
|
191
228
|
const wType = toWasmType(rawType, checker, mod);
|
|
192
229
|
const gIdx = mod.globals.length;
|
|
193
230
|
let needsRuntimeZero = false;
|
|
@@ -252,6 +289,9 @@ export function _collectPackageGlobals(progs, mod, checker) {
|
|
|
252
289
|
}
|
|
253
290
|
}
|
|
254
291
|
|
|
292
|
+
if (values.length === 0 && _zeroHasString(rawType, mod))
|
|
293
|
+
needsRuntimeZero = true;
|
|
294
|
+
|
|
255
295
|
let litNode = values?.[i];
|
|
256
296
|
let sign = 1;
|
|
257
297
|
if (
|
|
@@ -302,7 +342,7 @@ export function _collectPackageGlobals(progs, mod, checker) {
|
|
|
302
342
|
if (needsRuntimeZero) pending.push({ name, zeroType: rawType });
|
|
303
343
|
if (!isConst && i === 0 && values.length === 1 && names.length > 1) {
|
|
304
344
|
// `var q, r = pair()`: one call fills every name.
|
|
305
|
-
pending.push({ name, names, expr: values[0] });
|
|
345
|
+
pending.push({ name, names, expr: values[0], types: spreadTypes });
|
|
306
346
|
}
|
|
307
347
|
if (values[i] && !isConstInit && names.length === values.length) {
|
|
308
348
|
(isConst ? pendingConsts : pending).push({
|
|
@@ -355,7 +395,7 @@ export function _orderConstInits(entries) {
|
|
|
355
395
|
// initializer in declaration order and invoking package `init()` functions;
|
|
356
396
|
// registered as the module start function.
|
|
357
397
|
export function _makeGlobalInitFunc(pending, initNames = []) {
|
|
358
|
-
const stmts = pending.map(({ name, names, expr, zeroType }) =>
|
|
398
|
+
const stmts = pending.map(({ name, names, expr, types, zeroType }) =>
|
|
359
399
|
zeroType
|
|
360
400
|
? { kind: "GlobalZeroInit", name, goType: zeroType }
|
|
361
401
|
: {
|
|
@@ -364,7 +404,7 @@ export function _makeGlobalInitFunc(pending, initNames = []) {
|
|
|
364
404
|
? names.map((n, i) => ({
|
|
365
405
|
kind: "Ident",
|
|
366
406
|
name: n,
|
|
367
|
-
_type: expr._type?.types?.[i],
|
|
407
|
+
_type: types?.[i] ?? expr._type?.types?.[i],
|
|
368
408
|
}))
|
|
369
409
|
: [{ kind: "Ident", name, _type: expr._type }],
|
|
370
410
|
op: "=",
|
package/src/backend/wasm/glue.js
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
// src/backend/wasm/glue.js
|
|
2
2
|
// Runtime imports and JS glue for executing GoFront WASM modules.
|
|
3
3
|
|
|
4
|
-
import { WASM_IMPORTS_JS
|
|
4
|
+
import { WASM_IMPORTS_JS } from "./boundary.js";
|
|
5
5
|
|
|
6
6
|
// The import table is authored once as source text (it is spliced into the
|
|
7
7
|
// production bundle by the facade); evaluate that same text here so tests
|
|
8
8
|
// exercise exactly what ships. The text is a compile-time constant.
|
|
9
|
-
const makeImports = new Function(
|
|
10
|
-
`${WASM_IMPORTS_JS}\n${WASM_STDLIB_JS}\nreturn __gfw_imports;`,
|
|
11
|
-
)();
|
|
9
|
+
const makeImports = new Function(`${WASM_IMPORTS_JS}\nreturn __gfw_imports;`)();
|
|
12
10
|
|
|
13
11
|
function createWasmImports({
|
|
14
12
|
stringTable = [],
|