gofront 1.9.0 → 1.9.2
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 +1 -1
- package/package.json +3 -3
- package/src/backend/js/expressions.js +329 -58
- package/src/backend/js/index.js +90 -10
- package/src/backend/js/runtime.js +51 -9
- package/src/backend/js/statements.js +193 -84
- package/src/backend/js/type-info.js +123 -8
- package/src/backend/shared/fmt-types.js +30 -1
- package/src/backend/wasm/emit-builtins.js +250 -9
- package/src/backend/wasm/emit-exprs.js +63 -22
- package/src/backend/wasm/emit-shared.js +4 -1
- package/src/backend/wasm/emit-stmts.js +239 -205
- package/src/backend/wasm/emit.js +73 -26
- package/src/backend/wasm/globals.js +1 -0
- package/src/backend/wasm/index.js +8 -0
- package/src/backend/wasm/module-emitter.js +211 -57
- package/src/backend/wasm/monomorph.js +36 -2
- package/src/backend/wasm/natives.js +4 -0
- package/src/backend/wasm/types.js +32 -3
- package/src/backend/wasm/variadic.js +6 -1
- package/src/cli/minifier.js +20 -6
- package/src/compiler.js +6 -1
- package/src/lexer/lexer.js +1 -0
- package/src/link/stdlib.js +1 -0
- package/src/link/type-names.js +72 -16
- package/src/lower/embedding.js +43 -15
- package/src/lower/fmt-formatters.js +38 -14
- package/src/lower/functions.js +223 -7
- package/src/lower/gen-types.js +21 -2
- package/src/lower/index.js +7 -1
- package/src/lower/json-codecs.js +114 -6
- package/src/parser/declarations.js +4 -2
- package/src/parser/expressions.js +25 -41
- package/src/parser/statements.js +107 -0
- package/src/parser/types.js +11 -0
- package/src/typechecker/assignability.js +94 -12
- package/src/typechecker/constants.js +124 -2
- package/src/typechecker/expressions.js +171 -17
- package/src/typechecker/index.js +158 -29
- package/src/typechecker/operand-text.js +1 -0
- package/src/typechecker/resolve.js +308 -39
- package/src/typechecker/statements.js +171 -21
- package/src/typechecker/types.js +84 -2
package/src/backend/js/index.js
CHANGED
|
@@ -24,20 +24,27 @@ import {
|
|
|
24
24
|
markRedundantNilChecks,
|
|
25
25
|
normalizeDefers,
|
|
26
26
|
scanAddressTaken,
|
|
27
|
+
splitForwardedTuples,
|
|
27
28
|
takesElementAddress,
|
|
28
29
|
} from "../../lower/index.js";
|
|
29
30
|
import { orderPackageInits } from "../../typechecker/init-order.js";
|
|
30
31
|
import {
|
|
31
32
|
BROWSER_GLOBALS,
|
|
32
33
|
isComplex,
|
|
34
|
+
isMethodPromotedOverField,
|
|
33
35
|
isNumeric,
|
|
34
36
|
} from "../../typechecker/types.js";
|
|
37
|
+
import { goTypeName } from "../shared/fmt-types.js";
|
|
35
38
|
import {
|
|
36
39
|
unicodeIsJs,
|
|
37
40
|
unicodeToLowerJs,
|
|
38
41
|
unicodeToUpperJs,
|
|
39
42
|
} from "../shared/host-unicode.js";
|
|
40
|
-
import {
|
|
43
|
+
import {
|
|
44
|
+
comparesToNil,
|
|
45
|
+
derefsReceiverFirst,
|
|
46
|
+
expressionGenMethods,
|
|
47
|
+
} from "./expressions.js";
|
|
41
48
|
import {
|
|
42
49
|
HELPER_APPEND,
|
|
43
50
|
HELPER_BOUNDS,
|
|
@@ -367,7 +374,9 @@ export class CodeGen {
|
|
|
367
374
|
this._precisionWarned = new Set();
|
|
368
375
|
// Defer lowering (argument/receiver hoisting, recover-target marking) is
|
|
369
376
|
// idempotent and owned by the backends: the wasm path runs it via
|
|
370
|
-
// `lower()`, the JS path here.
|
|
377
|
+
// `lower()`, the JS path here. So is splitting multi-value calls
|
|
378
|
+
// forwarded into interfaces into temps.
|
|
379
|
+
splitForwardedTuples(program);
|
|
371
380
|
normalizeDefers(program);
|
|
372
381
|
// Strict mode: index checks the loop shape proves unnecessary are elided.
|
|
373
382
|
if (this.strict) {
|
|
@@ -584,13 +593,35 @@ export class CodeGen {
|
|
|
584
593
|
const name = d.recvType.name;
|
|
585
594
|
if (!methods.has(name)) methods.set(name, []);
|
|
586
595
|
methods.get(name).push(d);
|
|
587
|
-
if (d.recvPointer && comparesToNil(d.body, d.recvName))
|
|
588
|
-
this.nilSafeMethods.add(`${name}.${d.name}`);
|
|
589
596
|
}
|
|
590
597
|
}
|
|
598
|
+
this._markNilSafeMethods(methods);
|
|
591
599
|
return methods;
|
|
592
600
|
}
|
|
593
601
|
|
|
602
|
+
// Pointer-receiver methods that can run with a nil receiver: they compare
|
|
603
|
+
// it with nil, or do not read through it before anything could skip the
|
|
604
|
+
// read (a method that does panics on nil either way, so it keeps the
|
|
605
|
+
// faster direct call). Calls to these go through the prototype (see
|
|
606
|
+
// _nilSafeMethodClass).
|
|
607
|
+
_markNilSafeMethods(methods) {
|
|
608
|
+
for (const [name, decls] of methods) {
|
|
609
|
+
const safe = new Set();
|
|
610
|
+
const ptr = decls.filter((d) => d.recvPointer);
|
|
611
|
+
for (const d of ptr) safe.add(d.name);
|
|
612
|
+
for (let changed = true; changed; ) {
|
|
613
|
+
changed = false;
|
|
614
|
+
for (const d of ptr) {
|
|
615
|
+
if (!safe.has(d.name) || comparesToNil(d.body, d.recvName)) continue;
|
|
616
|
+
if (!derefsReceiverFirst(d.body?.stmts, d.recvName, safe)) continue;
|
|
617
|
+
safe.delete(d.name);
|
|
618
|
+
changed = true;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
for (const m of safe) this.nilSafeMethods.add(`${name}.${m}`);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
594
625
|
_collectNamedWrappers(program, methods) {
|
|
595
626
|
for (const d of program.decls) {
|
|
596
627
|
if (
|
|
@@ -703,6 +734,30 @@ export class CodeGen {
|
|
|
703
734
|
}
|
|
704
735
|
|
|
705
736
|
genTypeDeclWithMethods(decl, methodDecls) {
|
|
737
|
+
this._genTypeDeclBody(decl, methodDecls);
|
|
738
|
+
this._genUnhashableMark(decl.name);
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
// Marks a defined type Go cannot hash so `__ik` can name it in the panic
|
|
742
|
+
// of an interface-keyed map insert: a struct class carries `__uh`, a
|
|
743
|
+
// defined slice or map type registers its interface tag.
|
|
744
|
+
_genUnhashableMark(name) {
|
|
745
|
+
const t = this.checker?.types.get(name);
|
|
746
|
+
if (t?.kind !== "named" || t.typeParams?.length || t.typeArgs?.length)
|
|
747
|
+
return;
|
|
748
|
+
if (!this._isUnhashableType(t)) return;
|
|
749
|
+
const goName = JSON.stringify(goTypeName(t));
|
|
750
|
+
if (this.structNames.has(name)) {
|
|
751
|
+
this.line(
|
|
752
|
+
`Object.defineProperty(${name}, "__uh", { value: ${goName} });`,
|
|
753
|
+
);
|
|
754
|
+
} else if (this._isNamedComposite(t)) {
|
|
755
|
+
this.useHelper("structKey");
|
|
756
|
+
this.line(`__uht.set(${JSON.stringify(name)}, ${goName});`);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
_genTypeDeclBody(decl, methodDecls) {
|
|
706
761
|
const definedStruct =
|
|
707
762
|
decl.type.kind === "StructType" ? null : this._definedStructAst(decl);
|
|
708
763
|
if (decl.type.kind === "StructType") {
|
|
@@ -816,8 +871,11 @@ export class CodeGen {
|
|
|
816
871
|
if (this.checker) {
|
|
817
872
|
const resolved = this.checker.types.get(name)?.underlying;
|
|
818
873
|
if (resolved?.kind === "struct") {
|
|
819
|
-
for (const [fName, fType] of resolved.fields.entries())
|
|
874
|
+
for (const [fName, fType] of resolved.fields.entries()) {
|
|
875
|
+
// A method promoted shallower shadows the deeper field.
|
|
876
|
+
if (isMethodPromotedOverField(resolved, fName)) continue;
|
|
820
877
|
fields.push({ name: fName, zero: this.zeroValueForType(fType) });
|
|
878
|
+
}
|
|
821
879
|
}
|
|
822
880
|
} else {
|
|
823
881
|
for (const f of structTypeAst.fields) {
|
|
@@ -837,8 +895,10 @@ export class CodeGen {
|
|
|
837
895
|
const fields = [];
|
|
838
896
|
// Cached per type name, so never bound to the current generic body.
|
|
839
897
|
this._withTypeParamZeros(null, () => {
|
|
840
|
-
for (const [name, type] of resolved.fields.entries())
|
|
898
|
+
for (const [name, type] of resolved.fields.entries()) {
|
|
899
|
+
if (isMethodPromotedOverField(resolved, name)) continue;
|
|
841
900
|
fields.push({ name, zero: this.zeroValueForType(type) });
|
|
901
|
+
}
|
|
842
902
|
});
|
|
843
903
|
this.structFields.set(typeName, fields);
|
|
844
904
|
return fields;
|
|
@@ -1038,7 +1098,17 @@ export class CodeGen {
|
|
|
1038
1098
|
// A promoted method runs on the embedded value (`this.In`), so it and the
|
|
1039
1099
|
// outer struct's promoted-field accesses see the same storage. Embedded
|
|
1040
1100
|
// types that are not compiled Go structs keep the borrowed-`this` form.
|
|
1041
|
-
_embedStub(
|
|
1101
|
+
_embedStub(
|
|
1102
|
+
method,
|
|
1103
|
+
embedName,
|
|
1104
|
+
valueEmbed = false,
|
|
1105
|
+
fieldName = null,
|
|
1106
|
+
nilPtrRecv = false,
|
|
1107
|
+
ifaceEmbed = false,
|
|
1108
|
+
) {
|
|
1109
|
+
// An embedded interface forwards to the interface value in its field.
|
|
1110
|
+
if (ifaceEmbed)
|
|
1111
|
+
return `${method}(...__a) { return this.${fieldName}.${method}(...__a); }`;
|
|
1042
1112
|
// The field is named after the type without its package suffix
|
|
1043
1113
|
// (`Mutex$$sync` is the field `Mutex`), or after the alias it embeds.
|
|
1044
1114
|
const field = fieldName || embedName.replace(/\$\$.*$/, "");
|
|
@@ -1046,12 +1116,20 @@ export class CodeGen {
|
|
|
1046
1116
|
// the value as the receiver.
|
|
1047
1117
|
if (valueEmbed)
|
|
1048
1118
|
return `${method}(...__a) { return ${embedName}.$${method}(this.${field}, ...__a); }`;
|
|
1049
|
-
const call = this.
|
|
1050
|
-
? `this.${field}.${method}(...__a)`
|
|
1051
|
-
: `${embedName}.prototype.${method}.call(this, ...__a)`;
|
|
1119
|
+
const call = this._embedStubCall(method, embedName, field, nilPtrRecv);
|
|
1052
1120
|
return `${method}(...__a) { return ${call}; }`;
|
|
1053
1121
|
}
|
|
1054
1122
|
|
|
1123
|
+
_embedStubCall(method, embedName, field, nilPtrRecv) {
|
|
1124
|
+
if (!this.structNames.has(embedName))
|
|
1125
|
+
return `${embedName}.prototype.${method}.call(this, ...__a)`;
|
|
1126
|
+
// A nil embedded pointer still reaches a pointer-receiver method, as in
|
|
1127
|
+
// Go: call it through the prototype instead of reading it off null.
|
|
1128
|
+
return nilPtrRecv
|
|
1129
|
+
? `${embedName}.prototype.${method}.call(this.${field}, ...__a)`
|
|
1130
|
+
: `this.${field}.${method}(...__a)`;
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1055
1133
|
_genEmbeddedMethodStubs(name, methodDecls) {
|
|
1056
1134
|
const stubs = computeEmbeddedStubs(name, methodDecls, this.checker);
|
|
1057
1135
|
for (const stub of stubs) {
|
|
@@ -1062,6 +1140,8 @@ export class CodeGen {
|
|
|
1062
1140
|
stub.embedName,
|
|
1063
1141
|
stub.valueEmbed,
|
|
1064
1142
|
stub.fieldName,
|
|
1143
|
+
stub.nilPtrRecv,
|
|
1144
|
+
stub.ifaceEmbed,
|
|
1065
1145
|
),
|
|
1066
1146
|
);
|
|
1067
1147
|
}
|
|
@@ -311,11 +311,21 @@ export const HELPER_VCLONE = `var __vclone = __vclone || function __vclone(z, x)
|
|
|
311
311
|
if (x === null || typeof x !== "object") return x;
|
|
312
312
|
const zero = z?.();
|
|
313
313
|
if (zero === null || zero === undefined || typeof zero !== "object") return x;
|
|
314
|
+
// A copy keeps the hidden tags (__t, __map, ...) that name the value's type
|
|
315
|
+
// in an interface: the generic code boxes the value first and copies after.
|
|
316
|
+
const keep = (v, c) => {
|
|
317
|
+
for (const k of Object.getOwnPropertyNames(v)) {
|
|
318
|
+
const d = Object.getOwnPropertyDescriptor(v, k);
|
|
319
|
+
if (!d.enumerable && k !== "length") Object.defineProperty(c, k, d);
|
|
320
|
+
}
|
|
321
|
+
return c;
|
|
322
|
+
};
|
|
314
323
|
const cp = (v) => {
|
|
315
324
|
if (v === null || typeof v !== "object") return v;
|
|
316
|
-
if (
|
|
317
|
-
if (
|
|
318
|
-
|
|
325
|
+
if (v.__nilm === true) return v;
|
|
326
|
+
if (Array.isArray(v)) return keep(v, v.map(cp));
|
|
327
|
+
if (typeof v.__clone === "function") return keep(v, v.__clone());
|
|
328
|
+
return keep(v, Object.assign(Object.create(Object.getPrototypeOf(v)), v));
|
|
319
329
|
};
|
|
320
330
|
return cp(x);
|
|
321
331
|
};`;
|
|
@@ -439,25 +449,41 @@ var __ifps = __ifps || function(x) {
|
|
|
439
449
|
}
|
|
440
450
|
return x;
|
|
441
451
|
};
|
|
442
|
-
var
|
|
452
|
+
var __tfn = __tfn || function(f, t) {
|
|
453
|
+
// A defined func type tags a fresh wrapper, so the shared func keeps its own type.
|
|
454
|
+
if (typeof f !== "function" || f.__t === t) return f;
|
|
455
|
+
const w = function(...a) { return f.apply(this, a); };
|
|
456
|
+
Object.defineProperty(w, "__t", { value: t, configurable: true, writable: true });
|
|
457
|
+
return w;
|
|
458
|
+
};
|
|
459
|
+
var __tnm = __tnm || function(f) {
|
|
460
|
+
// The type name of a type argument's info factory, spelled as a static tag.
|
|
461
|
+
const id = f?.nm ?? f?.id;
|
|
462
|
+
return typeof id === "string" ? id.replace(/ void\\b/g, "").replace(/\\bbyte\\b/g, "uint8").replace(/\\brune\\b/g, "int32").replace(/\\bany\\b/g, "interface {}") : undefined;
|
|
463
|
+
};
|
|
464
|
+
var __ity = __ity || function(x, t, ptr, sl) {
|
|
465
|
+
if (t === undefined) return x;
|
|
443
466
|
// The frozen nil map cannot carry a tag: a copy of it keeps its map type.
|
|
444
467
|
if (x !== null && x !== undefined && x.__nilm === true && x.__t === undefined)
|
|
445
468
|
return Object.freeze(Object.defineProperties({}, { __map: { value: true }, __nilm: { value: true }, __t: { value: t } }));
|
|
446
469
|
if (x === null || x === undefined) {
|
|
447
470
|
// A nil slice keeps its type as an empty tagged array (not a nil interface).
|
|
448
|
-
if (!ptr && t.charCodeAt(0) !== 91) return x;
|
|
471
|
+
if (!ptr && !sl && t.charCodeAt(0) !== 91) return x;
|
|
449
472
|
const n = ptr ? {} : [];
|
|
450
473
|
if (!ptr) Object.defineProperty(n, "__nils", { value: true });
|
|
451
474
|
Object.defineProperty(n, "__nilp", { value: true });
|
|
452
475
|
Object.defineProperty(n, "__t", { value: t });
|
|
453
476
|
return n;
|
|
454
477
|
}
|
|
478
|
+
// A func already tagged with another type keeps it: tag a fresh wrapper.
|
|
479
|
+
if (typeof x === "function" && x.__t !== undefined && x.__t !== t) x = __tfn(x, undefined);
|
|
455
480
|
if ((typeof x === "object" || typeof x === "function") && Object.isExtensible(x))
|
|
456
481
|
Object.defineProperty(x, "__t", { value: t, configurable: true, writable: true });
|
|
457
482
|
return x;
|
|
458
483
|
};
|
|
459
484
|
var __isty = __isty || function(v, t, k, c, n) {
|
|
460
485
|
if (v === null || v === undefined || (typeof v !== "object" && typeof v !== "function")) return false;
|
|
486
|
+
if (k === "namedfunc") return typeof v === "function" && v.__t === t;
|
|
461
487
|
if (k === "func") return typeof v === "function" && (v.__t === undefined || t === "" || v.__t === t);
|
|
462
488
|
if (v.__t !== undefined && t !== "") return v.__t === t;
|
|
463
489
|
if (v.__nilp === true) return false;
|
|
@@ -605,9 +631,9 @@ var __zu64 = __zu64 || Object.assign(() => 0n, { k: "uint64" });
|
|
|
605
631
|
var __tbox = __tbox || function(f, x) {
|
|
606
632
|
return f && f.b ? f.b(x) : x;
|
|
607
633
|
};
|
|
608
|
-
var __ti = __ti || function(f, k, id, is, u, b, fb) {
|
|
634
|
+
var __ti = __ti || function(f, k, id, is, u, b, fb, nm) {
|
|
609
635
|
const g = () => f();
|
|
610
|
-
g.k = k; g.id = id; g.is = is; g.u = u; g.b = b; g.fb = fb;
|
|
636
|
+
g.k = k; g.id = id; g.is = is; g.u = u; g.b = b; g.fb = fb; g.nm = nm;
|
|
611
637
|
return g;
|
|
612
638
|
};
|
|
613
639
|
var __tar = __tar || function(f, op, a, b) {
|
|
@@ -707,6 +733,8 @@ var __isnm = __isnm || function(m) {
|
|
|
707
733
|
// through `toString` to an injective encoding, and the key value is kept in
|
|
708
734
|
// a table so ranging and fmt can recover it.
|
|
709
735
|
export const HELPER_STRUCT_KEY = `var __kt = __kt || new Map();
|
|
736
|
+
// Tags of defined slice and map types Go cannot hash, with their Go names.
|
|
737
|
+
var __uht = __uht || new Map();
|
|
710
738
|
var __kcl = __kcl || function(o) {
|
|
711
739
|
if (o === null || typeof o !== "object") return o;
|
|
712
740
|
if (typeof o.__clone === "function") return o.__clone();
|
|
@@ -739,10 +767,11 @@ var __pk = __pk || function(p) {
|
|
|
739
767
|
};
|
|
740
768
|
var __ik = __ik || function(k, tag) {
|
|
741
769
|
const t = typeof k;
|
|
770
|
+
// A nil slice, map or func of a static type Go cannot hash panics too.
|
|
771
|
+
if (tag !== undefined && tag.charCodeAt(0) === 33) throw new Error("runtime error: hash of unhashable type " + tag.slice(1));
|
|
742
772
|
if (k === null || k === undefined) return "\\u0001nil";
|
|
743
773
|
if (tag !== undefined) {
|
|
744
774
|
if (tag === "*") return __pk(k);
|
|
745
|
-
if (tag.charCodeAt(0) === 33) throw new Error("runtime error: hash of unhashable type " + tag.slice(1));
|
|
746
775
|
if (t === "number" || t === "bigint") {
|
|
747
776
|
const s = "\\u0001" + tag + ":" + String(k);
|
|
748
777
|
if (!__kt.has(s)) __kt.set(s, k);
|
|
@@ -752,7 +781,13 @@ var __ik = __ik || function(k, tag) {
|
|
|
752
781
|
if (t === "number" || t === "boolean" || t === "bigint") return "\\u0001" + t + ":" + String(k);
|
|
753
782
|
// A slice, map or func in an \`any\` carries its type tag: Go cannot hash it.
|
|
754
783
|
if ((t === "object" || t === "function") && typeof k.__t === "string" && /^(\\[\\]|map\\[|func\\()/.test(k.__t))
|
|
755
|
-
throw new Error("runtime error: hash of unhashable type " + k.__t
|
|
784
|
+
throw new Error("runtime error: hash of unhashable type " + k.__t);
|
|
785
|
+
// A struct class or defined slice/map type that Go cannot hash is marked
|
|
786
|
+
// once at its declaration, so this is one lookup per insert.
|
|
787
|
+
if (t === "object") {
|
|
788
|
+
const uh = k.constructor?.__uh ?? __uht.get(k.__t);
|
|
789
|
+
if (uh !== undefined) throw new Error("runtime error: hash of unhashable type " + uh);
|
|
790
|
+
}
|
|
756
791
|
if (k !== null && t === "object" && typeof k.__gfUnbox === "function" && !k.constructor.name.endsWith("$Ptr")) {
|
|
757
792
|
const v = k.__gfUnbox(), vt = typeof v;
|
|
758
793
|
if (vt === "string" || vt === "number" || vt === "boolean" || vt === "bigint") {
|
|
@@ -834,6 +869,13 @@ export const HELPER_IFEQ = `var __ifeq = __ifeq || function(a, b) {
|
|
|
834
869
|
if (typeof a === "object" && typeof b === "object" && a.__v === true && b.__v === true && a.constructor === b.constructor) {
|
|
835
870
|
return __equal(a, b);
|
|
836
871
|
}
|
|
872
|
+
// Array values of the same type compare element by element; slices and
|
|
873
|
+
// defined slice types (registered in __uht) are not comparable.
|
|
874
|
+
if (typeof a === "object" && typeof b === "object" && typeof a.__t === "string" && a.__t === b.__t &&
|
|
875
|
+
(Array.isArray(a) || ArrayBuffer.isView(a)) && (Array.isArray(b) || ArrayBuffer.isView(b)) &&
|
|
876
|
+
!a.__t.startsWith("[]") && (typeof __uht === "undefined" || !__uht.has(a.__t))) {
|
|
877
|
+
return __equal(a, b);
|
|
878
|
+
}
|
|
837
879
|
return false;
|
|
838
880
|
};`;
|
|
839
881
|
|