gofront 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -13
- package/package.json +8 -37
- package/src/backend/js/expressions.js +1341 -148
- package/src/backend/js/fmt-runtime.js +573 -0
- package/src/backend/js/index.js +375 -57
- package/src/backend/js/runtime.js +1460 -133
- package/src/backend/js/statements.js +900 -86
- package/src/backend/js/stdlib/bits.js +87 -14
- package/src/backend/js/stdlib/builder.js +41 -6
- package/src/backend/js/stdlib/bytes.js +253 -7
- package/src/backend/js/stdlib/errors.js +2 -1
- package/src/backend/js/stdlib/fmt.js +349 -10
- package/src/backend/js/stdlib/index.js +8 -3
- package/src/backend/js/stdlib/json.js +247 -52
- package/src/backend/js/stdlib/maps.js +0 -8
- package/src/backend/js/stdlib/math.js +17 -1
- package/src/backend/js/stdlib/simd.js +10 -0
- package/src/backend/js/stdlib/slices.js +36 -6
- package/src/backend/js/stdlib/strconv.js +10 -5
- package/src/backend/js/stdlib/strings.js +83 -4
- package/src/backend/js/stdlib/testing.js +7 -0
- package/src/backend/js/stdlib/time.js +79 -55
- package/src/backend/js/stdlib/unicode.js +1 -1
- package/src/backend/js/stdlib/utf8.js +1 -1
- package/src/backend/js/templ.js +38 -76
- package/src/backend/js/type-info.js +272 -0
- package/src/backend/shared/fmt-types.js +188 -0
- package/src/backend/wasm/boundary.js +436 -80
- package/src/backend/wasm/emit-builtins.js +303 -13
- package/src/backend/wasm/emit-exprs.js +1397 -203
- package/src/backend/wasm/emit-maps.js +61 -13
- package/src/backend/wasm/emit-stmts.js +1359 -164
- package/src/backend/wasm/emit.js +512 -77
- package/src/backend/wasm/encode.js +43 -13
- package/src/backend/wasm/globals.js +452 -0
- package/src/backend/wasm/index.js +194 -2489
- package/src/backend/wasm/method-expr.js +381 -0
- package/src/backend/wasm/module-emitter.js +2240 -0
- package/src/backend/wasm/monomorph.js +62 -4
- package/src/backend/wasm/prescan.js +397 -0
- package/src/backend/wasm/stdlib/bytes.js +1804 -127
- package/src/backend/wasm/stdlib/core.js +1574 -199
- package/src/backend/wasm/stdlib/index.js +0 -5
- package/src/backend/wasm/stdlib/io.js +1567 -150
- package/src/backend/wasm/stdlib/packages.js +2 -0
- package/src/backend/wasm/stdlib/simd.js +293 -28
- package/src/backend/wasm/stdlib/time.js +1041 -12
- package/src/backend/wasm/types.js +200 -15
- package/src/backend/wasm/variadic.js +93 -0
- package/src/backend/wasm/wat.js +47 -20
- package/src/{cli-core.js → cli/cli-core.js} +236 -10
- package/src/{test-runner.js → cli/test-runner.js} +332 -39
- package/src/{vendor.js → cli/vendor.js} +3 -4
- package/src/compiler.js +107 -269
- package/src/index.js +15 -169
- package/src/{lexer.js → lexer/lexer.js} +102 -55
- package/src/{tokens.js → lexer/tokens.js} +1 -0
- package/src/link/sourcemap.js +97 -0
- package/src/link/stdlib.js +515 -0
- package/src/link/wasm.js +294 -0
- package/src/lower/bounds.js +394 -0
- package/src/lower/boxing.js +1 -1
- package/src/lower/captures.js +176 -32
- package/src/lower/index.js +26 -18
- package/src/lower/json-codecs.js +1398 -0
- package/src/lower/ownership.js +22 -1
- package/src/lower/range.js +86 -1
- package/src/lower/reachability.js +132 -0
- package/src/parser/declarations.js +58 -13
- package/src/parser/expressions.js +29 -7
- package/src/parser/index.js +2 -1
- package/src/parser/statements.js +37 -22
- package/src/{templ-parser.js → parser/templ.js} +138 -15
- package/src/parser/types.js +69 -9
- package/src/resolver.js +22 -1
- package/src/typechecker/assignability.js +176 -17
- package/src/typechecker/const-fold.js +356 -0
- package/src/typechecker/constants.js +256 -0
- package/src/typechecker/expressions.js +931 -122
- package/src/typechecker/index.js +459 -67
- package/src/typechecker/init-order.js +330 -0
- package/src/typechecker/resolve.js +451 -56
- package/src/typechecker/statements.js +90 -9
- package/src/typechecker/stdlib/core.js +333 -55
- package/src/typechecker/stdlib/excluded.js +898 -0
- package/src/typechecker/stdlib/extended.js +322 -26
- package/src/typechecker/{stdlib.js → stdlib/index.js} +5 -5
- package/src/typechecker/stdlib/simd.js +65 -15
- package/src/typechecker/termination.js +1 -1
- package/src/typechecker/types.js +149 -25
- package/stdlib/bytes/iter.go +192 -0
- package/stdlib/bytes/search.go +240 -0
- package/stdlib/bytes/split.go +218 -0
- package/stdlib/cmp/cmp.go +65 -0
- package/stdlib/encoding/json/errors.go +31 -0
- package/stdlib/encoding/json/indent.go +71 -0
- package/stdlib/encoding/json/number.go +12 -0
- package/stdlib/encoding/json/stream.go +173 -0
- package/stdlib/errors/overlay.go +12 -0
- package/stdlib/hash/hash.go +53 -0
- package/stdlib/internal/jsonrt/dec.go +738 -0
- package/stdlib/internal/jsonrt/enc.go +366 -0
- package/stdlib/internal/jsonrt/indent.go +135 -0
- package/stdlib/internal/jsonrt/number.go +27 -0
- package/stdlib/internal/jsonrt/reader.go +73 -0
- package/stdlib/internal/jsonrt/scanner.go +649 -0
- package/stdlib/io/io.go +346 -0
- package/stdlib/iter/iter.go +18 -0
- package/stdlib/maps/iter.go +65 -0
- package/stdlib/math/extra.go +270 -0
- package/stdlib/slices/extra.go +73 -0
- package/stdlib/slices/iter.go +113 -0
- package/stdlib/sort/sort.go +561 -0
- package/stdlib/strconv/quote.go +341 -0
- package/stdlib/strings/extra.go +35 -0
- package/stdlib/strings/iter.go +122 -0
- package/stdlib/sync/extra.go +105 -0
- package/stdlib/unicode/unicode.go +112 -0
- package/stdlib/unicode/utf8/bytes.go +239 -0
- package/src/backend/wasm/const-fold.js +0 -205
- package/src/backend/wasm/emit-stdlib.js +0 -13
- /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
- /package/src/{colors.js → cli/colors.js} +0 -0
- /package/src/{dev-server.js → cli/dev-server.js} +0 -0
- /package/src/{minifier.js → cli/minifier.js} +0 -0
- /package/src/{project-config.js → cli/project-config.js} +0 -0
- /package/src/{pwa.js → cli/pwa.js} +0 -0
- /package/src/{templ-lexer.js → lexer/templ-lexer.js} +0 -0
|
@@ -2,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
|
|
|
@@ -148,10 +154,7 @@ function classifyType(goType, mod) {
|
|
|
148
154
|
case "interface":
|
|
149
155
|
return classifyInterface(t, mod);
|
|
150
156
|
case "simd":
|
|
151
|
-
return
|
|
152
|
-
k: "unsupported",
|
|
153
|
-
what: "simd.F32x4 cannot cross the JS↔WASM boundary in 1.7.0 (use shared.Float32 instead)",
|
|
154
|
-
};
|
|
157
|
+
return classifySimd(t, mod);
|
|
155
158
|
default:
|
|
156
159
|
return {
|
|
157
160
|
k: "unsupported",
|
|
@@ -219,12 +222,11 @@ function classifyNamed(t, mod) {
|
|
|
219
222
|
if (
|
|
220
223
|
t.name === "simd.F32x4" ||
|
|
221
224
|
t.name === "F32x4" ||
|
|
225
|
+
t.name === "simd.Mask32x4" ||
|
|
226
|
+
t.name === "Mask32x4" ||
|
|
222
227
|
t.underlying?.kind === "simd"
|
|
223
228
|
) {
|
|
224
|
-
return
|
|
225
|
-
k: "unsupported",
|
|
226
|
-
what: "simd.F32x4 cannot cross the JS↔WASM boundary in 1.7.0 (use shared.Float32 instead)",
|
|
227
|
-
};
|
|
229
|
+
return classifySimd(t.underlying ?? t, mod, t.name);
|
|
228
230
|
}
|
|
229
231
|
if (t.name === "error") return { k: "error", wType: "anyref" };
|
|
230
232
|
if (t.name === "time.Duration")
|
|
@@ -239,6 +241,18 @@ function classifyNamed(t, mod) {
|
|
|
239
241
|
return { k: "unsupported", what: `named type '${t.name}'` };
|
|
240
242
|
}
|
|
241
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
|
+
|
|
242
256
|
function classifyAnonStruct(t, mod) {
|
|
243
257
|
const rawFields = [];
|
|
244
258
|
if (t.fields instanceof Map) {
|
|
@@ -282,13 +296,22 @@ function classifyPointer(t, mod) {
|
|
|
282
296
|
if (base.k === "anon_struct" && !base.ptr) return { ...base, ptr: true };
|
|
283
297
|
if (base.k === "time" && !base.ptr) return { ...base, ptr: true };
|
|
284
298
|
const boxInfo = mod.getBoxType(t.base);
|
|
299
|
+
const ptrInfo = mod.getPtrType ? mod.getPtrType(t.base) : null;
|
|
285
300
|
const key = `${base.k}${base.unsigned ? "_u" : ""}_${boxInfo.typeIndex}`;
|
|
286
301
|
return {
|
|
287
302
|
k: "ptr_box",
|
|
288
303
|
base,
|
|
289
304
|
boxInfo,
|
|
305
|
+
ptrInfo,
|
|
290
306
|
key,
|
|
291
|
-
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 },
|
|
292
315
|
};
|
|
293
316
|
}
|
|
294
317
|
|
|
@@ -352,7 +375,11 @@ function classifySlice(t, mod) {
|
|
|
352
375
|
k: "slice",
|
|
353
376
|
elem,
|
|
354
377
|
sliceInfo,
|
|
355
|
-
|
|
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}` : ""),
|
|
356
383
|
wType: { kind: "ref", nullable: true, typeIndex: sliceInfo.typeIndex },
|
|
357
384
|
};
|
|
358
385
|
}
|
|
@@ -387,6 +414,9 @@ function classifySig(t, mod) {
|
|
|
387
414
|
}
|
|
388
415
|
|
|
389
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" };
|
|
390
420
|
const sig = classifySig(t, mod);
|
|
391
421
|
if (sig.k === "unsupported") return sig;
|
|
392
422
|
const closureInfo = mod.getClosureType(t);
|
|
@@ -451,7 +481,7 @@ function classifyNamedInterface(t, mod) {
|
|
|
451
481
|
const desc = {
|
|
452
482
|
k: "iface",
|
|
453
483
|
name: t.name,
|
|
454
|
-
key: t.name,
|
|
484
|
+
key: t.name.replaceAll(".", "_"),
|
|
455
485
|
wType: "anyref",
|
|
456
486
|
methods: [],
|
|
457
487
|
concrete: [],
|
|
@@ -550,7 +580,7 @@ export function collectBoundaryMeta(progs, funcDecls, mod) {
|
|
|
550
580
|
} else if (d.kind === "ConstDecl" && pkgTarget === "wasm") {
|
|
551
581
|
collectConstDecl(d, pkgName, meta, errors);
|
|
552
582
|
} else if (d.kind === "VarDecl" && pkgTarget === "wasm") {
|
|
553
|
-
|
|
583
|
+
collectVarDecl(d, meta, mod);
|
|
554
584
|
}
|
|
555
585
|
}
|
|
556
586
|
}
|
|
@@ -632,17 +662,24 @@ function checkDesc(desc, where, errors) {
|
|
|
632
662
|
}
|
|
633
663
|
|
|
634
664
|
// Exported `var Buf = shared.NewFloat32(n)` globals are exposed to JS as
|
|
635
|
-
// TypedArray views (read once after instantiation; sizing is fixed).
|
|
636
|
-
|
|
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) {
|
|
637
670
|
for (const spec of d.decls ?? d.specs ?? [d]) {
|
|
638
671
|
const names = spec.names ?? (spec.name ? [spec.name] : []);
|
|
639
672
|
const values = spec.value ?? (spec.init ? [spec.init] : []);
|
|
640
673
|
for (let i = 0; i < names.length; i++) {
|
|
641
674
|
const name = names[i];
|
|
642
675
|
if (!isExported(name)) continue;
|
|
643
|
-
const goType =
|
|
644
|
-
|
|
645
|
-
|
|
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 });
|
|
646
683
|
}
|
|
647
684
|
}
|
|
648
685
|
}
|
|
@@ -657,6 +694,7 @@ function collectMethodMeta(fn, meta, errors, mod) {
|
|
|
657
694
|
exportName: fn._exportName,
|
|
658
695
|
fnName: fn.name,
|
|
659
696
|
ptrRecv: fn.params[0].type?.kind === "pointer",
|
|
697
|
+
variadic: Boolean(fn.params.at(-1)?._variadic),
|
|
660
698
|
params: fn.params
|
|
661
699
|
.slice(1)
|
|
662
700
|
.map((p) => checkDesc(classifyType(p.type, mod), where, errors)),
|
|
@@ -673,6 +711,7 @@ function collectFuncMeta(fn, meta, errors, mod) {
|
|
|
673
711
|
name: fn.name,
|
|
674
712
|
exportName: fn.name,
|
|
675
713
|
pkgName: fn._pkgName,
|
|
714
|
+
variadic: Boolean(fn.params?.at(-1)?._variadic),
|
|
676
715
|
params: (fn.params ?? []).map((p) =>
|
|
677
716
|
checkDesc(classifyType(p.type, mod), where, errors),
|
|
678
717
|
),
|
|
@@ -711,6 +750,11 @@ function visitNeed(desc, needs, errors, mod) {
|
|
|
711
750
|
case "error":
|
|
712
751
|
needs.error = true;
|
|
713
752
|
return;
|
|
753
|
+
case "f32x4":
|
|
754
|
+
case "mask4":
|
|
755
|
+
needs.simd = desc.arr;
|
|
756
|
+
visitNeed(desc.arr, needs, errors, mod);
|
|
757
|
+
return;
|
|
714
758
|
case "time":
|
|
715
759
|
needs.time = true;
|
|
716
760
|
return;
|
|
@@ -791,6 +835,7 @@ function collectNeeds(meta) {
|
|
|
791
835
|
ifaces: new Map(), // interface name -> desc
|
|
792
836
|
error: false,
|
|
793
837
|
time: false,
|
|
838
|
+
simd: null, // array desc carrying vectors across the boundary
|
|
794
839
|
shared: false, // any shared buffer on the surface
|
|
795
840
|
};
|
|
796
841
|
const errors = [];
|
|
@@ -835,16 +880,22 @@ export function scanBoundaryImports(mod, meta) {
|
|
|
835
880
|
function isGcRef(wType) {
|
|
836
881
|
return wType === "anyref" || (typeof wType === "object" && wType !== null);
|
|
837
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
|
+
|
|
838
887
|
function extType(wType) {
|
|
888
|
+
if (wType === "v128") return "externref";
|
|
839
889
|
if (wType === "i64") return "f64";
|
|
840
890
|
if (isGcRef(wType)) return "externref";
|
|
841
891
|
return wType;
|
|
842
892
|
}
|
|
843
893
|
function needsConv(wTypes) {
|
|
844
|
-
return wTypes.some((w) => w === "i64" || isGcRef(w));
|
|
894
|
+
return wTypes.some((w) => w === "i64" || w === "v128" || isGcRef(w));
|
|
845
895
|
}
|
|
846
896
|
// Instructions converting the value on the stack from its boundary form.
|
|
847
897
|
function fromExt(wType) {
|
|
898
|
+
if (wType === "v128") return [{ op: "call", funcIndex: v128Conv.from }];
|
|
848
899
|
if (wType === "i64") return [{ op: "i64.trunc_sat_f64_s" }];
|
|
849
900
|
if (wType === "anyref") return [{ op: "any.convert_extern" }];
|
|
850
901
|
if (isGcRef(wType)) {
|
|
@@ -857,6 +908,7 @@ function fromExt(wType) {
|
|
|
857
908
|
}
|
|
858
909
|
// Instructions converting the value on the stack to its boundary form.
|
|
859
910
|
function toExt(wType) {
|
|
911
|
+
if (wType === "v128") return [{ op: "call", funcIndex: v128Conv.to }];
|
|
860
912
|
if (wType === "i64") return [{ op: "f64.convert_i64_s" }];
|
|
861
913
|
if (isGcRef(wType)) return [{ op: "extern.convert_any" }];
|
|
862
914
|
return [];
|
|
@@ -875,6 +927,8 @@ function convertResults(wTypes, conv, localBase) {
|
|
|
875
927
|
}
|
|
876
928
|
|
|
877
929
|
function zeroInstr(wType) {
|
|
930
|
+
if (wType === "v128")
|
|
931
|
+
return { op: "v128.const", bytes: new Array(16).fill(0) };
|
|
878
932
|
if (wType === "i64") return { op: "i64.const", value: 0n };
|
|
879
933
|
if (wType === "f32" || wType === "f64")
|
|
880
934
|
return { op: `${wType}.const`, value: 0 };
|
|
@@ -926,6 +980,47 @@ export function addBoundaryHelpers(mod, meta) {
|
|
|
926
980
|
return idx;
|
|
927
981
|
};
|
|
928
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
|
+
}
|
|
929
1024
|
// Parameter `i` converted from its boundary form.
|
|
930
1025
|
const getIn = (i, wType) => [get(i), ...fromExt(wType)];
|
|
931
1026
|
const EXT = "externref";
|
|
@@ -1169,7 +1264,8 @@ export function addBoundaryHelpers(mod, meta) {
|
|
|
1169
1264
|
const paramW = sig.params;
|
|
1170
1265
|
const resultW = sig.results;
|
|
1171
1266
|
if (!needsConv(paramW) && !needsConv(resultW)) return;
|
|
1172
|
-
|
|
1267
|
+
// Exported names become JS identifiers: no dot of a qualified type.
|
|
1268
|
+
entry.callName = `__x_${entry.exportName}`.replaceAll(".", "_");
|
|
1173
1269
|
addFunc(
|
|
1174
1270
|
entry.callName,
|
|
1175
1271
|
paramW.map(extType),
|
|
@@ -1210,7 +1306,7 @@ export function addBoundaryHelpers(mod, meta) {
|
|
|
1210
1306
|
[get(0), get(1), { op: "struct.new", typeIndex }, ...toExt(wType)],
|
|
1211
1307
|
);
|
|
1212
1308
|
for (const v of meta.vars ?? []) {
|
|
1213
|
-
const g = mod.resolveGlobal(v.name);
|
|
1309
|
+
const g = v.shared ? mod.resolveGlobal(v.name) : null;
|
|
1214
1310
|
if (!g) continue;
|
|
1215
1311
|
addFunc(
|
|
1216
1312
|
`__var_${v.name}`,
|
|
@@ -1282,12 +1378,18 @@ export function addBoundaryHelpers(mod, meta) {
|
|
|
1282
1378
|
[EXT],
|
|
1283
1379
|
[...getIn(0, w), { op: "struct.new", typeIndex: ti }, ...toExt(ref)],
|
|
1284
1380
|
);
|
|
1381
|
+
const cast = {
|
|
1382
|
+
op: "ref.cast",
|
|
1383
|
+
typeIndex: boxInfo.typeIndex,
|
|
1384
|
+
typeName: boxInfo.name,
|
|
1385
|
+
};
|
|
1285
1386
|
addFunc(
|
|
1286
1387
|
`__box_get_${key}`,
|
|
1287
1388
|
[EXT],
|
|
1288
1389
|
[extType(w)],
|
|
1289
1390
|
[
|
|
1290
1391
|
...getIn(0, ref),
|
|
1392
|
+
cast,
|
|
1291
1393
|
{ op: "struct.get", typeIndex: ti, fieldIndex: 0 },
|
|
1292
1394
|
...toExt(w),
|
|
1293
1395
|
],
|
|
@@ -1298,6 +1400,7 @@ export function addBoundaryHelpers(mod, meta) {
|
|
|
1298
1400
|
[],
|
|
1299
1401
|
[
|
|
1300
1402
|
...getIn(0, ref),
|
|
1403
|
+
cast,
|
|
1301
1404
|
...getIn(1, w),
|
|
1302
1405
|
{ op: "struct.set", typeIndex: ti, fieldIndex: 0 },
|
|
1303
1406
|
],
|
|
@@ -1390,10 +1493,11 @@ export function addBoundaryHelpers(mod, meta) {
|
|
|
1390
1493
|
[EXT],
|
|
1391
1494
|
[
|
|
1392
1495
|
get(0),
|
|
1393
|
-
{ op: "f64.const", value: 1000000 },
|
|
1394
|
-
{ op: "f64.mul" },
|
|
1395
1496
|
{ op: "i64.trunc_sat_f64_s" },
|
|
1497
|
+
{ op: "i64.const", value: 1000000n },
|
|
1498
|
+
{ op: "i64.mul" },
|
|
1396
1499
|
get(1),
|
|
1500
|
+
{ op: "ref.null", heapType: "any" },
|
|
1397
1501
|
{ op: "struct.new", typeIndex: timeInfo.typeIndex },
|
|
1398
1502
|
...toExt(timeWType),
|
|
1399
1503
|
],
|
|
@@ -1445,6 +1549,16 @@ export function addBoundaryHelpers(mod, meta) {
|
|
|
1445
1549
|
[{ op: "global.get", index: g.index }, ...toExt(c.desc.wType)],
|
|
1446
1550
|
);
|
|
1447
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
|
+
}
|
|
1448
1562
|
}
|
|
1449
1563
|
|
|
1450
1564
|
// `(externref) -> i32`: `ref.test`s param 0 against each type index and
|
|
@@ -1529,6 +1643,10 @@ function inExpr(desc, v) {
|
|
|
1529
1643
|
return `(${v} === undefined ? null : ${v})`;
|
|
1530
1644
|
case "error":
|
|
1531
1645
|
return `${P}errin(${v})`;
|
|
1646
|
+
case "f32x4":
|
|
1647
|
+
return `${P}f32x4in(${v})`;
|
|
1648
|
+
case "mask4":
|
|
1649
|
+
return `${P}mask4in(${v})`;
|
|
1532
1650
|
case "time":
|
|
1533
1651
|
return desc.ptr ? `${P}timein_ptr(${v})` : `${P}timein(${v})`;
|
|
1534
1652
|
case "map":
|
|
@@ -1573,6 +1691,10 @@ function outExpr(desc, v) {
|
|
|
1573
1691
|
return `(${v} ?? "")`;
|
|
1574
1692
|
case "error":
|
|
1575
1693
|
return `${P}errout(${v})`;
|
|
1694
|
+
case "f32x4":
|
|
1695
|
+
return `${P}f32x4out(${v})`;
|
|
1696
|
+
case "mask4":
|
|
1697
|
+
return `${P}mask4out(${v})`;
|
|
1576
1698
|
case "time":
|
|
1577
1699
|
return desc.ptr ? `${P}timeout_ptr(${v})` : `${P}timeout(${v})`;
|
|
1578
1700
|
case "map":
|
|
@@ -1613,6 +1735,10 @@ function zeroExpr(desc) {
|
|
|
1613
1735
|
return "0";
|
|
1614
1736
|
case "string":
|
|
1615
1737
|
return '""';
|
|
1738
|
+
case "f32x4":
|
|
1739
|
+
return "new Float32Array(4)";
|
|
1740
|
+
case "mask4":
|
|
1741
|
+
return "[false, false, false, false]";
|
|
1616
1742
|
case "error":
|
|
1617
1743
|
case "time":
|
|
1618
1744
|
case "map":
|
|
@@ -1733,21 +1859,23 @@ function ${P}imports(stringTable, extraEnv, tag, write) {
|
|
|
1733
1859
|
let lineBuf = "";
|
|
1734
1860
|
let targs = [];
|
|
1735
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; } }
|
|
1736
1866
|
const late = { exports: null };
|
|
1737
|
-
|
|
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; };
|
|
1738
1871
|
const print = (v) => {
|
|
1739
|
-
const s = String(v);
|
|
1740
|
-
if (
|
|
1741
|
-
|
|
1742
|
-
}
|
|
1872
|
+
const s = Object.is(v, -0) ? "-0" : String(v);
|
|
1873
|
+
if (started) lineBuf += " ";
|
|
1874
|
+
started = true;
|
|
1743
1875
|
lineBuf += s;
|
|
1744
1876
|
};
|
|
1745
1877
|
const println = (v) => {
|
|
1746
|
-
|
|
1747
|
-
if (lineBuf.length > 0 && !lineBuf.endsWith(" ") && !s.startsWith(" ")) {
|
|
1748
|
-
lineBuf += " ";
|
|
1749
|
-
}
|
|
1750
|
-
lineBuf += s;
|
|
1878
|
+
print(v);
|
|
1751
1879
|
flush();
|
|
1752
1880
|
};
|
|
1753
1881
|
const targ = (v) => { targs.push(v); };
|
|
@@ -1787,6 +1915,7 @@ function ${P}imports(stringTable, extraEnv, tag, write) {
|
|
|
1787
1915
|
},
|
|
1788
1916
|
"str_to_upper": (s) => (s ? s.toUpperCase() : ""),
|
|
1789
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 ?? "")),
|
|
1790
1919
|
"str_trim_space": (s) => (s ? s.trim() : ""),
|
|
1791
1920
|
"str_contains": (s, sub) => (s && sub !== undefined ? (s.includes(sub) ? 1 : 0) : 0),
|
|
1792
1921
|
"str_has_prefix": (s, pre) => (s && pre !== undefined ? (s.startsWith(pre) ? 1 : 0) : 0),
|
|
@@ -1803,7 +1932,7 @@ function ${P}imports(stringTable, extraEnv, tag, write) {
|
|
|
1803
1932
|
},
|
|
1804
1933
|
"str_from_i64": (n) => String(n),
|
|
1805
1934
|
"str_from_i32": (n) => String(n),
|
|
1806
|
-
"str_from_f64": (f) => String(f),
|
|
1935
|
+
"str_from_f64": (f) => (Object.is(f, -0) ? "-0" : String(f)),
|
|
1807
1936
|
"is_string": (v) => (typeof v === "string" ? 1 : 0),
|
|
1808
1937
|
"str_to_bytes": (s) => {
|
|
1809
1938
|
utf8Encoded = new TextEncoder().encode(s ?? "");
|
|
@@ -1836,28 +1965,55 @@ function ${P}imports(stringTable, extraEnv, tag, write) {
|
|
|
1836
1965
|
"fmt_new_map": () => new Map(),
|
|
1837
1966
|
"fmt_map_set": (m, k, v) => { m.set(k, v); },
|
|
1838
1967
|
"fmt_new_struct": (ref, isPtr) => {
|
|
1839
|
-
|
|
1840
|
-
|
|
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;
|
|
1841
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];
|
|
1842
1984
|
const o = {};
|
|
1843
1985
|
Object.defineProperty(o, "__struct", { value: true, configurable: true });
|
|
1844
1986
|
if (isPtr !== 0) Object.defineProperty(o, "__p", { value: true, configurable: true });
|
|
1845
|
-
if (
|
|
1987
|
+
if (isRef) {
|
|
1988
|
+
if (seen) seen[slot] = o;
|
|
1989
|
+
else fmtStructMap.set(ref, slot ? [undefined, o] : [o, undefined]);
|
|
1990
|
+
}
|
|
1846
1991
|
return o;
|
|
1847
1992
|
},
|
|
1993
|
+
"fmt_struct_type": (o, i) => { Object.defineProperty(o, "__tn", { value: stringTable[i], configurable: true }); },
|
|
1848
1994
|
"fmt_struct_set": (o, k, v) => { o[typeof k === "number" ? stringTable[k] : k] = v; },
|
|
1849
1995
|
"fmt_box_i32": (v) => v,
|
|
1850
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); },
|
|
1851
1998
|
"fmt_box_f64": (v) => v,
|
|
1852
1999
|
"fmt_box_bool": (v) => v !== 0,
|
|
2000
|
+
"fmt_new_stringer": (r, s, isErr) => (isErr !== 0 ? new FmtErr(r, s) : new FmtStr(r, s)),
|
|
1853
2001
|
"fmt_new_box": () => ({ value: null, __p: true, __box: true }),
|
|
1854
2002
|
"testing_arg_i32": targ, "testing_arg_f32": targ, "testing_arg_f64": targ,
|
|
1855
2003
|
"testing_arg_str": targ, "testing_arg_any": targ,
|
|
1856
2004
|
"testing_arg_i64": (v) => targ(v > ${P}MAX || v < -${P}MAX ? v : Number(v)),
|
|
1857
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; } }),
|
|
1858
2010
|
"testing_call": (t, i) => { const a = targs; targs = []; fmtStructMap = new WeakMap(); t[stringTable[i]](...a); },
|
|
1859
2011
|
"testing_name": (t) => t.Name(),
|
|
1860
|
-
"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(),
|
|
1861
2017
|
};
|
|
1862
2018
|
// Stdlib imports (strings/strconv/utf8/fmt/t.Run) are a separate block so
|
|
1863
2019
|
// modules that never call them do not ship it (see WASM_STDLIB_JS).
|
|
@@ -1874,7 +2030,7 @@ function ${P}imports(stringTable, extraEnv, tag, write) {
|
|
|
1874
2030
|
}
|
|
1875
2031
|
Object.assign(env, extraEnv);
|
|
1876
2032
|
const m = {};
|
|
1877
|
-
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];
|
|
1878
2034
|
m.mod = (x, y) => x % y;
|
|
1879
2035
|
// Go rounds half away from zero; Math.round rounds half toward +Infinity.
|
|
1880
2036
|
m.round = (x) => Math.sign(x) * Math.round(Math.abs(x));
|
|
@@ -1886,30 +2042,16 @@ function ${P}imports(stringTable, extraEnv, tag, write) {
|
|
|
1886
2042
|
// module imports one of the packages that use it.
|
|
1887
2043
|
export const WASM_STDLIB_JS = `${HELPER_ERROR}
|
|
1888
2044
|
${HELPER_SPRINTF}
|
|
2045
|
+
${HELPER_SPRINT}
|
|
1889
2046
|
${HELPER_STRCONV}
|
|
1890
2047
|
${HELPER_PATH_CLEAN}
|
|
1891
2048
|
${HELPER_DURATION}
|
|
1892
2049
|
${HELPER_SHA256}
|
|
1893
2050
|
${HELPER_HEX}
|
|
1894
2051
|
${HELPER_BASE64}
|
|
1895
|
-
function ${P}
|
|
1896
|
-
let parts = [];
|
|
1897
|
-
let cutBefore = "", cutAfter = "";
|
|
1898
|
-
let strconvErr = null, strconvErrKind = 0;
|
|
1899
|
-
let decodeSize = 0;
|
|
1900
|
-
let replacerTarget = "", replacerPairs = [];
|
|
1901
|
-
let splitDir = "", splitFile = "";
|
|
1902
|
-
let joinParts = [];
|
|
1903
|
-
let matchHasErr = 0;
|
|
1904
|
-
let trimStart = 0, trimEnd = 0;
|
|
2052
|
+
function ${P}stdlib_fmt(stringTable, io) {
|
|
1905
2053
|
let sscanTokens = [];
|
|
1906
2054
|
let sscanHasErr = 0, sscanErrMsg = "";
|
|
1907
|
-
let timeParseErr = null, timeParseOffset = 0;
|
|
1908
|
-
let rawBytes = new Uint8Array(0);
|
|
1909
|
-
let rawPos = 0;
|
|
1910
|
-
let hexErr = null;
|
|
1911
|
-
let base64Err = null;
|
|
1912
|
-
let customBase64Encodings = [];
|
|
1913
2055
|
return {
|
|
1914
2056
|
"fmt_sscan_init": (s, mode) => {
|
|
1915
2057
|
sscanHasErr = 0;
|
|
@@ -2020,9 +2162,26 @@ function ${P}stdlib(stringTable, io) {
|
|
|
2020
2162
|
return 0;
|
|
2021
2163
|
},
|
|
2022
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(),
|
|
2023
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 ?? ""); },
|
|
2024
2174
|
"fmt_printf": (f) => { io.printf(__sprintf(f ?? "", ...io.take())); },
|
|
2025
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 {
|
|
2026
2185
|
"str_trim": (s, c) => ${P}trim(s ?? "", c ?? "", true, true),
|
|
2027
2186
|
"str_trim_left": (s, c) => ${P}trim(s ?? "", c ?? "", true, false),
|
|
2028
2187
|
"str_trim_right": (s, c) => ${P}trim(s ?? "", c ?? "", false, true),
|
|
@@ -2139,7 +2298,7 @@ function ${P}stdlib(stringTable, io) {
|
|
|
2139
2298
|
parts = r;
|
|
2140
2299
|
return parts.length;
|
|
2141
2300
|
},
|
|
2142
|
-
"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; },
|
|
2143
2302
|
"str_part": (i) => parts[i] ?? "",
|
|
2144
2303
|
"str_replacer_init": (s) => {
|
|
2145
2304
|
replacerTarget = s ?? "";
|
|
@@ -2254,7 +2413,7 @@ function ${P}stdlib(stringTable, io) {
|
|
|
2254
2413
|
// unicode
|
|
2255
2414
|
"unicode_is_letter": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && /\\p{L}/u.test(c)) ? 1 : 0; },
|
|
2256
2415
|
"unicode_is_digit": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && /\\p{Nd}/u.test(c)) ? 1 : 0; },
|
|
2257
|
-
"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; },
|
|
2258
2417
|
"unicode_is_upper": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && /\\p{Lu}/u.test(c)) ? 1 : 0; },
|
|
2259
2418
|
"unicode_is_lower": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && /\\p{Ll}/u.test(c)) ? 1 : 0; },
|
|
2260
2419
|
"unicode_is_punct": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && /\\p{P}/u.test(c)) ? 1 : 0; },
|
|
@@ -2263,6 +2422,22 @@ function ${P}stdlib(stringTable, io) {
|
|
|
2263
2422
|
"unicode_is_graphic": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && !/\\p{Cc}/u.test(c)) ? 1 : 0; },
|
|
2264
2423
|
"unicode_to_upper": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return c ? (c.toUpperCase().codePointAt(0) ?? r) : r; },
|
|
2265
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 {
|
|
2266
2441
|
// html
|
|
2267
2442
|
"html_escape": (s) => (s ?? "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'"),
|
|
2268
2443
|
"html_unescape": (s) => (s ?? "").replace(/&(?:quot|apos|#39|#34|gt|lt|amp|#(?:[0-9]+|[xX][0-9a-fA-F]+));/g, (m) => {
|
|
@@ -2327,32 +2502,66 @@ function ${P}stdlib(stringTable, io) {
|
|
|
2327
2502
|
"time_now_ms": () => (typeof globalThis.__GOFRONT_CLOCK === "function" ? globalThis.__GOFRONT_CLOCK() : Date.now()),
|
|
2328
2503
|
"time_timezone_offset": (ms) => -new Date(ms).getTimezoneOffset() * 60,
|
|
2329
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),
|
|
2330
2517
|
"time_parse": (layout, val) => {
|
|
2331
2518
|
try {
|
|
2332
|
-
|
|
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);
|
|
2333
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
|
+
}
|
|
2334
2530
|
timeParseErr = null;
|
|
2335
|
-
|
|
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;
|
|
2336
2543
|
return d.getTime();
|
|
2337
2544
|
} catch {
|
|
2338
2545
|
timeParseErr = "parsing time: cannot parse";
|
|
2339
2546
|
timeParseOffset = 0;
|
|
2547
|
+
timeParseSubNs = 0;
|
|
2340
2548
|
return 0;
|
|
2341
2549
|
}
|
|
2342
2550
|
},
|
|
2343
2551
|
"time_parse_err": () => timeParseErr,
|
|
2344
2552
|
"time_parse_offset": () => timeParseOffset,
|
|
2553
|
+
"time_parse_sub_ns": () => timeParseSubNs,
|
|
2345
2554
|
"time_format": (y, m, d, hr, min, sec, wd, ms, offsetSec, layout) => {
|
|
2346
2555
|
const DAYS = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
|
|
2347
2556
|
const MONTHS = ["January","February","March","April","May","June","July","August","September","October","November","December"];
|
|
2348
2557
|
const pad = (n, w = 2) => String(n).padStart(w, "0");
|
|
2349
|
-
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 => {
|
|
2350
2559
|
if (t.startsWith(".")) {
|
|
2351
2560
|
const isZeroPad = t.includes("0");
|
|
2352
2561
|
const maxLen = t.length - 1;
|
|
2353
|
-
let frac = pad(ms %
|
|
2354
|
-
if (maxLen
|
|
2355
|
-
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");
|
|
2356
2565
|
if (!isZeroPad) {
|
|
2357
2566
|
frac = frac.replace(/0+$/, "");
|
|
2358
2567
|
return frac.length > 0 ? "." + frac : "";
|
|
@@ -2383,6 +2592,15 @@ function ${P}stdlib(stringTable, io) {
|
|
|
2383
2592
|
const o = Math.floor(offsetSec / 60);
|
|
2384
2593
|
return o === 0 ? "Z" : (o > 0 ? "+" : "-") + pad(Math.floor(Math.abs(o) / 60)) + ":" + pad(Math.abs(o) % 60);
|
|
2385
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";
|
|
2386
2604
|
default: return t;
|
|
2387
2605
|
}
|
|
2388
2606
|
});
|
|
@@ -2442,8 +2660,50 @@ function ${P}stdlib(stringTable, io) {
|
|
|
2442
2660
|
return rawBytes.length;
|
|
2443
2661
|
},
|
|
2444
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
|
+
},
|
|
2445
2697
|
};
|
|
2446
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
|
+
}
|
|
2447
2707
|
function ${P}trim(s, cutset, left, right) {
|
|
2448
2708
|
const cs = new Set(Array.from(cutset));
|
|
2449
2709
|
const chars = Array.from(s);
|
|
@@ -2536,6 +2796,13 @@ function ${P}idxview(n, getAt, setAt) {
|
|
|
2536
2796
|
}`;
|
|
2537
2797
|
|
|
2538
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
|
+
|
|
2539
2806
|
export function generateFacade(
|
|
2540
2807
|
meta,
|
|
2541
2808
|
{ stringTable = [], callMain = false, stdlibEnv = true } = {},
|
|
@@ -2547,18 +2814,42 @@ export function generateFacade(
|
|
|
2547
2814
|
out.push(`const ${P}env = {};`);
|
|
2548
2815
|
for (const [key, desc] of needs.funcs) {
|
|
2549
2816
|
const names = desc.params.map((_, i) => `a${i}`);
|
|
2550
|
-
const
|
|
2551
|
-
isPtrBoth(d) ? `${P}view_${d.name}(${names[i]})` : outExpr(d, names[i]),
|
|
2552
|
-
);
|
|
2553
|
-
const call = `fn(${args.join(", ")})`;
|
|
2817
|
+
const hasPtrBox = desc.params.some((d) => d.k === "ptr_box");
|
|
2554
2818
|
let body;
|
|
2555
|
-
if (
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
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
|
+
}
|
|
2562
2853
|
const sig = ["fn", ...names].join(", ");
|
|
2563
2854
|
out.push(
|
|
2564
2855
|
`${P}env["__invoke$${key}"] = (${sig}) => { try { ${body} } catch (__e) { if (__w && __w.__push_panic) __w.__push_panic(__e.message ?? String(__e)); throw __e; } };`,
|
|
@@ -2596,12 +2887,17 @@ ${desc.fields.map((f) => `\to.${f.name} = ${outExpr(f.desc, `__w.__get_${name}_$
|
|
|
2596
2887
|
for (const [key, desc] of needs.boxes) {
|
|
2597
2888
|
out.push(`function ${P}boxin_${key}(b) {
|
|
2598
2889
|
if (b == null) return null;
|
|
2890
|
+
if (b.__ref) return b.__ref;
|
|
2599
2891
|
const v = b.value !== undefined ? b.value : ${zeroExpr(desc.base)};
|
|
2600
2892
|
return __w.__box_new_${key}(${inExpr(desc.base, "v")});
|
|
2601
2893
|
}
|
|
2602
2894
|
function ${P}boxout_${key}(r) {
|
|
2603
2895
|
if (r == null) return null;
|
|
2604
|
-
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
|
+
};
|
|
2605
2901
|
}
|
|
2606
2902
|
function ${P}boxback_${key}(r, b) {
|
|
2607
2903
|
if (r == null || b == null) return;
|
|
@@ -2610,6 +2906,27 @@ function ${P}boxback_${key}(r, b) {
|
|
|
2610
2906
|
}
|
|
2611
2907
|
|
|
2612
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
|
+
}
|
|
2613
2930
|
out.push(`function ${P}slin_${key}(arr) {
|
|
2614
2931
|
if (arr == null) return __w.__slice_nil_${key}();
|
|
2615
2932
|
const n = arr.length;
|
|
@@ -2655,6 +2972,34 @@ function ${P}mapout_${key}(m) {
|
|
|
2655
2972
|
}`);
|
|
2656
2973
|
}
|
|
2657
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
|
+
|
|
2658
3003
|
for (const [key, desc] of needs.arrays) {
|
|
2659
3004
|
out.push(`function ${P}arrzero_${key}(n) {
|
|
2660
3005
|
const out = new Array(n);
|
|
@@ -2794,15 +3139,17 @@ function ${P}shin(v, Ctor) {
|
|
|
2794
3139
|
for (const c of meta.consts) {
|
|
2795
3140
|
if (c.dynamic) {
|
|
2796
3141
|
out.push(
|
|
2797
|
-
`const ${c.name} = ${outExpr(c.desc, `__w.__const_${c.name}()`)};`,
|
|
3142
|
+
`const ${facadeName(c.name)} = ${outExpr(c.desc, `__w.__const_${c.name}()`)};`,
|
|
2798
3143
|
);
|
|
2799
3144
|
} else {
|
|
2800
|
-
out.push(`const ${c.name} = ${c.value};`);
|
|
3145
|
+
out.push(`const ${facadeName(c.name)} = ${c.value};`);
|
|
2801
3146
|
}
|
|
2802
3147
|
}
|
|
2803
3148
|
for (const v of meta.vars ?? [])
|
|
2804
3149
|
out.push(
|
|
2805
|
-
|
|
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}()`)};`,
|
|
2806
3153
|
);
|
|
2807
3154
|
|
|
2808
3155
|
for (const f of meta.funcs) {
|
|
@@ -2811,13 +3158,22 @@ function ${P}shin(v, Ctor) {
|
|
|
2811
3158
|
f.params,
|
|
2812
3159
|
f.returns,
|
|
2813
3160
|
);
|
|
2814
|
-
out.push(
|
|
3161
|
+
out.push(
|
|
3162
|
+
`function ${facadeName(f.name)}(${restParams(names, f.variadic)}) {\n${body}\n}`,
|
|
3163
|
+
);
|
|
2815
3164
|
}
|
|
2816
3165
|
|
|
2817
3166
|
if (callMain) out.push("__w.main();");
|
|
2818
3167
|
return bindExports(out.join("\n"));
|
|
2819
3168
|
}
|
|
2820
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
|
+
|
|
2821
3177
|
// Rewrites `__w.name(...)` into calls through module-level `const` bindings.
|
|
2822
3178
|
// V8 only inlines a JS→wasm call (and so passes f32/f64 arguments unboxed)
|
|
2823
3179
|
// when the callee is a compile-time constant; a property load off the exports
|
|
@@ -2974,7 +3330,7 @@ class ${name} {
|
|
|
2974
3330
|
[recv],
|
|
2975
3331
|
);
|
|
2976
3332
|
lines.push(
|
|
2977
|
-
`\t${m.name}(${names
|
|
3333
|
+
`\t${m.name}(${restParams(names, m.variadic)}) {\n${indent(body, "\t")}\n\t}`,
|
|
2978
3334
|
);
|
|
2979
3335
|
}
|
|
2980
3336
|
lines.push("}");
|