gofront 1.6.1 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -49
- package/package.json +12 -8
- package/src/backend/js/expressions.js +1627 -148
- package/src/backend/js/fmt-runtime.js +573 -0
- package/src/backend/js/index.js +508 -102
- package/src/backend/js/runtime.js +1997 -106
- package/src/backend/js/statements.js +931 -87
- package/src/backend/js/stdlib/bits.js +87 -14
- package/src/backend/js/stdlib/builder.js +41 -6
- package/src/backend/js/stdlib/bytes.js +253 -7
- package/src/backend/js/stdlib/errors.js +7 -1
- package/src/backend/js/stdlib/fmt.js +349 -10
- package/src/backend/js/stdlib/index.js +19 -2
- package/src/backend/js/stdlib/json.js +1167 -0
- package/src/backend/js/stdlib/maps.js +0 -8
- package/src/backend/js/stdlib/math.js +17 -1
- package/src/backend/js/stdlib/simd.js +34 -0
- package/src/backend/js/stdlib/slices.js +36 -6
- package/src/backend/js/stdlib/strconv.js +10 -5
- package/src/backend/js/stdlib/strings.js +83 -4
- package/src/backend/js/stdlib/testing.js +7 -0
- package/src/backend/js/stdlib/time.js +79 -55
- package/src/backend/js/stdlib/unicode.js +1 -1
- package/src/backend/js/stdlib/utf8.js +1 -1
- package/src/backend/js/templ.js +38 -76
- package/src/backend/js/type-info.js +272 -0
- package/src/backend/shared/fmt-types.js +188 -0
- package/src/backend/wasm/boundary.js +446 -72
- package/src/backend/wasm/emit-builtins.js +320 -13
- package/src/backend/wasm/emit-exprs.js +1404 -199
- package/src/backend/wasm/emit-maps.js +61 -13
- package/src/backend/wasm/emit-stmts.js +1359 -164
- package/src/backend/wasm/emit.js +518 -74
- package/src/backend/wasm/encode.js +128 -13
- package/src/backend/wasm/globals.js +452 -0
- package/src/backend/wasm/index.js +194 -2489
- package/src/backend/wasm/method-expr.js +381 -0
- package/src/backend/wasm/module-emitter.js +2240 -0
- package/src/backend/wasm/monomorph.js +62 -4
- package/src/backend/wasm/optimize.js +4 -2
- package/src/backend/wasm/prescan.js +397 -0
- package/src/backend/wasm/stdlib/bytes.js +1804 -127
- package/src/backend/wasm/stdlib/core.js +1574 -199
- package/src/backend/wasm/stdlib/index.js +3 -5
- package/src/backend/wasm/stdlib/io.js +1567 -150
- package/src/backend/wasm/stdlib/packages.js +2 -0
- package/src/backend/wasm/stdlib/simd.js +499 -0
- package/src/backend/wasm/stdlib/time.js +1041 -12
- package/src/backend/wasm/types.js +221 -15
- package/src/backend/wasm/variadic.js +93 -0
- package/src/backend/wasm/wat.js +84 -20
- package/src/{cli-core.js → cli/cli-core.js} +236 -10
- package/src/{test-runner.js → cli/test-runner.js} +332 -39
- package/src/{vendor.js → cli/vendor.js} +89 -15
- package/src/compiler.js +107 -269
- package/src/dts-parser.js +71 -14
- package/src/index.js +15 -169
- package/src/{lexer.js → lexer/lexer.js} +102 -55
- package/src/{tokens.js → lexer/tokens.js} +1 -0
- package/src/link/sourcemap.js +97 -0
- package/src/link/stdlib.js +515 -0
- package/src/link/wasm.js +294 -0
- package/src/lower/bounds.js +394 -0
- package/src/lower/boxing.js +1 -6
- package/src/lower/captures.js +176 -32
- package/src/lower/embedding.js +2 -1
- package/src/lower/index.js +26 -18
- package/src/lower/json-codecs.js +1398 -0
- package/src/lower/ownership.js +22 -1
- package/src/lower/range.js +86 -1
- package/src/lower/reachability.js +132 -0
- package/src/parser/declarations.js +58 -13
- package/src/parser/expressions.js +29 -7
- package/src/parser/index.js +2 -1
- package/src/parser/statements.js +37 -22
- package/src/parser/struct-tag.js +144 -0
- package/src/{templ-parser.js → parser/templ.js} +138 -15
- package/src/parser/types.js +130 -13
- package/src/resolver.js +22 -1
- package/src/typechecker/assignability.js +215 -11
- package/src/typechecker/const-fold.js +356 -0
- package/src/typechecker/constants.js +256 -0
- package/src/typechecker/expressions.js +960 -99
- package/src/typechecker/index.js +491 -72
- package/src/typechecker/init-order.js +330 -0
- package/src/typechecker/resolve.js +596 -52
- package/src/typechecker/statements.js +157 -17
- package/src/typechecker/stdlib/core.js +333 -55
- package/src/typechecker/stdlib/excluded.js +898 -0
- package/src/typechecker/stdlib/extended.js +359 -26
- package/src/typechecker/{stdlib.js → stdlib/index.js} +6 -4
- package/src/typechecker/stdlib/simd.js +123 -0
- package/src/typechecker/termination.js +1 -1
- package/src/typechecker/types.js +165 -26
- package/stdlib/bytes/iter.go +192 -0
- package/stdlib/bytes/search.go +240 -0
- package/stdlib/bytes/split.go +218 -0
- package/stdlib/cmp/cmp.go +65 -0
- package/stdlib/encoding/json/errors.go +31 -0
- package/stdlib/encoding/json/indent.go +71 -0
- package/stdlib/encoding/json/number.go +12 -0
- package/stdlib/encoding/json/stream.go +173 -0
- package/stdlib/errors/overlay.go +12 -0
- package/stdlib/hash/hash.go +53 -0
- package/stdlib/internal/jsonrt/dec.go +738 -0
- package/stdlib/internal/jsonrt/enc.go +366 -0
- package/stdlib/internal/jsonrt/indent.go +135 -0
- package/stdlib/internal/jsonrt/number.go +27 -0
- package/stdlib/internal/jsonrt/reader.go +73 -0
- package/stdlib/internal/jsonrt/scanner.go +649 -0
- package/stdlib/io/io.go +346 -0
- package/stdlib/iter/iter.go +18 -0
- package/stdlib/maps/iter.go +65 -0
- package/stdlib/math/extra.go +270 -0
- package/stdlib/slices/extra.go +73 -0
- package/stdlib/slices/iter.go +113 -0
- package/stdlib/sort/sort.go +561 -0
- package/stdlib/strconv/quote.go +341 -0
- package/stdlib/strings/extra.go +35 -0
- package/stdlib/strings/iter.go +122 -0
- package/stdlib/sync/extra.go +105 -0
- package/stdlib/unicode/unicode.go +112 -0
- package/stdlib/unicode/utf8/bytes.go +239 -0
- package/src/backend/wasm/const-fold.js +0 -205
- package/src/backend/wasm/emit-stdlib.js +0 -13
- /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
- /package/src/{colors.js → cli/colors.js} +0 -0
- /package/src/{dev-server.js → cli/dev-server.js} +0 -0
- /package/src/{minifier.js → cli/minifier.js} +0 -0
- /package/src/{project-config.js → cli/project-config.js} +0 -0
- /package/src/{pwa.js → cli/pwa.js} +0 -0
- /package/src/{templ-lexer.js → lexer/templ-lexer.js} +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// JS <-> WASM boundary: type classification, marshalling helpers (WASM IR)
|
|
3
3
|
// and the generated JS facade that exposes a wasm package to JS callers.
|
|
4
4
|
//
|
|
5
|
-
// Boundary v1 rules (
|
|
5
|
+
// Boundary v1 rules (plans/v1.5.0/wasm-mvp-plan.md, Phase 5):
|
|
6
6
|
// - primitives: bool <-> i32, narrow ints <-> i32, int/int64 <-> f64 at the
|
|
7
7
|
// boundary (i64 inside; exact for |v| <= 2^53, which the facade already
|
|
8
8
|
// enforces, and keeps BigInt boxing off hot paths), floats direct,
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
// - `*testing.T`: the harness's JS test object, passed as an opaque
|
|
35
35
|
// externref (test methods are routed back to JS via `env.testing_*`).
|
|
36
36
|
|
|
37
|
+
import { BROWSER_GLOBALS } from "../../typechecker/types.js";
|
|
37
38
|
import {
|
|
38
39
|
HELPER_BASE64,
|
|
39
40
|
HELPER_DURATION,
|
|
@@ -41,6 +42,7 @@ import {
|
|
|
41
42
|
HELPER_HEX,
|
|
42
43
|
HELPER_PATH_CLEAN,
|
|
43
44
|
HELPER_SHA256,
|
|
45
|
+
HELPER_SPRINT,
|
|
44
46
|
HELPER_SPRINTF,
|
|
45
47
|
HELPER_STRCONV,
|
|
46
48
|
} from "../js/runtime.js";
|
|
@@ -98,6 +100,10 @@ function funcSigFromAst(t) {
|
|
|
98
100
|
? t.returnType.types
|
|
99
101
|
: [t.returnType]
|
|
100
102
|
: [],
|
|
103
|
+
// `_variadic`: already desugared to a slice by variadic.js
|
|
104
|
+
variadic: Boolean(
|
|
105
|
+
t.params?.at(-1)?.variadic || t.params?.at(-1)?._variadic,
|
|
106
|
+
),
|
|
101
107
|
};
|
|
102
108
|
}
|
|
103
109
|
|
|
@@ -147,6 +153,8 @@ function classifyType(goType, mod) {
|
|
|
147
153
|
return classifyFunc(t, mod);
|
|
148
154
|
case "interface":
|
|
149
155
|
return classifyInterface(t, mod);
|
|
156
|
+
case "simd":
|
|
157
|
+
return classifySimd(t, mod);
|
|
150
158
|
default:
|
|
151
159
|
return {
|
|
152
160
|
k: "unsupported",
|
|
@@ -211,6 +219,15 @@ function classifyBasic(n) {
|
|
|
211
219
|
}
|
|
212
220
|
|
|
213
221
|
function classifyNamed(t, mod) {
|
|
222
|
+
if (
|
|
223
|
+
t.name === "simd.F32x4" ||
|
|
224
|
+
t.name === "F32x4" ||
|
|
225
|
+
t.name === "simd.Mask32x4" ||
|
|
226
|
+
t.name === "Mask32x4" ||
|
|
227
|
+
t.underlying?.kind === "simd"
|
|
228
|
+
) {
|
|
229
|
+
return classifySimd(t.underlying ?? t, mod, t.name);
|
|
230
|
+
}
|
|
214
231
|
if (t.name === "error") return { k: "error", wType: "anyref" };
|
|
215
232
|
if (t.name === "time.Duration")
|
|
216
233
|
return { k: "i64", unsigned: false, wType: "i64" };
|
|
@@ -224,6 +241,18 @@ function classifyNamed(t, mod) {
|
|
|
224
241
|
return { k: "unsupported", what: `named type '${t.name}'` };
|
|
225
242
|
}
|
|
226
243
|
|
|
244
|
+
// F32x4 crosses as a copied 4-element Float32Array, Mask32x4 as a 4-element
|
|
245
|
+
// boolean array. Both travel through the wasm boundary as a GC array of the
|
|
246
|
+
// four i32 lane words (`arr`), converted by __v128_from/__v128_to.
|
|
247
|
+
function classifySimd(t, mod, name = "") {
|
|
248
|
+
const isMask = t.simd === "Mask32x4" || name.endsWith("Mask32x4");
|
|
249
|
+
const arr = classifyArray(
|
|
250
|
+
{ kind: "array", elem: { kind: "basic", name: "int32" }, size: 4 },
|
|
251
|
+
mod,
|
|
252
|
+
);
|
|
253
|
+
return { k: isMask ? "mask4" : "f32x4", wType: "v128", arr };
|
|
254
|
+
}
|
|
255
|
+
|
|
227
256
|
function classifyAnonStruct(t, mod) {
|
|
228
257
|
const rawFields = [];
|
|
229
258
|
if (t.fields instanceof Map) {
|
|
@@ -267,13 +296,22 @@ function classifyPointer(t, mod) {
|
|
|
267
296
|
if (base.k === "anon_struct" && !base.ptr) return { ...base, ptr: true };
|
|
268
297
|
if (base.k === "time" && !base.ptr) return { ...base, ptr: true };
|
|
269
298
|
const boxInfo = mod.getBoxType(t.base);
|
|
299
|
+
const ptrInfo = mod.getPtrType ? mod.getPtrType(t.base) : null;
|
|
270
300
|
const key = `${base.k}${base.unsigned ? "_u" : ""}_${boxInfo.typeIndex}`;
|
|
271
301
|
return {
|
|
272
302
|
k: "ptr_box",
|
|
273
303
|
base,
|
|
274
304
|
boxInfo,
|
|
305
|
+
ptrInfo,
|
|
275
306
|
key,
|
|
276
|
-
wType:
|
|
307
|
+
wType: ptrInfo
|
|
308
|
+
? {
|
|
309
|
+
kind: "ref",
|
|
310
|
+
nullable: true,
|
|
311
|
+
typeIndex: ptrInfo.typeIndex,
|
|
312
|
+
typeName: ptrInfo.name,
|
|
313
|
+
}
|
|
314
|
+
: { kind: "ref", nullable: true, typeIndex: boxInfo.typeIndex },
|
|
277
315
|
};
|
|
278
316
|
}
|
|
279
317
|
|
|
@@ -337,7 +375,11 @@ function classifySlice(t, mod) {
|
|
|
337
375
|
k: "slice",
|
|
338
376
|
elem,
|
|
339
377
|
sliceInfo,
|
|
340
|
-
|
|
378
|
+
// []F32x4 and []Mask32x4 share a wasm slice type but cross differently,
|
|
379
|
+
// so they get separate helper keys.
|
|
380
|
+
key:
|
|
381
|
+
wTypeKey(sliceInfo.elemWType) +
|
|
382
|
+
(elem.k === "f32x4" || elem.k === "mask4" ? `_${elem.k}` : ""),
|
|
341
383
|
wType: { kind: "ref", nullable: true, typeIndex: sliceInfo.typeIndex },
|
|
342
384
|
};
|
|
343
385
|
}
|
|
@@ -372,6 +414,9 @@ function classifySig(t, mod) {
|
|
|
372
414
|
}
|
|
373
415
|
|
|
374
416
|
function classifyFunc(t, mod) {
|
|
417
|
+
// A variadic func value is a slice-taking closure in wasm but a rest-param
|
|
418
|
+
// function in JS; no adapter yet, so it may not cross.
|
|
419
|
+
if (t.variadic) return { k: "unsupported", what: "variadic func value" };
|
|
375
420
|
const sig = classifySig(t, mod);
|
|
376
421
|
if (sig.k === "unsupported") return sig;
|
|
377
422
|
const closureInfo = mod.getClosureType(t);
|
|
@@ -436,7 +481,7 @@ function classifyNamedInterface(t, mod) {
|
|
|
436
481
|
const desc = {
|
|
437
482
|
k: "iface",
|
|
438
483
|
name: t.name,
|
|
439
|
-
key: t.name,
|
|
484
|
+
key: t.name.replaceAll(".", "_"),
|
|
440
485
|
wType: "anyref",
|
|
441
486
|
methods: [],
|
|
442
487
|
concrete: [],
|
|
@@ -506,6 +551,9 @@ function literalConst(spec, i) {
|
|
|
506
551
|
}
|
|
507
552
|
|
|
508
553
|
function unsupportedMsg(where, what) {
|
|
554
|
+
if (typeof what === "string" && what.includes("boundary")) {
|
|
555
|
+
return `${where}: ${what}`;
|
|
556
|
+
}
|
|
509
557
|
return `${where}: ${what} is not yet supported across the wasm boundary (planned)`;
|
|
510
558
|
}
|
|
511
559
|
|
|
@@ -532,7 +580,7 @@ export function collectBoundaryMeta(progs, funcDecls, mod) {
|
|
|
532
580
|
} else if (d.kind === "ConstDecl" && pkgTarget === "wasm") {
|
|
533
581
|
collectConstDecl(d, pkgName, meta, errors);
|
|
534
582
|
} else if (d.kind === "VarDecl" && pkgTarget === "wasm") {
|
|
535
|
-
|
|
583
|
+
collectVarDecl(d, meta, mod);
|
|
536
584
|
}
|
|
537
585
|
}
|
|
538
586
|
}
|
|
@@ -614,17 +662,24 @@ function checkDesc(desc, where, errors) {
|
|
|
614
662
|
}
|
|
615
663
|
|
|
616
664
|
// Exported `var Buf = shared.NewFloat32(n)` globals are exposed to JS as
|
|
617
|
-
// TypedArray views (read once after instantiation; sizing is fixed).
|
|
618
|
-
|
|
665
|
+
// TypedArray views (read once after instantiation; sizing is fixed). Other
|
|
666
|
+
// exported vars of a supported boundary type are read once when the module
|
|
667
|
+
// loads, after package initialization (`var Limit = 40` is `Limit` in the
|
|
668
|
+
// facade); a var of an unsupported type is left out.
|
|
669
|
+
function collectVarDecl(d, meta, mod) {
|
|
619
670
|
for (const spec of d.decls ?? d.specs ?? [d]) {
|
|
620
671
|
const names = spec.names ?? (spec.name ? [spec.name] : []);
|
|
621
672
|
const values = spec.value ?? (spec.init ? [spec.init] : []);
|
|
622
673
|
for (let i = 0; i < names.length; i++) {
|
|
623
674
|
const name = names[i];
|
|
624
675
|
if (!isExported(name)) continue;
|
|
625
|
-
const goType =
|
|
626
|
-
|
|
627
|
-
|
|
676
|
+
const goType =
|
|
677
|
+
spec.type ?? mod.resolveGlobal(name)?.goType ?? values[i]?._type;
|
|
678
|
+
const shared = Boolean(getSharedInfo(goType, mod.checker));
|
|
679
|
+
const desc = classifyType(goType, mod);
|
|
680
|
+
if (!shared && (!desc || desc.k === "unsupported" || desc.k === "void"))
|
|
681
|
+
continue;
|
|
682
|
+
meta.vars.push({ name, desc, shared });
|
|
628
683
|
}
|
|
629
684
|
}
|
|
630
685
|
}
|
|
@@ -639,6 +694,7 @@ function collectMethodMeta(fn, meta, errors, mod) {
|
|
|
639
694
|
exportName: fn._exportName,
|
|
640
695
|
fnName: fn.name,
|
|
641
696
|
ptrRecv: fn.params[0].type?.kind === "pointer",
|
|
697
|
+
variadic: Boolean(fn.params.at(-1)?._variadic),
|
|
642
698
|
params: fn.params
|
|
643
699
|
.slice(1)
|
|
644
700
|
.map((p) => checkDesc(classifyType(p.type, mod), where, errors)),
|
|
@@ -655,6 +711,7 @@ function collectFuncMeta(fn, meta, errors, mod) {
|
|
|
655
711
|
name: fn.name,
|
|
656
712
|
exportName: fn.name,
|
|
657
713
|
pkgName: fn._pkgName,
|
|
714
|
+
variadic: Boolean(fn.params?.at(-1)?._variadic),
|
|
658
715
|
params: (fn.params ?? []).map((p) =>
|
|
659
716
|
checkDesc(classifyType(p.type, mod), where, errors),
|
|
660
717
|
),
|
|
@@ -693,6 +750,11 @@ function visitNeed(desc, needs, errors, mod) {
|
|
|
693
750
|
case "error":
|
|
694
751
|
needs.error = true;
|
|
695
752
|
return;
|
|
753
|
+
case "f32x4":
|
|
754
|
+
case "mask4":
|
|
755
|
+
needs.simd = desc.arr;
|
|
756
|
+
visitNeed(desc.arr, needs, errors, mod);
|
|
757
|
+
return;
|
|
696
758
|
case "time":
|
|
697
759
|
needs.time = true;
|
|
698
760
|
return;
|
|
@@ -773,6 +835,7 @@ function collectNeeds(meta) {
|
|
|
773
835
|
ifaces: new Map(), // interface name -> desc
|
|
774
836
|
error: false,
|
|
775
837
|
time: false,
|
|
838
|
+
simd: null, // array desc carrying vectors across the boundary
|
|
776
839
|
shared: false, // any shared buffer on the surface
|
|
777
840
|
};
|
|
778
841
|
const errors = [];
|
|
@@ -817,16 +880,22 @@ export function scanBoundaryImports(mod, meta) {
|
|
|
817
880
|
function isGcRef(wType) {
|
|
818
881
|
return wType === "anyref" || (typeof wType === "object" && wType !== null);
|
|
819
882
|
}
|
|
883
|
+
// A v128 crosses as an externref holding a GC array of its four i32 lanes;
|
|
884
|
+
// the converting helpers are registered first thing in addBoundaryHelpers.
|
|
885
|
+
const v128Conv = { from: null, to: null };
|
|
886
|
+
|
|
820
887
|
function extType(wType) {
|
|
888
|
+
if (wType === "v128") return "externref";
|
|
821
889
|
if (wType === "i64") return "f64";
|
|
822
890
|
if (isGcRef(wType)) return "externref";
|
|
823
891
|
return wType;
|
|
824
892
|
}
|
|
825
893
|
function needsConv(wTypes) {
|
|
826
|
-
return wTypes.some((w) => w === "i64" || isGcRef(w));
|
|
894
|
+
return wTypes.some((w) => w === "i64" || w === "v128" || isGcRef(w));
|
|
827
895
|
}
|
|
828
896
|
// Instructions converting the value on the stack from its boundary form.
|
|
829
897
|
function fromExt(wType) {
|
|
898
|
+
if (wType === "v128") return [{ op: "call", funcIndex: v128Conv.from }];
|
|
830
899
|
if (wType === "i64") return [{ op: "i64.trunc_sat_f64_s" }];
|
|
831
900
|
if (wType === "anyref") return [{ op: "any.convert_extern" }];
|
|
832
901
|
if (isGcRef(wType)) {
|
|
@@ -839,6 +908,7 @@ function fromExt(wType) {
|
|
|
839
908
|
}
|
|
840
909
|
// Instructions converting the value on the stack to its boundary form.
|
|
841
910
|
function toExt(wType) {
|
|
911
|
+
if (wType === "v128") return [{ op: "call", funcIndex: v128Conv.to }];
|
|
842
912
|
if (wType === "i64") return [{ op: "f64.convert_i64_s" }];
|
|
843
913
|
if (isGcRef(wType)) return [{ op: "extern.convert_any" }];
|
|
844
914
|
return [];
|
|
@@ -857,6 +927,8 @@ function convertResults(wTypes, conv, localBase) {
|
|
|
857
927
|
}
|
|
858
928
|
|
|
859
929
|
function zeroInstr(wType) {
|
|
930
|
+
if (wType === "v128")
|
|
931
|
+
return { op: "v128.const", bytes: new Array(16).fill(0) };
|
|
860
932
|
if (wType === "i64") return { op: "i64.const", value: 0n };
|
|
861
933
|
if (wType === "f32" || wType === "f64")
|
|
862
934
|
return { op: `${wType}.const`, value: 0 };
|
|
@@ -908,6 +980,47 @@ export function addBoundaryHelpers(mod, meta) {
|
|
|
908
980
|
return idx;
|
|
909
981
|
};
|
|
910
982
|
const get = (i) => ({ op: "local.get", index: i });
|
|
983
|
+
if (needs.simd) {
|
|
984
|
+
const { arrInfo } = needs.simd;
|
|
985
|
+
const arrRef = needs.simd.wType;
|
|
986
|
+
v128Conv.to = addFunc(
|
|
987
|
+
"__v128_to",
|
|
988
|
+
["v128"],
|
|
989
|
+
["externref"],
|
|
990
|
+
[
|
|
991
|
+
...[0, 1, 2, 3].flatMap((lane) => [
|
|
992
|
+
get(0),
|
|
993
|
+
{ op: "i32x4.extract_lane", lane },
|
|
994
|
+
]),
|
|
995
|
+
{ op: "array.new_fixed", typeIndex: arrInfo.typeIndex, size: 4 },
|
|
996
|
+
{ op: "extern.convert_any" },
|
|
997
|
+
],
|
|
998
|
+
false,
|
|
999
|
+
);
|
|
1000
|
+
v128Conv.from = addFunc(
|
|
1001
|
+
"__v128_from",
|
|
1002
|
+
["externref"],
|
|
1003
|
+
["v128"],
|
|
1004
|
+
[
|
|
1005
|
+
get(0),
|
|
1006
|
+
{ op: "any.convert_extern" },
|
|
1007
|
+
{ op: "ref.cast_null", typeIndex: arrInfo.typeIndex },
|
|
1008
|
+
{ op: "local.set", index: 1 },
|
|
1009
|
+
get(1),
|
|
1010
|
+
{ op: "i32.const", value: 0 },
|
|
1011
|
+
{ op: "array.get", typeIndex: arrInfo.typeIndex },
|
|
1012
|
+
{ op: "i32x4.splat" },
|
|
1013
|
+
...[1, 2, 3].flatMap((lane) => [
|
|
1014
|
+
get(1),
|
|
1015
|
+
{ op: "i32.const", value: lane },
|
|
1016
|
+
{ op: "array.get", typeIndex: arrInfo.typeIndex },
|
|
1017
|
+
{ op: "i32x4.replace_lane", lane },
|
|
1018
|
+
]),
|
|
1019
|
+
],
|
|
1020
|
+
false,
|
|
1021
|
+
[arrRef],
|
|
1022
|
+
);
|
|
1023
|
+
}
|
|
911
1024
|
// Parameter `i` converted from its boundary form.
|
|
912
1025
|
const getIn = (i, wType) => [get(i), ...fromExt(wType)];
|
|
913
1026
|
const EXT = "externref";
|
|
@@ -1151,7 +1264,8 @@ export function addBoundaryHelpers(mod, meta) {
|
|
|
1151
1264
|
const paramW = sig.params;
|
|
1152
1265
|
const resultW = sig.results;
|
|
1153
1266
|
if (!needsConv(paramW) && !needsConv(resultW)) return;
|
|
1154
|
-
|
|
1267
|
+
// Exported names become JS identifiers: no dot of a qualified type.
|
|
1268
|
+
entry.callName = `__x_${entry.exportName}`.replaceAll(".", "_");
|
|
1155
1269
|
addFunc(
|
|
1156
1270
|
entry.callName,
|
|
1157
1271
|
paramW.map(extType),
|
|
@@ -1192,7 +1306,7 @@ export function addBoundaryHelpers(mod, meta) {
|
|
|
1192
1306
|
[get(0), get(1), { op: "struct.new", typeIndex }, ...toExt(wType)],
|
|
1193
1307
|
);
|
|
1194
1308
|
for (const v of meta.vars ?? []) {
|
|
1195
|
-
const g = mod.resolveGlobal(v.name);
|
|
1309
|
+
const g = v.shared ? mod.resolveGlobal(v.name) : null;
|
|
1196
1310
|
if (!g) continue;
|
|
1197
1311
|
addFunc(
|
|
1198
1312
|
`__var_${v.name}`,
|
|
@@ -1264,12 +1378,18 @@ export function addBoundaryHelpers(mod, meta) {
|
|
|
1264
1378
|
[EXT],
|
|
1265
1379
|
[...getIn(0, w), { op: "struct.new", typeIndex: ti }, ...toExt(ref)],
|
|
1266
1380
|
);
|
|
1381
|
+
const cast = {
|
|
1382
|
+
op: "ref.cast",
|
|
1383
|
+
typeIndex: boxInfo.typeIndex,
|
|
1384
|
+
typeName: boxInfo.name,
|
|
1385
|
+
};
|
|
1267
1386
|
addFunc(
|
|
1268
1387
|
`__box_get_${key}`,
|
|
1269
1388
|
[EXT],
|
|
1270
1389
|
[extType(w)],
|
|
1271
1390
|
[
|
|
1272
1391
|
...getIn(0, ref),
|
|
1392
|
+
cast,
|
|
1273
1393
|
{ op: "struct.get", typeIndex: ti, fieldIndex: 0 },
|
|
1274
1394
|
...toExt(w),
|
|
1275
1395
|
],
|
|
@@ -1280,6 +1400,7 @@ export function addBoundaryHelpers(mod, meta) {
|
|
|
1280
1400
|
[],
|
|
1281
1401
|
[
|
|
1282
1402
|
...getIn(0, ref),
|
|
1403
|
+
cast,
|
|
1283
1404
|
...getIn(1, w),
|
|
1284
1405
|
{ op: "struct.set", typeIndex: ti, fieldIndex: 0 },
|
|
1285
1406
|
],
|
|
@@ -1372,10 +1493,11 @@ export function addBoundaryHelpers(mod, meta) {
|
|
|
1372
1493
|
[EXT],
|
|
1373
1494
|
[
|
|
1374
1495
|
get(0),
|
|
1375
|
-
{ op: "f64.const", value: 1000000 },
|
|
1376
|
-
{ op: "f64.mul" },
|
|
1377
1496
|
{ op: "i64.trunc_sat_f64_s" },
|
|
1497
|
+
{ op: "i64.const", value: 1000000n },
|
|
1498
|
+
{ op: "i64.mul" },
|
|
1378
1499
|
get(1),
|
|
1500
|
+
{ op: "ref.null", heapType: "any" },
|
|
1379
1501
|
{ op: "struct.new", typeIndex: timeInfo.typeIndex },
|
|
1380
1502
|
...toExt(timeWType),
|
|
1381
1503
|
],
|
|
@@ -1427,6 +1549,16 @@ export function addBoundaryHelpers(mod, meta) {
|
|
|
1427
1549
|
[{ op: "global.get", index: g.index }, ...toExt(c.desc.wType)],
|
|
1428
1550
|
);
|
|
1429
1551
|
}
|
|
1552
|
+
for (const v of meta.vars ?? []) {
|
|
1553
|
+
const g = v.shared ? null : mod.resolveGlobal(v.name);
|
|
1554
|
+
if (!g) continue;
|
|
1555
|
+
addFunc(
|
|
1556
|
+
`__var_${v.name}`,
|
|
1557
|
+
[],
|
|
1558
|
+
[extType(v.desc.wType)],
|
|
1559
|
+
[{ op: "global.get", index: g.index }, ...toExt(v.desc.wType)],
|
|
1560
|
+
);
|
|
1561
|
+
}
|
|
1430
1562
|
}
|
|
1431
1563
|
|
|
1432
1564
|
// `(externref) -> i32`: `ref.test`s param 0 against each type index and
|
|
@@ -1511,6 +1643,10 @@ function inExpr(desc, v) {
|
|
|
1511
1643
|
return `(${v} === undefined ? null : ${v})`;
|
|
1512
1644
|
case "error":
|
|
1513
1645
|
return `${P}errin(${v})`;
|
|
1646
|
+
case "f32x4":
|
|
1647
|
+
return `${P}f32x4in(${v})`;
|
|
1648
|
+
case "mask4":
|
|
1649
|
+
return `${P}mask4in(${v})`;
|
|
1514
1650
|
case "time":
|
|
1515
1651
|
return desc.ptr ? `${P}timein_ptr(${v})` : `${P}timein(${v})`;
|
|
1516
1652
|
case "map":
|
|
@@ -1555,6 +1691,10 @@ function outExpr(desc, v) {
|
|
|
1555
1691
|
return `(${v} ?? "")`;
|
|
1556
1692
|
case "error":
|
|
1557
1693
|
return `${P}errout(${v})`;
|
|
1694
|
+
case "f32x4":
|
|
1695
|
+
return `${P}f32x4out(${v})`;
|
|
1696
|
+
case "mask4":
|
|
1697
|
+
return `${P}mask4out(${v})`;
|
|
1558
1698
|
case "time":
|
|
1559
1699
|
return desc.ptr ? `${P}timeout_ptr(${v})` : `${P}timeout(${v})`;
|
|
1560
1700
|
case "map":
|
|
@@ -1595,6 +1735,10 @@ function zeroExpr(desc) {
|
|
|
1595
1735
|
return "0";
|
|
1596
1736
|
case "string":
|
|
1597
1737
|
return '""';
|
|
1738
|
+
case "f32x4":
|
|
1739
|
+
return "new Float32Array(4)";
|
|
1740
|
+
case "mask4":
|
|
1741
|
+
return "[false, false, false, false]";
|
|
1598
1742
|
case "error":
|
|
1599
1743
|
case "time":
|
|
1600
1744
|
case "map":
|
|
@@ -1715,21 +1859,23 @@ function ${P}imports(stringTable, extraEnv, tag, write) {
|
|
|
1715
1859
|
let lineBuf = "";
|
|
1716
1860
|
let targs = [];
|
|
1717
1861
|
let fmtStructMap = new WeakMap();
|
|
1862
|
+
// A value whose type has String()/Error(): the method already ran in WASM.
|
|
1863
|
+
// __gfUnbox hands fmt the raw value for the other verbs.
|
|
1864
|
+
class FmtStr { constructor(r, s) { this.r = r; this.s = s; } __gfUnbox() { return this.r; } String() { return this.s; } }
|
|
1865
|
+
class FmtErr { constructor(r, s) { this.r = r; this.s = s; } __gfUnbox() { return this.r; } Error() { return this.s; } }
|
|
1718
1866
|
const late = { exports: null };
|
|
1719
|
-
|
|
1867
|
+
// Go separates operands with a space unconditionally, so track whether the
|
|
1868
|
+
// current line already holds an operand (an empty string leaves lineBuf empty).
|
|
1869
|
+
let started = false;
|
|
1870
|
+
const flush = () => { write(lineBuf); lineBuf = ""; started = false; };
|
|
1720
1871
|
const print = (v) => {
|
|
1721
|
-
const s = String(v);
|
|
1722
|
-
if (
|
|
1723
|
-
|
|
1724
|
-
}
|
|
1872
|
+
const s = Object.is(v, -0) ? "-0" : String(v);
|
|
1873
|
+
if (started) lineBuf += " ";
|
|
1874
|
+
started = true;
|
|
1725
1875
|
lineBuf += s;
|
|
1726
1876
|
};
|
|
1727
1877
|
const println = (v) => {
|
|
1728
|
-
|
|
1729
|
-
if (lineBuf.length > 0 && !lineBuf.endsWith(" ") && !s.startsWith(" ")) {
|
|
1730
|
-
lineBuf += " ";
|
|
1731
|
-
}
|
|
1732
|
-
lineBuf += s;
|
|
1878
|
+
print(v);
|
|
1733
1879
|
flush();
|
|
1734
1880
|
};
|
|
1735
1881
|
const targ = (v) => { targs.push(v); };
|
|
@@ -1769,6 +1915,7 @@ function ${P}imports(stringTable, extraEnv, tag, write) {
|
|
|
1769
1915
|
},
|
|
1770
1916
|
"str_to_upper": (s) => (s ? s.toUpperCase() : ""),
|
|
1771
1917
|
"str_to_lower": (s) => (s ? s.toLowerCase() : ""),
|
|
1918
|
+
"str_to_valid_utf8": (s, repl) => (s ? s.replace(/(?:[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF])+/g, repl ?? "") : (s ?? "")),
|
|
1772
1919
|
"str_trim_space": (s) => (s ? s.trim() : ""),
|
|
1773
1920
|
"str_contains": (s, sub) => (s && sub !== undefined ? (s.includes(sub) ? 1 : 0) : 0),
|
|
1774
1921
|
"str_has_prefix": (s, pre) => (s && pre !== undefined ? (s.startsWith(pre) ? 1 : 0) : 0),
|
|
@@ -1785,7 +1932,7 @@ function ${P}imports(stringTable, extraEnv, tag, write) {
|
|
|
1785
1932
|
},
|
|
1786
1933
|
"str_from_i64": (n) => String(n),
|
|
1787
1934
|
"str_from_i32": (n) => String(n),
|
|
1788
|
-
"str_from_f64": (f) => String(f),
|
|
1935
|
+
"str_from_f64": (f) => (Object.is(f, -0) ? "-0" : String(f)),
|
|
1789
1936
|
"is_string": (v) => (typeof v === "string" ? 1 : 0),
|
|
1790
1937
|
"str_to_bytes": (s) => {
|
|
1791
1938
|
utf8Encoded = new TextEncoder().encode(s ?? "");
|
|
@@ -1818,28 +1965,55 @@ function ${P}imports(stringTable, extraEnv, tag, write) {
|
|
|
1818
1965
|
"fmt_new_map": () => new Map(),
|
|
1819
1966
|
"fmt_map_set": (m, k, v) => { m.set(k, v); },
|
|
1820
1967
|
"fmt_new_struct": (ref, isPtr) => {
|
|
1821
|
-
|
|
1822
|
-
|
|
1968
|
+
// One object per struct and per kind: a value and a pointer to it
|
|
1969
|
+
// in the same call (fmt.Println(&s, s)) must stay distinct.
|
|
1970
|
+
const slot = isPtr;
|
|
1971
|
+
if (isPtr === 2) {
|
|
1972
|
+
const seenBox = ref !== null && ref !== undefined && typeof ref === "object" ? fmtStructMap.get(ref) : undefined;
|
|
1973
|
+
if (seenBox?.[2]) return seenBox[2];
|
|
1974
|
+
const b = { value: null, __p: true, __box: true };
|
|
1975
|
+
if (ref !== null && ref !== undefined && typeof ref === "object") {
|
|
1976
|
+
if (seenBox) seenBox[2] = b;
|
|
1977
|
+
else fmtStructMap.set(ref, [undefined, undefined, b]);
|
|
1978
|
+
}
|
|
1979
|
+
return b;
|
|
1823
1980
|
}
|
|
1981
|
+
const isRef = ref !== null && ref !== undefined && typeof ref === "object";
|
|
1982
|
+
const seen = isRef ? fmtStructMap.get(ref) : undefined;
|
|
1983
|
+
if (seen?.[slot]) return seen[slot];
|
|
1824
1984
|
const o = {};
|
|
1825
1985
|
Object.defineProperty(o, "__struct", { value: true, configurable: true });
|
|
1826
1986
|
if (isPtr !== 0) Object.defineProperty(o, "__p", { value: true, configurable: true });
|
|
1827
|
-
if (
|
|
1987
|
+
if (isRef) {
|
|
1988
|
+
if (seen) seen[slot] = o;
|
|
1989
|
+
else fmtStructMap.set(ref, slot ? [undefined, o] : [o, undefined]);
|
|
1990
|
+
}
|
|
1828
1991
|
return o;
|
|
1829
1992
|
},
|
|
1993
|
+
"fmt_struct_type": (o, i) => { Object.defineProperty(o, "__tn", { value: stringTable[i], configurable: true }); },
|
|
1830
1994
|
"fmt_struct_set": (o, k, v) => { o[typeof k === "number" ? stringTable[k] : k] = v; },
|
|
1831
1995
|
"fmt_box_i32": (v) => v,
|
|
1832
1996
|
"fmt_box_i64": (v) => (v > ${P}MAX || v < -${P}MAX ? v : Number(v)),
|
|
1997
|
+
"fmt_box_u64": (v) => { v = BigInt.asUintN(64, v); return v > ${P}MAX ? v : Number(v); },
|
|
1833
1998
|
"fmt_box_f64": (v) => v,
|
|
1834
1999
|
"fmt_box_bool": (v) => v !== 0,
|
|
2000
|
+
"fmt_new_stringer": (r, s, isErr) => (isErr !== 0 ? new FmtErr(r, s) : new FmtStr(r, s)),
|
|
1835
2001
|
"fmt_new_box": () => ({ value: null, __p: true, __box: true }),
|
|
1836
2002
|
"testing_arg_i32": targ, "testing_arg_f32": targ, "testing_arg_f64": targ,
|
|
1837
2003
|
"testing_arg_str": targ, "testing_arg_any": targ,
|
|
1838
2004
|
"testing_arg_i64": (v) => targ(v > ${P}MAX || v < -${P}MAX ? v : Number(v)),
|
|
1839
2005
|
"testing_arg_bool": (v) => targ(v !== 0),
|
|
2006
|
+
"testing_arg_u64": (v) => { v = BigInt.asUintN(64, v); targ(v > ${P}MAX ? v : Number(v)); },
|
|
2007
|
+
"fmt_arg_type": (i) => { if (targs.length > 0) targs[targs.length - 1] = __ft(targs[targs.length - 1], JSON.parse(stringTable[i])); },
|
|
2008
|
+
"testing_arg_spread": (a) => { if (a) for (const v of a) targs.push(v); },
|
|
2009
|
+
"testing_arg_err": (s) => targ({ Error() { return s; } }),
|
|
1840
2010
|
"testing_call": (t, i) => { const a = targs; targs = []; fmtStructMap = new WeakMap(); t[stringTable[i]](...a); },
|
|
1841
2011
|
"testing_name": (t) => t.Name(),
|
|
1842
|
-
"testing_flag": (t, i) => (t[stringTable[i]]()
|
|
2012
|
+
"testing_flag": (t, i) => Number(t[stringTable[i]]()),
|
|
2013
|
+
"testing_n": (t) => BigInt(t.N ?? 0),
|
|
2014
|
+
"testing_elapsed": (t) => BigInt(t.Elapsed()),
|
|
2015
|
+
"testing_context": (t) => t.Context(),
|
|
2016
|
+
"testing_output": (t) => t.Output(),
|
|
1843
2017
|
};
|
|
1844
2018
|
// Stdlib imports (strings/strconv/utf8/fmt/t.Run) are a separate block so
|
|
1845
2019
|
// modules that never call them do not ship it (see WASM_STDLIB_JS).
|
|
@@ -1856,7 +2030,7 @@ function ${P}imports(stringTable, extraEnv, tag, write) {
|
|
|
1856
2030
|
}
|
|
1857
2031
|
Object.assign(env, extraEnv);
|
|
1858
2032
|
const m = {};
|
|
1859
|
-
for (const k of ["sin", "cos", "tan", "asin", "acos", "atan", "atan2", "pow", "exp", "log", "log2", "log10", "cbrt", "hypot", "random", "sinh", "cosh", "tanh"]) m[k] = Math[k];
|
|
2033
|
+
for (const k of ["sin", "cos", "tan", "asin", "acos", "atan", "atan2", "pow", "exp", "log", "log2", "log10", "cbrt", "hypot", "random", "sinh", "cosh", "tanh", "log1p", "expm1"]) m[k] = Math[k];
|
|
1860
2034
|
m.mod = (x, y) => x % y;
|
|
1861
2035
|
// Go rounds half away from zero; Math.round rounds half toward +Infinity.
|
|
1862
2036
|
m.round = (x) => Math.sign(x) * Math.round(Math.abs(x));
|
|
@@ -1868,30 +2042,16 @@ function ${P}imports(stringTable, extraEnv, tag, write) {
|
|
|
1868
2042
|
// module imports one of the packages that use it.
|
|
1869
2043
|
export const WASM_STDLIB_JS = `${HELPER_ERROR}
|
|
1870
2044
|
${HELPER_SPRINTF}
|
|
2045
|
+
${HELPER_SPRINT}
|
|
1871
2046
|
${HELPER_STRCONV}
|
|
1872
2047
|
${HELPER_PATH_CLEAN}
|
|
1873
2048
|
${HELPER_DURATION}
|
|
1874
2049
|
${HELPER_SHA256}
|
|
1875
2050
|
${HELPER_HEX}
|
|
1876
2051
|
${HELPER_BASE64}
|
|
1877
|
-
function ${P}
|
|
1878
|
-
let parts = [];
|
|
1879
|
-
let cutBefore = "", cutAfter = "";
|
|
1880
|
-
let strconvErr = null, strconvErrKind = 0;
|
|
1881
|
-
let decodeSize = 0;
|
|
1882
|
-
let replacerTarget = "", replacerPairs = [];
|
|
1883
|
-
let splitDir = "", splitFile = "";
|
|
1884
|
-
let joinParts = [];
|
|
1885
|
-
let matchHasErr = 0;
|
|
1886
|
-
let trimStart = 0, trimEnd = 0;
|
|
2052
|
+
function ${P}stdlib_fmt(stringTable, io) {
|
|
1887
2053
|
let sscanTokens = [];
|
|
1888
2054
|
let sscanHasErr = 0, sscanErrMsg = "";
|
|
1889
|
-
let timeParseErr = null, timeParseOffset = 0;
|
|
1890
|
-
let rawBytes = new Uint8Array(0);
|
|
1891
|
-
let rawPos = 0;
|
|
1892
|
-
let hexErr = null;
|
|
1893
|
-
let base64Err = null;
|
|
1894
|
-
let customBase64Encodings = [];
|
|
1895
2055
|
return {
|
|
1896
2056
|
"fmt_sscan_init": (s, mode) => {
|
|
1897
2057
|
sscanHasErr = 0;
|
|
@@ -2002,9 +2162,26 @@ function ${P}stdlib(stringTable, io) {
|
|
|
2002
2162
|
return 0;
|
|
2003
2163
|
},
|
|
2004
2164
|
"testing_run": (t, name, fn) => (t.Run(name, (sub) => { io.late.exports.__testing_run_cb(fn, sub); }) ? 1 : 0),
|
|
2165
|
+
"testing_cleanup": (t, fn) => { t.Cleanup(() => { io.late.exports.__testing_cleanup_cb(fn); }); },
|
|
2166
|
+
"testing_n": (t) => BigInt(t.N ?? 0),
|
|
2167
|
+
"testing_elapsed": (t) => BigInt(t.Elapsed()),
|
|
2168
|
+
"testing_context": (t) => t.Context(),
|
|
2169
|
+
"testing_output": (t) => t.Output(),
|
|
2005
2170
|
"fmt_sprintf": (f) => __sprintf(f ?? "", ...io.take()),
|
|
2171
|
+
"fmt_sprint": (ln) => __sprint(ln !== 0, ...io.take()),
|
|
2172
|
+
"fmt_print": (ln) => { io.printf(__sprint(ln !== 0, ...io.take())); },
|
|
2173
|
+
"fmt_print_raw": (s) => { io.printf(s ?? ""); },
|
|
2006
2174
|
"fmt_printf": (f) => { io.printf(__sprintf(f ?? "", ...io.take())); },
|
|
2007
2175
|
"fmt_format_v": (v) => { const res = __sprintf("%v", v); io.take(); return res; },
|
|
2176
|
+
};
|
|
2177
|
+
}
|
|
2178
|
+
function ${P}stdlib_str(stringTable, io) {
|
|
2179
|
+
let parts = [];
|
|
2180
|
+
let cutBefore = "", cutAfter = "";
|
|
2181
|
+
let replacerTarget = "", replacerPairs = [];
|
|
2182
|
+
let strconvErr = null, strconvErrKind = 0;
|
|
2183
|
+
let decodeSize = 0;
|
|
2184
|
+
return {
|
|
2008
2185
|
"str_trim": (s, c) => ${P}trim(s ?? "", c ?? "", true, true),
|
|
2009
2186
|
"str_trim_left": (s, c) => ${P}trim(s ?? "", c ?? "", true, false),
|
|
2010
2187
|
"str_trim_right": (s, c) => ${P}trim(s ?? "", c ?? "", false, true),
|
|
@@ -2121,7 +2298,7 @@ function ${P}stdlib(stringTable, io) {
|
|
|
2121
2298
|
parts = r;
|
|
2122
2299
|
return parts.length;
|
|
2123
2300
|
},
|
|
2124
|
-
"str_fields": (s) => { parts = (s ?? "").split(
|
|
2301
|
+
"str_fields": (s) => { parts = (s ?? "").split(/[ \\t\\n\\v\\f\\r\\u0085\\u00A0\\u1680\\u2000-\\u200A\\u2028\\u2029\\u202F\\u205F\\u3000]+/).filter((p) => p !== ""); return parts.length; },
|
|
2125
2302
|
"str_part": (i) => parts[i] ?? "",
|
|
2126
2303
|
"str_replacer_init": (s) => {
|
|
2127
2304
|
replacerTarget = s ?? "";
|
|
@@ -2236,7 +2413,7 @@ function ${P}stdlib(stringTable, io) {
|
|
|
2236
2413
|
// unicode
|
|
2237
2414
|
"unicode_is_letter": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && /\\p{L}/u.test(c)) ? 1 : 0; },
|
|
2238
2415
|
"unicode_is_digit": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && /\\p{Nd}/u.test(c)) ? 1 : 0; },
|
|
2239
|
-
"unicode_is_space": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c &&
|
|
2416
|
+
"unicode_is_space": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && /[ \\t\\n\\v\\f\\r\\u0085\\u00A0\\u1680\\u2000-\\u200A\\u2028\\u2029\\u202F\\u205F\\u3000]/.test(c)) ? 1 : 0; },
|
|
2240
2417
|
"unicode_is_upper": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && /\\p{Lu}/u.test(c)) ? 1 : 0; },
|
|
2241
2418
|
"unicode_is_lower": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && /\\p{Ll}/u.test(c)) ? 1 : 0; },
|
|
2242
2419
|
"unicode_is_punct": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && /\\p{P}/u.test(c)) ? 1 : 0; },
|
|
@@ -2245,6 +2422,22 @@ function ${P}stdlib(stringTable, io) {
|
|
|
2245
2422
|
"unicode_is_graphic": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && !/\\p{Cc}/u.test(c)) ? 1 : 0; },
|
|
2246
2423
|
"unicode_to_upper": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return c ? (c.toUpperCase().codePointAt(0) ?? r) : r; },
|
|
2247
2424
|
"unicode_to_lower": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return c ? (c.toLowerCase().codePointAt(0) ?? r) : r; },
|
|
2425
|
+
};
|
|
2426
|
+
}
|
|
2427
|
+
function ${P}stdlib_sys(stringTable, io) {
|
|
2428
|
+
let splitDir = "", splitFile = "";
|
|
2429
|
+
let joinParts = [];
|
|
2430
|
+
let matchHasErr = 0;
|
|
2431
|
+
let trimStart = 0, trimEnd = 0;
|
|
2432
|
+
let timeParseErr = null, timeParseOffset = 0, timeParseDurationErr = null, timeParseSubNs = 0;
|
|
2433
|
+
let rawBytes = new Uint8Array(0);
|
|
2434
|
+
let rawPos = 0;
|
|
2435
|
+
let hexErr = null;
|
|
2436
|
+
let base64Err = null;
|
|
2437
|
+
let customBase64Encodings = [];
|
|
2438
|
+
let bitsRem32 = 0;
|
|
2439
|
+
let bitsRem64 = 0n;
|
|
2440
|
+
return {
|
|
2248
2441
|
// html
|
|
2249
2442
|
"html_escape": (s) => (s ?? "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'"),
|
|
2250
2443
|
"html_unescape": (s) => (s ?? "").replace(/&(?:quot|apos|#39|#34|gt|lt|amp|#(?:[0-9]+|[xX][0-9a-fA-F]+));/g, (m) => {
|
|
@@ -2309,32 +2502,66 @@ function ${P}stdlib(stringTable, io) {
|
|
|
2309
2502
|
"time_now_ms": () => (typeof globalThis.__GOFRONT_CLOCK === "function" ? globalThis.__GOFRONT_CLOCK() : Date.now()),
|
|
2310
2503
|
"time_timezone_offset": (ms) => -new Date(ms).getTimezoneOffset() * 60,
|
|
2311
2504
|
"time_duration_string": (d) => __durationString(d),
|
|
2505
|
+
"time_parse_duration": (s) => {
|
|
2506
|
+
const res = __timeParseDuration(s);
|
|
2507
|
+
if (res[1]) {
|
|
2508
|
+
timeParseDurationErr = res[1].message || String(res[1]);
|
|
2509
|
+
return 0n;
|
|
2510
|
+
}
|
|
2511
|
+
timeParseDurationErr = null;
|
|
2512
|
+
return BigInt(res[0]);
|
|
2513
|
+
},
|
|
2514
|
+
"time_parse_duration_err": () => timeParseDurationErr,
|
|
2515
|
+
"time_month_string": (m) => __monthString(m),
|
|
2516
|
+
"time_weekday_string": (w) => __weekdayString(w),
|
|
2312
2517
|
"time_parse": (layout, val) => {
|
|
2313
2518
|
try {
|
|
2314
|
-
|
|
2519
|
+
let s = String(val ?? "");
|
|
2520
|
+
if (s.length >= 2 && s.startsWith('"') && s.endsWith('"')) {
|
|
2521
|
+
s = s.slice(1, -1);
|
|
2522
|
+
}
|
|
2523
|
+
const d = new Date(s);
|
|
2315
2524
|
if (isNaN(d.getTime())) throw 0;
|
|
2525
|
+
let subNs = 0;
|
|
2526
|
+
const m = /\\.([0-9]{1,9})/.exec(s);
|
|
2527
|
+
if (m) {
|
|
2528
|
+
subNs = Number(m[1].padEnd(9, "0")) % 1000000;
|
|
2529
|
+
}
|
|
2316
2530
|
timeParseErr = null;
|
|
2317
|
-
|
|
2531
|
+
if (s.endsWith("Z") || s.endsWith("z")) {
|
|
2532
|
+
timeParseOffset = 0;
|
|
2533
|
+
} else {
|
|
2534
|
+
const tzMatch = /([+-])(\\d{2}):?(\\d{2})$/.exec(s);
|
|
2535
|
+
if (tzMatch) {
|
|
2536
|
+
const sign = tzMatch[1] === "+" ? 1 : -1;
|
|
2537
|
+
timeParseOffset = sign * (Number(tzMatch[2]) * 3600 + Number(tzMatch[3]) * 60);
|
|
2538
|
+
} else {
|
|
2539
|
+
timeParseOffset = -d.getTimezoneOffset() * 60;
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
timeParseSubNs = subNs;
|
|
2318
2543
|
return d.getTime();
|
|
2319
2544
|
} catch {
|
|
2320
2545
|
timeParseErr = "parsing time: cannot parse";
|
|
2321
2546
|
timeParseOffset = 0;
|
|
2547
|
+
timeParseSubNs = 0;
|
|
2322
2548
|
return 0;
|
|
2323
2549
|
}
|
|
2324
2550
|
},
|
|
2325
2551
|
"time_parse_err": () => timeParseErr,
|
|
2326
2552
|
"time_parse_offset": () => timeParseOffset,
|
|
2553
|
+
"time_parse_sub_ns": () => timeParseSubNs,
|
|
2327
2554
|
"time_format": (y, m, d, hr, min, sec, wd, ms, offsetSec, layout) => {
|
|
2328
2555
|
const DAYS = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
|
|
2329
2556
|
const MONTHS = ["January","February","March","April","May","June","July","August","September","October","November","December"];
|
|
2330
2557
|
const pad = (n, w = 2) => String(n).padStart(w, "0");
|
|
2331
|
-
return (layout ?? "").replace(/2006|06|Monday|Mon|January|Jan|01|02|15|PM|pm|03|04|05|Z07:00|\\.(?:0+|9+)|1|2|3|4|5/g, t => {
|
|
2558
|
+
return (layout ?? "").replace(/2006|06|Monday|Mon|January|Jan|01|02|15|PM|pm|03|04|05|Z07:00|-07:00|-0700|MST|\\.(?:0+|9+)|1|2|3|4|5/g, t => {
|
|
2332
2559
|
if (t.startsWith(".")) {
|
|
2333
2560
|
const isZeroPad = t.includes("0");
|
|
2334
2561
|
const maxLen = t.length - 1;
|
|
2335
|
-
let frac = pad(ms %
|
|
2336
|
-
if (maxLen
|
|
2337
|
-
else frac = frac.
|
|
2562
|
+
let frac = pad(ms % 1000000000, 9);
|
|
2563
|
+
if (maxLen <= 9) frac = frac.slice(0, maxLen);
|
|
2564
|
+
else frac = frac.padEnd(maxLen, "0");
|
|
2338
2565
|
if (!isZeroPad) {
|
|
2339
2566
|
frac = frac.replace(/0+$/, "");
|
|
2340
2567
|
return frac.length > 0 ? "." + frac : "";
|
|
@@ -2365,6 +2592,15 @@ function ${P}stdlib(stringTable, io) {
|
|
|
2365
2592
|
const o = Math.floor(offsetSec / 60);
|
|
2366
2593
|
return o === 0 ? "Z" : (o > 0 ? "+" : "-") + pad(Math.floor(Math.abs(o) / 60)) + ":" + pad(Math.abs(o) % 60);
|
|
2367
2594
|
}
|
|
2595
|
+
case "-07:00": {
|
|
2596
|
+
const o = Math.floor(offsetSec / 60);
|
|
2597
|
+
return (o >= 0 ? "+" : "-") + pad(Math.floor(Math.abs(o) / 60)) + ":" + pad(Math.abs(o) % 60);
|
|
2598
|
+
}
|
|
2599
|
+
case "-0700": {
|
|
2600
|
+
const o = Math.floor(offsetSec / 60);
|
|
2601
|
+
return (o >= 0 ? "+" : "-") + pad(Math.floor(Math.abs(o) / 60)) + pad(Math.abs(o) % 60);
|
|
2602
|
+
}
|
|
2603
|
+
case "MST": return offsetSec === 0 ? "UTC" : "LOCAL";
|
|
2368
2604
|
default: return t;
|
|
2369
2605
|
}
|
|
2370
2606
|
});
|
|
@@ -2424,8 +2660,50 @@ function ${P}stdlib(stringTable, io) {
|
|
|
2424
2660
|
return rawBytes.length;
|
|
2425
2661
|
},
|
|
2426
2662
|
"base64_get_err": () => base64Err,
|
|
2663
|
+
"bits_div32": (hi, lo, y) => {
|
|
2664
|
+
const yu = y >>> 0;
|
|
2665
|
+
if (yu === 0) throw new Error("runtime error: integer divide by zero");
|
|
2666
|
+
const hiu = hi >>> 0;
|
|
2667
|
+
if (hiu >= yu) throw new Error("runtime error: integer overflow");
|
|
2668
|
+
const z = (BigInt(hiu) << 32n) | BigInt(lo >>> 0);
|
|
2669
|
+
const yB = BigInt(yu);
|
|
2670
|
+
bitsRem32 = Number(z % yB);
|
|
2671
|
+
return Number(z / yB);
|
|
2672
|
+
},
|
|
2673
|
+
"bits_div32_rem": () => bitsRem32,
|
|
2674
|
+
"bits_rem32": (hi, lo, y) => {
|
|
2675
|
+
const yu = y >>> 0;
|
|
2676
|
+
if (yu === 0) throw new Error("runtime error: integer divide by zero");
|
|
2677
|
+
const z = (BigInt(hi >>> 0) << 32n) | BigInt(lo >>> 0);
|
|
2678
|
+
return Number(z % BigInt(yu));
|
|
2679
|
+
},
|
|
2680
|
+
"bits_div64": (hi, lo, y) => {
|
|
2681
|
+
const yB = BigInt.asUintN(64, BigInt(y));
|
|
2682
|
+
if (yB === 0n) throw new Error("runtime error: integer divide by zero");
|
|
2683
|
+
const hiB = BigInt.asUintN(64, BigInt(hi));
|
|
2684
|
+
if (hiB >= yB) throw new Error("runtime error: integer overflow");
|
|
2685
|
+
const loB = BigInt.asUintN(64, BigInt(lo));
|
|
2686
|
+
const z = (hiB << 64n) | loB;
|
|
2687
|
+
bitsRem64 = z % yB;
|
|
2688
|
+
return z / yB;
|
|
2689
|
+
},
|
|
2690
|
+
"bits_div64_rem": () => bitsRem64,
|
|
2691
|
+
"bits_rem64": (hi, lo, y) => {
|
|
2692
|
+
const yB = BigInt.asUintN(64, BigInt(y));
|
|
2693
|
+
if (yB === 0n) throw new Error("runtime error: integer divide by zero");
|
|
2694
|
+
const z = ((BigInt.asUintN(64, BigInt(hi)) % yB) << 64n) | BigInt.asUintN(64, BigInt(lo));
|
|
2695
|
+
return z % yB;
|
|
2696
|
+
},
|
|
2427
2697
|
};
|
|
2428
2698
|
}
|
|
2699
|
+
function ${P}stdlib(stringTable, io) {
|
|
2700
|
+
return Object.assign(
|
|
2701
|
+
{},
|
|
2702
|
+
${P}stdlib_fmt(stringTable, io),
|
|
2703
|
+
${P}stdlib_str(stringTable, io),
|
|
2704
|
+
${P}stdlib_sys(stringTable, io),
|
|
2705
|
+
);
|
|
2706
|
+
}
|
|
2429
2707
|
function ${P}trim(s, cutset, left, right) {
|
|
2430
2708
|
const cs = new Set(Array.from(cutset));
|
|
2431
2709
|
const chars = Array.from(s);
|
|
@@ -2518,6 +2796,13 @@ function ${P}idxview(n, getAt, setAt) {
|
|
|
2518
2796
|
}`;
|
|
2519
2797
|
|
|
2520
2798
|
// Generates the JS facade for the linked wasm module.
|
|
2799
|
+
// A facade binding for a Go name that shadows a browser global carries the
|
|
2800
|
+
// same `$` suffix as JS output uses (see jsBindingName in backend/js), so the
|
|
2801
|
+
// flat bundle keeps reaching the real global.
|
|
2802
|
+
function facadeName(name) {
|
|
2803
|
+
return BROWSER_GLOBALS.has(name) ? `${name}$` : name;
|
|
2804
|
+
}
|
|
2805
|
+
|
|
2521
2806
|
export function generateFacade(
|
|
2522
2807
|
meta,
|
|
2523
2808
|
{ stringTable = [], callMain = false, stdlibEnv = true } = {},
|
|
@@ -2529,18 +2814,42 @@ export function generateFacade(
|
|
|
2529
2814
|
out.push(`const ${P}env = {};`);
|
|
2530
2815
|
for (const [key, desc] of needs.funcs) {
|
|
2531
2816
|
const names = desc.params.map((_, i) => `a${i}`);
|
|
2532
|
-
const
|
|
2533
|
-
isPtrBoth(d) ? `${P}view_${d.name}(${names[i]})` : outExpr(d, names[i]),
|
|
2534
|
-
);
|
|
2535
|
-
const call = `fn(${args.join(", ")})`;
|
|
2817
|
+
const hasPtrBox = desc.params.some((d) => d.k === "ptr_box");
|
|
2536
2818
|
let body;
|
|
2537
|
-
if (
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2819
|
+
if (hasPtrBox) {
|
|
2820
|
+
const argVars = desc.params.map((_, i) => `__arg${i}`);
|
|
2821
|
+
const decls = desc.params.map(
|
|
2822
|
+
(d, i) =>
|
|
2823
|
+
`const __arg${i} = ${isPtrBoth(d) ? `${P}view_${d.name}(${names[i]})` : outExpr(d, names[i])};`,
|
|
2824
|
+
);
|
|
2825
|
+
const call = `fn(${argVars.join(", ")})`;
|
|
2826
|
+
const writebacks = desc.params
|
|
2827
|
+
.map((d, i) =>
|
|
2828
|
+
d.k === "ptr_box"
|
|
2829
|
+
? `if (__arg${i} && __arg${i}.value !== undefined) __w.__box_set_${d.key}(${names[i]}, ${inExpr(d.base, `__arg${i}.value`)});`
|
|
2830
|
+
: "",
|
|
2831
|
+
)
|
|
2832
|
+
.filter(Boolean);
|
|
2833
|
+
if (desc.returns.length === 0) {
|
|
2834
|
+
body = `${decls.join(" ")} ${call}; ${writebacks.join(" ")}`;
|
|
2835
|
+
} else if (desc.returns.length === 1) {
|
|
2836
|
+
body = `${decls.join(" ")} const __r = ${call}; ${writebacks.join(" ")} return ${inExpr(desc.returns[0], "__r")};`;
|
|
2837
|
+
} else {
|
|
2838
|
+
body = `${decls.join(" ")} const __r = ${call}; ${writebacks.join(" ")} return [${desc.returns.map((d, i) => inExpr(d, `__r[${i}]`)).join(", ")}];`;
|
|
2839
|
+
}
|
|
2840
|
+
} else {
|
|
2841
|
+
const args = desc.params.map((d, i) =>
|
|
2842
|
+
isPtrBoth(d) ? `${P}view_${d.name}(${names[i]})` : outExpr(d, names[i]),
|
|
2843
|
+
);
|
|
2844
|
+
const call = `fn(${args.join(", ")})`;
|
|
2845
|
+
if (desc.returns.length === 0) body = `${call};`;
|
|
2846
|
+
else if (desc.returns.length === 1)
|
|
2847
|
+
body = `return ${inExpr(desc.returns[0], call)};`;
|
|
2848
|
+
else
|
|
2849
|
+
body = `const __r = ${call}; return [${desc.returns
|
|
2850
|
+
.map((d, i) => inExpr(d, `__r[${i}]`))
|
|
2851
|
+
.join(", ")}];`;
|
|
2852
|
+
}
|
|
2544
2853
|
const sig = ["fn", ...names].join(", ");
|
|
2545
2854
|
out.push(
|
|
2546
2855
|
`${P}env["__invoke$${key}"] = (${sig}) => { try { ${body} } catch (__e) { if (__w && __w.__push_panic) __w.__push_panic(__e.message ?? String(__e)); throw __e; } };`,
|
|
@@ -2578,12 +2887,17 @@ ${desc.fields.map((f) => `\to.${f.name} = ${outExpr(f.desc, `__w.__get_${name}_$
|
|
|
2578
2887
|
for (const [key, desc] of needs.boxes) {
|
|
2579
2888
|
out.push(`function ${P}boxin_${key}(b) {
|
|
2580
2889
|
if (b == null) return null;
|
|
2890
|
+
if (b.__ref) return b.__ref;
|
|
2581
2891
|
const v = b.value !== undefined ? b.value : ${zeroExpr(desc.base)};
|
|
2582
2892
|
return __w.__box_new_${key}(${inExpr(desc.base, "v")});
|
|
2583
2893
|
}
|
|
2584
2894
|
function ${P}boxout_${key}(r) {
|
|
2585
2895
|
if (r == null) return null;
|
|
2586
|
-
return {
|
|
2896
|
+
return {
|
|
2897
|
+
__ref: r,
|
|
2898
|
+
get value() { return ${outExpr(desc.base, `__w.__box_get_${key}(r)`)}; },
|
|
2899
|
+
set value(v) { __w.__box_set_${key}(r, ${inExpr(desc.base, "v")}); }
|
|
2900
|
+
};
|
|
2587
2901
|
}
|
|
2588
2902
|
function ${P}boxback_${key}(r, b) {
|
|
2589
2903
|
if (r == null || b == null) return;
|
|
@@ -2592,6 +2906,27 @@ function ${P}boxback_${key}(r, b) {
|
|
|
2592
2906
|
}
|
|
2593
2907
|
|
|
2594
2908
|
for (const [key, desc] of needs.slices) {
|
|
2909
|
+
if (desc.elem.k === "f32x4") {
|
|
2910
|
+
// []F32x4 travels as one Float32Array of 4n lanes.
|
|
2911
|
+
out.push(`function ${P}slin_${key}(arr) {
|
|
2912
|
+
if (arr == null) return __w.__slice_nil_${key}();
|
|
2913
|
+
const n = arr.length >> 2;
|
|
2914
|
+
const s = __w.__slice_new_${key}(n);
|
|
2915
|
+
for (let i = 0; i < n; i++) __w.__slice_set_${key}(s, i, ${P}f32x4in(arr.subarray ? arr.subarray(4 * i, 4 * i + 4) : arr.slice(4 * i, 4 * i + 4)));
|
|
2916
|
+
return s;
|
|
2917
|
+
}
|
|
2918
|
+
function ${P}slout_${key}(s) {
|
|
2919
|
+
if (s == null || __w.__slice_is_nil_${key}(s)) return null;
|
|
2920
|
+
const n = __w.__slice_len_${key}(s);
|
|
2921
|
+
const out = new Float32Array(4 * n);
|
|
2922
|
+
for (let i = 0; i < n; i++) out.set(${P}f32x4out(__w.__slice_get_${key}(s, i)), 4 * i);
|
|
2923
|
+
return out;
|
|
2924
|
+
}
|
|
2925
|
+
function ${P}slview_${key}(s) {
|
|
2926
|
+
return ${P}slout_${key}(s);
|
|
2927
|
+
}`);
|
|
2928
|
+
continue;
|
|
2929
|
+
}
|
|
2595
2930
|
out.push(`function ${P}slin_${key}(arr) {
|
|
2596
2931
|
if (arr == null) return __w.__slice_nil_${key}();
|
|
2597
2932
|
const n = arr.length;
|
|
@@ -2637,6 +2972,34 @@ function ${P}mapout_${key}(m) {
|
|
|
2637
2972
|
}`);
|
|
2638
2973
|
}
|
|
2639
2974
|
|
|
2975
|
+
if (needs.simd) {
|
|
2976
|
+
const k = needs.simd.key;
|
|
2977
|
+
out.push(`function ${P}v4in(words) {
|
|
2978
|
+
const a = __w.__array_new_${k}(4);
|
|
2979
|
+
for (let i = 0; i < 4; i++) __w.__array_set_${k}(a, i, words[i]);
|
|
2980
|
+
return a;
|
|
2981
|
+
}
|
|
2982
|
+
function ${P}v4out(a) {
|
|
2983
|
+
const words = new Int32Array(4);
|
|
2984
|
+
for (let i = 0; i < 4; i++) words[i] = __w.__array_get_${k}(a, i);
|
|
2985
|
+
return words;
|
|
2986
|
+
}
|
|
2987
|
+
function ${P}f32x4in(v) {
|
|
2988
|
+
return ${P}v4in(new Int32Array(new Float32Array(v ?? 4).buffer));
|
|
2989
|
+
}
|
|
2990
|
+
function ${P}f32x4out(a) {
|
|
2991
|
+
return new Float32Array(${P}v4out(a).buffer);
|
|
2992
|
+
}
|
|
2993
|
+
function ${P}mask4in(v) {
|
|
2994
|
+
const w = new Int32Array(4);
|
|
2995
|
+
if (v) for (let i = 0; i < 4; i++) w[i] = v[i] ? -1 : 0;
|
|
2996
|
+
return ${P}v4in(w);
|
|
2997
|
+
}
|
|
2998
|
+
function ${P}mask4out(a) {
|
|
2999
|
+
return Array.from(${P}v4out(a), (w) => w !== 0);
|
|
3000
|
+
}`);
|
|
3001
|
+
}
|
|
3002
|
+
|
|
2640
3003
|
for (const [key, desc] of needs.arrays) {
|
|
2641
3004
|
out.push(`function ${P}arrzero_${key}(n) {
|
|
2642
3005
|
const out = new Array(n);
|
|
@@ -2776,15 +3139,17 @@ function ${P}shin(v, Ctor) {
|
|
|
2776
3139
|
for (const c of meta.consts) {
|
|
2777
3140
|
if (c.dynamic) {
|
|
2778
3141
|
out.push(
|
|
2779
|
-
`const ${c.name} = ${outExpr(c.desc, `__w.__const_${c.name}()`)};`,
|
|
3142
|
+
`const ${facadeName(c.name)} = ${outExpr(c.desc, `__w.__const_${c.name}()`)};`,
|
|
2780
3143
|
);
|
|
2781
3144
|
} else {
|
|
2782
|
-
out.push(`const ${c.name} = ${c.value};`);
|
|
3145
|
+
out.push(`const ${facadeName(c.name)} = ${c.value};`);
|
|
2783
3146
|
}
|
|
2784
3147
|
}
|
|
2785
3148
|
for (const v of meta.vars ?? [])
|
|
2786
3149
|
out.push(
|
|
2787
|
-
|
|
3150
|
+
v.shared
|
|
3151
|
+
? `const ${facadeName(v.name)} = ${P}shout(__w.__var_${v.name}(), ${v.desc.ctor});`
|
|
3152
|
+
: `const ${facadeName(v.name)} = ${outExpr(v.desc, `__w.__var_${v.name}()`)};`,
|
|
2788
3153
|
);
|
|
2789
3154
|
|
|
2790
3155
|
for (const f of meta.funcs) {
|
|
@@ -2793,13 +3158,22 @@ function ${P}shin(v, Ctor) {
|
|
|
2793
3158
|
f.params,
|
|
2794
3159
|
f.returns,
|
|
2795
3160
|
);
|
|
2796
|
-
out.push(
|
|
3161
|
+
out.push(
|
|
3162
|
+
`function ${facadeName(f.name)}(${restParams(names, f.variadic)}) {\n${body}\n}`,
|
|
3163
|
+
);
|
|
2797
3164
|
}
|
|
2798
3165
|
|
|
2799
3166
|
if (callMain) out.push("__w.main();");
|
|
2800
3167
|
return bindExports(out.join("\n"));
|
|
2801
3168
|
}
|
|
2802
3169
|
|
|
3170
|
+
// A variadic Go function is a slice parameter in wasm; JS callers pass the
|
|
3171
|
+
// trailing arguments spread, so the facade collects them with a rest param.
|
|
3172
|
+
function restParams(names, variadic) {
|
|
3173
|
+
if (!variadic || names.length === 0) return names.join(", ");
|
|
3174
|
+
return [...names.slice(0, -1), `...${names.at(-1)}`].join(", ");
|
|
3175
|
+
}
|
|
3176
|
+
|
|
2803
3177
|
// Rewrites `__w.name(...)` into calls through module-level `const` bindings.
|
|
2804
3178
|
// V8 only inlines a JS→wasm call (and so passes f32/f64 arguments unboxed)
|
|
2805
3179
|
// when the callee is a compile-time constant; a property load off the exports
|
|
@@ -2956,7 +3330,7 @@ class ${name} {
|
|
|
2956
3330
|
[recv],
|
|
2957
3331
|
);
|
|
2958
3332
|
lines.push(
|
|
2959
|
-
`\t${m.name}(${names
|
|
3333
|
+
`\t${m.name}(${restParams(names, m.variadic)}) {\n${indent(body, "\t")}\n\t}`,
|
|
2960
3334
|
);
|
|
2961
3335
|
}
|
|
2962
3336
|
lines.push("}");
|