gofront 1.4.0 → 1.6.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 +174 -403
- package/package.json +16 -19
- package/src/{codegen → backend/js}/expressions.js +265 -77
- package/src/{codegen → backend/js}/index.js +120 -89
- package/src/{codegen → backend/js}/runtime.js +102 -0
- package/src/{codegen → backend/js}/source-map.js +32 -0
- package/src/{codegen → backend/js}/statements.js +139 -64
- package/src/backend/js/stdlib/bits.js +32 -0
- package/src/{codegen → backend/js}/stdlib/errors.js +2 -2
- package/src/{codegen → backend/js}/stdlib/fmt.js +26 -8
- package/src/{codegen → backend/js}/stdlib/index.js +6 -0
- package/src/{codegen → backend/js}/stdlib/maps.js +1 -1
- package/src/{codegen → backend/js}/stdlib/path.js +2 -2
- package/src/backend/js/stdlib/shared.js +17 -0
- package/src/{codegen → backend/js}/stdlib/slices.js +1 -1
- package/src/{codegen → backend/js}/stdlib/sort.js +1 -1
- package/src/backend/js/stdlib/strconv.js +58 -0
- package/src/{codegen → backend/js}/stdlib/strings.js +4 -1
- package/src/{codegen → backend/js}/stdlib/testing.js +2 -2
- package/src/{codegen → backend/js}/stdlib/time.js +3 -3
- package/src/{codegen → backend/js}/templ.js +3 -3
- package/src/backend/wasm/boundary.js +1798 -0
- package/src/backend/wasm/emit-builtins.js +1103 -0
- package/src/backend/wasm/emit-exprs.js +1908 -0
- package/src/backend/wasm/emit-maps.js +1863 -0
- package/src/backend/wasm/emit-shared.js +519 -0
- package/src/backend/wasm/emit-stdlib.js +3006 -0
- package/src/backend/wasm/emit-stmts.js +2472 -0
- package/src/backend/wasm/emit.js +1376 -0
- package/src/backend/wasm/encode.js +937 -0
- package/src/backend/wasm/glue.js +144 -0
- package/src/backend/wasm/index.js +2790 -0
- package/src/backend/wasm/monomorph.js +711 -0
- package/src/backend/wasm/optimize.js +227 -0
- package/src/backend/wasm/types.js +544 -0
- package/src/backend/wasm/wat.js +302 -0
- package/src/cli-core.js +323 -75
- package/src/compiler.js +447 -14
- package/src/dev-server.js +56 -15
- package/src/index.js +99 -120
- package/src/lexer.js +18 -0
- package/src/lower/boxing.js +35 -0
- package/src/lower/captures.js +172 -0
- package/src/lower/embedding.js +30 -0
- package/src/lower/escape.js +164 -0
- package/src/lower/functions.js +302 -0
- package/src/lower/globals.js +316 -0
- package/src/lower/index.js +118 -0
- package/src/lower/ownership.js +137 -0
- package/src/lower/range.js +67 -0
- package/src/lower/walk.js +25 -0
- package/src/minifier.js +2 -0
- package/src/parser/index.js +8 -3
- package/src/resolver.js +3 -1
- package/src/test-runner.js +94 -20
- package/src/typechecker/assignability.js +42 -1
- package/src/typechecker/expressions.js +182 -3
- package/src/typechecker/index.js +261 -5
- package/src/typechecker/resolve.js +35 -6
- package/src/typechecker/stdlib/core.js +34 -19
- package/src/typechecker/stdlib/extended.js +18 -3
- package/src/typechecker/stdlib/shared.js +53 -0
- package/src/typechecker/stdlib.js +2 -0
- package/src/typechecker/types.js +259 -6
- package/src/vendor.js +2 -2
- package/src/codegen/stdlib/strconv.js +0 -33
- /package/src/{codegen → backend/js}/stdlib/builder.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/bytes.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/gom.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/html.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/io.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/math.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/os.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/rand.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/regexp.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/unicode.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/utf8.js +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Code generator: walks the typed AST and emits clean JavaScript.
|
|
2
2
|
//
|
|
3
|
-
// Split into sub-modules under
|
|
3
|
+
// Split into sub-modules under backend/js/:
|
|
4
4
|
// source-map.js — VLQ encoder and source map builder
|
|
5
5
|
// statements.js — genBlock, genStmt, genFor, genSwitch, etc.
|
|
6
6
|
// expressions.js — genExpr, genCall, genCompositeLit, helpers
|
|
@@ -17,7 +17,14 @@
|
|
|
17
17
|
// - make(map[K]V) → {}
|
|
18
18
|
// - for range → for...of with .entries()
|
|
19
19
|
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
computeEmbeddedStubs,
|
|
22
|
+
hasDirectRecover,
|
|
23
|
+
isReferenceType,
|
|
24
|
+
normalizeDefers,
|
|
25
|
+
scanAddressTaken,
|
|
26
|
+
} from "../../lower/index.js";
|
|
27
|
+
import { isComplex, isNumeric } from "../../typechecker/types.js";
|
|
21
28
|
import { expressionGenMethods } from "./expressions.js";
|
|
22
29
|
import {
|
|
23
30
|
HELPER_APPEND,
|
|
@@ -29,20 +36,25 @@ import {
|
|
|
29
36
|
HELPER_IFACE_BOX,
|
|
30
37
|
HELPER_INJECT_STYLES,
|
|
31
38
|
HELPER_LEN,
|
|
39
|
+
HELPER_NIL_CALL,
|
|
40
|
+
HELPER_PANIC,
|
|
32
41
|
HELPER_PATH_CLEAN,
|
|
33
42
|
HELPER_S,
|
|
34
43
|
HELPER_SCLONE,
|
|
35
44
|
HELPER_SORT_SLICE,
|
|
36
45
|
HELPER_SPRINTF,
|
|
46
|
+
HELPER_STRCONV,
|
|
37
47
|
HELPER_TESTING,
|
|
38
48
|
HELPER_TIME_FMT,
|
|
39
49
|
HELPER_TIME_PARSE,
|
|
40
50
|
} from "./runtime.js";
|
|
41
|
-
import { buildSourceMap } from "./source-map.js";
|
|
51
|
+
import { buildSourceMap, buildWasmSourceMap } from "./source-map.js";
|
|
42
52
|
import { statementGenMethods } from "./statements.js";
|
|
43
53
|
import { stdlibGenMethods } from "./stdlib/index.js";
|
|
44
54
|
import { templGenMethods } from "./templ.js";
|
|
45
55
|
|
|
56
|
+
export { buildSourceMap, buildWasmSourceMap };
|
|
57
|
+
|
|
46
58
|
// Valid Go identifiers that cannot be used as JS bindings; emitted with a `$` suffix.
|
|
47
59
|
const JS_RESERVED = new Set([
|
|
48
60
|
"arguments",
|
|
@@ -83,6 +95,30 @@ const JS_RESERVED = new Set([
|
|
|
83
95
|
const jsSafeName = (n) =>
|
|
84
96
|
typeof n === "string" && JS_RESERVED.has(n) ? `${n}$` : n;
|
|
85
97
|
|
|
98
|
+
// Runtime helpers by registry name, in output order. Mark one as needed with
|
|
99
|
+
// `this.useHelper(name)`; `_prependHelpers` emits every marked entry.
|
|
100
|
+
const HELPER_MAP = [
|
|
101
|
+
["len", HELPER_LEN],
|
|
102
|
+
["append", HELPER_APPEND],
|
|
103
|
+
["sliceGuard", HELPER_S],
|
|
104
|
+
["sclone", HELPER_SCLONE],
|
|
105
|
+
["ifaceBox", HELPER_IFACE_BOX],
|
|
106
|
+
["equal", HELPER_EQUAL],
|
|
107
|
+
["cmul", HELPER_CMUL],
|
|
108
|
+
["cdiv", HELPER_CDIV],
|
|
109
|
+
["panicRt", HELPER_PANIC],
|
|
110
|
+
["nilCall", HELPER_NIL_CALL],
|
|
111
|
+
["sprintf", HELPER_SPRINTF],
|
|
112
|
+
["error", HELPER_ERROR],
|
|
113
|
+
["errorIs", HELPER_ERROR_IS],
|
|
114
|
+
["strconv", HELPER_STRCONV],
|
|
115
|
+
["pathClean", HELPER_PATH_CLEAN],
|
|
116
|
+
["sortSlice", HELPER_SORT_SLICE],
|
|
117
|
+
["timeFmt", HELPER_TIME_FMT],
|
|
118
|
+
["timeParse", HELPER_TIME_PARSE],
|
|
119
|
+
["testing", HELPER_TESTING],
|
|
120
|
+
];
|
|
121
|
+
|
|
86
122
|
export class CodeGen {
|
|
87
123
|
// jsImports: Map<importPath, string[]> — npm package imports to emit at top of file
|
|
88
124
|
// bundledPackages: Set<string> — GoFront package names bundled inline;
|
|
@@ -91,8 +127,11 @@ export class CodeGen {
|
|
|
91
127
|
checker = null,
|
|
92
128
|
jsImports = new Map(),
|
|
93
129
|
bundledPackages = new Set(),
|
|
130
|
+
options = {},
|
|
94
131
|
) {
|
|
95
132
|
this.checker = checker;
|
|
133
|
+
this.target = options.target ?? "js";
|
|
134
|
+
this.strict = Boolean(options.strict || this.target === "both");
|
|
96
135
|
this.out = [];
|
|
97
136
|
this.indent = 0;
|
|
98
137
|
this.structNames = new Set();
|
|
@@ -106,22 +145,7 @@ export class CodeGen {
|
|
|
106
145
|
this._currentSrcFileIdx = 0; // updated as each top-level decl is generated
|
|
107
146
|
this._boxedVars = new Set(); // address-taken scalar variables that need boxing
|
|
108
147
|
// Runtime helper usage tracking — only emit helpers that are actually used
|
|
109
|
-
this.
|
|
110
|
-
this._usesAppend = false;
|
|
111
|
-
this._usesSliceGuard = false;
|
|
112
|
-
this._usesSprintf = false;
|
|
113
|
-
this._usesEqual = false;
|
|
114
|
-
this._usesCmul = false;
|
|
115
|
-
this._usesCdiv = false;
|
|
116
|
-
this._usesError = false;
|
|
117
|
-
this._usesErrorIs = false;
|
|
118
|
-
this._usesPathClean = false;
|
|
119
|
-
this._usesSortSlice = false;
|
|
120
|
-
this._usesTimeFmt = false;
|
|
121
|
-
this._usesTimeParse = false;
|
|
122
|
-
this._usesTesting = false;
|
|
123
|
-
this._usesSClone = false;
|
|
124
|
-
this._usesIfaceBox = false;
|
|
148
|
+
this._helpers = new Set();
|
|
125
149
|
// Per-function context for Go value semantics (see _withFnCtx)
|
|
126
150
|
this._fnCtx = null;
|
|
127
151
|
// Iterator (range-over-func) context
|
|
@@ -207,6 +231,10 @@ export class CodeGen {
|
|
|
207
231
|
|
|
208
232
|
generate(program, options = {}) {
|
|
209
233
|
const isTest = options.isTest ?? false;
|
|
234
|
+
// Defer lowering (argument/receiver hoisting, recover-target marking) is
|
|
235
|
+
// idempotent and owned by the backends: the wasm path runs it via
|
|
236
|
+
// `lower()`, the JS path here.
|
|
237
|
+
normalizeDefers(program);
|
|
210
238
|
// Ensure _pkgName is set on all decls (generateAll sets it for
|
|
211
239
|
// multi-file; for single-file compiles we derive it from the AST).
|
|
212
240
|
const pkgName = program.pkg?.name;
|
|
@@ -397,28 +425,21 @@ export class CodeGen {
|
|
|
397
425
|
return methods;
|
|
398
426
|
}
|
|
399
427
|
|
|
428
|
+
/** Marks a runtime helper (key of HELPER_MAP) as needed by the output. */
|
|
429
|
+
useHelper(name) {
|
|
430
|
+
this._helpers.add(name);
|
|
431
|
+
}
|
|
432
|
+
|
|
400
433
|
_prependHelpers(isTest = false) {
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
[this._usesCmul, HELPER_CMUL],
|
|
411
|
-
[this._usesCdiv, HELPER_CDIV],
|
|
412
|
-
[this._usesSprintf || needsTesting, HELPER_SPRINTF],
|
|
413
|
-
[this._usesError, HELPER_ERROR],
|
|
414
|
-
[this._usesErrorIs, HELPER_ERROR_IS],
|
|
415
|
-
[this._usesPathClean, HELPER_PATH_CLEAN],
|
|
416
|
-
[this._usesSortSlice, HELPER_SORT_SLICE],
|
|
417
|
-
[this._usesTimeFmt, HELPER_TIME_FMT],
|
|
418
|
-
[this._usesTimeParse, HELPER_TIME_PARSE],
|
|
419
|
-
[needsTesting, HELPER_TESTING],
|
|
420
|
-
];
|
|
421
|
-
const helpers = HELPER_MAP.filter(([flag]) => flag).map(([, h]) => h);
|
|
434
|
+
if (isTest || this._helpers.has("testing")) {
|
|
435
|
+
this.useHelper("sprintf");
|
|
436
|
+
this.useHelper("testing");
|
|
437
|
+
}
|
|
438
|
+
const helpers = [];
|
|
439
|
+
if (this.collectedCss.length > 0) helpers.push(HELPER_INJECT_STYLES);
|
|
440
|
+
for (const [name, src] of HELPER_MAP) {
|
|
441
|
+
if (this._helpers.has(name)) helpers.push(src);
|
|
442
|
+
}
|
|
422
443
|
if (helpers.length > 0) {
|
|
423
444
|
const helperLines = helpers.flatMap((h) => h.split("\n"));
|
|
424
445
|
helperLines.push("");
|
|
@@ -621,12 +642,13 @@ export class CodeGen {
|
|
|
621
642
|
}
|
|
622
643
|
|
|
623
644
|
_genEmbeddedMethodStubs(name, methodDecls) {
|
|
624
|
-
|
|
625
|
-
const
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
645
|
+
const stubs = computeEmbeddedStubs(name, methodDecls, this.checker);
|
|
646
|
+
for (const stub of stubs) {
|
|
647
|
+
this.blank();
|
|
648
|
+
this.line(
|
|
649
|
+
`${stub.methodName}(...__a) { return ${stub.embedName}.prototype.${stub.methodName}.call(this, ...__a); }`,
|
|
650
|
+
);
|
|
651
|
+
}
|
|
630
652
|
}
|
|
631
653
|
|
|
632
654
|
genMethod(decl, recvField = null) {
|
|
@@ -650,7 +672,7 @@ export class CodeGen {
|
|
|
650
672
|
}
|
|
651
673
|
}
|
|
652
674
|
this._emitParamCopies(decl.params);
|
|
653
|
-
this._withNamedReturns(decl, () => this._genBody(decl.body));
|
|
675
|
+
this._withNamedReturns(decl, () => this._genBody(decl.body, decl));
|
|
654
676
|
}),
|
|
655
677
|
);
|
|
656
678
|
this._unwrappedRecv = prevUnwrapped;
|
|
@@ -707,7 +729,7 @@ export class CodeGen {
|
|
|
707
729
|
this._withFnCtx(decl.body, () =>
|
|
708
730
|
this.indented(() => {
|
|
709
731
|
this._emitParamCopies(decl.params);
|
|
710
|
-
this._withNamedReturns(decl, () => this._genBody(decl.body));
|
|
732
|
+
this._withNamedReturns(decl, () => this._genBody(decl.body, decl));
|
|
711
733
|
}),
|
|
712
734
|
);
|
|
713
735
|
this._boxedVars = prevBoxed;
|
|
@@ -715,45 +737,74 @@ export class CodeGen {
|
|
|
715
737
|
}
|
|
716
738
|
|
|
717
739
|
_withNamedReturns(decl, fn) {
|
|
740
|
+
const prevDecl = this._currentDecl;
|
|
741
|
+
this._currentDecl = decl;
|
|
718
742
|
const named = decl.returnType?._namedReturns;
|
|
719
743
|
const prev = this.namedReturnVars;
|
|
720
744
|
if (named) {
|
|
721
745
|
// Emit zero-value declarations for named return vars
|
|
722
|
-
for (
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
}
|
|
746
|
+
for (let i = 0; i < named.length; i++) {
|
|
747
|
+
const { name, type } = named[i];
|
|
748
|
+
const varName = !name || name === "_" ? `__ret_blank$${i}` : name;
|
|
749
|
+
this._markOwnership(varName, true);
|
|
750
|
+
this.line(`let ${varName} = ${this.zeroValueForTypeNode(type)};`);
|
|
727
751
|
}
|
|
728
|
-
this.namedReturnVars = named.map((r) =>
|
|
752
|
+
this.namedReturnVars = named.map((r, i) =>
|
|
753
|
+
!r.name || r.name === "_" ? `__ret_blank$${i}` : r.name,
|
|
754
|
+
);
|
|
729
755
|
} else {
|
|
730
756
|
this.namedReturnVars = null;
|
|
731
757
|
}
|
|
732
758
|
fn();
|
|
733
759
|
this.namedReturnVars = prev;
|
|
760
|
+
this._currentDecl = prevDecl;
|
|
734
761
|
}
|
|
735
762
|
|
|
736
763
|
// Emit a function body, wrapping in try/catch/finally for defer if needed.
|
|
737
|
-
|
|
764
|
+
// `fnNode` is the enclosing FuncDecl/MethodDecl/FuncLit; it decides whether
|
|
765
|
+
// the recover-arming prologue is needed (see HELPER_PANIC).
|
|
766
|
+
_genBody(body, fnNode = null) {
|
|
767
|
+
const prevRecoverOk = this._recoverOkInScope;
|
|
768
|
+
this._recoverOkInScope = false;
|
|
769
|
+
const isTarget = Boolean(fnNode?._isDeferTarget);
|
|
770
|
+
const forwards = isTarget && Boolean(fnNode._deferForward);
|
|
771
|
+
if (!forwards && (isTarget || hasDirectRecover(body))) {
|
|
772
|
+
this.useHelper("panicRt");
|
|
773
|
+
this.line(
|
|
774
|
+
"const __recoverOk = __gopanic.armed; __gopanic.armed = false;",
|
|
775
|
+
);
|
|
776
|
+
this._recoverOkInScope = true;
|
|
777
|
+
}
|
|
738
778
|
if (!body._hasDefer) {
|
|
739
779
|
this.genBlock(body);
|
|
780
|
+
this._recoverOkInScope = prevRecoverOk;
|
|
740
781
|
return;
|
|
741
782
|
}
|
|
783
|
+
this.useHelper("panicRt");
|
|
742
784
|
this.line("const __defers = [];");
|
|
743
|
-
this.line("
|
|
785
|
+
this.line("const __frame = { pn: null };");
|
|
744
786
|
this.line("try {");
|
|
745
787
|
this.indented(() => this.genBlock(body));
|
|
746
788
|
this.line("} catch (__err) {");
|
|
747
|
-
this.indented(() =>
|
|
789
|
+
this.indented(() => {
|
|
790
|
+
this.line("__frame.pn = { err: __err, recovered: false };");
|
|
791
|
+
this.line("__gopanic.stack.push(__frame.pn);");
|
|
792
|
+
});
|
|
748
793
|
this.line("} finally {");
|
|
749
794
|
this.indented(() => {
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
)
|
|
753
|
-
|
|
795
|
+
// Runs defers LIFO and rethrows an unrecovered panic.
|
|
796
|
+
this.line("__runDefers(__defers, __frame);");
|
|
797
|
+
if (this.namedReturnVars?.length > 0) {
|
|
798
|
+
const vars = this.namedReturnVars;
|
|
799
|
+
this.line(
|
|
800
|
+
vars.length === 1
|
|
801
|
+
? `return ${vars[0]};`
|
|
802
|
+
: `return [${vars.join(", ")}];`,
|
|
803
|
+
);
|
|
804
|
+
}
|
|
754
805
|
});
|
|
755
806
|
this.line("}");
|
|
756
|
-
// If a recover() cleared
|
|
807
|
+
// If a recover() cleared the panic, execution reaches here.
|
|
757
808
|
// Return named return vars so deferred mutations are visible to the caller.
|
|
758
809
|
if (this.namedReturnVars?.length > 0) {
|
|
759
810
|
const vars = this.namedReturnVars;
|
|
@@ -762,41 +813,21 @@ export class CodeGen {
|
|
|
762
813
|
? `return ${vars[0]};`
|
|
763
814
|
: `return [${vars.join(", ")}];`,
|
|
764
815
|
);
|
|
816
|
+
} else if (this._currentDecl?.returnType) {
|
|
817
|
+
this.line(
|
|
818
|
+
`return ${this.zeroValueForTypeNode(this._currentDecl.returnType)};`,
|
|
819
|
+
);
|
|
765
820
|
}
|
|
821
|
+
this._recoverOkInScope = prevRecoverOk;
|
|
766
822
|
}
|
|
767
823
|
|
|
768
824
|
// Scan AST node for _addressTaken idents on scalars and populate _boxedVars.
|
|
769
825
|
_scanAddressTaken(node) {
|
|
770
|
-
|
|
771
|
-
if (Array.isArray(node)) {
|
|
772
|
-
for (const child of node) this._scanAddressTaken(child);
|
|
773
|
-
return;
|
|
774
|
-
}
|
|
775
|
-
// &x — the operand ident will have _addressTaken set by typechecker
|
|
776
|
-
if (node.kind === "Ident" && node._addressTaken) {
|
|
777
|
-
// Check if the type is a scalar (needs boxing) vs reference type (no boxing)
|
|
778
|
-
const t = node._type;
|
|
779
|
-
if (t && !this._isReferenceType(t)) {
|
|
780
|
-
this._boxedVars.add(node.name);
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
// Recurse into FuncLit too — closures may take address of outer vars
|
|
784
|
-
for (const key of Object.keys(node)) {
|
|
785
|
-
if (key.startsWith("_")) continue;
|
|
786
|
-
this._scanAddressTaken(node[key]);
|
|
787
|
-
}
|
|
826
|
+
scanAddressTaken(node, this._boxedVars);
|
|
788
827
|
}
|
|
789
828
|
|
|
790
829
|
_isReferenceType(t) {
|
|
791
|
-
|
|
792
|
-
const base = t.kind === "named" ? t.underlying : t;
|
|
793
|
-
return (
|
|
794
|
-
base?.kind === "struct" ||
|
|
795
|
-
base?.kind === "slice" ||
|
|
796
|
-
base?.kind === "map" ||
|
|
797
|
-
base?.kind === "func" ||
|
|
798
|
-
base?.kind === "interface"
|
|
799
|
-
);
|
|
830
|
+
return isReferenceType(t);
|
|
800
831
|
}
|
|
801
832
|
|
|
802
833
|
// ── Variable / const declarations ────────────────────────────
|
|
@@ -55,6 +55,13 @@ var __ifp = __ifp || function(x) {
|
|
|
55
55
|
return x;
|
|
56
56
|
};`;
|
|
57
57
|
|
|
58
|
+
// Nil-interface method call guard (strict mode only): matches Go's panic
|
|
59
|
+
// message instead of a JS TypeError.
|
|
60
|
+
export const HELPER_NIL_CALL = `var __nilcall = __nilcall || function(x) {
|
|
61
|
+
if (x == null) throw new Error("runtime error: invalid memory address or nil pointer dereference");
|
|
62
|
+
return x;
|
|
63
|
+
};`;
|
|
64
|
+
|
|
58
65
|
export const HELPER_EQUAL = `var __equal = __equal || function __equal(a, b) {
|
|
59
66
|
if (a === b) return true;
|
|
60
67
|
if (a === null || b === null) return false;
|
|
@@ -86,6 +93,35 @@ export const HELPER_CDIV = `var __cdiv = __cdiv || function(a, b) {
|
|
|
86
93
|
return { re: (a.re * b.re + a.im * b.im) / d, im: (a.im * b.re - a.re * b.im) / d };
|
|
87
94
|
};`;
|
|
88
95
|
|
|
96
|
+
// Panic/defer runtime. \`stack\` holds the in-flight panic of every frame
|
|
97
|
+
// currently running its defers; \`armed\` is set right before a deferred
|
|
98
|
+
// function is invoked and consumed by that function's prologue, so recover()
|
|
99
|
+
// only takes effect when called directly by the deferred function (Go spec).
|
|
100
|
+
export const HELPER_PANIC = `var __gopanic = __gopanic || { stack: [], armed: false };
|
|
101
|
+
var __runDefers = __runDefers || function(defers, frame) {
|
|
102
|
+
for (let i = defers.length - 1; i >= 0; i--) {
|
|
103
|
+
__gopanic.armed = true;
|
|
104
|
+
try { defers[i](); }
|
|
105
|
+
catch (e) {
|
|
106
|
+
if (frame.pn !== null) __gopanic.stack.pop();
|
|
107
|
+
frame.pn = { err: e, recovered: false };
|
|
108
|
+
__gopanic.stack.push(frame.pn);
|
|
109
|
+
} finally { __gopanic.armed = false; }
|
|
110
|
+
}
|
|
111
|
+
if (frame.pn !== null) {
|
|
112
|
+
__gopanic.stack.pop();
|
|
113
|
+
if (!frame.pn.recovered) throw frame.pn.err;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
var __recover = __recover || function(ok) {
|
|
117
|
+
if (!ok) return null;
|
|
118
|
+
const s = __gopanic.stack;
|
|
119
|
+
const top = s.length > 0 ? s[s.length - 1] : null;
|
|
120
|
+
if (top === null || top.recovered) return null;
|
|
121
|
+
top.recovered = true;
|
|
122
|
+
return top.err?.message ?? String(top.err);
|
|
123
|
+
};`;
|
|
124
|
+
|
|
89
125
|
export const HELPER_SPRINTF = `var __sprintf = __sprintf || function(f, ...a) {
|
|
90
126
|
let i = 0;
|
|
91
127
|
return f.replace(/%([#+\\- 0]*)([0-9]*)\\.?([0-9]*)[sdvftxXqobeEgGw%]/g, (m) => {
|
|
@@ -150,6 +186,72 @@ export const HELPER_ERROR_IS = `var __errorIs = __errorIs || function(err, targe
|
|
|
150
186
|
return false;
|
|
151
187
|
};`;
|
|
152
188
|
|
|
189
|
+
// Go-semantics strconv parsers shared by the JS backend and the wasm runtime
|
|
190
|
+
// imports (boundary.js), so both targets agree. Each parser returns
|
|
191
|
+
// [value, errorMessage | null]; integer values are BigInt (the wasm side
|
|
192
|
+
// needs i64, the JS side converts with Number()).
|
|
193
|
+
export const HELPER_STRCONV = `var __strconv_atoi = __strconv_atoi || function(s) {
|
|
194
|
+
if (!/^[+-]?[0-9]+$/.test(s)) return [0n, 'strconv.Atoi: parsing "' + s + '": invalid syntax'];
|
|
195
|
+
const v = BigInt(s);
|
|
196
|
+
if (v > 9223372036854775807n || v < -9223372036854775808n) {
|
|
197
|
+
return [v > 0n ? 9223372036854775807n : -9223372036854775808n, 'strconv.Atoi: parsing "' + s + '": value out of range'];
|
|
198
|
+
}
|
|
199
|
+
return [v, null];
|
|
200
|
+
};
|
|
201
|
+
var __strconv_parse_int = __strconv_parse_int || function(s, base, bits) {
|
|
202
|
+
let str = s, neg = false;
|
|
203
|
+
if (str[0] === "-" || str[0] === "+") { neg = str[0] === "-"; str = str.slice(1); }
|
|
204
|
+
if (base === 0) {
|
|
205
|
+
if (/^0[xX]/.test(str)) { base = 16; str = str.slice(2); }
|
|
206
|
+
else if (/^0[bB]/.test(str)) { base = 2; str = str.slice(2); }
|
|
207
|
+
else if (/^0[oO]/.test(str)) { base = 8; str = str.slice(2); }
|
|
208
|
+
else if (/^0[0-9]/.test(str)) { base = 8; str = str.slice(1); }
|
|
209
|
+
else base = 10;
|
|
210
|
+
str = str.replaceAll("_", "");
|
|
211
|
+
}
|
|
212
|
+
if (base < 2 || base > 36) return [0n, 'strconv.ParseInt: parsing "' + s + '": invalid base ' + base];
|
|
213
|
+
const digits = "0123456789abcdefghijklmnopqrstuvwxyz".slice(0, base);
|
|
214
|
+
if (str === "" || [...str.toLowerCase()].some((c) => !digits.includes(c))) {
|
|
215
|
+
return [0n, 'strconv.ParseInt: parsing "' + s + '": invalid syntax'];
|
|
216
|
+
}
|
|
217
|
+
let v = 0n; const B = BigInt(base);
|
|
218
|
+
for (const c of str.toLowerCase()) v = v * B + BigInt(digits.indexOf(c));
|
|
219
|
+
if (neg) v = -v;
|
|
220
|
+
const w = bits === 0 ? 64 : bits;
|
|
221
|
+
const max = (1n << BigInt(w - 1)) - 1n, min = -(1n << BigInt(w - 1));
|
|
222
|
+
if (v > max || v < min) return [v > max ? max : min, 'strconv.ParseInt: parsing "' + s + '": value out of range'];
|
|
223
|
+
return [v, null];
|
|
224
|
+
};
|
|
225
|
+
var __strconv_parse_float = __strconv_parse_float || function(s) {
|
|
226
|
+
const str = s.replaceAll("_", "");
|
|
227
|
+
if (/^[+-]?(inf|infinity)$/i.test(str)) return [str[0] === "-" ? -Infinity : Infinity, null];
|
|
228
|
+
if (/^[+-]?nan$/i.test(str)) return [NaN, null];
|
|
229
|
+
if (str === "" || !/^[+-]?(\\d+\\.?\\d*|\\.\\d+)([eE][+-]?\\d+)?$|^[+-]?0[xX][0-9a-fA-F]+$/.test(str)) {
|
|
230
|
+
return [0, 'strconv.ParseFloat: parsing "' + s + '": invalid syntax'];
|
|
231
|
+
}
|
|
232
|
+
return [Number(str), null];
|
|
233
|
+
};
|
|
234
|
+
var __strconv_parse_bool = __strconv_parse_bool || function(s) {
|
|
235
|
+
if (["1", "t", "T", "TRUE", "true", "True"].includes(s)) return [true, null];
|
|
236
|
+
if (["0", "f", "F", "FALSE", "false", "False"].includes(s)) return [false, null];
|
|
237
|
+
return [false, 'strconv.ParseBool: parsing "' + s + '": invalid syntax'];
|
|
238
|
+
};
|
|
239
|
+
var __strconv_format_float = __strconv_format_float || function(f, fmtc, prec) {
|
|
240
|
+
const c = String.fromCharCode(fmtc);
|
|
241
|
+
if (!Number.isFinite(f)) return f !== f ? "NaN" : (f > 0 ? "+Inf" : "-Inf");
|
|
242
|
+
if (c === "f") return prec < 0 ? String(f) : f.toFixed(prec);
|
|
243
|
+
if (c === "e" || c === "E") {
|
|
244
|
+
const r = prec < 0 ? f.toExponential() : f.toExponential(prec);
|
|
245
|
+
const o = r.replace(/e([+-])(\\d)$/, "e$10$2");
|
|
246
|
+
return c === "E" ? o.toUpperCase() : o;
|
|
247
|
+
}
|
|
248
|
+
if (c === "g" || c === "G") {
|
|
249
|
+
const r = prec < 0 ? String(f) : f.toPrecision(prec);
|
|
250
|
+
return c === "G" ? r.toUpperCase() : r;
|
|
251
|
+
}
|
|
252
|
+
return String(f);
|
|
253
|
+
};`;
|
|
254
|
+
|
|
153
255
|
export const HELPER_PATH_CLEAN = `var __pathClean = __pathClean || function(p) {
|
|
154
256
|
if (!p) return ".";
|
|
155
257
|
const abs = p.startsWith("/");
|
|
@@ -57,3 +57,35 @@ export function buildSourceMap(sources, mappings, sourcesContent) {
|
|
|
57
57
|
if (sourcesContent) map.sourcesContent = sourcesContent;
|
|
58
58
|
return JSON.stringify(map);
|
|
59
59
|
}
|
|
60
|
+
|
|
61
|
+
// WASM source maps use the convention "generated line 0, generated column =
|
|
62
|
+
// byte offset into the module". `mappings` is Array<{ offset, srcFileIdx,
|
|
63
|
+
// srcLine }> (0-based line), one segment per function body (function-level
|
|
64
|
+
// granularity); DevTools shows the Go file/line for each wasm function.
|
|
65
|
+
export function buildWasmSourceMap(sources, mappings, sourcesContent) {
|
|
66
|
+
const sorted = [...mappings].sort((a, b) => a.offset - b.offset);
|
|
67
|
+
const segments = [];
|
|
68
|
+
let prevOffset = 0;
|
|
69
|
+
let prevSrcFile = 0;
|
|
70
|
+
let prevSrcLine = 0;
|
|
71
|
+
for (const m of sorted) {
|
|
72
|
+
const fileIdx = m.srcFileIdx ?? 0;
|
|
73
|
+
segments.push(
|
|
74
|
+
vlqEncode(m.offset - prevOffset) +
|
|
75
|
+
vlqEncode(fileIdx - prevSrcFile) +
|
|
76
|
+
vlqEncode(m.srcLine - prevSrcLine) +
|
|
77
|
+
vlqEncode(0),
|
|
78
|
+
);
|
|
79
|
+
prevOffset = m.offset;
|
|
80
|
+
prevSrcFile = fileIdx;
|
|
81
|
+
prevSrcLine = m.srcLine;
|
|
82
|
+
}
|
|
83
|
+
const map = {
|
|
84
|
+
version: 3,
|
|
85
|
+
sources: sources.map((s) => s.replace(/\\/g, "/")),
|
|
86
|
+
names: [],
|
|
87
|
+
mappings: segments.join(","),
|
|
88
|
+
};
|
|
89
|
+
if (sourcesContent) map.sourcesContent = sourcesContent;
|
|
90
|
+
return JSON.stringify(map);
|
|
91
|
+
}
|