gofront 1.6.0 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/backend/js/expressions.js +238 -109
- package/src/backend/js/index.js +38 -1
- package/src/backend/js/runtime.js +953 -53
- package/src/backend/js/statements.js +55 -67
- package/src/backend/js/stdlib/base64.js +12 -0
- package/src/backend/js/stdlib/binary.js +102 -0
- package/src/backend/js/stdlib/bits.js +15 -1
- package/src/backend/js/stdlib/builder.js +28 -9
- package/src/backend/js/stdlib/bytes.js +75 -18
- package/src/backend/js/stdlib/errors.js +20 -1
- package/src/backend/js/stdlib/fmt.js +70 -13
- package/src/backend/js/stdlib/hex.js +17 -0
- package/src/backend/js/stdlib/html.js +1 -1
- package/src/backend/js/stdlib/index.js +16 -0
- package/src/backend/js/stdlib/io.js +105 -3
- package/src/backend/js/stdlib/math.js +13 -1
- package/src/backend/js/stdlib/path.js +18 -4
- package/src/backend/js/stdlib/rand.js +10 -5
- package/src/backend/js/stdlib/sha256.js +12 -0
- package/src/backend/js/stdlib/slices.js +52 -26
- package/src/backend/js/stdlib/strconv.js +21 -5
- package/src/backend/js/stdlib/strings.js +148 -15
- package/src/backend/js/stdlib/sync.js +13 -0
- package/src/backend/js/stdlib/time.js +90 -19
- package/src/backend/js/stdlib/unicode.js +2 -2
- package/src/backend/wasm/boundary.js +1207 -41
- package/src/backend/wasm/const-fold.js +205 -0
- package/src/backend/wasm/emit-builtins.js +203 -76
- package/src/backend/wasm/emit-exprs.js +464 -276
- package/src/backend/wasm/emit-maps.js +144 -8
- package/src/backend/wasm/emit-stdlib.js +12 -3005
- package/src/backend/wasm/emit-stmts.js +264 -270
- package/src/backend/wasm/emit.js +115 -87
- package/src/backend/wasm/encode.js +55 -29
- package/src/backend/wasm/index.js +1001 -32
- package/src/backend/wasm/optimize.js +13 -4
- package/src/backend/wasm/stdlib/bytes.js +5039 -0
- package/src/backend/wasm/stdlib/core.js +8903 -0
- package/src/backend/wasm/stdlib/index.js +40 -0
- package/src/backend/wasm/stdlib/io.js +2161 -0
- package/src/backend/wasm/stdlib/packages.js +2400 -0
- package/src/backend/wasm/stdlib/pure.js +720 -0
- package/src/backend/wasm/stdlib/time.js +1200 -0
- package/src/backend/wasm/types.js +153 -1
- package/src/cli-core.js +1 -1
- package/src/compiler.js +18 -4
- package/src/dts-parser.js +79 -126
- package/src/index.js +2 -6
- package/src/lexer.js +80 -29
- package/src/lower/boxing.js +1 -2
- package/src/lower/captures.js +2 -6
- package/src/lower/ownership.js +30 -9
- package/src/minifier.js +20 -68
- package/src/parser/declarations.js +3 -6
- package/src/parser/expressions.js +3 -8
- package/src/parser/index.js +18 -3
- package/src/parser/statements.js +25 -38
- package/src/parser/types.js +1 -2
- package/src/resolver.js +2 -27
- package/src/templ-lexer.js +29 -63
- package/src/templ-parser.js +22 -45
- package/src/typechecker/assignability.js +81 -13
- package/src/typechecker/expressions.js +102 -2
- package/src/typechecker/resolve.js +36 -6
- package/src/typechecker/stdlib/core.js +280 -89
- package/src/typechecker/stdlib/extended.js +453 -1
- package/src/typechecker/types.js +344 -6
|
@@ -34,8 +34,22 @@
|
|
|
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 {
|
|
38
|
-
|
|
37
|
+
import {
|
|
38
|
+
HELPER_BASE64,
|
|
39
|
+
HELPER_DURATION,
|
|
40
|
+
HELPER_ERROR,
|
|
41
|
+
HELPER_HEX,
|
|
42
|
+
HELPER_PATH_CLEAN,
|
|
43
|
+
HELPER_SHA256,
|
|
44
|
+
HELPER_SPRINTF,
|
|
45
|
+
HELPER_STRCONV,
|
|
46
|
+
} from "../js/runtime.js";
|
|
47
|
+
import {
|
|
48
|
+
anonIfaceName,
|
|
49
|
+
getInterfaceMethodList,
|
|
50
|
+
getSharedInfo,
|
|
51
|
+
isTestingT,
|
|
52
|
+
} from "./types.js";
|
|
39
53
|
|
|
40
54
|
// ── Type classification ──────────────────────────────────────
|
|
41
55
|
|
|
@@ -94,6 +108,14 @@ function classifyType(goType, mod) {
|
|
|
94
108
|
if (isTestingT(goType)) return { k: "extern", wType: "externref" };
|
|
95
109
|
|
|
96
110
|
if (goType.kind === "TypeName" || goType.kind === "Ident") {
|
|
111
|
+
if (goType.name === "error") return { k: "error", wType: "anyref" };
|
|
112
|
+
if (goType.name === "time.Duration" || goType.name === "Duration")
|
|
113
|
+
return { k: "i64", unsigned: false, wType: "i64" };
|
|
114
|
+
if (goType.name === "time.Time" || goType.name === "Time") {
|
|
115
|
+
const info = mod.getStructType("time.Time");
|
|
116
|
+
if (info)
|
|
117
|
+
return { k: "time", name: "time.Time", info, wType: toRef(info) };
|
|
118
|
+
}
|
|
97
119
|
const info = mod.getStructType(goType.name);
|
|
98
120
|
if (info) return structDesc(info, false);
|
|
99
121
|
const resolved = mod.checker?.types?.get(goType.name);
|
|
@@ -117,6 +139,10 @@ function classifyType(goType, mod) {
|
|
|
117
139
|
return classifySlice(t, mod);
|
|
118
140
|
case "array":
|
|
119
141
|
return classifyArray(t, mod);
|
|
142
|
+
case "map":
|
|
143
|
+
return classifyMap(t, mod);
|
|
144
|
+
case "struct":
|
|
145
|
+
return classifyStruct(t, mod);
|
|
120
146
|
case "func":
|
|
121
147
|
return classifyFunc(t, mod);
|
|
122
148
|
case "interface":
|
|
@@ -130,11 +156,6 @@ function classifyType(goType, mod) {
|
|
|
130
156
|
}
|
|
131
157
|
|
|
132
158
|
const UNSUPPORTED_KINDS = {
|
|
133
|
-
map: "map",
|
|
134
|
-
MapType: "map",
|
|
135
|
-
struct: "anonymous struct",
|
|
136
|
-
StructType: "anonymous struct",
|
|
137
|
-
InterfaceType: "interface",
|
|
138
159
|
ChanType: "chan",
|
|
139
160
|
chan: "chan",
|
|
140
161
|
};
|
|
@@ -149,13 +170,21 @@ function normalizeAstType(t) {
|
|
|
149
170
|
return { kind: "slice", elem: t.elem };
|
|
150
171
|
case "ArrayType":
|
|
151
172
|
return { kind: "array", elem: t.elem, size: t.size };
|
|
173
|
+
case "MapType":
|
|
174
|
+
return { kind: "map", key: t.key, val: t.value ?? t.elem };
|
|
152
175
|
case "FuncType":
|
|
153
176
|
return funcSigFromAst(t);
|
|
154
177
|
case "InterfaceType":
|
|
155
178
|
return {
|
|
156
179
|
kind: "interface",
|
|
157
180
|
name: t.name,
|
|
158
|
-
methods:
|
|
181
|
+
methods: t.methods,
|
|
182
|
+
};
|
|
183
|
+
case "StructType":
|
|
184
|
+
return {
|
|
185
|
+
kind: "struct",
|
|
186
|
+
name: t.name,
|
|
187
|
+
fields: t.fields,
|
|
159
188
|
};
|
|
160
189
|
default:
|
|
161
190
|
return t;
|
|
@@ -169,7 +198,7 @@ const BASIC_DESCS = {
|
|
|
169
198
|
float64: { k: "f64", wType: "f64" },
|
|
170
199
|
string: { k: "string", wType: "externref" },
|
|
171
200
|
any: { k: "any", wType: "anyref" },
|
|
172
|
-
error: { k: "
|
|
201
|
+
error: { k: "error", wType: "anyref" },
|
|
173
202
|
};
|
|
174
203
|
|
|
175
204
|
function classifyBasic(n) {
|
|
@@ -182,17 +211,111 @@ function classifyBasic(n) {
|
|
|
182
211
|
}
|
|
183
212
|
|
|
184
213
|
function classifyNamed(t, mod) {
|
|
214
|
+
if (t.name === "error") return { k: "error", wType: "anyref" };
|
|
215
|
+
if (t.name === "time.Duration")
|
|
216
|
+
return { k: "i64", unsigned: false, wType: "i64" };
|
|
217
|
+
if (t.name === "time.Time") {
|
|
218
|
+
const info = mod.getStructType("time.Time");
|
|
219
|
+
if (info) return { k: "time", name: "time.Time", info, wType: toRef(info) };
|
|
220
|
+
}
|
|
185
221
|
const info = mod.getStructType(t.name);
|
|
186
222
|
if (info) return structDesc(info, false);
|
|
187
223
|
if (t.underlying) return classifyType(t.underlying, mod);
|
|
188
224
|
return { k: "unsupported", what: `named type '${t.name}'` };
|
|
189
225
|
}
|
|
190
226
|
|
|
227
|
+
function classifyAnonStruct(t, mod) {
|
|
228
|
+
const rawFields = [];
|
|
229
|
+
if (t.fields instanceof Map) {
|
|
230
|
+
for (const [k, v] of t.fields.entries())
|
|
231
|
+
rawFields.push({ name: k, goType: v });
|
|
232
|
+
} else if (Array.isArray(t.fields)) {
|
|
233
|
+
for (const f of t.fields) {
|
|
234
|
+
const names = f.names ?? (f.name ? [f.name] : []);
|
|
235
|
+
for (const n of names) rawFields.push({ name: n, goType: f.type });
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
const info = mod.getAnonStructType(rawFields);
|
|
239
|
+
const fields = [];
|
|
240
|
+
for (const f of info.fields) {
|
|
241
|
+
const fd = classifyType(f.goType, mod);
|
|
242
|
+
if (fd.k === "unsupported") return fd;
|
|
243
|
+
fields.push({ name: f.name, desc: fd });
|
|
244
|
+
}
|
|
245
|
+
return {
|
|
246
|
+
k: "anon_struct",
|
|
247
|
+
name: info.name,
|
|
248
|
+
key: info.name,
|
|
249
|
+
info,
|
|
250
|
+
fields,
|
|
251
|
+
wType: toRef(info),
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function classifyStruct(t, mod) {
|
|
256
|
+
if (t.name) {
|
|
257
|
+
const info = mod.getStructType(t.name);
|
|
258
|
+
if (info) return structDesc(info, false);
|
|
259
|
+
}
|
|
260
|
+
return classifyAnonStruct(t, mod);
|
|
261
|
+
}
|
|
262
|
+
|
|
191
263
|
function classifyPointer(t, mod) {
|
|
192
264
|
const base = classifyType(t.base, mod);
|
|
265
|
+
if (base.k === "unsupported") return base;
|
|
193
266
|
if (base.k === "struct" && !base.ptr) return { ...base, ptr: true };
|
|
194
|
-
|
|
195
|
-
|
|
267
|
+
if (base.k === "anon_struct" && !base.ptr) return { ...base, ptr: true };
|
|
268
|
+
if (base.k === "time" && !base.ptr) return { ...base, ptr: true };
|
|
269
|
+
const boxInfo = mod.getBoxType(t.base);
|
|
270
|
+
const key = `${base.k}${base.unsigned ? "_u" : ""}_${boxInfo.typeIndex}`;
|
|
271
|
+
return {
|
|
272
|
+
k: "ptr_box",
|
|
273
|
+
base,
|
|
274
|
+
boxInfo,
|
|
275
|
+
key,
|
|
276
|
+
wType: { kind: "ref", nullable: true, typeIndex: boxInfo.typeIndex },
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function isAllowedMapKey(kDesc) {
|
|
281
|
+
if (kDesc.k === "string" || kDesc.k === "bool") return true;
|
|
282
|
+
if (
|
|
283
|
+
kDesc.k === "i32" ||
|
|
284
|
+
kDesc.k === "i64" ||
|
|
285
|
+
kDesc.k === "f32" ||
|
|
286
|
+
kDesc.k === "f64"
|
|
287
|
+
)
|
|
288
|
+
return true;
|
|
289
|
+
return false;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function classifyMap(t, mod) {
|
|
293
|
+
const keyType = t.key;
|
|
294
|
+
const valType = t.val ?? t.value ?? t.elem;
|
|
295
|
+
const keyDesc = classifyType(keyType, mod);
|
|
296
|
+
if (keyDesc.k === "unsupported") return keyDesc;
|
|
297
|
+
const valDesc = classifyType(valType, mod);
|
|
298
|
+
if (valDesc.k === "unsupported") return valDesc;
|
|
299
|
+
if (!isAllowedMapKey(keyDesc)) {
|
|
300
|
+
return {
|
|
301
|
+
k: "unsupported",
|
|
302
|
+
what: `map with unsupported key type '${keyType?.name ?? keyDesc.k}'`,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
const mapInfo = mod.getMapType(keyType, valType);
|
|
306
|
+
const keySliceDesc = classifySlice({ kind: "slice", elem: keyType }, mod);
|
|
307
|
+
const valSliceDesc = classifySlice({ kind: "slice", elem: valType }, mod);
|
|
308
|
+
const key = `m_${mapInfo.suffix}`;
|
|
309
|
+
return {
|
|
310
|
+
k: "map",
|
|
311
|
+
key,
|
|
312
|
+
mapInfo,
|
|
313
|
+
keyDesc,
|
|
314
|
+
valDesc,
|
|
315
|
+
keySliceDesc,
|
|
316
|
+
valSliceDesc,
|
|
317
|
+
wType: mapInfo.wType,
|
|
318
|
+
};
|
|
196
319
|
}
|
|
197
320
|
|
|
198
321
|
function classifyShared(t, mod) {
|
|
@@ -263,12 +386,50 @@ function classifyFunc(t, mod) {
|
|
|
263
386
|
}
|
|
264
387
|
|
|
265
388
|
function classifyInterface(t, mod) {
|
|
266
|
-
if (t.name === "error") return { k: "
|
|
267
|
-
|
|
268
|
-
if (
|
|
269
|
-
return { k: "unsupported", what: "anonymous interface" };
|
|
389
|
+
if (t.name === "error") return { k: "error", wType: "anyref" };
|
|
390
|
+
const mList = getInterfaceMethodList(t);
|
|
391
|
+
if (mList.length === 0) return { k: "any", wType: "anyref" };
|
|
270
392
|
mod._boundaryIfaces ??= new Map();
|
|
271
|
-
|
|
393
|
+
if (t.name && t.methods instanceof Map) {
|
|
394
|
+
return mod._boundaryIfaces.get(t.name) ?? classifyNamedInterface(t, mod);
|
|
395
|
+
}
|
|
396
|
+
return classifyAnonInterface(t, mod);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function classifyAnonInterface(t, mod) {
|
|
400
|
+
const key = anonIfaceName(t, mod.checker, mod);
|
|
401
|
+
if (mod._boundaryIfaces.has(key)) return mod._boundaryIfaces.get(key);
|
|
402
|
+
const mList = getInterfaceMethodList(t);
|
|
403
|
+
const desc = {
|
|
404
|
+
k: "iface",
|
|
405
|
+
name: key,
|
|
406
|
+
key,
|
|
407
|
+
wType: "anyref",
|
|
408
|
+
methods: [],
|
|
409
|
+
concrete: [],
|
|
410
|
+
isAnon: true,
|
|
411
|
+
};
|
|
412
|
+
mod._boundaryIfaces.set(key, desc);
|
|
413
|
+
for (const m of mList) {
|
|
414
|
+
const sig = classifySig(
|
|
415
|
+
{ kind: "func", params: m.params, returns: m.returns },
|
|
416
|
+
mod,
|
|
417
|
+
);
|
|
418
|
+
if (sig.k === "unsupported") {
|
|
419
|
+
const err = {
|
|
420
|
+
k: "unsupported",
|
|
421
|
+
what: `${sig.what} (in anonymous interface method ${m.name})`,
|
|
422
|
+
};
|
|
423
|
+
mod._boundaryIfaces.set(key, err);
|
|
424
|
+
return err;
|
|
425
|
+
}
|
|
426
|
+
desc.methods.push({
|
|
427
|
+
name: m.name,
|
|
428
|
+
dispatch: `__dispatch_${key}_${m.name}`,
|
|
429
|
+
...sig,
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
return desc;
|
|
272
433
|
}
|
|
273
434
|
|
|
274
435
|
function classifyNamedInterface(t, mod) {
|
|
@@ -325,7 +486,14 @@ function literalConst(spec, i) {
|
|
|
325
486
|
}
|
|
326
487
|
if (node?.kind !== "BasicLit") return null;
|
|
327
488
|
switch (node.litKind) {
|
|
328
|
-
case "INT":
|
|
489
|
+
case "INT": {
|
|
490
|
+
const raw = String(node.value);
|
|
491
|
+
const clean = /^0[0-7]+$/.test(raw) ? `0o${raw.slice(1)}` : raw;
|
|
492
|
+
const bi = BigInt(`${sign}${clean}`);
|
|
493
|
+
return bi > 9007199254740991n || bi < -9007199254740991n
|
|
494
|
+
? `${sign}${clean}n`
|
|
495
|
+
: `${sign}${clean}`;
|
|
496
|
+
}
|
|
329
497
|
case "FLOAT":
|
|
330
498
|
return `${sign}${node.value}`;
|
|
331
499
|
case "STRING":
|
|
@@ -394,14 +562,48 @@ function collectConstDecl(d, pkgName, meta, errors) {
|
|
|
394
562
|
const name = spec.names[i];
|
|
395
563
|
if (!isExported(name)) continue;
|
|
396
564
|
const value = literalConst(spec, i);
|
|
397
|
-
if (value !== null)
|
|
398
|
-
|
|
565
|
+
if (value !== null) {
|
|
566
|
+
meta.consts.push({ name, value, dynamic: false });
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
const folded = meta._mod.constCache?.get(name);
|
|
570
|
+
if (folded) {
|
|
571
|
+
let val = null;
|
|
572
|
+
if (folded.litKind === "INT") {
|
|
573
|
+
const s = String(folded.value);
|
|
574
|
+
const clean = /^0[0-7]+$/.test(s) ? `0o${s.slice(1)}` : s;
|
|
575
|
+
const bi = BigInt(clean);
|
|
576
|
+
val =
|
|
577
|
+
bi > 9007199254740991n || bi < -9007199254740991n
|
|
578
|
+
? `${clean}n`
|
|
579
|
+
: clean;
|
|
580
|
+
} else if (folded.litKind === "FLOAT") {
|
|
581
|
+
val = folded.value;
|
|
582
|
+
} else if (folded.litKind === "STRING") {
|
|
583
|
+
val = JSON.stringify(String(folded.value));
|
|
584
|
+
} else if (folded.litKind === "BOOL") {
|
|
585
|
+
val = folded.value === "true" ? "true" : "false";
|
|
586
|
+
}
|
|
587
|
+
if (val !== null) {
|
|
588
|
+
meta.consts.push({ name, value: val, dynamic: false });
|
|
589
|
+
continue;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
const glob = meta._mod.resolveGlobal(name);
|
|
593
|
+
const rawType = spec.type ?? spec.value?.[i]?._type ?? glob?.goType;
|
|
594
|
+
let desc = rawType ? classifyType(rawType, meta._mod) : null;
|
|
595
|
+
if (!desc || desc.k === "void") {
|
|
596
|
+
if (glob?.type) {
|
|
597
|
+
desc = { k: glob.type, wType: glob.type };
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
if (!desc || desc.k === "unsupported") {
|
|
399
601
|
errors.push(
|
|
400
|
-
unsupportedMsg(
|
|
401
|
-
`${pkgName}.${name}`,
|
|
402
|
-
"a constant with a non-literal value",
|
|
403
|
-
),
|
|
602
|
+
unsupportedMsg(`${pkgName}.${name}`, desc?.what ?? "unsupported"),
|
|
404
603
|
);
|
|
604
|
+
} else {
|
|
605
|
+
meta.consts.push({ name, dynamic: true, desc });
|
|
606
|
+
}
|
|
405
607
|
}
|
|
406
608
|
}
|
|
407
609
|
}
|
|
@@ -483,6 +685,17 @@ function visitNeed(desc, needs, errors, mod) {
|
|
|
483
685
|
case "struct":
|
|
484
686
|
visitStructNeed(desc, needs, errors, mod);
|
|
485
687
|
return;
|
|
688
|
+
case "anon_struct":
|
|
689
|
+
if (needs.anonStructs.has(desc.name)) return;
|
|
690
|
+
needs.anonStructs.set(desc.name, desc);
|
|
691
|
+
for (const f of desc.fields) visitNeed(f.desc, needs, errors, mod);
|
|
692
|
+
return;
|
|
693
|
+
case "error":
|
|
694
|
+
needs.error = true;
|
|
695
|
+
return;
|
|
696
|
+
case "time":
|
|
697
|
+
needs.time = true;
|
|
698
|
+
return;
|
|
486
699
|
case "slice":
|
|
487
700
|
case "array": {
|
|
488
701
|
const map = desc.k === "slice" ? needs.slices : needs.arrays;
|
|
@@ -491,6 +704,19 @@ function visitNeed(desc, needs, errors, mod) {
|
|
|
491
704
|
visitNeed(desc.elem, needs, errors, mod);
|
|
492
705
|
return;
|
|
493
706
|
}
|
|
707
|
+
case "map":
|
|
708
|
+
if (needs.maps.has(desc.key)) return;
|
|
709
|
+
needs.maps.set(desc.key, desc);
|
|
710
|
+
visitNeed(desc.keyDesc, needs, errors, mod);
|
|
711
|
+
visitNeed(desc.valDesc, needs, errors, mod);
|
|
712
|
+
visitNeed(desc.keySliceDesc, needs, errors, mod);
|
|
713
|
+
visitNeed(desc.valSliceDesc, needs, errors, mod);
|
|
714
|
+
return;
|
|
715
|
+
case "ptr_box":
|
|
716
|
+
if (needs.boxes.has(desc.key)) return;
|
|
717
|
+
needs.boxes.set(desc.key, desc);
|
|
718
|
+
visitNeed(desc.base, needs, errors, mod);
|
|
719
|
+
return;
|
|
494
720
|
case "func":
|
|
495
721
|
if (needs.funcs.has(desc.key)) return;
|
|
496
722
|
needs.funcs.set(desc.key, desc);
|
|
@@ -538,10 +764,15 @@ function collectNeeds(meta) {
|
|
|
538
764
|
const mod = meta._mod;
|
|
539
765
|
const needs = {
|
|
540
766
|
structs: new Map(), // name -> { info, fields: [{ name, desc }] }
|
|
767
|
+
anonStructs: new Map(), // name -> desc
|
|
541
768
|
slices: new Map(), // key -> desc
|
|
542
769
|
arrays: new Map(), // key -> desc
|
|
770
|
+
maps: new Map(), // key -> desc
|
|
771
|
+
boxes: new Map(), // key -> desc
|
|
543
772
|
funcs: new Map(), // key -> desc
|
|
544
773
|
ifaces: new Map(), // interface name -> desc
|
|
774
|
+
error: false,
|
|
775
|
+
time: false,
|
|
545
776
|
shared: false, // any shared buffer on the surface
|
|
546
777
|
};
|
|
547
778
|
const errors = [];
|
|
@@ -552,6 +783,9 @@ function collectNeeds(meta) {
|
|
|
552
783
|
}
|
|
553
784
|
for (const f of meta.funcs) visitSignatureNeeds(f, needs, errors, mod);
|
|
554
785
|
for (const v of meta.vars ?? []) visitNeed(v.desc, needs, errors, mod);
|
|
786
|
+
for (const c of meta.consts ?? []) {
|
|
787
|
+
if (c.dynamic) visitNeed(c.desc, needs, errors, mod);
|
|
788
|
+
}
|
|
555
789
|
if (errors.length > 0) throw new Error([...new Set(errors)].join("\n"));
|
|
556
790
|
for (const desc of needs.ifaces.values())
|
|
557
791
|
desc.concrete = concreteCandidates(desc, needs, meta, mod);
|
|
@@ -968,6 +1202,231 @@ export function addBoundaryHelpers(mod, meta) {
|
|
|
968
1202
|
);
|
|
969
1203
|
}
|
|
970
1204
|
}
|
|
1205
|
+
|
|
1206
|
+
for (const [name, desc] of needs.anonStructs) {
|
|
1207
|
+
const { info } = desc;
|
|
1208
|
+
const ref = desc.wType;
|
|
1209
|
+
const ti = info.typeIndex;
|
|
1210
|
+
const fieldWTypes = info.fields.map((f) => f.wType);
|
|
1211
|
+
addFunc(
|
|
1212
|
+
`__new_${name}`,
|
|
1213
|
+
fieldWTypes.map(extType),
|
|
1214
|
+
[EXT],
|
|
1215
|
+
[
|
|
1216
|
+
...fieldWTypes.flatMap((w, i) => getIn(i, w)),
|
|
1217
|
+
{ op: "struct.new", typeIndex: ti },
|
|
1218
|
+
...toExt(ref),
|
|
1219
|
+
],
|
|
1220
|
+
);
|
|
1221
|
+
addFunc(
|
|
1222
|
+
`__zero_${name}`,
|
|
1223
|
+
[],
|
|
1224
|
+
[EXT],
|
|
1225
|
+
[
|
|
1226
|
+
...fieldWTypes.map((w) => zeroInstr(w)),
|
|
1227
|
+
{ op: "struct.new", typeIndex: ti },
|
|
1228
|
+
...toExt(ref),
|
|
1229
|
+
],
|
|
1230
|
+
);
|
|
1231
|
+
for (let i = 0; i < info.fields.length; i++) {
|
|
1232
|
+
const f = info.fields[i];
|
|
1233
|
+
addFunc(
|
|
1234
|
+
`__get_${name}_${f.name}`,
|
|
1235
|
+
[EXT],
|
|
1236
|
+
[extType(f.wType)],
|
|
1237
|
+
[
|
|
1238
|
+
...getIn(0, ref),
|
|
1239
|
+
{ op: "struct.get", typeIndex: ti, fieldIndex: i },
|
|
1240
|
+
...toExt(f.wType),
|
|
1241
|
+
],
|
|
1242
|
+
);
|
|
1243
|
+
addFunc(
|
|
1244
|
+
`__set_${name}_${f.name}`,
|
|
1245
|
+
[EXT, extType(f.wType)],
|
|
1246
|
+
[],
|
|
1247
|
+
[
|
|
1248
|
+
...getIn(0, ref),
|
|
1249
|
+
...getIn(1, f.wType),
|
|
1250
|
+
{ op: "struct.set", typeIndex: ti, fieldIndex: i },
|
|
1251
|
+
],
|
|
1252
|
+
);
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
for (const [key, desc] of needs.boxes) {
|
|
1257
|
+
const { boxInfo } = desc;
|
|
1258
|
+
const ref = desc.wType;
|
|
1259
|
+
const ti = boxInfo.typeIndex;
|
|
1260
|
+
const w = boxInfo.wType;
|
|
1261
|
+
addFunc(
|
|
1262
|
+
`__box_new_${key}`,
|
|
1263
|
+
[extType(w)],
|
|
1264
|
+
[EXT],
|
|
1265
|
+
[...getIn(0, w), { op: "struct.new", typeIndex: ti }, ...toExt(ref)],
|
|
1266
|
+
);
|
|
1267
|
+
addFunc(
|
|
1268
|
+
`__box_get_${key}`,
|
|
1269
|
+
[EXT],
|
|
1270
|
+
[extType(w)],
|
|
1271
|
+
[
|
|
1272
|
+
...getIn(0, ref),
|
|
1273
|
+
{ op: "struct.get", typeIndex: ti, fieldIndex: 0 },
|
|
1274
|
+
...toExt(w),
|
|
1275
|
+
],
|
|
1276
|
+
);
|
|
1277
|
+
addFunc(
|
|
1278
|
+
`__box_set_${key}`,
|
|
1279
|
+
[EXT, extType(w)],
|
|
1280
|
+
[],
|
|
1281
|
+
[
|
|
1282
|
+
...getIn(0, ref),
|
|
1283
|
+
...getIn(1, w),
|
|
1284
|
+
{ op: "struct.set", typeIndex: ti, fieldIndex: 0 },
|
|
1285
|
+
],
|
|
1286
|
+
);
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
for (const [key, desc] of needs.maps) {
|
|
1290
|
+
const { mapInfo, keySliceDesc, valSliceDesc } = desc;
|
|
1291
|
+
const makeIdx = mod.resolveFuncIndex(mapInfo.makeFuncName);
|
|
1292
|
+
addFunc(
|
|
1293
|
+
`__map_new_${key}`,
|
|
1294
|
+
["i32"],
|
|
1295
|
+
[EXT],
|
|
1296
|
+
[get(0), { op: "call", funcIndex: makeIdx }, ...toExt(desc.wType)],
|
|
1297
|
+
);
|
|
1298
|
+
const setIdx = mod.resolveFuncIndex(mapInfo.setFuncName);
|
|
1299
|
+
addFunc(
|
|
1300
|
+
`__map_set_${key}`,
|
|
1301
|
+
[EXT, extType(mapInfo.keyWType), extType(mapInfo.valWType)],
|
|
1302
|
+
[],
|
|
1303
|
+
[
|
|
1304
|
+
...getIn(0, desc.wType),
|
|
1305
|
+
...getIn(1, mapInfo.keyWType),
|
|
1306
|
+
...getIn(2, mapInfo.valWType),
|
|
1307
|
+
{ op: "call", funcIndex: setIdx },
|
|
1308
|
+
],
|
|
1309
|
+
);
|
|
1310
|
+
const keysIdx = mod.resolveFuncIndex(mapInfo.keysFuncName);
|
|
1311
|
+
addFunc(
|
|
1312
|
+
`__map_keys_${key}`,
|
|
1313
|
+
[EXT],
|
|
1314
|
+
[EXT],
|
|
1315
|
+
[
|
|
1316
|
+
...getIn(0, desc.wType),
|
|
1317
|
+
{ op: "call", funcIndex: keysIdx },
|
|
1318
|
+
...toExt(keySliceDesc.wType),
|
|
1319
|
+
],
|
|
1320
|
+
);
|
|
1321
|
+
const valuesIdx = mod.resolveFuncIndex(mapInfo.valuesFuncName);
|
|
1322
|
+
addFunc(
|
|
1323
|
+
`__map_values_${key}`,
|
|
1324
|
+
[EXT],
|
|
1325
|
+
[EXT],
|
|
1326
|
+
[
|
|
1327
|
+
...getIn(0, desc.wType),
|
|
1328
|
+
{ op: "call", funcIndex: valuesIdx },
|
|
1329
|
+
...toExt(valSliceDesc.wType),
|
|
1330
|
+
],
|
|
1331
|
+
);
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
if (needs.error) {
|
|
1335
|
+
const errDispatcherIdx = mod.funcMap.get("__dispatch_error_Error");
|
|
1336
|
+
if (errDispatcherIdx !== undefined) {
|
|
1337
|
+
addFunc(
|
|
1338
|
+
"__error_msg",
|
|
1339
|
+
[EXT],
|
|
1340
|
+
[EXT],
|
|
1341
|
+
[
|
|
1342
|
+
get(0),
|
|
1343
|
+
{ op: "ref.is_null" },
|
|
1344
|
+
{ op: "if", blockType: "externref" },
|
|
1345
|
+
{ op: "ref.null", heapType: "extern" },
|
|
1346
|
+
{ op: "else" },
|
|
1347
|
+
...getIn(0, "anyref"),
|
|
1348
|
+
{ op: "call", funcIndex: errDispatcherIdx },
|
|
1349
|
+
...toExt("externref"),
|
|
1350
|
+
{ op: "end" },
|
|
1351
|
+
],
|
|
1352
|
+
);
|
|
1353
|
+
}
|
|
1354
|
+
const errCandidates = mod
|
|
1355
|
+
.findInterfaceCandidates(null, "Error")
|
|
1356
|
+
.map((c) => c.testTypeIndex);
|
|
1357
|
+
addFunc(
|
|
1358
|
+
"__is_error",
|
|
1359
|
+
[EXT],
|
|
1360
|
+
["i32"],
|
|
1361
|
+
refTestChain(errCandidates, () => 1),
|
|
1362
|
+
);
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
if (needs.time) {
|
|
1366
|
+
const timeInfo = mod.getStructType("time.Time");
|
|
1367
|
+
if (timeInfo) {
|
|
1368
|
+
const timeWType = toRef(timeInfo);
|
|
1369
|
+
addFunc(
|
|
1370
|
+
"__time_make",
|
|
1371
|
+
["f64", "i32"],
|
|
1372
|
+
[EXT],
|
|
1373
|
+
[
|
|
1374
|
+
get(0),
|
|
1375
|
+
{ op: "f64.const", value: 1000000 },
|
|
1376
|
+
{ op: "f64.mul" },
|
|
1377
|
+
{ op: "i64.trunc_sat_f64_s" },
|
|
1378
|
+
get(1),
|
|
1379
|
+
{ op: "struct.new", typeIndex: timeInfo.typeIndex },
|
|
1380
|
+
...toExt(timeWType),
|
|
1381
|
+
],
|
|
1382
|
+
);
|
|
1383
|
+
addFunc(
|
|
1384
|
+
"__time_unix_milli",
|
|
1385
|
+
[EXT],
|
|
1386
|
+
["f64"],
|
|
1387
|
+
[
|
|
1388
|
+
get(0),
|
|
1389
|
+
{ op: "ref.is_null" },
|
|
1390
|
+
{ op: "if", blockType: "f64" },
|
|
1391
|
+
{ op: "f64.const", value: 0 },
|
|
1392
|
+
{ op: "else" },
|
|
1393
|
+
...getIn(0, timeWType),
|
|
1394
|
+
{ op: "struct.get", typeIndex: timeInfo.typeIndex, fieldIndex: 0 },
|
|
1395
|
+
{ op: "i64.const", value: 1000000n },
|
|
1396
|
+
{ op: "i64.div_s" },
|
|
1397
|
+
{ op: "f64.convert_i64_s" },
|
|
1398
|
+
{ op: "end" },
|
|
1399
|
+
],
|
|
1400
|
+
);
|
|
1401
|
+
addFunc(
|
|
1402
|
+
"__time_offset",
|
|
1403
|
+
[EXT],
|
|
1404
|
+
["i32"],
|
|
1405
|
+
[
|
|
1406
|
+
get(0),
|
|
1407
|
+
{ op: "ref.is_null" },
|
|
1408
|
+
{ op: "if", blockType: "i32" },
|
|
1409
|
+
{ op: "i32.const", value: 0 },
|
|
1410
|
+
{ op: "else" },
|
|
1411
|
+
...getIn(0, timeWType),
|
|
1412
|
+
{ op: "struct.get", typeIndex: timeInfo.typeIndex, fieldIndex: 1 },
|
|
1413
|
+
{ op: "end" },
|
|
1414
|
+
],
|
|
1415
|
+
);
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
for (const c of meta.consts ?? []) {
|
|
1420
|
+
if (!c.dynamic) continue;
|
|
1421
|
+
const g = mod.resolveGlobal(c.name);
|
|
1422
|
+
if (!g) continue;
|
|
1423
|
+
addFunc(
|
|
1424
|
+
`__const_${c.name}`,
|
|
1425
|
+
[],
|
|
1426
|
+
[extType(c.desc.wType)],
|
|
1427
|
+
[{ op: "global.get", index: g.index }, ...toExt(c.desc.wType)],
|
|
1428
|
+
);
|
|
1429
|
+
}
|
|
971
1430
|
}
|
|
972
1431
|
|
|
973
1432
|
// `(externref) -> i32`: `ref.test`s param 0 against each type index and
|
|
@@ -1050,6 +1509,18 @@ function inExpr(desc, v) {
|
|
|
1050
1509
|
return `${P}strin(${v})`;
|
|
1051
1510
|
case "any":
|
|
1052
1511
|
return `(${v} === undefined ? null : ${v})`;
|
|
1512
|
+
case "error":
|
|
1513
|
+
return `${P}errin(${v})`;
|
|
1514
|
+
case "time":
|
|
1515
|
+
return desc.ptr ? `${P}timein_ptr(${v})` : `${P}timein(${v})`;
|
|
1516
|
+
case "map":
|
|
1517
|
+
return `${P}mapin_${desc.key}(${v})`;
|
|
1518
|
+
case "ptr_box":
|
|
1519
|
+
return `${P}boxin_${desc.key}(${v})`;
|
|
1520
|
+
case "anon_struct":
|
|
1521
|
+
return desc.ptr
|
|
1522
|
+
? `(${v} == null ? null : ${P}to_${desc.name}(${v}))`
|
|
1523
|
+
: `${P}to_${desc.name}(${v})`;
|
|
1053
1524
|
case "struct":
|
|
1054
1525
|
if (desc.info.pkgTarget === "both")
|
|
1055
1526
|
return `${P}to_${desc.name}(${v}, ${desc.ptr ? "false" : "true"})`;
|
|
@@ -1082,6 +1553,18 @@ function outExpr(desc, v) {
|
|
|
1082
1553
|
return desc.unsigned ? `${P}u64out(${v})` : `${P}i64out(${v})`;
|
|
1083
1554
|
case "string":
|
|
1084
1555
|
return `(${v} ?? "")`;
|
|
1556
|
+
case "error":
|
|
1557
|
+
return `${P}errout(${v})`;
|
|
1558
|
+
case "time":
|
|
1559
|
+
return desc.ptr ? `${P}timeout_ptr(${v})` : `${P}timeout(${v})`;
|
|
1560
|
+
case "map":
|
|
1561
|
+
return `${P}mapout_${desc.key}(${v})`;
|
|
1562
|
+
case "ptr_box":
|
|
1563
|
+
return `${P}boxout_${desc.key}(${v})`;
|
|
1564
|
+
case "anon_struct":
|
|
1565
|
+
return desc.ptr
|
|
1566
|
+
? `(${v} == null ? null : ${P}from_${desc.name}(${v}))`
|
|
1567
|
+
: `${P}from_${desc.name}(${v})`;
|
|
1085
1568
|
case "struct":
|
|
1086
1569
|
return desc.info.pkgTarget === "both"
|
|
1087
1570
|
? `${P}from_${desc.name}(${v})`
|
|
@@ -1112,6 +1595,15 @@ function zeroExpr(desc) {
|
|
|
1112
1595
|
return "0";
|
|
1113
1596
|
case "string":
|
|
1114
1597
|
return '""';
|
|
1598
|
+
case "error":
|
|
1599
|
+
case "time":
|
|
1600
|
+
case "map":
|
|
1601
|
+
case "ptr_box":
|
|
1602
|
+
return "null";
|
|
1603
|
+
case "anon_struct":
|
|
1604
|
+
return desc.ptr
|
|
1605
|
+
? "null"
|
|
1606
|
+
: `{ ${desc.fields.map((f) => `"${f.name}": ${zeroExpr(f.desc)}`).join(", ")} }`;
|
|
1115
1607
|
case "struct":
|
|
1116
1608
|
return desc.ptr ? "null" : `new ${desc.name}()`;
|
|
1117
1609
|
case "array":
|
|
@@ -1147,6 +1639,10 @@ function isBothStruct(d) {
|
|
|
1147
1639
|
return d.k === "struct" && d.info.pkgTarget === "both";
|
|
1148
1640
|
}
|
|
1149
1641
|
|
|
1642
|
+
function isPtrWriteBack(d) {
|
|
1643
|
+
return isPtrBoth(d) || d.k === "ptr_box" || (d.k === "anon_struct" && d.ptr);
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1150
1646
|
function returnLines(returns, resultExpr) {
|
|
1151
1647
|
if (returns.length === 0) return [];
|
|
1152
1648
|
if (returns.length === 1)
|
|
@@ -1161,13 +1657,21 @@ function genFuncBody(exportName, params, returns, leadArgs = []) {
|
|
|
1161
1657
|
const names = params.map((_, i) => `a${i}`);
|
|
1162
1658
|
const lines = [];
|
|
1163
1659
|
const argExprs = [...leadArgs];
|
|
1164
|
-
const writeBack = params.some(
|
|
1165
|
-
if (
|
|
1660
|
+
const writeBack = params.some(isPtrWriteBack);
|
|
1661
|
+
if (params.some(isPtrBoth)) lines.push("\tconst __m = new Map();");
|
|
1166
1662
|
for (let i = 0; i < params.length; i++) {
|
|
1167
1663
|
const d = params[i];
|
|
1168
1664
|
if (isPtrBoth(d)) {
|
|
1169
1665
|
lines.push(`\tconst __p${i} = ${P}to_${d.name}(a${i}, false, __m);`);
|
|
1170
1666
|
argExprs.push(`__p${i}`);
|
|
1667
|
+
} else if (d.k === "ptr_box") {
|
|
1668
|
+
lines.push(`\tconst __p${i} = ${P}boxin_${d.key}(a${i});`);
|
|
1669
|
+
argExprs.push(`__p${i}`);
|
|
1670
|
+
} else if (d.k === "anon_struct" && d.ptr) {
|
|
1671
|
+
lines.push(
|
|
1672
|
+
`\tconst __p${i} = a${i} == null ? null : ${P}to_${d.name}(a${i});`,
|
|
1673
|
+
);
|
|
1674
|
+
argExprs.push(`__p${i}`);
|
|
1171
1675
|
} else {
|
|
1172
1676
|
argExprs.push(inExpr(d, names[i]));
|
|
1173
1677
|
}
|
|
@@ -1185,8 +1689,12 @@ function genFuncBody(exportName, params, returns, leadArgs = []) {
|
|
|
1185
1689
|
} else {
|
|
1186
1690
|
lines.push(`\t\tconst __r = ${call};`);
|
|
1187
1691
|
for (let i = 0; i < params.length; i++) {
|
|
1188
|
-
|
|
1189
|
-
|
|
1692
|
+
const d = params[i];
|
|
1693
|
+
if (isPtrBoth(d)) lines.push(`\t\t${P}back_${d.name}(__p${i}, a${i});`);
|
|
1694
|
+
else if (d.k === "ptr_box")
|
|
1695
|
+
lines.push(`\t\t${P}boxback_${d.key}(__p${i}, a${i});`);
|
|
1696
|
+
else if (d.k === "anon_struct" && d.ptr)
|
|
1697
|
+
lines.push(`\t\t${P}back_${d.name}(__p${i}, a${i});`);
|
|
1190
1698
|
}
|
|
1191
1699
|
lines.push(...returnLines(returns, "__r").map((l) => `\t${l}`));
|
|
1192
1700
|
}
|
|
@@ -1206,6 +1714,7 @@ export const WASM_IMPORTS_JS = `const ${P}MAX = 9007199254740991n;
|
|
|
1206
1714
|
function ${P}imports(stringTable, extraEnv, tag, write) {
|
|
1207
1715
|
let lineBuf = "";
|
|
1208
1716
|
let targs = [];
|
|
1717
|
+
let fmtStructMap = new WeakMap();
|
|
1209
1718
|
const late = { exports: null };
|
|
1210
1719
|
const flush = () => { write(lineBuf); lineBuf = ""; };
|
|
1211
1720
|
const print = (v) => {
|
|
@@ -1224,6 +1733,8 @@ function ${P}imports(stringTable, extraEnv, tag, write) {
|
|
|
1224
1733
|
flush();
|
|
1225
1734
|
};
|
|
1226
1735
|
const targ = (v) => { targs.push(v); };
|
|
1736
|
+
let utf8Encoded = null;
|
|
1737
|
+
let convBytes = null, convPos = 0;
|
|
1227
1738
|
const env = {
|
|
1228
1739
|
"panicTag": tag,
|
|
1229
1740
|
"panic": (msg) => {
|
|
@@ -1276,6 +1787,25 @@ function ${P}imports(stringTable, extraEnv, tag, write) {
|
|
|
1276
1787
|
"str_from_i32": (n) => String(n),
|
|
1277
1788
|
"str_from_f64": (f) => String(f),
|
|
1278
1789
|
"is_string": (v) => (typeof v === "string" ? 1 : 0),
|
|
1790
|
+
"str_to_bytes": (s) => {
|
|
1791
|
+
utf8Encoded = new TextEncoder().encode(s ?? "");
|
|
1792
|
+
return utf8Encoded.length;
|
|
1793
|
+
},
|
|
1794
|
+
"str_byte_at": (i) => (utf8Encoded ? (utf8Encoded[i] ?? 0) : 0),
|
|
1795
|
+
"bytes_to_str_init": (len) => {
|
|
1796
|
+
convBytes = new Uint8Array(len);
|
|
1797
|
+
convPos = 0;
|
|
1798
|
+
},
|
|
1799
|
+
"bytes_to_str_push": (b) => {
|
|
1800
|
+
if (convBytes && convPos < convBytes.length) {
|
|
1801
|
+
convBytes[convPos++] = b & 0xFF;
|
|
1802
|
+
}
|
|
1803
|
+
},
|
|
1804
|
+
"bytes_to_str_finish": () => {
|
|
1805
|
+
const res = convBytes ? new TextDecoder().decode(convBytes) : "";
|
|
1806
|
+
convBytes = null;
|
|
1807
|
+
return res;
|
|
1808
|
+
},
|
|
1279
1809
|
"print_i32": print, "print_i64": print, "print_f32": print, "print_f64": print,
|
|
1280
1810
|
"print_str": print, "print_any": print,
|
|
1281
1811
|
"print_bool": (b) => print(b !== 0 ? "true" : "false"),
|
|
@@ -1283,11 +1813,31 @@ function ${P}imports(stringTable, extraEnv, tag, write) {
|
|
|
1283
1813
|
"println_str": println, "println_any": println,
|
|
1284
1814
|
"println_bool": (b) => println(b !== 0 ? "true" : "false"),
|
|
1285
1815
|
"println_empty": flush,
|
|
1816
|
+
"fmt_new_array": () => [],
|
|
1817
|
+
"fmt_array_push": (arr, v) => { arr.push(v); },
|
|
1818
|
+
"fmt_new_map": () => new Map(),
|
|
1819
|
+
"fmt_map_set": (m, k, v) => { m.set(k, v); },
|
|
1820
|
+
"fmt_new_struct": (ref, isPtr) => {
|
|
1821
|
+
if (ref !== null && ref !== undefined && typeof ref === "object" && fmtStructMap.has(ref)) {
|
|
1822
|
+
return fmtStructMap.get(ref);
|
|
1823
|
+
}
|
|
1824
|
+
const o = {};
|
|
1825
|
+
Object.defineProperty(o, "__struct", { value: true, configurable: true });
|
|
1826
|
+
if (isPtr !== 0) Object.defineProperty(o, "__p", { value: true, configurable: true });
|
|
1827
|
+
if (ref !== null && ref !== undefined && typeof ref === "object") fmtStructMap.set(ref, o);
|
|
1828
|
+
return o;
|
|
1829
|
+
},
|
|
1830
|
+
"fmt_struct_set": (o, k, v) => { o[typeof k === "number" ? stringTable[k] : k] = v; },
|
|
1831
|
+
"fmt_box_i32": (v) => v,
|
|
1832
|
+
"fmt_box_i64": (v) => (v > ${P}MAX || v < -${P}MAX ? v : Number(v)),
|
|
1833
|
+
"fmt_box_f64": (v) => v,
|
|
1834
|
+
"fmt_box_bool": (v) => v !== 0,
|
|
1835
|
+
"fmt_new_box": () => ({ value: null, __p: true, __box: true }),
|
|
1286
1836
|
"testing_arg_i32": targ, "testing_arg_f32": targ, "testing_arg_f64": targ,
|
|
1287
1837
|
"testing_arg_str": targ, "testing_arg_any": targ,
|
|
1288
1838
|
"testing_arg_i64": (v) => targ(v > ${P}MAX || v < -${P}MAX ? v : Number(v)),
|
|
1289
1839
|
"testing_arg_bool": (v) => targ(v !== 0),
|
|
1290
|
-
"testing_call": (t, i) => { const a = targs; targs = []; t[stringTable[i]](...a); },
|
|
1840
|
+
"testing_call": (t, i) => { const a = targs; targs = []; fmtStructMap = new WeakMap(); t[stringTable[i]](...a); },
|
|
1291
1841
|
"testing_name": (t) => t.Name(),
|
|
1292
1842
|
"testing_flag": (t, i) => (t[stringTable[i]]() ? 1 : 0),
|
|
1293
1843
|
};
|
|
@@ -1295,7 +1845,7 @@ function ${P}imports(stringTable, extraEnv, tag, write) {
|
|
|
1295
1845
|
// modules that never call them do not ship it (see WASM_STDLIB_JS).
|
|
1296
1846
|
if (typeof ${P}stdlib === "function") {
|
|
1297
1847
|
Object.assign(env, ${P}stdlib(stringTable, {
|
|
1298
|
-
"take": () => { const a = targs; targs = []; return a; },
|
|
1848
|
+
"take": () => { const a = targs; targs = []; fmtStructMap = new WeakMap(); return a; },
|
|
1299
1849
|
"printf": (s) => {
|
|
1300
1850
|
lineBuf += s;
|
|
1301
1851
|
const nl = lineBuf.lastIndexOf("\\n");
|
|
@@ -1306,7 +1856,7 @@ function ${P}imports(stringTable, extraEnv, tag, write) {
|
|
|
1306
1856
|
}
|
|
1307
1857
|
Object.assign(env, extraEnv);
|
|
1308
1858
|
const m = {};
|
|
1309
|
-
for (const k of ["sin", "cos", "tan", "asin", "acos", "atan", "atan2", "pow", "exp", "log", "log2", "log10", "cbrt", "hypot"]) m[k] = Math[k];
|
|
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];
|
|
1310
1860
|
m.mod = (x, y) => x % y;
|
|
1311
1861
|
// Go rounds half away from zero; Math.round rounds half toward +Infinity.
|
|
1312
1862
|
m.round = (x) => Math.sign(x) * Math.round(Math.abs(x));
|
|
@@ -1316,21 +1866,169 @@ function ${P}imports(stringTable, extraEnv, tag, write) {
|
|
|
1316
1866
|
|
|
1317
1867
|
// JS side of the STDLIB_IMPORTS table (index.js); emitted only when the
|
|
1318
1868
|
// module imports one of the packages that use it.
|
|
1319
|
-
export const WASM_STDLIB_JS = `${
|
|
1869
|
+
export const WASM_STDLIB_JS = `${HELPER_ERROR}
|
|
1870
|
+
${HELPER_SPRINTF}
|
|
1320
1871
|
${HELPER_STRCONV}
|
|
1872
|
+
${HELPER_PATH_CLEAN}
|
|
1873
|
+
${HELPER_DURATION}
|
|
1874
|
+
${HELPER_SHA256}
|
|
1875
|
+
${HELPER_HEX}
|
|
1876
|
+
${HELPER_BASE64}
|
|
1321
1877
|
function ${P}stdlib(stringTable, io) {
|
|
1322
1878
|
let parts = [];
|
|
1323
|
-
let
|
|
1879
|
+
let cutBefore = "", cutAfter = "";
|
|
1880
|
+
let strconvErr = null, strconvErrKind = 0;
|
|
1324
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;
|
|
1887
|
+
let sscanTokens = [];
|
|
1888
|
+
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 = [];
|
|
1325
1895
|
return {
|
|
1896
|
+
"fmt_sscan_init": (s, mode) => {
|
|
1897
|
+
sscanHasErr = 0;
|
|
1898
|
+
sscanErrMsg = "";
|
|
1899
|
+
const str = s ?? "";
|
|
1900
|
+
sscanTokens = (mode === 1 ? str.split("\\n")[0] : str).trim().split(/\\s+/).filter(Boolean);
|
|
1901
|
+
return sscanTokens.length;
|
|
1902
|
+
},
|
|
1903
|
+
"fmt_sscanf_init": (s, fmt) => {
|
|
1904
|
+
sscanHasErr = 0;
|
|
1905
|
+
sscanErrMsg = "";
|
|
1906
|
+
const str = s ?? "";
|
|
1907
|
+
const fmtStr = fmt ?? "";
|
|
1908
|
+
let si = 0, fi = 0;
|
|
1909
|
+
sscanTokens = [];
|
|
1910
|
+
while (fi < fmtStr.length) {
|
|
1911
|
+
if (/\\s/.test(fmtStr[fi])) {
|
|
1912
|
+
while (fi < fmtStr.length && /\\s/.test(fmtStr[fi])) fi++;
|
|
1913
|
+
while (si < str.length && /\\s/.test(str[si])) si++;
|
|
1914
|
+
continue;
|
|
1915
|
+
}
|
|
1916
|
+
if (fmtStr[fi] === "%") {
|
|
1917
|
+
if (fi + 1 < fmtStr.length && fmtStr[fi + 1] === "%") {
|
|
1918
|
+
if (str[si] !== "%") {
|
|
1919
|
+
sscanHasErr = 1;
|
|
1920
|
+
sscanErrMsg = "input does not match format";
|
|
1921
|
+
break;
|
|
1922
|
+
}
|
|
1923
|
+
si++;
|
|
1924
|
+
fi += 2;
|
|
1925
|
+
continue;
|
|
1926
|
+
}
|
|
1927
|
+
const vm = fmtStr.slice(fi).match(/^%[vdsfqtxXbcoeEfFgG]/);
|
|
1928
|
+
if (vm) {
|
|
1929
|
+
const verb = vm[0];
|
|
1930
|
+
fi += verb.length;
|
|
1931
|
+
if (verb !== "%c") {
|
|
1932
|
+
while (si < str.length && /\\s/.test(str[si])) si++;
|
|
1933
|
+
}
|
|
1934
|
+
if (si >= str.length) break;
|
|
1935
|
+
let tokRe = /^\\S+/;
|
|
1936
|
+
if (verb === "%d" || verb === "%i") tokRe = /^[+-]?\\d+/;
|
|
1937
|
+
else if (verb === "%f" || verb === "%F" || verb === "%e" || verb === "%E" || verb === "%g" || verb === "%G") tokRe = /^[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:[eE][+-]?\\d+)?/;
|
|
1938
|
+
else if (verb === "%t") tokRe = /^(?:true|false|1|0|t|f|T|F|TRUE|FALSE)/;
|
|
1939
|
+
else if (verb === "%x" || verb === "%X") tokRe = /^[+-]?(?:0x)?[0-9a-fA-F]+/;
|
|
1940
|
+
else if (verb === "%c") tokRe = /^[\\s\\S]/;
|
|
1941
|
+
const m = str.slice(si).match(tokRe);
|
|
1942
|
+
if (!m) {
|
|
1943
|
+
sscanHasErr = 1;
|
|
1944
|
+
sscanErrMsg = "input does not match format";
|
|
1945
|
+
break;
|
|
1946
|
+
}
|
|
1947
|
+
sscanTokens.push(m[0]);
|
|
1948
|
+
si += m[0].length;
|
|
1949
|
+
continue;
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
if (si >= str.length) break;
|
|
1953
|
+
if (str[si] !== fmtStr[fi]) {
|
|
1954
|
+
sscanHasErr = 1;
|
|
1955
|
+
sscanErrMsg = "input does not match format";
|
|
1956
|
+
break;
|
|
1957
|
+
}
|
|
1958
|
+
si++;
|
|
1959
|
+
fi++;
|
|
1960
|
+
}
|
|
1961
|
+
return sscanTokens.length;
|
|
1962
|
+
},
|
|
1963
|
+
"fmt_sscan_has_err": () => sscanHasErr,
|
|
1964
|
+
"fmt_sscan_err_msg": () => sscanErrMsg || "syntax error",
|
|
1965
|
+
"fmt_sscan_token_str": (i) => sscanTokens[i] ?? "",
|
|
1966
|
+
"fmt_sscan_token_i64": (i) => {
|
|
1967
|
+
if (i >= sscanTokens.length) { sscanHasErr = 1; sscanErrMsg = "unexpected EOF"; return 0n; }
|
|
1968
|
+
const t = sscanTokens[i];
|
|
1969
|
+
if (/^[+-]?(?:0[xX])?[0-9a-fA-F]+$/.test(t)) {
|
|
1970
|
+
try {
|
|
1971
|
+
if (/^[+-]?[0-9a-fA-F]+$/.test(t) && /[a-fA-F]/.test(t)) {
|
|
1972
|
+
return BigInt("0x" + t);
|
|
1973
|
+
}
|
|
1974
|
+
return BigInt(t);
|
|
1975
|
+
} catch {}
|
|
1976
|
+
}
|
|
1977
|
+
if (t.length === 1) {
|
|
1978
|
+
return BigInt(t.charCodeAt(0));
|
|
1979
|
+
}
|
|
1980
|
+
sscanHasErr = 1;
|
|
1981
|
+
sscanErrMsg = "expected integer";
|
|
1982
|
+
return 0n;
|
|
1983
|
+
},
|
|
1984
|
+
"fmt_sscan_token_f64": (i) => {
|
|
1985
|
+
if (i >= sscanTokens.length) { sscanHasErr = 1; sscanErrMsg = "unexpected EOF"; return 0; }
|
|
1986
|
+
const t = sscanTokens[i];
|
|
1987
|
+
const n = Number(t);
|
|
1988
|
+
if (Number.isNaN(n) || !/^[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:[eE][+-]?\\d+)?$/.test(t)) {
|
|
1989
|
+
sscanHasErr = 1;
|
|
1990
|
+
sscanErrMsg = "expected float";
|
|
1991
|
+
return 0;
|
|
1992
|
+
}
|
|
1993
|
+
return n;
|
|
1994
|
+
},
|
|
1995
|
+
"fmt_sscan_token_bool": (i) => {
|
|
1996
|
+
if (i >= sscanTokens.length) { sscanHasErr = 1; sscanErrMsg = "unexpected EOF"; return 0; }
|
|
1997
|
+
const t = sscanTokens[i]?.toLowerCase();
|
|
1998
|
+
if (t === "true" || t === "1" || t === "t") return 1;
|
|
1999
|
+
if (t === "false" || t === "0" || t === "f") return 0;
|
|
2000
|
+
sscanHasErr = 1;
|
|
2001
|
+
sscanErrMsg = "expected boolean";
|
|
2002
|
+
return 0;
|
|
2003
|
+
},
|
|
1326
2004
|
"testing_run": (t, name, fn) => (t.Run(name, (sub) => { io.late.exports.__testing_run_cb(fn, sub); }) ? 1 : 0),
|
|
1327
2005
|
"fmt_sprintf": (f) => __sprintf(f ?? "", ...io.take()),
|
|
1328
2006
|
"fmt_printf": (f) => { io.printf(__sprintf(f ?? "", ...io.take())); },
|
|
2007
|
+
"fmt_format_v": (v) => { const res = __sprintf("%v", v); io.take(); return res; },
|
|
1329
2008
|
"str_trim": (s, c) => ${P}trim(s ?? "", c ?? "", true, true),
|
|
1330
2009
|
"str_trim_left": (s, c) => ${P}trim(s ?? "", c ?? "", true, false),
|
|
1331
2010
|
"str_trim_right": (s, c) => ${P}trim(s ?? "", c ?? "", false, true),
|
|
1332
2011
|
"str_trim_prefix": (s, p) => (s && p && s.startsWith(p) ? s.slice(p.length) : (s ?? "")),
|
|
1333
2012
|
"str_trim_suffix": (s, p) => (s && p && s.endsWith(p) ? s.slice(0, s.length - p.length) : (s ?? "")),
|
|
2013
|
+
"str_cut": (s, sep) => {
|
|
2014
|
+
const i = (s ?? "").indexOf(sep ?? "");
|
|
2015
|
+
if (i < 0) { cutBefore = s ?? ""; cutAfter = ""; return 0; }
|
|
2016
|
+
cutBefore = (s ?? "").slice(0, i);
|
|
2017
|
+
cutAfter = (s ?? "").slice(i + (sep ?? "").length);
|
|
2018
|
+
return 1;
|
|
2019
|
+
},
|
|
2020
|
+
"str_cut_before": () => cutBefore,
|
|
2021
|
+
"str_cut_after": () => cutAfter,
|
|
2022
|
+
"str_cut_prefix": (s, p) => {
|
|
2023
|
+
s = s ?? ""; p = p ?? "";
|
|
2024
|
+
if (s.startsWith(p)) { cutAfter = s.slice(p.length); return 1; }
|
|
2025
|
+
cutAfter = s; return 0;
|
|
2026
|
+
},
|
|
2027
|
+
"str_cut_suffix": (s, suf) => {
|
|
2028
|
+
s = s ?? ""; suf = suf ?? "";
|
|
2029
|
+
if (s.endsWith(suf)) { cutBefore = s.slice(0, s.length - suf.length); return 1; }
|
|
2030
|
+
cutBefore = s; return 0;
|
|
2031
|
+
},
|
|
1334
2032
|
"str_replace": (s, o, n, cnt) => {
|
|
1335
2033
|
s = s ?? ""; o = o ?? ""; n = n ?? "";
|
|
1336
2034
|
if (cnt < 0) return s.replaceAll(o, n);
|
|
@@ -1350,18 +2048,175 @@ function ${P}stdlib(stringTable, io) {
|
|
|
1350
2048
|
"str_index_rune": (s, r) => (s ?? "").indexOf(String.fromCodePoint(r)),
|
|
1351
2049
|
"str_index_any": (s, cs) => { s = s ?? ""; let best = -1; for (const c of (cs ?? "")) { const i = s.indexOf(c); if (i >= 0 && (best < 0 || i < best)) best = i; } return best; },
|
|
1352
2050
|
"str_last_index_byte": (s, b) => (s ?? "").lastIndexOf(String.fromCharCode(b)),
|
|
2051
|
+
"str_last_index_any": (s, cs) => {
|
|
2052
|
+
s = s ?? ""; let best = -1;
|
|
2053
|
+
for (const c of (cs ?? "")) {
|
|
2054
|
+
const i = s.lastIndexOf(c);
|
|
2055
|
+
if (i > best) best = i;
|
|
2056
|
+
}
|
|
2057
|
+
return best;
|
|
2058
|
+
},
|
|
1353
2059
|
"str_title": (s) => (s ?? "").replace(/(^|[^\\p{L}\\p{N}_'])(\\p{Ll})/gu, (m, a, b) => a + b.toUpperCase()),
|
|
1354
2060
|
"str_split": (s, sep) => { parts = (s ?? "").split(sep ?? ""); return parts.length; },
|
|
2061
|
+
"str_split_n": (s, sep, n) => {
|
|
2062
|
+
s = s ?? ""; sep = sep ?? "";
|
|
2063
|
+
if (n === 0) return -1;
|
|
2064
|
+
if (n < 0) { parts = sep === "" ? [...s] : s.split(sep); return parts.length; }
|
|
2065
|
+
if (sep === "") {
|
|
2066
|
+
const chars = [...s];
|
|
2067
|
+
if (chars.length <= n) { parts = chars; return parts.length; }
|
|
2068
|
+
const r = chars.slice(0, n - 1);
|
|
2069
|
+
r.push(chars.slice(n - 1).join(""));
|
|
2070
|
+
parts = r;
|
|
2071
|
+
return parts.length;
|
|
2072
|
+
}
|
|
2073
|
+
const r = [];
|
|
2074
|
+
let cur = s;
|
|
2075
|
+
for (let i = 1; i < n && cur.length; i++) {
|
|
2076
|
+
const j = cur.indexOf(sep);
|
|
2077
|
+
if (j < 0) break;
|
|
2078
|
+
r.push(cur.slice(0, j));
|
|
2079
|
+
cur = cur.slice(j + sep.length);
|
|
2080
|
+
}
|
|
2081
|
+
r.push(cur);
|
|
2082
|
+
parts = r;
|
|
2083
|
+
return parts.length;
|
|
2084
|
+
},
|
|
2085
|
+
"str_split_after": (s, sep) => {
|
|
2086
|
+
s = s ?? ""; sep = sep ?? "";
|
|
2087
|
+
if (sep === "") { parts = [...s]; return parts.length; }
|
|
2088
|
+
const r = [];
|
|
2089
|
+
let cur = s;
|
|
2090
|
+
while (true) {
|
|
2091
|
+
const j = cur.indexOf(sep);
|
|
2092
|
+
if (j < 0) { r.push(cur); break; }
|
|
2093
|
+
r.push(cur.slice(0, j + sep.length));
|
|
2094
|
+
cur = cur.slice(j + sep.length);
|
|
2095
|
+
}
|
|
2096
|
+
parts = r;
|
|
2097
|
+
return parts.length;
|
|
2098
|
+
},
|
|
2099
|
+
"str_split_after_n": (s, sep, n) => {
|
|
2100
|
+
s = s ?? ""; sep = sep ?? "";
|
|
2101
|
+
if (n === 0) return -1;
|
|
2102
|
+
if (sep === "") {
|
|
2103
|
+
const chars = [...s];
|
|
2104
|
+
if (n < 0 || chars.length <= n) parts = chars;
|
|
2105
|
+
else {
|
|
2106
|
+
const r = chars.slice(0, n - 1);
|
|
2107
|
+
r.push(chars.slice(n - 1).join(""));
|
|
2108
|
+
parts = r;
|
|
2109
|
+
}
|
|
2110
|
+
return parts.length;
|
|
2111
|
+
}
|
|
2112
|
+
const r = [];
|
|
2113
|
+
let cur = s;
|
|
2114
|
+
for (let i = 1; (n < 0 || i < n) && cur.length; i++) {
|
|
2115
|
+
const j = cur.indexOf(sep);
|
|
2116
|
+
if (j < 0) break;
|
|
2117
|
+
r.push(cur.slice(0, j + sep.length));
|
|
2118
|
+
cur = cur.slice(j + sep.length);
|
|
2119
|
+
}
|
|
2120
|
+
r.push(cur);
|
|
2121
|
+
parts = r;
|
|
2122
|
+
return parts.length;
|
|
2123
|
+
},
|
|
1355
2124
|
"str_fields": (s) => { parts = (s ?? "").split(/\\s+/).filter((p) => p !== ""); return parts.length; },
|
|
1356
2125
|
"str_part": (i) => parts[i] ?? "",
|
|
1357
|
-
"
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
2126
|
+
"str_replacer_init": (s) => {
|
|
2127
|
+
replacerTarget = s ?? "";
|
|
2128
|
+
replacerPairs = [];
|
|
2129
|
+
},
|
|
2130
|
+
"str_replacer_add": (o, n) => {
|
|
2131
|
+
replacerPairs.push([o ?? "", n ?? ""]);
|
|
2132
|
+
},
|
|
2133
|
+
"str_replacer_run": () => {
|
|
2134
|
+
let s = replacerTarget;
|
|
2135
|
+
if (!replacerPairs.length || s === "") return s;
|
|
2136
|
+
let out = "";
|
|
2137
|
+
let i = 0;
|
|
2138
|
+
while (i < s.length) {
|
|
2139
|
+
let matched = false;
|
|
2140
|
+
for (let k = 0; k < replacerPairs.length; k++) {
|
|
2141
|
+
const [oldStr, newStr] = replacerPairs[k];
|
|
2142
|
+
if (oldStr.length > 0 && s.startsWith(oldStr, i)) {
|
|
2143
|
+
out += newStr;
|
|
2144
|
+
i += oldStr.length;
|
|
2145
|
+
matched = true;
|
|
2146
|
+
break;
|
|
2147
|
+
}
|
|
2148
|
+
}
|
|
2149
|
+
if (!matched) {
|
|
2150
|
+
out += s[i];
|
|
2151
|
+
i++;
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
return out;
|
|
2155
|
+
},
|
|
2156
|
+
"strconv_atoi": (s) => {
|
|
2157
|
+
const r = __strconv_atoi(s ?? "");
|
|
2158
|
+
strconvErr = r[1];
|
|
2159
|
+
strconvErrKind = r[1] ? (r[1].includes("invalid syntax") ? 1 : r[1].includes("value out of range") ? 2 : 0) : 0;
|
|
2160
|
+
return r[0];
|
|
2161
|
+
},
|
|
2162
|
+
"strconv_parse_int": (s, base, bits) => {
|
|
2163
|
+
const r = __strconv_parse_int(s ?? "", base, bits);
|
|
2164
|
+
strconvErr = r[1];
|
|
2165
|
+
strconvErrKind = r[1] ? (r[1].includes("invalid syntax") ? 1 : r[1].includes("value out of range") ? 2 : 0) : 0;
|
|
2166
|
+
return r[0];
|
|
2167
|
+
},
|
|
2168
|
+
"strconv_parse_float": (s) => {
|
|
2169
|
+
const r = __strconv_parse_float(s ?? "");
|
|
2170
|
+
strconvErr = r[1];
|
|
2171
|
+
strconvErrKind = r[1] ? (r[1].includes("invalid syntax") ? 1 : r[1].includes("value out of range") ? 2 : 0) : 0;
|
|
2172
|
+
return r[0];
|
|
2173
|
+
},
|
|
2174
|
+
"strconv_parse_bool": (s) => {
|
|
2175
|
+
const r = __strconv_parse_bool(s ?? "");
|
|
2176
|
+
strconvErr = r[1];
|
|
2177
|
+
strconvErrKind = r[1] ? (r[1].includes("invalid syntax") ? 1 : r[1].includes("value out of range") ? 2 : 0) : 0;
|
|
2178
|
+
return r[0] ? 1 : 0;
|
|
2179
|
+
},
|
|
1361
2180
|
"strconv_err": () => strconvErr,
|
|
2181
|
+
"strconv_err_kind": () => strconvErrKind,
|
|
1362
2182
|
"strconv_format_float": (f, fmtc, prec, _bits) => __strconv_format_float(f, fmtc, prec),
|
|
1363
2183
|
"strconv_format_int": (n, base) => n.toString(base),
|
|
2184
|
+
"strconv_format_uint": (n, base) => BigInt.asUintN(64, BigInt(n)).toString(base),
|
|
2185
|
+
"strconv_parse_uint": (s, base, bits) => {
|
|
2186
|
+
const r = __strconv_parse_uint(s ?? "", base, bits);
|
|
2187
|
+
strconvErr = r[1];
|
|
2188
|
+
strconvErrKind = r[1] ? (r[1].includes("invalid syntax") ? 1 : r[1].includes("value out of range") ? 2 : 0) : 0;
|
|
2189
|
+
return r[0];
|
|
2190
|
+
},
|
|
1364
2191
|
"strconv_quote": (s) => JSON.stringify(s ?? ""),
|
|
2192
|
+
"strconv_unquote": (s) => {
|
|
2193
|
+
s = s ?? "";
|
|
2194
|
+
if (s.startsWith("\`") && s.endsWith("\`") && s.length >= 2) {
|
|
2195
|
+
if (s.slice(1, -1).includes("\`")) {
|
|
2196
|
+
strconvErr = "invalid syntax";
|
|
2197
|
+
strconvErrKind = 1;
|
|
2198
|
+
return "";
|
|
2199
|
+
}
|
|
2200
|
+
strconvErr = null;
|
|
2201
|
+
strconvErrKind = 0;
|
|
2202
|
+
return s.slice(1, -1);
|
|
2203
|
+
}
|
|
2204
|
+
try {
|
|
2205
|
+
const v = JSON.parse(s);
|
|
2206
|
+
if (typeof v !== "string") {
|
|
2207
|
+
strconvErr = "invalid syntax";
|
|
2208
|
+
strconvErrKind = 1;
|
|
2209
|
+
return "";
|
|
2210
|
+
}
|
|
2211
|
+
strconvErr = null;
|
|
2212
|
+
strconvErrKind = 0;
|
|
2213
|
+
return v;
|
|
2214
|
+
} catch (e) {
|
|
2215
|
+
strconvErr = "invalid syntax";
|
|
2216
|
+
strconvErrKind = 1;
|
|
2217
|
+
return "";
|
|
2218
|
+
}
|
|
2219
|
+
},
|
|
1365
2220
|
"utf8_rune_count": (s) => { let n = 0; for (const _ of (s ?? "")) n++; return n; },
|
|
1366
2221
|
"utf8_rune_len": (r) => (r < 0 || r > 0x10ffff || (r >= 0xd800 && r <= 0xdfff) ? -1 : r < 0x80 ? 1 : r < 0x800 ? 2 : r < 0x10000 ? 3 : 4),
|
|
1367
2222
|
"utf8_valid_string": (s) => (s ?? "").isWellFormed ? ((s ?? "").isWellFormed() ? 1 : 0) : (/[\\uD800-\\uDFFF]/.test((s ?? "").replace(/[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]/g, "")) ? 0 : 1),
|
|
@@ -1378,14 +2233,205 @@ function ${P}stdlib(stringTable, io) {
|
|
|
1378
2233
|
},
|
|
1379
2234
|
"utf8_decode_size": () => decodeSize,
|
|
1380
2235
|
"utf8_full_rune": (s) => { s = s ?? ""; if (s.length === 0) return 0; const c = s.charCodeAt(0); return (c >= 0xd800 && c <= 0xdbff && s.length < 2) ? 0 : 1; },
|
|
2236
|
+
// unicode
|
|
2237
|
+
"unicode_is_letter": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && /\\p{L}/u.test(c)) ? 1 : 0; },
|
|
2238
|
+
"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 && /\\s/u.test(c)) ? 1 : 0; },
|
|
2240
|
+
"unicode_is_upper": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && /\\p{Lu}/u.test(c)) ? 1 : 0; },
|
|
2241
|
+
"unicode_is_lower": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && /\\p{Ll}/u.test(c)) ? 1 : 0; },
|
|
2242
|
+
"unicode_is_punct": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && /\\p{P}/u.test(c)) ? 1 : 0; },
|
|
2243
|
+
"unicode_is_control": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && /\\p{Cc}/u.test(c)) ? 1 : 0; },
|
|
2244
|
+
"unicode_is_print": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && !/\\p{Cc}/u.test(c)) ? 1 : 0; },
|
|
2245
|
+
"unicode_is_graphic": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && !/\\p{Cc}/u.test(c)) ? 1 : 0; },
|
|
2246
|
+
"unicode_to_upper": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return c ? (c.toUpperCase().codePointAt(0) ?? r) : r; },
|
|
2247
|
+
"unicode_to_lower": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return c ? (c.toLowerCase().codePointAt(0) ?? r) : r; },
|
|
2248
|
+
// html
|
|
2249
|
+
"html_escape": (s) => (s ?? "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'"),
|
|
2250
|
+
"html_unescape": (s) => (s ?? "").replace(/&(?:quot|apos|#39|#34|gt|lt|amp|#(?:[0-9]+|[xX][0-9a-fA-F]+));/g, (m) => {
|
|
2251
|
+
switch (m) {
|
|
2252
|
+
case """: case """: return '"';
|
|
2253
|
+
case "'": case "'": return "'";
|
|
2254
|
+
case ">": return ">";
|
|
2255
|
+
case "<": return "<";
|
|
2256
|
+
case "&": return "&";
|
|
2257
|
+
default: {
|
|
2258
|
+
if (m.startsWith("&#")) {
|
|
2259
|
+
const raw = m.slice(2, -1);
|
|
2260
|
+
const cp = (raw.startsWith("x") || raw.startsWith("X")) ? parseInt(raw.slice(1), 16) : parseInt(raw, 10);
|
|
2261
|
+
return (cp >= 0 && cp <= 0x10FFFF && !(cp >= 0xD800 && cp <= 0xDFFF)) ? String.fromCodePoint(cp) : "\\uFFFD";
|
|
2262
|
+
}
|
|
2263
|
+
return m;
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2266
|
+
}),
|
|
2267
|
+
// path
|
|
2268
|
+
"path_base": (p) => { if (!p) return "."; const stripped = p.replace(/\\/+$/, ""); if (!stripped) return "/"; const i = stripped.lastIndexOf("/"); return i < 0 ? stripped : stripped.slice(i + 1) || "/"; },
|
|
2269
|
+
"path_dir": (p) => { const s = p ?? ""; const i = s.lastIndexOf("/"); if (i < 0) return "."; const res = s.slice(0, i); return res === "" ? "/" : __pathClean(res); },
|
|
2270
|
+
"path_ext": (p) => { const s = p ?? ""; const b = s.slice(s.lastIndexOf("/") + 1); const i = b.lastIndexOf("."); return i <= 0 ? "" : b.slice(i); },
|
|
2271
|
+
"path_is_abs": (p) => (p ?? "").startsWith("/") ? 1 : 0,
|
|
2272
|
+
"path_clean": (p) => __pathClean(p ?? ""),
|
|
2273
|
+
"path_split": (p) => {
|
|
2274
|
+
const s = p ?? "";
|
|
2275
|
+
const i = s.lastIndexOf("/");
|
|
2276
|
+
if (i < 0) { splitDir = ""; splitFile = s; }
|
|
2277
|
+
else { splitDir = s.slice(0, i + 1); splitFile = s.slice(i + 1); }
|
|
2278
|
+
},
|
|
2279
|
+
"path_split_dir": () => splitDir,
|
|
2280
|
+
"path_split_file": () => splitFile,
|
|
2281
|
+
"path_join_init": () => { joinParts = []; },
|
|
2282
|
+
"path_join_push": (s) => { if (s) joinParts.push(s); },
|
|
2283
|
+
"path_join_run": () => {
|
|
2284
|
+
if (joinParts.length === 0) return "";
|
|
2285
|
+
const res = __pathClean(joinParts.join("/"));
|
|
2286
|
+
joinParts = [];
|
|
2287
|
+
return res;
|
|
2288
|
+
},
|
|
2289
|
+
"path_match": (pat, name) => {
|
|
2290
|
+
const r = __pathMatch(pat ?? "", name ?? "");
|
|
2291
|
+
matchHasErr = r[1] ? 1 : 0;
|
|
2292
|
+
return r[0] ? 1 : 0;
|
|
2293
|
+
},
|
|
2294
|
+
"path_match_has_err": () => matchHasErr,
|
|
2295
|
+
// bytes trim
|
|
2296
|
+
"bytes_trim": (s, cutset, left, right) => {
|
|
2297
|
+
const cs = new Set(Array.from(cutset ?? ""));
|
|
2298
|
+
const chars = Array.from(s ?? "");
|
|
2299
|
+
let a = 0, b = chars.length;
|
|
2300
|
+
if (left) while (a < b && cs.has(chars[a])) a++;
|
|
2301
|
+
if (right) while (b > a && cs.has(chars[b - 1])) b--;
|
|
2302
|
+
const enc = new TextEncoder();
|
|
2303
|
+
trimStart = a === 0 ? 0 : enc.encode(chars.slice(0, a).join("")).length;
|
|
2304
|
+
trimEnd = b === chars.length ? enc.encode(s ?? "").length : enc.encode(chars.slice(0, b).join("")).length;
|
|
2305
|
+
},
|
|
2306
|
+
"bytes_trim_start": () => trimStart,
|
|
2307
|
+
"bytes_trim_end": () => trimEnd,
|
|
2308
|
+
// time
|
|
2309
|
+
"time_now_ms": () => (typeof globalThis.__GOFRONT_CLOCK === "function" ? globalThis.__GOFRONT_CLOCK() : Date.now()),
|
|
2310
|
+
"time_timezone_offset": (ms) => -new Date(ms).getTimezoneOffset() * 60,
|
|
2311
|
+
"time_duration_string": (d) => __durationString(d),
|
|
2312
|
+
"time_parse": (layout, val) => {
|
|
2313
|
+
try {
|
|
2314
|
+
const d = new Date(val ?? "");
|
|
2315
|
+
if (isNaN(d.getTime())) throw 0;
|
|
2316
|
+
timeParseErr = null;
|
|
2317
|
+
timeParseOffset = -d.getTimezoneOffset() * 60;
|
|
2318
|
+
return d.getTime();
|
|
2319
|
+
} catch {
|
|
2320
|
+
timeParseErr = "parsing time: cannot parse";
|
|
2321
|
+
timeParseOffset = 0;
|
|
2322
|
+
return 0;
|
|
2323
|
+
}
|
|
2324
|
+
},
|
|
2325
|
+
"time_parse_err": () => timeParseErr,
|
|
2326
|
+
"time_parse_offset": () => timeParseOffset,
|
|
2327
|
+
"time_format": (y, m, d, hr, min, sec, wd, ms, offsetSec, layout) => {
|
|
2328
|
+
const DAYS = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
|
|
2329
|
+
const MONTHS = ["January","February","March","April","May","June","July","August","September","October","November","December"];
|
|
2330
|
+
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 => {
|
|
2332
|
+
if (t.startsWith(".")) {
|
|
2333
|
+
const isZeroPad = t.includes("0");
|
|
2334
|
+
const maxLen = t.length - 1;
|
|
2335
|
+
let frac = pad(ms % 1000, 3);
|
|
2336
|
+
if (maxLen > 3) frac = frac.padEnd(maxLen, "0");
|
|
2337
|
+
else frac = frac.slice(0, maxLen);
|
|
2338
|
+
if (!isZeroPad) {
|
|
2339
|
+
frac = frac.replace(/0+$/, "");
|
|
2340
|
+
return frac.length > 0 ? "." + frac : "";
|
|
2341
|
+
}
|
|
2342
|
+
return "." + frac;
|
|
2343
|
+
}
|
|
2344
|
+
switch (t) {
|
|
2345
|
+
case "2006": return String(y);
|
|
2346
|
+
case "06": return String(y).slice(-2);
|
|
2347
|
+
case "Monday": return DAYS[wd];
|
|
2348
|
+
case "Mon": return DAYS[wd].slice(0, 3);
|
|
2349
|
+
case "January": return MONTHS[m - 1];
|
|
2350
|
+
case "Jan": return MONTHS[m - 1].slice(0, 3);
|
|
2351
|
+
case "01": return pad(m);
|
|
2352
|
+
case "1": return String(m);
|
|
2353
|
+
case "02": return pad(d);
|
|
2354
|
+
case "2": return String(d);
|
|
2355
|
+
case "15": return pad(hr);
|
|
2356
|
+
case "PM": return hr < 12 ? "AM" : "PM";
|
|
2357
|
+
case "pm": return hr < 12 ? "am" : "pm";
|
|
2358
|
+
case "03": return pad(hr % 12 || 12);
|
|
2359
|
+
case "3": return String(hr % 12 || 12);
|
|
2360
|
+
case "04": return pad(min);
|
|
2361
|
+
case "4": return String(min);
|
|
2362
|
+
case "05": return pad(sec);
|
|
2363
|
+
case "5": return String(sec);
|
|
2364
|
+
case "Z07:00": {
|
|
2365
|
+
const o = Math.floor(offsetSec / 60);
|
|
2366
|
+
return o === 0 ? "Z" : (o > 0 ? "+" : "-") + pad(Math.floor(Math.abs(o) / 60)) + ":" + pad(Math.abs(o) % 60);
|
|
2367
|
+
}
|
|
2368
|
+
default: return t;
|
|
2369
|
+
}
|
|
2370
|
+
});
|
|
2371
|
+
},
|
|
2372
|
+
"raw_bytes_init": (len) => {
|
|
2373
|
+
rawBytes = new Uint8Array(len);
|
|
2374
|
+
rawPos = 0;
|
|
2375
|
+
},
|
|
2376
|
+
"raw_bytes_push": (b) => {
|
|
2377
|
+
if (rawPos < rawBytes.length) {
|
|
2378
|
+
rawBytes[rawPos++] = b & 0xff;
|
|
2379
|
+
}
|
|
2380
|
+
},
|
|
2381
|
+
"raw_bytes_len": () => rawBytes.length,
|
|
2382
|
+
"raw_bytes_get": (i) => rawBytes[i] ?? 0,
|
|
2383
|
+
"crypto_rand_fill": (len) => {
|
|
2384
|
+
rawBytes = new Uint8Array(len);
|
|
2385
|
+
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
2386
|
+
const CHUNK = 65536;
|
|
2387
|
+
for (let offset = 0; offset < len; offset += CHUNK) {
|
|
2388
|
+
crypto.getRandomValues(rawBytes.subarray(offset, Math.min(offset + CHUNK, len)));
|
|
2389
|
+
}
|
|
2390
|
+
} else {
|
|
2391
|
+
for (let i = 0; i < len; i++) rawBytes[i] = Math.floor(Math.random() * 256);
|
|
2392
|
+
}
|
|
2393
|
+
return len;
|
|
2394
|
+
},
|
|
2395
|
+
"crypto_sha256_sum": () => {
|
|
2396
|
+
const d = __sha256.Sum256(rawBytes);
|
|
2397
|
+
rawBytes = new Uint8Array(d);
|
|
2398
|
+
return 32;
|
|
2399
|
+
},
|
|
2400
|
+
"hex_encode_to_string": () => {
|
|
2401
|
+
return __hex_encode_to_string(rawBytes);
|
|
2402
|
+
},
|
|
2403
|
+
"hex_decode_string": (s) => {
|
|
2404
|
+
const [res, err] = __hex_decode_string(s ?? "");
|
|
2405
|
+
hexErr = err ? (err.message ?? String(err)) : null;
|
|
2406
|
+
rawBytes = new Uint8Array(res);
|
|
2407
|
+
return rawBytes.length;
|
|
2408
|
+
},
|
|
2409
|
+
"hex_get_err": () => hexErr,
|
|
2410
|
+
"base64_new_encoding": (alphabet) => {
|
|
2411
|
+
const id = customBase64Encodings.length + 4;
|
|
2412
|
+
customBase64Encodings.push(__base64_make(alphabet ?? "", "="));
|
|
2413
|
+
return id;
|
|
2414
|
+
},
|
|
2415
|
+
"base64_encode_to_string": (mode) => {
|
|
2416
|
+
const enc = mode === 1 ? __base64_url : mode === 2 ? __base64_raw_std : mode === 3 ? __base64_raw_url : mode >= 4 ? customBase64Encodings[mode - 4] : __base64_std;
|
|
2417
|
+
return (enc ?? __base64_std).EncodeToString(rawBytes);
|
|
2418
|
+
},
|
|
2419
|
+
"base64_decode_string": (mode, s) => {
|
|
2420
|
+
const enc = mode === 1 ? __base64_url : mode === 2 ? __base64_raw_std : mode === 3 ? __base64_raw_url : mode >= 4 ? customBase64Encodings[mode - 4] : __base64_std;
|
|
2421
|
+
const [res, err] = (enc ?? __base64_std).DecodeString(s ?? "");
|
|
2422
|
+
base64Err = err ? (err.message ?? String(err)) : null;
|
|
2423
|
+
rawBytes = new Uint8Array(res);
|
|
2424
|
+
return rawBytes.length;
|
|
2425
|
+
},
|
|
2426
|
+
"base64_get_err": () => base64Err,
|
|
1381
2427
|
};
|
|
1382
2428
|
}
|
|
1383
2429
|
function ${P}trim(s, cutset, left, right) {
|
|
1384
|
-
const cs = Array.from(cutset);
|
|
2430
|
+
const cs = new Set(Array.from(cutset));
|
|
1385
2431
|
const chars = Array.from(s);
|
|
1386
2432
|
let a = 0, b = chars.length;
|
|
1387
|
-
if (left) while (a < b && cs.
|
|
1388
|
-
if (right) while (b > a && cs.
|
|
2433
|
+
if (left) while (a < b && cs.has(chars[a])) a++;
|
|
2434
|
+
if (right) while (b > a && cs.has(chars[b - 1])) b--;
|
|
1389
2435
|
return chars.slice(a, b).join("");
|
|
1390
2436
|
}`;
|
|
1391
2437
|
|
|
@@ -1512,6 +2558,39 @@ export function generateFacade(
|
|
|
1512
2558
|
}
|
|
1513
2559
|
}
|
|
1514
2560
|
|
|
2561
|
+
for (const [name, desc] of needs.anonStructs) {
|
|
2562
|
+
out.push(`function ${P}to_${name}(o) {
|
|
2563
|
+
if (o == null) return __w.__zero_${name}();
|
|
2564
|
+
return __w.__new_${name}(${desc.fields.map((f) => inExpr(f.desc, `(o.${f.name} ?? ${zeroExpr(f.desc)})`)).join(", ")});
|
|
2565
|
+
}
|
|
2566
|
+
function ${P}from_${name}(r) {
|
|
2567
|
+
if (r == null) return null;
|
|
2568
|
+
return {
|
|
2569
|
+
${desc.fields.map((f) => `\t\t"${f.name}": ${outExpr(f.desc, `__w.__get_${name}_${f.name}(r)`)}`).join(",\n")}
|
|
2570
|
+
};
|
|
2571
|
+
}
|
|
2572
|
+
function ${P}back_${name}(r, o) {
|
|
2573
|
+
if (r == null || o == null) return;
|
|
2574
|
+
${desc.fields.map((f) => `\to.${f.name} = ${outExpr(f.desc, `__w.__get_${name}_${f.name}(r)`)};`).join("\n")}
|
|
2575
|
+
}`);
|
|
2576
|
+
}
|
|
2577
|
+
|
|
2578
|
+
for (const [key, desc] of needs.boxes) {
|
|
2579
|
+
out.push(`function ${P}boxin_${key}(b) {
|
|
2580
|
+
if (b == null) return null;
|
|
2581
|
+
const v = b.value !== undefined ? b.value : ${zeroExpr(desc.base)};
|
|
2582
|
+
return __w.__box_new_${key}(${inExpr(desc.base, "v")});
|
|
2583
|
+
}
|
|
2584
|
+
function ${P}boxout_${key}(r) {
|
|
2585
|
+
if (r == null) return null;
|
|
2586
|
+
return { "value": ${outExpr(desc.base, `__w.__box_get_${key}(r)`)} };
|
|
2587
|
+
}
|
|
2588
|
+
function ${P}boxback_${key}(r, b) {
|
|
2589
|
+
if (r == null || b == null) return;
|
|
2590
|
+
b.value = ${outExpr(desc.base, `__w.__box_get_${key}(r)`)};
|
|
2591
|
+
}`);
|
|
2592
|
+
}
|
|
2593
|
+
|
|
1515
2594
|
for (const [key, desc] of needs.slices) {
|
|
1516
2595
|
out.push(`function ${P}slin_${key}(arr) {
|
|
1517
2596
|
if (arr == null) return __w.__slice_nil_${key}();
|
|
@@ -1530,8 +2609,34 @@ function ${P}slout_${key}(s) {
|
|
|
1530
2609
|
function ${P}slview_${key}(s) {
|
|
1531
2610
|
if (s == null || __w.__slice_is_nil_${key}(s)) return null;
|
|
1532
2611
|
return ${P}idxview(__w.__slice_len_${key}(s), (i) => ${viewExpr(desc.elem, `__w.__slice_get_${key}(s, i)`)}, (i, v) => __w.__slice_set_${key}(s, i, ${inExpr(desc.elem, "v")}));
|
|
2612
|
+
} `);
|
|
2613
|
+
}
|
|
2614
|
+
|
|
2615
|
+
for (const [key, desc] of needs.maps) {
|
|
2616
|
+
out.push(`function ${P}mapin_${key}(m) {
|
|
2617
|
+
if (m == null) return null;
|
|
2618
|
+
const entries = m instanceof Map ? m.entries() : Object.entries(m);
|
|
2619
|
+
const size = m instanceof Map ? m.size : Object.keys(m).length;
|
|
2620
|
+
const r = __w.__map_new_${key}(size);
|
|
2621
|
+
for (const [k, v] of entries) {
|
|
2622
|
+
__w.__map_set_${key}(r, ${desc.keyDesc.k === "bool" ? `(k === "true" || k === true ? 1 : 0)` : inExpr(desc.keyDesc, "k")}, ${inExpr(desc.valDesc, "v")});
|
|
2623
|
+
}
|
|
2624
|
+
return r;
|
|
2625
|
+
}
|
|
2626
|
+
function ${P}mapout_${key}(m) {
|
|
2627
|
+
if (m == null) return null;
|
|
2628
|
+
const ks = ${P}slout_${desc.keySliceDesc.key}(__w.__map_keys_${key}(m));
|
|
2629
|
+
const vs = ${P}slout_${desc.valSliceDesc.key}(__w.__map_values_${key}(m));
|
|
2630
|
+
const out = new Map();
|
|
2631
|
+
if (ks) {
|
|
2632
|
+
for (let i = 0; i < ks.length; i++) {
|
|
2633
|
+
out.set(ks[i], vs[i]);
|
|
2634
|
+
}
|
|
2635
|
+
}
|
|
2636
|
+
return out;
|
|
1533
2637
|
}`);
|
|
1534
2638
|
}
|
|
2639
|
+
|
|
1535
2640
|
for (const [key, desc] of needs.arrays) {
|
|
1536
2641
|
out.push(`function ${P}arrzero_${key}(n) {
|
|
1537
2642
|
const out = new Array(n);
|
|
@@ -1555,7 +2660,7 @@ function ${P}arrout_${key}(a) {
|
|
|
1555
2660
|
function ${P}arrview_${key}(a) {
|
|
1556
2661
|
if (a == null) return null;
|
|
1557
2662
|
return ${P}idxview(__w.__array_len_${key}(a), (i) => ${viewExpr(desc.elem, `__w.__array_get_${key}(a, i)`)}, (i, v) => __w.__array_set_${key}(a, i, ${inExpr(desc.elem, "v")}));
|
|
1558
|
-
}`);
|
|
2663
|
+
} `);
|
|
1559
2664
|
}
|
|
1560
2665
|
|
|
1561
2666
|
for (const [key, desc] of needs.funcs) {
|
|
@@ -1594,6 +2699,59 @@ ${indent(body, "\t")}
|
|
|
1594
2699
|
|
|
1595
2700
|
for (const [key, desc] of needs.ifaces) genIfaceFacade(out, key, desc);
|
|
1596
2701
|
|
|
2702
|
+
if (needs.error) {
|
|
2703
|
+
out.push(`class ${P}Error extends Error {
|
|
2704
|
+
constructor(msg, ref) {
|
|
2705
|
+
super(msg);
|
|
2706
|
+
this.name = "GoError";
|
|
2707
|
+
this._msg = msg;
|
|
2708
|
+
this.__ref = ref;
|
|
2709
|
+
}
|
|
2710
|
+
Error() {
|
|
2711
|
+
return this.message;
|
|
2712
|
+
}
|
|
2713
|
+
}
|
|
2714
|
+
const ${P}err_w2j = new WeakMap();
|
|
2715
|
+
function ${P}errout(r) {
|
|
2716
|
+
if (r == null) return null;
|
|
2717
|
+
let e = ${P}err_w2j.get(r);
|
|
2718
|
+
if (!e) {
|
|
2719
|
+
const msg = __w.__error_msg ? (__w.__error_msg(r) ?? "") : "";
|
|
2720
|
+
e = new ${P}Error(msg, r);
|
|
2721
|
+
${P}err_w2j.set(r, e);
|
|
2722
|
+
}
|
|
2723
|
+
return e;
|
|
2724
|
+
}
|
|
2725
|
+
function ${P}errin(e) {
|
|
2726
|
+
if (e == null) return null;
|
|
2727
|
+
if (e.__ref === undefined) throw new TypeError("GoFront: expected a WASM-owned error");
|
|
2728
|
+
if (!__w.__is_error(e.__ref)) throw new TypeError("GoFront: value does not implement error");
|
|
2729
|
+
return e.__ref;
|
|
2730
|
+
}`);
|
|
2731
|
+
}
|
|
2732
|
+
|
|
2733
|
+
if (needs.time) {
|
|
2734
|
+
out.push(`function ${P}timeout(r) {
|
|
2735
|
+
if (r == null) return null;
|
|
2736
|
+
const ms = __w.__time_unix_milli ? __w.__time_unix_milli(r) : 0;
|
|
2737
|
+
const off = __w.__time_offset ? __w.__time_offset(r) : 0;
|
|
2738
|
+
return { _d: new Date(ms), _offset: off };
|
|
2739
|
+
}
|
|
2740
|
+
function ${P}timeout_ptr(r) {
|
|
2741
|
+
return ${P}timeout(r);
|
|
2742
|
+
}
|
|
2743
|
+
function ${P}timein(v) {
|
|
2744
|
+
if (v == null) return null;
|
|
2745
|
+
const d = v._d ? v._d : (v instanceof Date ? v : new Date(v));
|
|
2746
|
+
const ms = d.getTime();
|
|
2747
|
+
const off = typeof v?._offset === "number" ? v._offset : -d.getTimezoneOffset() * 60;
|
|
2748
|
+
return __w.__time_make ? __w.__time_make(ms, off) : null;
|
|
2749
|
+
}
|
|
2750
|
+
function ${P}timein_ptr(v) {
|
|
2751
|
+
return ${P}timein(v);
|
|
2752
|
+
}`);
|
|
2753
|
+
}
|
|
2754
|
+
|
|
1597
2755
|
if (needs.shared) {
|
|
1598
2756
|
// Zero-copy TypedArray views over the exported memory. Memory never
|
|
1599
2757
|
// grows after the start function (allocation is startup-only), but the
|
|
@@ -1615,7 +2773,15 @@ function ${P}shin(v, Ctor) {
|
|
|
1615
2773
|
}`);
|
|
1616
2774
|
}
|
|
1617
2775
|
|
|
1618
|
-
for (const c of meta.consts)
|
|
2776
|
+
for (const c of meta.consts) {
|
|
2777
|
+
if (c.dynamic) {
|
|
2778
|
+
out.push(
|
|
2779
|
+
`const ${c.name} = ${outExpr(c.desc, `__w.__const_${c.name}()`)};`,
|
|
2780
|
+
);
|
|
2781
|
+
} else {
|
|
2782
|
+
out.push(`const ${c.name} = ${c.value};`);
|
|
2783
|
+
}
|
|
2784
|
+
}
|
|
1619
2785
|
for (const v of meta.vars ?? [])
|
|
1620
2786
|
out.push(
|
|
1621
2787
|
`const ${v.name} = ${P}shout(__w.__var_${v.name}(), ${v.desc.ctor});`,
|