gofront 1.6.0 → 1.7.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 +35 -39
- package/package.json +37 -4
- package/src/backend/js/expressions.js +559 -144
- package/src/backend/js/index.js +174 -49
- package/src/backend/js/runtime.js +1518 -54
- package/src/backend/js/statements.js +91 -73
- 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 +25 -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 +28 -0
- package/src/backend/js/stdlib/io.js +105 -3
- package/src/backend/js/stdlib/json.js +972 -0
- 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/simd.js +24 -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 +1225 -41
- package/src/backend/wasm/const-fold.js +205 -0
- package/src/backend/wasm/emit-builtins.js +220 -76
- package/src/backend/wasm/emit-exprs.js +475 -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 +124 -87
- package/src/backend/wasm/encode.js +140 -29
- package/src/backend/wasm/index.js +1001 -32
- package/src/backend/wasm/optimize.js +17 -6
- 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 +43 -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/simd.js +234 -0
- package/src/backend/wasm/stdlib/time.js +1200 -0
- package/src/backend/wasm/types.js +174 -1
- package/src/backend/wasm/wat.js +37 -0
- package/src/cli-core.js +1 -1
- package/src/compiler.js +18 -4
- package/src/dts-parser.js +150 -140
- package/src/index.js +2 -6
- package/src/lexer.js +80 -29
- package/src/lower/boxing.js +1 -7
- package/src/lower/captures.js +2 -6
- package/src/lower/embedding.js +2 -1
- 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/struct-tag.js +144 -0
- package/src/parser/types.js +64 -8
- 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 +126 -13
- package/src/typechecker/expressions.js +168 -16
- package/src/typechecker/index.js +35 -8
- package/src/typechecker/resolve.js +187 -8
- package/src/typechecker/statements.js +68 -9
- package/src/typechecker/stdlib/core.js +280 -89
- package/src/typechecker/stdlib/extended.js +490 -1
- package/src/typechecker/stdlib/simd.js +73 -0
- package/src/typechecker/stdlib.js +2 -0
- package/src/typechecker/types.js +362 -9
- package/src/vendor.js +86 -11
|
@@ -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,10 +139,19 @@ 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":
|
|
123
149
|
return classifyInterface(t, mod);
|
|
150
|
+
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
|
+
};
|
|
124
155
|
default:
|
|
125
156
|
return {
|
|
126
157
|
k: "unsupported",
|
|
@@ -130,11 +161,6 @@ function classifyType(goType, mod) {
|
|
|
130
161
|
}
|
|
131
162
|
|
|
132
163
|
const UNSUPPORTED_KINDS = {
|
|
133
|
-
map: "map",
|
|
134
|
-
MapType: "map",
|
|
135
|
-
struct: "anonymous struct",
|
|
136
|
-
StructType: "anonymous struct",
|
|
137
|
-
InterfaceType: "interface",
|
|
138
164
|
ChanType: "chan",
|
|
139
165
|
chan: "chan",
|
|
140
166
|
};
|
|
@@ -149,13 +175,21 @@ function normalizeAstType(t) {
|
|
|
149
175
|
return { kind: "slice", elem: t.elem };
|
|
150
176
|
case "ArrayType":
|
|
151
177
|
return { kind: "array", elem: t.elem, size: t.size };
|
|
178
|
+
case "MapType":
|
|
179
|
+
return { kind: "map", key: t.key, val: t.value ?? t.elem };
|
|
152
180
|
case "FuncType":
|
|
153
181
|
return funcSigFromAst(t);
|
|
154
182
|
case "InterfaceType":
|
|
155
183
|
return {
|
|
156
184
|
kind: "interface",
|
|
157
185
|
name: t.name,
|
|
158
|
-
methods:
|
|
186
|
+
methods: t.methods,
|
|
187
|
+
};
|
|
188
|
+
case "StructType":
|
|
189
|
+
return {
|
|
190
|
+
kind: "struct",
|
|
191
|
+
name: t.name,
|
|
192
|
+
fields: t.fields,
|
|
159
193
|
};
|
|
160
194
|
default:
|
|
161
195
|
return t;
|
|
@@ -169,7 +203,7 @@ const BASIC_DESCS = {
|
|
|
169
203
|
float64: { k: "f64", wType: "f64" },
|
|
170
204
|
string: { k: "string", wType: "externref" },
|
|
171
205
|
any: { k: "any", wType: "anyref" },
|
|
172
|
-
error: { k: "
|
|
206
|
+
error: { k: "error", wType: "anyref" },
|
|
173
207
|
};
|
|
174
208
|
|
|
175
209
|
function classifyBasic(n) {
|
|
@@ -182,17 +216,121 @@ function classifyBasic(n) {
|
|
|
182
216
|
}
|
|
183
217
|
|
|
184
218
|
function classifyNamed(t, mod) {
|
|
219
|
+
if (
|
|
220
|
+
t.name === "simd.F32x4" ||
|
|
221
|
+
t.name === "F32x4" ||
|
|
222
|
+
t.underlying?.kind === "simd"
|
|
223
|
+
) {
|
|
224
|
+
return {
|
|
225
|
+
k: "unsupported",
|
|
226
|
+
what: "simd.F32x4 cannot cross the JS↔WASM boundary in 1.7.0 (use shared.Float32 instead)",
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
if (t.name === "error") return { k: "error", wType: "anyref" };
|
|
230
|
+
if (t.name === "time.Duration")
|
|
231
|
+
return { k: "i64", unsigned: false, wType: "i64" };
|
|
232
|
+
if (t.name === "time.Time") {
|
|
233
|
+
const info = mod.getStructType("time.Time");
|
|
234
|
+
if (info) return { k: "time", name: "time.Time", info, wType: toRef(info) };
|
|
235
|
+
}
|
|
185
236
|
const info = mod.getStructType(t.name);
|
|
186
237
|
if (info) return structDesc(info, false);
|
|
187
238
|
if (t.underlying) return classifyType(t.underlying, mod);
|
|
188
239
|
return { k: "unsupported", what: `named type '${t.name}'` };
|
|
189
240
|
}
|
|
190
241
|
|
|
242
|
+
function classifyAnonStruct(t, mod) {
|
|
243
|
+
const rawFields = [];
|
|
244
|
+
if (t.fields instanceof Map) {
|
|
245
|
+
for (const [k, v] of t.fields.entries())
|
|
246
|
+
rawFields.push({ name: k, goType: v });
|
|
247
|
+
} else if (Array.isArray(t.fields)) {
|
|
248
|
+
for (const f of t.fields) {
|
|
249
|
+
const names = f.names ?? (f.name ? [f.name] : []);
|
|
250
|
+
for (const n of names) rawFields.push({ name: n, goType: f.type });
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
const info = mod.getAnonStructType(rawFields);
|
|
254
|
+
const fields = [];
|
|
255
|
+
for (const f of info.fields) {
|
|
256
|
+
const fd = classifyType(f.goType, mod);
|
|
257
|
+
if (fd.k === "unsupported") return fd;
|
|
258
|
+
fields.push({ name: f.name, desc: fd });
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
k: "anon_struct",
|
|
262
|
+
name: info.name,
|
|
263
|
+
key: info.name,
|
|
264
|
+
info,
|
|
265
|
+
fields,
|
|
266
|
+
wType: toRef(info),
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function classifyStruct(t, mod) {
|
|
271
|
+
if (t.name) {
|
|
272
|
+
const info = mod.getStructType(t.name);
|
|
273
|
+
if (info) return structDesc(info, false);
|
|
274
|
+
}
|
|
275
|
+
return classifyAnonStruct(t, mod);
|
|
276
|
+
}
|
|
277
|
+
|
|
191
278
|
function classifyPointer(t, mod) {
|
|
192
279
|
const base = classifyType(t.base, mod);
|
|
280
|
+
if (base.k === "unsupported") return base;
|
|
193
281
|
if (base.k === "struct" && !base.ptr) return { ...base, ptr: true };
|
|
194
|
-
|
|
195
|
-
|
|
282
|
+
if (base.k === "anon_struct" && !base.ptr) return { ...base, ptr: true };
|
|
283
|
+
if (base.k === "time" && !base.ptr) return { ...base, ptr: true };
|
|
284
|
+
const boxInfo = mod.getBoxType(t.base);
|
|
285
|
+
const key = `${base.k}${base.unsigned ? "_u" : ""}_${boxInfo.typeIndex}`;
|
|
286
|
+
return {
|
|
287
|
+
k: "ptr_box",
|
|
288
|
+
base,
|
|
289
|
+
boxInfo,
|
|
290
|
+
key,
|
|
291
|
+
wType: { kind: "ref", nullable: true, typeIndex: boxInfo.typeIndex },
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function isAllowedMapKey(kDesc) {
|
|
296
|
+
if (kDesc.k === "string" || kDesc.k === "bool") return true;
|
|
297
|
+
if (
|
|
298
|
+
kDesc.k === "i32" ||
|
|
299
|
+
kDesc.k === "i64" ||
|
|
300
|
+
kDesc.k === "f32" ||
|
|
301
|
+
kDesc.k === "f64"
|
|
302
|
+
)
|
|
303
|
+
return true;
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function classifyMap(t, mod) {
|
|
308
|
+
const keyType = t.key;
|
|
309
|
+
const valType = t.val ?? t.value ?? t.elem;
|
|
310
|
+
const keyDesc = classifyType(keyType, mod);
|
|
311
|
+
if (keyDesc.k === "unsupported") return keyDesc;
|
|
312
|
+
const valDesc = classifyType(valType, mod);
|
|
313
|
+
if (valDesc.k === "unsupported") return valDesc;
|
|
314
|
+
if (!isAllowedMapKey(keyDesc)) {
|
|
315
|
+
return {
|
|
316
|
+
k: "unsupported",
|
|
317
|
+
what: `map with unsupported key type '${keyType?.name ?? keyDesc.k}'`,
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
const mapInfo = mod.getMapType(keyType, valType);
|
|
321
|
+
const keySliceDesc = classifySlice({ kind: "slice", elem: keyType }, mod);
|
|
322
|
+
const valSliceDesc = classifySlice({ kind: "slice", elem: valType }, mod);
|
|
323
|
+
const key = `m_${mapInfo.suffix}`;
|
|
324
|
+
return {
|
|
325
|
+
k: "map",
|
|
326
|
+
key,
|
|
327
|
+
mapInfo,
|
|
328
|
+
keyDesc,
|
|
329
|
+
valDesc,
|
|
330
|
+
keySliceDesc,
|
|
331
|
+
valSliceDesc,
|
|
332
|
+
wType: mapInfo.wType,
|
|
333
|
+
};
|
|
196
334
|
}
|
|
197
335
|
|
|
198
336
|
function classifyShared(t, mod) {
|
|
@@ -263,12 +401,50 @@ function classifyFunc(t, mod) {
|
|
|
263
401
|
}
|
|
264
402
|
|
|
265
403
|
function classifyInterface(t, mod) {
|
|
266
|
-
if (t.name === "error") return { k: "
|
|
267
|
-
|
|
268
|
-
if (
|
|
269
|
-
return { k: "unsupported", what: "anonymous interface" };
|
|
404
|
+
if (t.name === "error") return { k: "error", wType: "anyref" };
|
|
405
|
+
const mList = getInterfaceMethodList(t);
|
|
406
|
+
if (mList.length === 0) return { k: "any", wType: "anyref" };
|
|
270
407
|
mod._boundaryIfaces ??= new Map();
|
|
271
|
-
|
|
408
|
+
if (t.name && t.methods instanceof Map) {
|
|
409
|
+
return mod._boundaryIfaces.get(t.name) ?? classifyNamedInterface(t, mod);
|
|
410
|
+
}
|
|
411
|
+
return classifyAnonInterface(t, mod);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function classifyAnonInterface(t, mod) {
|
|
415
|
+
const key = anonIfaceName(t, mod.checker, mod);
|
|
416
|
+
if (mod._boundaryIfaces.has(key)) return mod._boundaryIfaces.get(key);
|
|
417
|
+
const mList = getInterfaceMethodList(t);
|
|
418
|
+
const desc = {
|
|
419
|
+
k: "iface",
|
|
420
|
+
name: key,
|
|
421
|
+
key,
|
|
422
|
+
wType: "anyref",
|
|
423
|
+
methods: [],
|
|
424
|
+
concrete: [],
|
|
425
|
+
isAnon: true,
|
|
426
|
+
};
|
|
427
|
+
mod._boundaryIfaces.set(key, desc);
|
|
428
|
+
for (const m of mList) {
|
|
429
|
+
const sig = classifySig(
|
|
430
|
+
{ kind: "func", params: m.params, returns: m.returns },
|
|
431
|
+
mod,
|
|
432
|
+
);
|
|
433
|
+
if (sig.k === "unsupported") {
|
|
434
|
+
const err = {
|
|
435
|
+
k: "unsupported",
|
|
436
|
+
what: `${sig.what} (in anonymous interface method ${m.name})`,
|
|
437
|
+
};
|
|
438
|
+
mod._boundaryIfaces.set(key, err);
|
|
439
|
+
return err;
|
|
440
|
+
}
|
|
441
|
+
desc.methods.push({
|
|
442
|
+
name: m.name,
|
|
443
|
+
dispatch: `__dispatch_${key}_${m.name}`,
|
|
444
|
+
...sig,
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
return desc;
|
|
272
448
|
}
|
|
273
449
|
|
|
274
450
|
function classifyNamedInterface(t, mod) {
|
|
@@ -325,7 +501,14 @@ function literalConst(spec, i) {
|
|
|
325
501
|
}
|
|
326
502
|
if (node?.kind !== "BasicLit") return null;
|
|
327
503
|
switch (node.litKind) {
|
|
328
|
-
case "INT":
|
|
504
|
+
case "INT": {
|
|
505
|
+
const raw = String(node.value);
|
|
506
|
+
const clean = /^0[0-7]+$/.test(raw) ? `0o${raw.slice(1)}` : raw;
|
|
507
|
+
const bi = BigInt(`${sign}${clean}`);
|
|
508
|
+
return bi > 9007199254740991n || bi < -9007199254740991n
|
|
509
|
+
? `${sign}${clean}n`
|
|
510
|
+
: `${sign}${clean}`;
|
|
511
|
+
}
|
|
329
512
|
case "FLOAT":
|
|
330
513
|
return `${sign}${node.value}`;
|
|
331
514
|
case "STRING":
|
|
@@ -338,6 +521,9 @@ function literalConst(spec, i) {
|
|
|
338
521
|
}
|
|
339
522
|
|
|
340
523
|
function unsupportedMsg(where, what) {
|
|
524
|
+
if (typeof what === "string" && what.includes("boundary")) {
|
|
525
|
+
return `${where}: ${what}`;
|
|
526
|
+
}
|
|
341
527
|
return `${where}: ${what} is not yet supported across the wasm boundary (planned)`;
|
|
342
528
|
}
|
|
343
529
|
|
|
@@ -394,14 +580,48 @@ function collectConstDecl(d, pkgName, meta, errors) {
|
|
|
394
580
|
const name = spec.names[i];
|
|
395
581
|
if (!isExported(name)) continue;
|
|
396
582
|
const value = literalConst(spec, i);
|
|
397
|
-
if (value !== null)
|
|
398
|
-
|
|
583
|
+
if (value !== null) {
|
|
584
|
+
meta.consts.push({ name, value, dynamic: false });
|
|
585
|
+
continue;
|
|
586
|
+
}
|
|
587
|
+
const folded = meta._mod.constCache?.get(name);
|
|
588
|
+
if (folded) {
|
|
589
|
+
let val = null;
|
|
590
|
+
if (folded.litKind === "INT") {
|
|
591
|
+
const s = String(folded.value);
|
|
592
|
+
const clean = /^0[0-7]+$/.test(s) ? `0o${s.slice(1)}` : s;
|
|
593
|
+
const bi = BigInt(clean);
|
|
594
|
+
val =
|
|
595
|
+
bi > 9007199254740991n || bi < -9007199254740991n
|
|
596
|
+
? `${clean}n`
|
|
597
|
+
: clean;
|
|
598
|
+
} else if (folded.litKind === "FLOAT") {
|
|
599
|
+
val = folded.value;
|
|
600
|
+
} else if (folded.litKind === "STRING") {
|
|
601
|
+
val = JSON.stringify(String(folded.value));
|
|
602
|
+
} else if (folded.litKind === "BOOL") {
|
|
603
|
+
val = folded.value === "true" ? "true" : "false";
|
|
604
|
+
}
|
|
605
|
+
if (val !== null) {
|
|
606
|
+
meta.consts.push({ name, value: val, dynamic: false });
|
|
607
|
+
continue;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
const glob = meta._mod.resolveGlobal(name);
|
|
611
|
+
const rawType = spec.type ?? spec.value?.[i]?._type ?? glob?.goType;
|
|
612
|
+
let desc = rawType ? classifyType(rawType, meta._mod) : null;
|
|
613
|
+
if (!desc || desc.k === "void") {
|
|
614
|
+
if (glob?.type) {
|
|
615
|
+
desc = { k: glob.type, wType: glob.type };
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
if (!desc || desc.k === "unsupported") {
|
|
399
619
|
errors.push(
|
|
400
|
-
unsupportedMsg(
|
|
401
|
-
`${pkgName}.${name}`,
|
|
402
|
-
"a constant with a non-literal value",
|
|
403
|
-
),
|
|
620
|
+
unsupportedMsg(`${pkgName}.${name}`, desc?.what ?? "unsupported"),
|
|
404
621
|
);
|
|
622
|
+
} else {
|
|
623
|
+
meta.consts.push({ name, dynamic: true, desc });
|
|
624
|
+
}
|
|
405
625
|
}
|
|
406
626
|
}
|
|
407
627
|
}
|
|
@@ -483,6 +703,17 @@ function visitNeed(desc, needs, errors, mod) {
|
|
|
483
703
|
case "struct":
|
|
484
704
|
visitStructNeed(desc, needs, errors, mod);
|
|
485
705
|
return;
|
|
706
|
+
case "anon_struct":
|
|
707
|
+
if (needs.anonStructs.has(desc.name)) return;
|
|
708
|
+
needs.anonStructs.set(desc.name, desc);
|
|
709
|
+
for (const f of desc.fields) visitNeed(f.desc, needs, errors, mod);
|
|
710
|
+
return;
|
|
711
|
+
case "error":
|
|
712
|
+
needs.error = true;
|
|
713
|
+
return;
|
|
714
|
+
case "time":
|
|
715
|
+
needs.time = true;
|
|
716
|
+
return;
|
|
486
717
|
case "slice":
|
|
487
718
|
case "array": {
|
|
488
719
|
const map = desc.k === "slice" ? needs.slices : needs.arrays;
|
|
@@ -491,6 +722,19 @@ function visitNeed(desc, needs, errors, mod) {
|
|
|
491
722
|
visitNeed(desc.elem, needs, errors, mod);
|
|
492
723
|
return;
|
|
493
724
|
}
|
|
725
|
+
case "map":
|
|
726
|
+
if (needs.maps.has(desc.key)) return;
|
|
727
|
+
needs.maps.set(desc.key, desc);
|
|
728
|
+
visitNeed(desc.keyDesc, needs, errors, mod);
|
|
729
|
+
visitNeed(desc.valDesc, needs, errors, mod);
|
|
730
|
+
visitNeed(desc.keySliceDesc, needs, errors, mod);
|
|
731
|
+
visitNeed(desc.valSliceDesc, needs, errors, mod);
|
|
732
|
+
return;
|
|
733
|
+
case "ptr_box":
|
|
734
|
+
if (needs.boxes.has(desc.key)) return;
|
|
735
|
+
needs.boxes.set(desc.key, desc);
|
|
736
|
+
visitNeed(desc.base, needs, errors, mod);
|
|
737
|
+
return;
|
|
494
738
|
case "func":
|
|
495
739
|
if (needs.funcs.has(desc.key)) return;
|
|
496
740
|
needs.funcs.set(desc.key, desc);
|
|
@@ -538,10 +782,15 @@ function collectNeeds(meta) {
|
|
|
538
782
|
const mod = meta._mod;
|
|
539
783
|
const needs = {
|
|
540
784
|
structs: new Map(), // name -> { info, fields: [{ name, desc }] }
|
|
785
|
+
anonStructs: new Map(), // name -> desc
|
|
541
786
|
slices: new Map(), // key -> desc
|
|
542
787
|
arrays: new Map(), // key -> desc
|
|
788
|
+
maps: new Map(), // key -> desc
|
|
789
|
+
boxes: new Map(), // key -> desc
|
|
543
790
|
funcs: new Map(), // key -> desc
|
|
544
791
|
ifaces: new Map(), // interface name -> desc
|
|
792
|
+
error: false,
|
|
793
|
+
time: false,
|
|
545
794
|
shared: false, // any shared buffer on the surface
|
|
546
795
|
};
|
|
547
796
|
const errors = [];
|
|
@@ -552,6 +801,9 @@ function collectNeeds(meta) {
|
|
|
552
801
|
}
|
|
553
802
|
for (const f of meta.funcs) visitSignatureNeeds(f, needs, errors, mod);
|
|
554
803
|
for (const v of meta.vars ?? []) visitNeed(v.desc, needs, errors, mod);
|
|
804
|
+
for (const c of meta.consts ?? []) {
|
|
805
|
+
if (c.dynamic) visitNeed(c.desc, needs, errors, mod);
|
|
806
|
+
}
|
|
555
807
|
if (errors.length > 0) throw new Error([...new Set(errors)].join("\n"));
|
|
556
808
|
for (const desc of needs.ifaces.values())
|
|
557
809
|
desc.concrete = concreteCandidates(desc, needs, meta, mod);
|
|
@@ -968,6 +1220,231 @@ export function addBoundaryHelpers(mod, meta) {
|
|
|
968
1220
|
);
|
|
969
1221
|
}
|
|
970
1222
|
}
|
|
1223
|
+
|
|
1224
|
+
for (const [name, desc] of needs.anonStructs) {
|
|
1225
|
+
const { info } = desc;
|
|
1226
|
+
const ref = desc.wType;
|
|
1227
|
+
const ti = info.typeIndex;
|
|
1228
|
+
const fieldWTypes = info.fields.map((f) => f.wType);
|
|
1229
|
+
addFunc(
|
|
1230
|
+
`__new_${name}`,
|
|
1231
|
+
fieldWTypes.map(extType),
|
|
1232
|
+
[EXT],
|
|
1233
|
+
[
|
|
1234
|
+
...fieldWTypes.flatMap((w, i) => getIn(i, w)),
|
|
1235
|
+
{ op: "struct.new", typeIndex: ti },
|
|
1236
|
+
...toExt(ref),
|
|
1237
|
+
],
|
|
1238
|
+
);
|
|
1239
|
+
addFunc(
|
|
1240
|
+
`__zero_${name}`,
|
|
1241
|
+
[],
|
|
1242
|
+
[EXT],
|
|
1243
|
+
[
|
|
1244
|
+
...fieldWTypes.map((w) => zeroInstr(w)),
|
|
1245
|
+
{ op: "struct.new", typeIndex: ti },
|
|
1246
|
+
...toExt(ref),
|
|
1247
|
+
],
|
|
1248
|
+
);
|
|
1249
|
+
for (let i = 0; i < info.fields.length; i++) {
|
|
1250
|
+
const f = info.fields[i];
|
|
1251
|
+
addFunc(
|
|
1252
|
+
`__get_${name}_${f.name}`,
|
|
1253
|
+
[EXT],
|
|
1254
|
+
[extType(f.wType)],
|
|
1255
|
+
[
|
|
1256
|
+
...getIn(0, ref),
|
|
1257
|
+
{ op: "struct.get", typeIndex: ti, fieldIndex: i },
|
|
1258
|
+
...toExt(f.wType),
|
|
1259
|
+
],
|
|
1260
|
+
);
|
|
1261
|
+
addFunc(
|
|
1262
|
+
`__set_${name}_${f.name}`,
|
|
1263
|
+
[EXT, extType(f.wType)],
|
|
1264
|
+
[],
|
|
1265
|
+
[
|
|
1266
|
+
...getIn(0, ref),
|
|
1267
|
+
...getIn(1, f.wType),
|
|
1268
|
+
{ op: "struct.set", typeIndex: ti, fieldIndex: i },
|
|
1269
|
+
],
|
|
1270
|
+
);
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
for (const [key, desc] of needs.boxes) {
|
|
1275
|
+
const { boxInfo } = desc;
|
|
1276
|
+
const ref = desc.wType;
|
|
1277
|
+
const ti = boxInfo.typeIndex;
|
|
1278
|
+
const w = boxInfo.wType;
|
|
1279
|
+
addFunc(
|
|
1280
|
+
`__box_new_${key}`,
|
|
1281
|
+
[extType(w)],
|
|
1282
|
+
[EXT],
|
|
1283
|
+
[...getIn(0, w), { op: "struct.new", typeIndex: ti }, ...toExt(ref)],
|
|
1284
|
+
);
|
|
1285
|
+
addFunc(
|
|
1286
|
+
`__box_get_${key}`,
|
|
1287
|
+
[EXT],
|
|
1288
|
+
[extType(w)],
|
|
1289
|
+
[
|
|
1290
|
+
...getIn(0, ref),
|
|
1291
|
+
{ op: "struct.get", typeIndex: ti, fieldIndex: 0 },
|
|
1292
|
+
...toExt(w),
|
|
1293
|
+
],
|
|
1294
|
+
);
|
|
1295
|
+
addFunc(
|
|
1296
|
+
`__box_set_${key}`,
|
|
1297
|
+
[EXT, extType(w)],
|
|
1298
|
+
[],
|
|
1299
|
+
[
|
|
1300
|
+
...getIn(0, ref),
|
|
1301
|
+
...getIn(1, w),
|
|
1302
|
+
{ op: "struct.set", typeIndex: ti, fieldIndex: 0 },
|
|
1303
|
+
],
|
|
1304
|
+
);
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
for (const [key, desc] of needs.maps) {
|
|
1308
|
+
const { mapInfo, keySliceDesc, valSliceDesc } = desc;
|
|
1309
|
+
const makeIdx = mod.resolveFuncIndex(mapInfo.makeFuncName);
|
|
1310
|
+
addFunc(
|
|
1311
|
+
`__map_new_${key}`,
|
|
1312
|
+
["i32"],
|
|
1313
|
+
[EXT],
|
|
1314
|
+
[get(0), { op: "call", funcIndex: makeIdx }, ...toExt(desc.wType)],
|
|
1315
|
+
);
|
|
1316
|
+
const setIdx = mod.resolveFuncIndex(mapInfo.setFuncName);
|
|
1317
|
+
addFunc(
|
|
1318
|
+
`__map_set_${key}`,
|
|
1319
|
+
[EXT, extType(mapInfo.keyWType), extType(mapInfo.valWType)],
|
|
1320
|
+
[],
|
|
1321
|
+
[
|
|
1322
|
+
...getIn(0, desc.wType),
|
|
1323
|
+
...getIn(1, mapInfo.keyWType),
|
|
1324
|
+
...getIn(2, mapInfo.valWType),
|
|
1325
|
+
{ op: "call", funcIndex: setIdx },
|
|
1326
|
+
],
|
|
1327
|
+
);
|
|
1328
|
+
const keysIdx = mod.resolveFuncIndex(mapInfo.keysFuncName);
|
|
1329
|
+
addFunc(
|
|
1330
|
+
`__map_keys_${key}`,
|
|
1331
|
+
[EXT],
|
|
1332
|
+
[EXT],
|
|
1333
|
+
[
|
|
1334
|
+
...getIn(0, desc.wType),
|
|
1335
|
+
{ op: "call", funcIndex: keysIdx },
|
|
1336
|
+
...toExt(keySliceDesc.wType),
|
|
1337
|
+
],
|
|
1338
|
+
);
|
|
1339
|
+
const valuesIdx = mod.resolveFuncIndex(mapInfo.valuesFuncName);
|
|
1340
|
+
addFunc(
|
|
1341
|
+
`__map_values_${key}`,
|
|
1342
|
+
[EXT],
|
|
1343
|
+
[EXT],
|
|
1344
|
+
[
|
|
1345
|
+
...getIn(0, desc.wType),
|
|
1346
|
+
{ op: "call", funcIndex: valuesIdx },
|
|
1347
|
+
...toExt(valSliceDesc.wType),
|
|
1348
|
+
],
|
|
1349
|
+
);
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
if (needs.error) {
|
|
1353
|
+
const errDispatcherIdx = mod.funcMap.get("__dispatch_error_Error");
|
|
1354
|
+
if (errDispatcherIdx !== undefined) {
|
|
1355
|
+
addFunc(
|
|
1356
|
+
"__error_msg",
|
|
1357
|
+
[EXT],
|
|
1358
|
+
[EXT],
|
|
1359
|
+
[
|
|
1360
|
+
get(0),
|
|
1361
|
+
{ op: "ref.is_null" },
|
|
1362
|
+
{ op: "if", blockType: "externref" },
|
|
1363
|
+
{ op: "ref.null", heapType: "extern" },
|
|
1364
|
+
{ op: "else" },
|
|
1365
|
+
...getIn(0, "anyref"),
|
|
1366
|
+
{ op: "call", funcIndex: errDispatcherIdx },
|
|
1367
|
+
...toExt("externref"),
|
|
1368
|
+
{ op: "end" },
|
|
1369
|
+
],
|
|
1370
|
+
);
|
|
1371
|
+
}
|
|
1372
|
+
const errCandidates = mod
|
|
1373
|
+
.findInterfaceCandidates(null, "Error")
|
|
1374
|
+
.map((c) => c.testTypeIndex);
|
|
1375
|
+
addFunc(
|
|
1376
|
+
"__is_error",
|
|
1377
|
+
[EXT],
|
|
1378
|
+
["i32"],
|
|
1379
|
+
refTestChain(errCandidates, () => 1),
|
|
1380
|
+
);
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
if (needs.time) {
|
|
1384
|
+
const timeInfo = mod.getStructType("time.Time");
|
|
1385
|
+
if (timeInfo) {
|
|
1386
|
+
const timeWType = toRef(timeInfo);
|
|
1387
|
+
addFunc(
|
|
1388
|
+
"__time_make",
|
|
1389
|
+
["f64", "i32"],
|
|
1390
|
+
[EXT],
|
|
1391
|
+
[
|
|
1392
|
+
get(0),
|
|
1393
|
+
{ op: "f64.const", value: 1000000 },
|
|
1394
|
+
{ op: "f64.mul" },
|
|
1395
|
+
{ op: "i64.trunc_sat_f64_s" },
|
|
1396
|
+
get(1),
|
|
1397
|
+
{ op: "struct.new", typeIndex: timeInfo.typeIndex },
|
|
1398
|
+
...toExt(timeWType),
|
|
1399
|
+
],
|
|
1400
|
+
);
|
|
1401
|
+
addFunc(
|
|
1402
|
+
"__time_unix_milli",
|
|
1403
|
+
[EXT],
|
|
1404
|
+
["f64"],
|
|
1405
|
+
[
|
|
1406
|
+
get(0),
|
|
1407
|
+
{ op: "ref.is_null" },
|
|
1408
|
+
{ op: "if", blockType: "f64" },
|
|
1409
|
+
{ op: "f64.const", value: 0 },
|
|
1410
|
+
{ op: "else" },
|
|
1411
|
+
...getIn(0, timeWType),
|
|
1412
|
+
{ op: "struct.get", typeIndex: timeInfo.typeIndex, fieldIndex: 0 },
|
|
1413
|
+
{ op: "i64.const", value: 1000000n },
|
|
1414
|
+
{ op: "i64.div_s" },
|
|
1415
|
+
{ op: "f64.convert_i64_s" },
|
|
1416
|
+
{ op: "end" },
|
|
1417
|
+
],
|
|
1418
|
+
);
|
|
1419
|
+
addFunc(
|
|
1420
|
+
"__time_offset",
|
|
1421
|
+
[EXT],
|
|
1422
|
+
["i32"],
|
|
1423
|
+
[
|
|
1424
|
+
get(0),
|
|
1425
|
+
{ op: "ref.is_null" },
|
|
1426
|
+
{ op: "if", blockType: "i32" },
|
|
1427
|
+
{ op: "i32.const", value: 0 },
|
|
1428
|
+
{ op: "else" },
|
|
1429
|
+
...getIn(0, timeWType),
|
|
1430
|
+
{ op: "struct.get", typeIndex: timeInfo.typeIndex, fieldIndex: 1 },
|
|
1431
|
+
{ op: "end" },
|
|
1432
|
+
],
|
|
1433
|
+
);
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
for (const c of meta.consts ?? []) {
|
|
1438
|
+
if (!c.dynamic) continue;
|
|
1439
|
+
const g = mod.resolveGlobal(c.name);
|
|
1440
|
+
if (!g) continue;
|
|
1441
|
+
addFunc(
|
|
1442
|
+
`__const_${c.name}`,
|
|
1443
|
+
[],
|
|
1444
|
+
[extType(c.desc.wType)],
|
|
1445
|
+
[{ op: "global.get", index: g.index }, ...toExt(c.desc.wType)],
|
|
1446
|
+
);
|
|
1447
|
+
}
|
|
971
1448
|
}
|
|
972
1449
|
|
|
973
1450
|
// `(externref) -> i32`: `ref.test`s param 0 against each type index and
|
|
@@ -1050,6 +1527,18 @@ function inExpr(desc, v) {
|
|
|
1050
1527
|
return `${P}strin(${v})`;
|
|
1051
1528
|
case "any":
|
|
1052
1529
|
return `(${v} === undefined ? null : ${v})`;
|
|
1530
|
+
case "error":
|
|
1531
|
+
return `${P}errin(${v})`;
|
|
1532
|
+
case "time":
|
|
1533
|
+
return desc.ptr ? `${P}timein_ptr(${v})` : `${P}timein(${v})`;
|
|
1534
|
+
case "map":
|
|
1535
|
+
return `${P}mapin_${desc.key}(${v})`;
|
|
1536
|
+
case "ptr_box":
|
|
1537
|
+
return `${P}boxin_${desc.key}(${v})`;
|
|
1538
|
+
case "anon_struct":
|
|
1539
|
+
return desc.ptr
|
|
1540
|
+
? `(${v} == null ? null : ${P}to_${desc.name}(${v}))`
|
|
1541
|
+
: `${P}to_${desc.name}(${v})`;
|
|
1053
1542
|
case "struct":
|
|
1054
1543
|
if (desc.info.pkgTarget === "both")
|
|
1055
1544
|
return `${P}to_${desc.name}(${v}, ${desc.ptr ? "false" : "true"})`;
|
|
@@ -1082,6 +1571,18 @@ function outExpr(desc, v) {
|
|
|
1082
1571
|
return desc.unsigned ? `${P}u64out(${v})` : `${P}i64out(${v})`;
|
|
1083
1572
|
case "string":
|
|
1084
1573
|
return `(${v} ?? "")`;
|
|
1574
|
+
case "error":
|
|
1575
|
+
return `${P}errout(${v})`;
|
|
1576
|
+
case "time":
|
|
1577
|
+
return desc.ptr ? `${P}timeout_ptr(${v})` : `${P}timeout(${v})`;
|
|
1578
|
+
case "map":
|
|
1579
|
+
return `${P}mapout_${desc.key}(${v})`;
|
|
1580
|
+
case "ptr_box":
|
|
1581
|
+
return `${P}boxout_${desc.key}(${v})`;
|
|
1582
|
+
case "anon_struct":
|
|
1583
|
+
return desc.ptr
|
|
1584
|
+
? `(${v} == null ? null : ${P}from_${desc.name}(${v}))`
|
|
1585
|
+
: `${P}from_${desc.name}(${v})`;
|
|
1085
1586
|
case "struct":
|
|
1086
1587
|
return desc.info.pkgTarget === "both"
|
|
1087
1588
|
? `${P}from_${desc.name}(${v})`
|
|
@@ -1112,6 +1613,15 @@ function zeroExpr(desc) {
|
|
|
1112
1613
|
return "0";
|
|
1113
1614
|
case "string":
|
|
1114
1615
|
return '""';
|
|
1616
|
+
case "error":
|
|
1617
|
+
case "time":
|
|
1618
|
+
case "map":
|
|
1619
|
+
case "ptr_box":
|
|
1620
|
+
return "null";
|
|
1621
|
+
case "anon_struct":
|
|
1622
|
+
return desc.ptr
|
|
1623
|
+
? "null"
|
|
1624
|
+
: `{ ${desc.fields.map((f) => `"${f.name}": ${zeroExpr(f.desc)}`).join(", ")} }`;
|
|
1115
1625
|
case "struct":
|
|
1116
1626
|
return desc.ptr ? "null" : `new ${desc.name}()`;
|
|
1117
1627
|
case "array":
|
|
@@ -1147,6 +1657,10 @@ function isBothStruct(d) {
|
|
|
1147
1657
|
return d.k === "struct" && d.info.pkgTarget === "both";
|
|
1148
1658
|
}
|
|
1149
1659
|
|
|
1660
|
+
function isPtrWriteBack(d) {
|
|
1661
|
+
return isPtrBoth(d) || d.k === "ptr_box" || (d.k === "anon_struct" && d.ptr);
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1150
1664
|
function returnLines(returns, resultExpr) {
|
|
1151
1665
|
if (returns.length === 0) return [];
|
|
1152
1666
|
if (returns.length === 1)
|
|
@@ -1161,13 +1675,21 @@ function genFuncBody(exportName, params, returns, leadArgs = []) {
|
|
|
1161
1675
|
const names = params.map((_, i) => `a${i}`);
|
|
1162
1676
|
const lines = [];
|
|
1163
1677
|
const argExprs = [...leadArgs];
|
|
1164
|
-
const writeBack = params.some(
|
|
1165
|
-
if (
|
|
1678
|
+
const writeBack = params.some(isPtrWriteBack);
|
|
1679
|
+
if (params.some(isPtrBoth)) lines.push("\tconst __m = new Map();");
|
|
1166
1680
|
for (let i = 0; i < params.length; i++) {
|
|
1167
1681
|
const d = params[i];
|
|
1168
1682
|
if (isPtrBoth(d)) {
|
|
1169
1683
|
lines.push(`\tconst __p${i} = ${P}to_${d.name}(a${i}, false, __m);`);
|
|
1170
1684
|
argExprs.push(`__p${i}`);
|
|
1685
|
+
} else if (d.k === "ptr_box") {
|
|
1686
|
+
lines.push(`\tconst __p${i} = ${P}boxin_${d.key}(a${i});`);
|
|
1687
|
+
argExprs.push(`__p${i}`);
|
|
1688
|
+
} else if (d.k === "anon_struct" && d.ptr) {
|
|
1689
|
+
lines.push(
|
|
1690
|
+
`\tconst __p${i} = a${i} == null ? null : ${P}to_${d.name}(a${i});`,
|
|
1691
|
+
);
|
|
1692
|
+
argExprs.push(`__p${i}`);
|
|
1171
1693
|
} else {
|
|
1172
1694
|
argExprs.push(inExpr(d, names[i]));
|
|
1173
1695
|
}
|
|
@@ -1185,8 +1707,12 @@ function genFuncBody(exportName, params, returns, leadArgs = []) {
|
|
|
1185
1707
|
} else {
|
|
1186
1708
|
lines.push(`\t\tconst __r = ${call};`);
|
|
1187
1709
|
for (let i = 0; i < params.length; i++) {
|
|
1188
|
-
|
|
1189
|
-
|
|
1710
|
+
const d = params[i];
|
|
1711
|
+
if (isPtrBoth(d)) lines.push(`\t\t${P}back_${d.name}(__p${i}, a${i});`);
|
|
1712
|
+
else if (d.k === "ptr_box")
|
|
1713
|
+
lines.push(`\t\t${P}boxback_${d.key}(__p${i}, a${i});`);
|
|
1714
|
+
else if (d.k === "anon_struct" && d.ptr)
|
|
1715
|
+
lines.push(`\t\t${P}back_${d.name}(__p${i}, a${i});`);
|
|
1190
1716
|
}
|
|
1191
1717
|
lines.push(...returnLines(returns, "__r").map((l) => `\t${l}`));
|
|
1192
1718
|
}
|
|
@@ -1206,6 +1732,7 @@ export const WASM_IMPORTS_JS = `const ${P}MAX = 9007199254740991n;
|
|
|
1206
1732
|
function ${P}imports(stringTable, extraEnv, tag, write) {
|
|
1207
1733
|
let lineBuf = "";
|
|
1208
1734
|
let targs = [];
|
|
1735
|
+
let fmtStructMap = new WeakMap();
|
|
1209
1736
|
const late = { exports: null };
|
|
1210
1737
|
const flush = () => { write(lineBuf); lineBuf = ""; };
|
|
1211
1738
|
const print = (v) => {
|
|
@@ -1224,6 +1751,8 @@ function ${P}imports(stringTable, extraEnv, tag, write) {
|
|
|
1224
1751
|
flush();
|
|
1225
1752
|
};
|
|
1226
1753
|
const targ = (v) => { targs.push(v); };
|
|
1754
|
+
let utf8Encoded = null;
|
|
1755
|
+
let convBytes = null, convPos = 0;
|
|
1227
1756
|
const env = {
|
|
1228
1757
|
"panicTag": tag,
|
|
1229
1758
|
"panic": (msg) => {
|
|
@@ -1276,6 +1805,25 @@ function ${P}imports(stringTable, extraEnv, tag, write) {
|
|
|
1276
1805
|
"str_from_i32": (n) => String(n),
|
|
1277
1806
|
"str_from_f64": (f) => String(f),
|
|
1278
1807
|
"is_string": (v) => (typeof v === "string" ? 1 : 0),
|
|
1808
|
+
"str_to_bytes": (s) => {
|
|
1809
|
+
utf8Encoded = new TextEncoder().encode(s ?? "");
|
|
1810
|
+
return utf8Encoded.length;
|
|
1811
|
+
},
|
|
1812
|
+
"str_byte_at": (i) => (utf8Encoded ? (utf8Encoded[i] ?? 0) : 0),
|
|
1813
|
+
"bytes_to_str_init": (len) => {
|
|
1814
|
+
convBytes = new Uint8Array(len);
|
|
1815
|
+
convPos = 0;
|
|
1816
|
+
},
|
|
1817
|
+
"bytes_to_str_push": (b) => {
|
|
1818
|
+
if (convBytes && convPos < convBytes.length) {
|
|
1819
|
+
convBytes[convPos++] = b & 0xFF;
|
|
1820
|
+
}
|
|
1821
|
+
},
|
|
1822
|
+
"bytes_to_str_finish": () => {
|
|
1823
|
+
const res = convBytes ? new TextDecoder().decode(convBytes) : "";
|
|
1824
|
+
convBytes = null;
|
|
1825
|
+
return res;
|
|
1826
|
+
},
|
|
1279
1827
|
"print_i32": print, "print_i64": print, "print_f32": print, "print_f64": print,
|
|
1280
1828
|
"print_str": print, "print_any": print,
|
|
1281
1829
|
"print_bool": (b) => print(b !== 0 ? "true" : "false"),
|
|
@@ -1283,11 +1831,31 @@ function ${P}imports(stringTable, extraEnv, tag, write) {
|
|
|
1283
1831
|
"println_str": println, "println_any": println,
|
|
1284
1832
|
"println_bool": (b) => println(b !== 0 ? "true" : "false"),
|
|
1285
1833
|
"println_empty": flush,
|
|
1834
|
+
"fmt_new_array": () => [],
|
|
1835
|
+
"fmt_array_push": (arr, v) => { arr.push(v); },
|
|
1836
|
+
"fmt_new_map": () => new Map(),
|
|
1837
|
+
"fmt_map_set": (m, k, v) => { m.set(k, v); },
|
|
1838
|
+
"fmt_new_struct": (ref, isPtr) => {
|
|
1839
|
+
if (ref !== null && ref !== undefined && typeof ref === "object" && fmtStructMap.has(ref)) {
|
|
1840
|
+
return fmtStructMap.get(ref);
|
|
1841
|
+
}
|
|
1842
|
+
const o = {};
|
|
1843
|
+
Object.defineProperty(o, "__struct", { value: true, configurable: true });
|
|
1844
|
+
if (isPtr !== 0) Object.defineProperty(o, "__p", { value: true, configurable: true });
|
|
1845
|
+
if (ref !== null && ref !== undefined && typeof ref === "object") fmtStructMap.set(ref, o);
|
|
1846
|
+
return o;
|
|
1847
|
+
},
|
|
1848
|
+
"fmt_struct_set": (o, k, v) => { o[typeof k === "number" ? stringTable[k] : k] = v; },
|
|
1849
|
+
"fmt_box_i32": (v) => v,
|
|
1850
|
+
"fmt_box_i64": (v) => (v > ${P}MAX || v < -${P}MAX ? v : Number(v)),
|
|
1851
|
+
"fmt_box_f64": (v) => v,
|
|
1852
|
+
"fmt_box_bool": (v) => v !== 0,
|
|
1853
|
+
"fmt_new_box": () => ({ value: null, __p: true, __box: true }),
|
|
1286
1854
|
"testing_arg_i32": targ, "testing_arg_f32": targ, "testing_arg_f64": targ,
|
|
1287
1855
|
"testing_arg_str": targ, "testing_arg_any": targ,
|
|
1288
1856
|
"testing_arg_i64": (v) => targ(v > ${P}MAX || v < -${P}MAX ? v : Number(v)),
|
|
1289
1857
|
"testing_arg_bool": (v) => targ(v !== 0),
|
|
1290
|
-
"testing_call": (t, i) => { const a = targs; targs = []; t[stringTable[i]](...a); },
|
|
1858
|
+
"testing_call": (t, i) => { const a = targs; targs = []; fmtStructMap = new WeakMap(); t[stringTable[i]](...a); },
|
|
1291
1859
|
"testing_name": (t) => t.Name(),
|
|
1292
1860
|
"testing_flag": (t, i) => (t[stringTable[i]]() ? 1 : 0),
|
|
1293
1861
|
};
|
|
@@ -1295,7 +1863,7 @@ function ${P}imports(stringTable, extraEnv, tag, write) {
|
|
|
1295
1863
|
// modules that never call them do not ship it (see WASM_STDLIB_JS).
|
|
1296
1864
|
if (typeof ${P}stdlib === "function") {
|
|
1297
1865
|
Object.assign(env, ${P}stdlib(stringTable, {
|
|
1298
|
-
"take": () => { const a = targs; targs = []; return a; },
|
|
1866
|
+
"take": () => { const a = targs; targs = []; fmtStructMap = new WeakMap(); return a; },
|
|
1299
1867
|
"printf": (s) => {
|
|
1300
1868
|
lineBuf += s;
|
|
1301
1869
|
const nl = lineBuf.lastIndexOf("\\n");
|
|
@@ -1306,7 +1874,7 @@ function ${P}imports(stringTable, extraEnv, tag, write) {
|
|
|
1306
1874
|
}
|
|
1307
1875
|
Object.assign(env, extraEnv);
|
|
1308
1876
|
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];
|
|
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];
|
|
1310
1878
|
m.mod = (x, y) => x % y;
|
|
1311
1879
|
// Go rounds half away from zero; Math.round rounds half toward +Infinity.
|
|
1312
1880
|
m.round = (x) => Math.sign(x) * Math.round(Math.abs(x));
|
|
@@ -1316,21 +1884,169 @@ function ${P}imports(stringTable, extraEnv, tag, write) {
|
|
|
1316
1884
|
|
|
1317
1885
|
// JS side of the STDLIB_IMPORTS table (index.js); emitted only when the
|
|
1318
1886
|
// module imports one of the packages that use it.
|
|
1319
|
-
export const WASM_STDLIB_JS = `${
|
|
1887
|
+
export const WASM_STDLIB_JS = `${HELPER_ERROR}
|
|
1888
|
+
${HELPER_SPRINTF}
|
|
1320
1889
|
${HELPER_STRCONV}
|
|
1890
|
+
${HELPER_PATH_CLEAN}
|
|
1891
|
+
${HELPER_DURATION}
|
|
1892
|
+
${HELPER_SHA256}
|
|
1893
|
+
${HELPER_HEX}
|
|
1894
|
+
${HELPER_BASE64}
|
|
1321
1895
|
function ${P}stdlib(stringTable, io) {
|
|
1322
1896
|
let parts = [];
|
|
1323
|
-
let
|
|
1897
|
+
let cutBefore = "", cutAfter = "";
|
|
1898
|
+
let strconvErr = null, strconvErrKind = 0;
|
|
1324
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;
|
|
1905
|
+
let sscanTokens = [];
|
|
1906
|
+
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 = [];
|
|
1325
1913
|
return {
|
|
1914
|
+
"fmt_sscan_init": (s, mode) => {
|
|
1915
|
+
sscanHasErr = 0;
|
|
1916
|
+
sscanErrMsg = "";
|
|
1917
|
+
const str = s ?? "";
|
|
1918
|
+
sscanTokens = (mode === 1 ? str.split("\\n")[0] : str).trim().split(/\\s+/).filter(Boolean);
|
|
1919
|
+
return sscanTokens.length;
|
|
1920
|
+
},
|
|
1921
|
+
"fmt_sscanf_init": (s, fmt) => {
|
|
1922
|
+
sscanHasErr = 0;
|
|
1923
|
+
sscanErrMsg = "";
|
|
1924
|
+
const str = s ?? "";
|
|
1925
|
+
const fmtStr = fmt ?? "";
|
|
1926
|
+
let si = 0, fi = 0;
|
|
1927
|
+
sscanTokens = [];
|
|
1928
|
+
while (fi < fmtStr.length) {
|
|
1929
|
+
if (/\\s/.test(fmtStr[fi])) {
|
|
1930
|
+
while (fi < fmtStr.length && /\\s/.test(fmtStr[fi])) fi++;
|
|
1931
|
+
while (si < str.length && /\\s/.test(str[si])) si++;
|
|
1932
|
+
continue;
|
|
1933
|
+
}
|
|
1934
|
+
if (fmtStr[fi] === "%") {
|
|
1935
|
+
if (fi + 1 < fmtStr.length && fmtStr[fi + 1] === "%") {
|
|
1936
|
+
if (str[si] !== "%") {
|
|
1937
|
+
sscanHasErr = 1;
|
|
1938
|
+
sscanErrMsg = "input does not match format";
|
|
1939
|
+
break;
|
|
1940
|
+
}
|
|
1941
|
+
si++;
|
|
1942
|
+
fi += 2;
|
|
1943
|
+
continue;
|
|
1944
|
+
}
|
|
1945
|
+
const vm = fmtStr.slice(fi).match(/^%[vdsfqtxXbcoeEfFgG]/);
|
|
1946
|
+
if (vm) {
|
|
1947
|
+
const verb = vm[0];
|
|
1948
|
+
fi += verb.length;
|
|
1949
|
+
if (verb !== "%c") {
|
|
1950
|
+
while (si < str.length && /\\s/.test(str[si])) si++;
|
|
1951
|
+
}
|
|
1952
|
+
if (si >= str.length) break;
|
|
1953
|
+
let tokRe = /^\\S+/;
|
|
1954
|
+
if (verb === "%d" || verb === "%i") tokRe = /^[+-]?\\d+/;
|
|
1955
|
+
else if (verb === "%f" || verb === "%F" || verb === "%e" || verb === "%E" || verb === "%g" || verb === "%G") tokRe = /^[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:[eE][+-]?\\d+)?/;
|
|
1956
|
+
else if (verb === "%t") tokRe = /^(?:true|false|1|0|t|f|T|F|TRUE|FALSE)/;
|
|
1957
|
+
else if (verb === "%x" || verb === "%X") tokRe = /^[+-]?(?:0x)?[0-9a-fA-F]+/;
|
|
1958
|
+
else if (verb === "%c") tokRe = /^[\\s\\S]/;
|
|
1959
|
+
const m = str.slice(si).match(tokRe);
|
|
1960
|
+
if (!m) {
|
|
1961
|
+
sscanHasErr = 1;
|
|
1962
|
+
sscanErrMsg = "input does not match format";
|
|
1963
|
+
break;
|
|
1964
|
+
}
|
|
1965
|
+
sscanTokens.push(m[0]);
|
|
1966
|
+
si += m[0].length;
|
|
1967
|
+
continue;
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
if (si >= str.length) break;
|
|
1971
|
+
if (str[si] !== fmtStr[fi]) {
|
|
1972
|
+
sscanHasErr = 1;
|
|
1973
|
+
sscanErrMsg = "input does not match format";
|
|
1974
|
+
break;
|
|
1975
|
+
}
|
|
1976
|
+
si++;
|
|
1977
|
+
fi++;
|
|
1978
|
+
}
|
|
1979
|
+
return sscanTokens.length;
|
|
1980
|
+
},
|
|
1981
|
+
"fmt_sscan_has_err": () => sscanHasErr,
|
|
1982
|
+
"fmt_sscan_err_msg": () => sscanErrMsg || "syntax error",
|
|
1983
|
+
"fmt_sscan_token_str": (i) => sscanTokens[i] ?? "",
|
|
1984
|
+
"fmt_sscan_token_i64": (i) => {
|
|
1985
|
+
if (i >= sscanTokens.length) { sscanHasErr = 1; sscanErrMsg = "unexpected EOF"; return 0n; }
|
|
1986
|
+
const t = sscanTokens[i];
|
|
1987
|
+
if (/^[+-]?(?:0[xX])?[0-9a-fA-F]+$/.test(t)) {
|
|
1988
|
+
try {
|
|
1989
|
+
if (/^[+-]?[0-9a-fA-F]+$/.test(t) && /[a-fA-F]/.test(t)) {
|
|
1990
|
+
return BigInt("0x" + t);
|
|
1991
|
+
}
|
|
1992
|
+
return BigInt(t);
|
|
1993
|
+
} catch {}
|
|
1994
|
+
}
|
|
1995
|
+
if (t.length === 1) {
|
|
1996
|
+
return BigInt(t.charCodeAt(0));
|
|
1997
|
+
}
|
|
1998
|
+
sscanHasErr = 1;
|
|
1999
|
+
sscanErrMsg = "expected integer";
|
|
2000
|
+
return 0n;
|
|
2001
|
+
},
|
|
2002
|
+
"fmt_sscan_token_f64": (i) => {
|
|
2003
|
+
if (i >= sscanTokens.length) { sscanHasErr = 1; sscanErrMsg = "unexpected EOF"; return 0; }
|
|
2004
|
+
const t = sscanTokens[i];
|
|
2005
|
+
const n = Number(t);
|
|
2006
|
+
if (Number.isNaN(n) || !/^[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:[eE][+-]?\\d+)?$/.test(t)) {
|
|
2007
|
+
sscanHasErr = 1;
|
|
2008
|
+
sscanErrMsg = "expected float";
|
|
2009
|
+
return 0;
|
|
2010
|
+
}
|
|
2011
|
+
return n;
|
|
2012
|
+
},
|
|
2013
|
+
"fmt_sscan_token_bool": (i) => {
|
|
2014
|
+
if (i >= sscanTokens.length) { sscanHasErr = 1; sscanErrMsg = "unexpected EOF"; return 0; }
|
|
2015
|
+
const t = sscanTokens[i]?.toLowerCase();
|
|
2016
|
+
if (t === "true" || t === "1" || t === "t") return 1;
|
|
2017
|
+
if (t === "false" || t === "0" || t === "f") return 0;
|
|
2018
|
+
sscanHasErr = 1;
|
|
2019
|
+
sscanErrMsg = "expected boolean";
|
|
2020
|
+
return 0;
|
|
2021
|
+
},
|
|
1326
2022
|
"testing_run": (t, name, fn) => (t.Run(name, (sub) => { io.late.exports.__testing_run_cb(fn, sub); }) ? 1 : 0),
|
|
1327
2023
|
"fmt_sprintf": (f) => __sprintf(f ?? "", ...io.take()),
|
|
1328
2024
|
"fmt_printf": (f) => { io.printf(__sprintf(f ?? "", ...io.take())); },
|
|
2025
|
+
"fmt_format_v": (v) => { const res = __sprintf("%v", v); io.take(); return res; },
|
|
1329
2026
|
"str_trim": (s, c) => ${P}trim(s ?? "", c ?? "", true, true),
|
|
1330
2027
|
"str_trim_left": (s, c) => ${P}trim(s ?? "", c ?? "", true, false),
|
|
1331
2028
|
"str_trim_right": (s, c) => ${P}trim(s ?? "", c ?? "", false, true),
|
|
1332
2029
|
"str_trim_prefix": (s, p) => (s && p && s.startsWith(p) ? s.slice(p.length) : (s ?? "")),
|
|
1333
2030
|
"str_trim_suffix": (s, p) => (s && p && s.endsWith(p) ? s.slice(0, s.length - p.length) : (s ?? "")),
|
|
2031
|
+
"str_cut": (s, sep) => {
|
|
2032
|
+
const i = (s ?? "").indexOf(sep ?? "");
|
|
2033
|
+
if (i < 0) { cutBefore = s ?? ""; cutAfter = ""; return 0; }
|
|
2034
|
+
cutBefore = (s ?? "").slice(0, i);
|
|
2035
|
+
cutAfter = (s ?? "").slice(i + (sep ?? "").length);
|
|
2036
|
+
return 1;
|
|
2037
|
+
},
|
|
2038
|
+
"str_cut_before": () => cutBefore,
|
|
2039
|
+
"str_cut_after": () => cutAfter,
|
|
2040
|
+
"str_cut_prefix": (s, p) => {
|
|
2041
|
+
s = s ?? ""; p = p ?? "";
|
|
2042
|
+
if (s.startsWith(p)) { cutAfter = s.slice(p.length); return 1; }
|
|
2043
|
+
cutAfter = s; return 0;
|
|
2044
|
+
},
|
|
2045
|
+
"str_cut_suffix": (s, suf) => {
|
|
2046
|
+
s = s ?? ""; suf = suf ?? "";
|
|
2047
|
+
if (s.endsWith(suf)) { cutBefore = s.slice(0, s.length - suf.length); return 1; }
|
|
2048
|
+
cutBefore = s; return 0;
|
|
2049
|
+
},
|
|
1334
2050
|
"str_replace": (s, o, n, cnt) => {
|
|
1335
2051
|
s = s ?? ""; o = o ?? ""; n = n ?? "";
|
|
1336
2052
|
if (cnt < 0) return s.replaceAll(o, n);
|
|
@@ -1350,18 +2066,175 @@ function ${P}stdlib(stringTable, io) {
|
|
|
1350
2066
|
"str_index_rune": (s, r) => (s ?? "").indexOf(String.fromCodePoint(r)),
|
|
1351
2067
|
"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
2068
|
"str_last_index_byte": (s, b) => (s ?? "").lastIndexOf(String.fromCharCode(b)),
|
|
2069
|
+
"str_last_index_any": (s, cs) => {
|
|
2070
|
+
s = s ?? ""; let best = -1;
|
|
2071
|
+
for (const c of (cs ?? "")) {
|
|
2072
|
+
const i = s.lastIndexOf(c);
|
|
2073
|
+
if (i > best) best = i;
|
|
2074
|
+
}
|
|
2075
|
+
return best;
|
|
2076
|
+
},
|
|
1353
2077
|
"str_title": (s) => (s ?? "").replace(/(^|[^\\p{L}\\p{N}_'])(\\p{Ll})/gu, (m, a, b) => a + b.toUpperCase()),
|
|
1354
2078
|
"str_split": (s, sep) => { parts = (s ?? "").split(sep ?? ""); return parts.length; },
|
|
2079
|
+
"str_split_n": (s, sep, n) => {
|
|
2080
|
+
s = s ?? ""; sep = sep ?? "";
|
|
2081
|
+
if (n === 0) return -1;
|
|
2082
|
+
if (n < 0) { parts = sep === "" ? [...s] : s.split(sep); return parts.length; }
|
|
2083
|
+
if (sep === "") {
|
|
2084
|
+
const chars = [...s];
|
|
2085
|
+
if (chars.length <= n) { parts = chars; return parts.length; }
|
|
2086
|
+
const r = chars.slice(0, n - 1);
|
|
2087
|
+
r.push(chars.slice(n - 1).join(""));
|
|
2088
|
+
parts = r;
|
|
2089
|
+
return parts.length;
|
|
2090
|
+
}
|
|
2091
|
+
const r = [];
|
|
2092
|
+
let cur = s;
|
|
2093
|
+
for (let i = 1; i < n && cur.length; i++) {
|
|
2094
|
+
const j = cur.indexOf(sep);
|
|
2095
|
+
if (j < 0) break;
|
|
2096
|
+
r.push(cur.slice(0, j));
|
|
2097
|
+
cur = cur.slice(j + sep.length);
|
|
2098
|
+
}
|
|
2099
|
+
r.push(cur);
|
|
2100
|
+
parts = r;
|
|
2101
|
+
return parts.length;
|
|
2102
|
+
},
|
|
2103
|
+
"str_split_after": (s, sep) => {
|
|
2104
|
+
s = s ?? ""; sep = sep ?? "";
|
|
2105
|
+
if (sep === "") { parts = [...s]; return parts.length; }
|
|
2106
|
+
const r = [];
|
|
2107
|
+
let cur = s;
|
|
2108
|
+
while (true) {
|
|
2109
|
+
const j = cur.indexOf(sep);
|
|
2110
|
+
if (j < 0) { r.push(cur); break; }
|
|
2111
|
+
r.push(cur.slice(0, j + sep.length));
|
|
2112
|
+
cur = cur.slice(j + sep.length);
|
|
2113
|
+
}
|
|
2114
|
+
parts = r;
|
|
2115
|
+
return parts.length;
|
|
2116
|
+
},
|
|
2117
|
+
"str_split_after_n": (s, sep, n) => {
|
|
2118
|
+
s = s ?? ""; sep = sep ?? "";
|
|
2119
|
+
if (n === 0) return -1;
|
|
2120
|
+
if (sep === "") {
|
|
2121
|
+
const chars = [...s];
|
|
2122
|
+
if (n < 0 || chars.length <= n) parts = chars;
|
|
2123
|
+
else {
|
|
2124
|
+
const r = chars.slice(0, n - 1);
|
|
2125
|
+
r.push(chars.slice(n - 1).join(""));
|
|
2126
|
+
parts = r;
|
|
2127
|
+
}
|
|
2128
|
+
return parts.length;
|
|
2129
|
+
}
|
|
2130
|
+
const r = [];
|
|
2131
|
+
let cur = s;
|
|
2132
|
+
for (let i = 1; (n < 0 || i < n) && cur.length; i++) {
|
|
2133
|
+
const j = cur.indexOf(sep);
|
|
2134
|
+
if (j < 0) break;
|
|
2135
|
+
r.push(cur.slice(0, j + sep.length));
|
|
2136
|
+
cur = cur.slice(j + sep.length);
|
|
2137
|
+
}
|
|
2138
|
+
r.push(cur);
|
|
2139
|
+
parts = r;
|
|
2140
|
+
return parts.length;
|
|
2141
|
+
},
|
|
1355
2142
|
"str_fields": (s) => { parts = (s ?? "").split(/\\s+/).filter((p) => p !== ""); return parts.length; },
|
|
1356
2143
|
"str_part": (i) => parts[i] ?? "",
|
|
1357
|
-
"
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
2144
|
+
"str_replacer_init": (s) => {
|
|
2145
|
+
replacerTarget = s ?? "";
|
|
2146
|
+
replacerPairs = [];
|
|
2147
|
+
},
|
|
2148
|
+
"str_replacer_add": (o, n) => {
|
|
2149
|
+
replacerPairs.push([o ?? "", n ?? ""]);
|
|
2150
|
+
},
|
|
2151
|
+
"str_replacer_run": () => {
|
|
2152
|
+
let s = replacerTarget;
|
|
2153
|
+
if (!replacerPairs.length || s === "") return s;
|
|
2154
|
+
let out = "";
|
|
2155
|
+
let i = 0;
|
|
2156
|
+
while (i < s.length) {
|
|
2157
|
+
let matched = false;
|
|
2158
|
+
for (let k = 0; k < replacerPairs.length; k++) {
|
|
2159
|
+
const [oldStr, newStr] = replacerPairs[k];
|
|
2160
|
+
if (oldStr.length > 0 && s.startsWith(oldStr, i)) {
|
|
2161
|
+
out += newStr;
|
|
2162
|
+
i += oldStr.length;
|
|
2163
|
+
matched = true;
|
|
2164
|
+
break;
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
if (!matched) {
|
|
2168
|
+
out += s[i];
|
|
2169
|
+
i++;
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
return out;
|
|
2173
|
+
},
|
|
2174
|
+
"strconv_atoi": (s) => {
|
|
2175
|
+
const r = __strconv_atoi(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];
|
|
2179
|
+
},
|
|
2180
|
+
"strconv_parse_int": (s, base, bits) => {
|
|
2181
|
+
const r = __strconv_parse_int(s ?? "", base, bits);
|
|
2182
|
+
strconvErr = r[1];
|
|
2183
|
+
strconvErrKind = r[1] ? (r[1].includes("invalid syntax") ? 1 : r[1].includes("value out of range") ? 2 : 0) : 0;
|
|
2184
|
+
return r[0];
|
|
2185
|
+
},
|
|
2186
|
+
"strconv_parse_float": (s) => {
|
|
2187
|
+
const r = __strconv_parse_float(s ?? "");
|
|
2188
|
+
strconvErr = r[1];
|
|
2189
|
+
strconvErrKind = r[1] ? (r[1].includes("invalid syntax") ? 1 : r[1].includes("value out of range") ? 2 : 0) : 0;
|
|
2190
|
+
return r[0];
|
|
2191
|
+
},
|
|
2192
|
+
"strconv_parse_bool": (s) => {
|
|
2193
|
+
const r = __strconv_parse_bool(s ?? "");
|
|
2194
|
+
strconvErr = r[1];
|
|
2195
|
+
strconvErrKind = r[1] ? (r[1].includes("invalid syntax") ? 1 : r[1].includes("value out of range") ? 2 : 0) : 0;
|
|
2196
|
+
return r[0] ? 1 : 0;
|
|
2197
|
+
},
|
|
1361
2198
|
"strconv_err": () => strconvErr,
|
|
2199
|
+
"strconv_err_kind": () => strconvErrKind,
|
|
1362
2200
|
"strconv_format_float": (f, fmtc, prec, _bits) => __strconv_format_float(f, fmtc, prec),
|
|
1363
2201
|
"strconv_format_int": (n, base) => n.toString(base),
|
|
2202
|
+
"strconv_format_uint": (n, base) => BigInt.asUintN(64, BigInt(n)).toString(base),
|
|
2203
|
+
"strconv_parse_uint": (s, base, bits) => {
|
|
2204
|
+
const r = __strconv_parse_uint(s ?? "", base, bits);
|
|
2205
|
+
strconvErr = r[1];
|
|
2206
|
+
strconvErrKind = r[1] ? (r[1].includes("invalid syntax") ? 1 : r[1].includes("value out of range") ? 2 : 0) : 0;
|
|
2207
|
+
return r[0];
|
|
2208
|
+
},
|
|
1364
2209
|
"strconv_quote": (s) => JSON.stringify(s ?? ""),
|
|
2210
|
+
"strconv_unquote": (s) => {
|
|
2211
|
+
s = s ?? "";
|
|
2212
|
+
if (s.startsWith("\`") && s.endsWith("\`") && s.length >= 2) {
|
|
2213
|
+
if (s.slice(1, -1).includes("\`")) {
|
|
2214
|
+
strconvErr = "invalid syntax";
|
|
2215
|
+
strconvErrKind = 1;
|
|
2216
|
+
return "";
|
|
2217
|
+
}
|
|
2218
|
+
strconvErr = null;
|
|
2219
|
+
strconvErrKind = 0;
|
|
2220
|
+
return s.slice(1, -1);
|
|
2221
|
+
}
|
|
2222
|
+
try {
|
|
2223
|
+
const v = JSON.parse(s);
|
|
2224
|
+
if (typeof v !== "string") {
|
|
2225
|
+
strconvErr = "invalid syntax";
|
|
2226
|
+
strconvErrKind = 1;
|
|
2227
|
+
return "";
|
|
2228
|
+
}
|
|
2229
|
+
strconvErr = null;
|
|
2230
|
+
strconvErrKind = 0;
|
|
2231
|
+
return v;
|
|
2232
|
+
} catch (e) {
|
|
2233
|
+
strconvErr = "invalid syntax";
|
|
2234
|
+
strconvErrKind = 1;
|
|
2235
|
+
return "";
|
|
2236
|
+
}
|
|
2237
|
+
},
|
|
1365
2238
|
"utf8_rune_count": (s) => { let n = 0; for (const _ of (s ?? "")) n++; return n; },
|
|
1366
2239
|
"utf8_rune_len": (r) => (r < 0 || r > 0x10ffff || (r >= 0xd800 && r <= 0xdfff) ? -1 : r < 0x80 ? 1 : r < 0x800 ? 2 : r < 0x10000 ? 3 : 4),
|
|
1367
2240
|
"utf8_valid_string": (s) => (s ?? "").isWellFormed ? ((s ?? "").isWellFormed() ? 1 : 0) : (/[\\uD800-\\uDFFF]/.test((s ?? "").replace(/[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]/g, "")) ? 0 : 1),
|
|
@@ -1378,14 +2251,205 @@ function ${P}stdlib(stringTable, io) {
|
|
|
1378
2251
|
},
|
|
1379
2252
|
"utf8_decode_size": () => decodeSize,
|
|
1380
2253
|
"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; },
|
|
2254
|
+
// unicode
|
|
2255
|
+
"unicode_is_letter": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && /\\p{L}/u.test(c)) ? 1 : 0; },
|
|
2256
|
+
"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 && /\\s/u.test(c)) ? 1 : 0; },
|
|
2258
|
+
"unicode_is_upper": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && /\\p{Lu}/u.test(c)) ? 1 : 0; },
|
|
2259
|
+
"unicode_is_lower": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && /\\p{Ll}/u.test(c)) ? 1 : 0; },
|
|
2260
|
+
"unicode_is_punct": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && /\\p{P}/u.test(c)) ? 1 : 0; },
|
|
2261
|
+
"unicode_is_control": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && /\\p{Cc}/u.test(c)) ? 1 : 0; },
|
|
2262
|
+
"unicode_is_print": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && !/\\p{Cc}/u.test(c)) ? 1 : 0; },
|
|
2263
|
+
"unicode_is_graphic": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return (c && !/\\p{Cc}/u.test(c)) ? 1 : 0; },
|
|
2264
|
+
"unicode_to_upper": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return c ? (c.toUpperCase().codePointAt(0) ?? r) : r; },
|
|
2265
|
+
"unicode_to_lower": (r) => { const c = (r >= 0 && r <= 0x10ffff) ? String.fromCodePoint(r) : null; return c ? (c.toLowerCase().codePointAt(0) ?? r) : r; },
|
|
2266
|
+
// html
|
|
2267
|
+
"html_escape": (s) => (s ?? "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'"),
|
|
2268
|
+
"html_unescape": (s) => (s ?? "").replace(/&(?:quot|apos|#39|#34|gt|lt|amp|#(?:[0-9]+|[xX][0-9a-fA-F]+));/g, (m) => {
|
|
2269
|
+
switch (m) {
|
|
2270
|
+
case """: case """: return '"';
|
|
2271
|
+
case "'": case "'": return "'";
|
|
2272
|
+
case ">": return ">";
|
|
2273
|
+
case "<": return "<";
|
|
2274
|
+
case "&": return "&";
|
|
2275
|
+
default: {
|
|
2276
|
+
if (m.startsWith("&#")) {
|
|
2277
|
+
const raw = m.slice(2, -1);
|
|
2278
|
+
const cp = (raw.startsWith("x") || raw.startsWith("X")) ? parseInt(raw.slice(1), 16) : parseInt(raw, 10);
|
|
2279
|
+
return (cp >= 0 && cp <= 0x10FFFF && !(cp >= 0xD800 && cp <= 0xDFFF)) ? String.fromCodePoint(cp) : "\\uFFFD";
|
|
2280
|
+
}
|
|
2281
|
+
return m;
|
|
2282
|
+
}
|
|
2283
|
+
}
|
|
2284
|
+
}),
|
|
2285
|
+
// path
|
|
2286
|
+
"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) || "/"; },
|
|
2287
|
+
"path_dir": (p) => { const s = p ?? ""; const i = s.lastIndexOf("/"); if (i < 0) return "."; const res = s.slice(0, i); return res === "" ? "/" : __pathClean(res); },
|
|
2288
|
+
"path_ext": (p) => { const s = p ?? ""; const b = s.slice(s.lastIndexOf("/") + 1); const i = b.lastIndexOf("."); return i <= 0 ? "" : b.slice(i); },
|
|
2289
|
+
"path_is_abs": (p) => (p ?? "").startsWith("/") ? 1 : 0,
|
|
2290
|
+
"path_clean": (p) => __pathClean(p ?? ""),
|
|
2291
|
+
"path_split": (p) => {
|
|
2292
|
+
const s = p ?? "";
|
|
2293
|
+
const i = s.lastIndexOf("/");
|
|
2294
|
+
if (i < 0) { splitDir = ""; splitFile = s; }
|
|
2295
|
+
else { splitDir = s.slice(0, i + 1); splitFile = s.slice(i + 1); }
|
|
2296
|
+
},
|
|
2297
|
+
"path_split_dir": () => splitDir,
|
|
2298
|
+
"path_split_file": () => splitFile,
|
|
2299
|
+
"path_join_init": () => { joinParts = []; },
|
|
2300
|
+
"path_join_push": (s) => { if (s) joinParts.push(s); },
|
|
2301
|
+
"path_join_run": () => {
|
|
2302
|
+
if (joinParts.length === 0) return "";
|
|
2303
|
+
const res = __pathClean(joinParts.join("/"));
|
|
2304
|
+
joinParts = [];
|
|
2305
|
+
return res;
|
|
2306
|
+
},
|
|
2307
|
+
"path_match": (pat, name) => {
|
|
2308
|
+
const r = __pathMatch(pat ?? "", name ?? "");
|
|
2309
|
+
matchHasErr = r[1] ? 1 : 0;
|
|
2310
|
+
return r[0] ? 1 : 0;
|
|
2311
|
+
},
|
|
2312
|
+
"path_match_has_err": () => matchHasErr,
|
|
2313
|
+
// bytes trim
|
|
2314
|
+
"bytes_trim": (s, cutset, left, right) => {
|
|
2315
|
+
const cs = new Set(Array.from(cutset ?? ""));
|
|
2316
|
+
const chars = Array.from(s ?? "");
|
|
2317
|
+
let a = 0, b = chars.length;
|
|
2318
|
+
if (left) while (a < b && cs.has(chars[a])) a++;
|
|
2319
|
+
if (right) while (b > a && cs.has(chars[b - 1])) b--;
|
|
2320
|
+
const enc = new TextEncoder();
|
|
2321
|
+
trimStart = a === 0 ? 0 : enc.encode(chars.slice(0, a).join("")).length;
|
|
2322
|
+
trimEnd = b === chars.length ? enc.encode(s ?? "").length : enc.encode(chars.slice(0, b).join("")).length;
|
|
2323
|
+
},
|
|
2324
|
+
"bytes_trim_start": () => trimStart,
|
|
2325
|
+
"bytes_trim_end": () => trimEnd,
|
|
2326
|
+
// time
|
|
2327
|
+
"time_now_ms": () => (typeof globalThis.__GOFRONT_CLOCK === "function" ? globalThis.__GOFRONT_CLOCK() : Date.now()),
|
|
2328
|
+
"time_timezone_offset": (ms) => -new Date(ms).getTimezoneOffset() * 60,
|
|
2329
|
+
"time_duration_string": (d) => __durationString(d),
|
|
2330
|
+
"time_parse": (layout, val) => {
|
|
2331
|
+
try {
|
|
2332
|
+
const d = new Date(val ?? "");
|
|
2333
|
+
if (isNaN(d.getTime())) throw 0;
|
|
2334
|
+
timeParseErr = null;
|
|
2335
|
+
timeParseOffset = -d.getTimezoneOffset() * 60;
|
|
2336
|
+
return d.getTime();
|
|
2337
|
+
} catch {
|
|
2338
|
+
timeParseErr = "parsing time: cannot parse";
|
|
2339
|
+
timeParseOffset = 0;
|
|
2340
|
+
return 0;
|
|
2341
|
+
}
|
|
2342
|
+
},
|
|
2343
|
+
"time_parse_err": () => timeParseErr,
|
|
2344
|
+
"time_parse_offset": () => timeParseOffset,
|
|
2345
|
+
"time_format": (y, m, d, hr, min, sec, wd, ms, offsetSec, layout) => {
|
|
2346
|
+
const DAYS = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
|
|
2347
|
+
const MONTHS = ["January","February","March","April","May","June","July","August","September","October","November","December"];
|
|
2348
|
+
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 => {
|
|
2350
|
+
if (t.startsWith(".")) {
|
|
2351
|
+
const isZeroPad = t.includes("0");
|
|
2352
|
+
const maxLen = t.length - 1;
|
|
2353
|
+
let frac = pad(ms % 1000, 3);
|
|
2354
|
+
if (maxLen > 3) frac = frac.padEnd(maxLen, "0");
|
|
2355
|
+
else frac = frac.slice(0, maxLen);
|
|
2356
|
+
if (!isZeroPad) {
|
|
2357
|
+
frac = frac.replace(/0+$/, "");
|
|
2358
|
+
return frac.length > 0 ? "." + frac : "";
|
|
2359
|
+
}
|
|
2360
|
+
return "." + frac;
|
|
2361
|
+
}
|
|
2362
|
+
switch (t) {
|
|
2363
|
+
case "2006": return String(y);
|
|
2364
|
+
case "06": return String(y).slice(-2);
|
|
2365
|
+
case "Monday": return DAYS[wd];
|
|
2366
|
+
case "Mon": return DAYS[wd].slice(0, 3);
|
|
2367
|
+
case "January": return MONTHS[m - 1];
|
|
2368
|
+
case "Jan": return MONTHS[m - 1].slice(0, 3);
|
|
2369
|
+
case "01": return pad(m);
|
|
2370
|
+
case "1": return String(m);
|
|
2371
|
+
case "02": return pad(d);
|
|
2372
|
+
case "2": return String(d);
|
|
2373
|
+
case "15": return pad(hr);
|
|
2374
|
+
case "PM": return hr < 12 ? "AM" : "PM";
|
|
2375
|
+
case "pm": return hr < 12 ? "am" : "pm";
|
|
2376
|
+
case "03": return pad(hr % 12 || 12);
|
|
2377
|
+
case "3": return String(hr % 12 || 12);
|
|
2378
|
+
case "04": return pad(min);
|
|
2379
|
+
case "4": return String(min);
|
|
2380
|
+
case "05": return pad(sec);
|
|
2381
|
+
case "5": return String(sec);
|
|
2382
|
+
case "Z07:00": {
|
|
2383
|
+
const o = Math.floor(offsetSec / 60);
|
|
2384
|
+
return o === 0 ? "Z" : (o > 0 ? "+" : "-") + pad(Math.floor(Math.abs(o) / 60)) + ":" + pad(Math.abs(o) % 60);
|
|
2385
|
+
}
|
|
2386
|
+
default: return t;
|
|
2387
|
+
}
|
|
2388
|
+
});
|
|
2389
|
+
},
|
|
2390
|
+
"raw_bytes_init": (len) => {
|
|
2391
|
+
rawBytes = new Uint8Array(len);
|
|
2392
|
+
rawPos = 0;
|
|
2393
|
+
},
|
|
2394
|
+
"raw_bytes_push": (b) => {
|
|
2395
|
+
if (rawPos < rawBytes.length) {
|
|
2396
|
+
rawBytes[rawPos++] = b & 0xff;
|
|
2397
|
+
}
|
|
2398
|
+
},
|
|
2399
|
+
"raw_bytes_len": () => rawBytes.length,
|
|
2400
|
+
"raw_bytes_get": (i) => rawBytes[i] ?? 0,
|
|
2401
|
+
"crypto_rand_fill": (len) => {
|
|
2402
|
+
rawBytes = new Uint8Array(len);
|
|
2403
|
+
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
2404
|
+
const CHUNK = 65536;
|
|
2405
|
+
for (let offset = 0; offset < len; offset += CHUNK) {
|
|
2406
|
+
crypto.getRandomValues(rawBytes.subarray(offset, Math.min(offset + CHUNK, len)));
|
|
2407
|
+
}
|
|
2408
|
+
} else {
|
|
2409
|
+
for (let i = 0; i < len; i++) rawBytes[i] = Math.floor(Math.random() * 256);
|
|
2410
|
+
}
|
|
2411
|
+
return len;
|
|
2412
|
+
},
|
|
2413
|
+
"crypto_sha256_sum": () => {
|
|
2414
|
+
const d = __sha256.Sum256(rawBytes);
|
|
2415
|
+
rawBytes = new Uint8Array(d);
|
|
2416
|
+
return 32;
|
|
2417
|
+
},
|
|
2418
|
+
"hex_encode_to_string": () => {
|
|
2419
|
+
return __hex_encode_to_string(rawBytes);
|
|
2420
|
+
},
|
|
2421
|
+
"hex_decode_string": (s) => {
|
|
2422
|
+
const [res, err] = __hex_decode_string(s ?? "");
|
|
2423
|
+
hexErr = err ? (err.message ?? String(err)) : null;
|
|
2424
|
+
rawBytes = new Uint8Array(res);
|
|
2425
|
+
return rawBytes.length;
|
|
2426
|
+
},
|
|
2427
|
+
"hex_get_err": () => hexErr,
|
|
2428
|
+
"base64_new_encoding": (alphabet) => {
|
|
2429
|
+
const id = customBase64Encodings.length + 4;
|
|
2430
|
+
customBase64Encodings.push(__base64_make(alphabet ?? "", "="));
|
|
2431
|
+
return id;
|
|
2432
|
+
},
|
|
2433
|
+
"base64_encode_to_string": (mode) => {
|
|
2434
|
+
const enc = mode === 1 ? __base64_url : mode === 2 ? __base64_raw_std : mode === 3 ? __base64_raw_url : mode >= 4 ? customBase64Encodings[mode - 4] : __base64_std;
|
|
2435
|
+
return (enc ?? __base64_std).EncodeToString(rawBytes);
|
|
2436
|
+
},
|
|
2437
|
+
"base64_decode_string": (mode, s) => {
|
|
2438
|
+
const enc = mode === 1 ? __base64_url : mode === 2 ? __base64_raw_std : mode === 3 ? __base64_raw_url : mode >= 4 ? customBase64Encodings[mode - 4] : __base64_std;
|
|
2439
|
+
const [res, err] = (enc ?? __base64_std).DecodeString(s ?? "");
|
|
2440
|
+
base64Err = err ? (err.message ?? String(err)) : null;
|
|
2441
|
+
rawBytes = new Uint8Array(res);
|
|
2442
|
+
return rawBytes.length;
|
|
2443
|
+
},
|
|
2444
|
+
"base64_get_err": () => base64Err,
|
|
1381
2445
|
};
|
|
1382
2446
|
}
|
|
1383
2447
|
function ${P}trim(s, cutset, left, right) {
|
|
1384
|
-
const cs = Array.from(cutset);
|
|
2448
|
+
const cs = new Set(Array.from(cutset));
|
|
1385
2449
|
const chars = Array.from(s);
|
|
1386
2450
|
let a = 0, b = chars.length;
|
|
1387
|
-
if (left) while (a < b && cs.
|
|
1388
|
-
if (right) while (b > a && cs.
|
|
2451
|
+
if (left) while (a < b && cs.has(chars[a])) a++;
|
|
2452
|
+
if (right) while (b > a && cs.has(chars[b - 1])) b--;
|
|
1389
2453
|
return chars.slice(a, b).join("");
|
|
1390
2454
|
}`;
|
|
1391
2455
|
|
|
@@ -1512,6 +2576,39 @@ export function generateFacade(
|
|
|
1512
2576
|
}
|
|
1513
2577
|
}
|
|
1514
2578
|
|
|
2579
|
+
for (const [name, desc] of needs.anonStructs) {
|
|
2580
|
+
out.push(`function ${P}to_${name}(o) {
|
|
2581
|
+
if (o == null) return __w.__zero_${name}();
|
|
2582
|
+
return __w.__new_${name}(${desc.fields.map((f) => inExpr(f.desc, `(o.${f.name} ?? ${zeroExpr(f.desc)})`)).join(", ")});
|
|
2583
|
+
}
|
|
2584
|
+
function ${P}from_${name}(r) {
|
|
2585
|
+
if (r == null) return null;
|
|
2586
|
+
return {
|
|
2587
|
+
${desc.fields.map((f) => `\t\t"${f.name}": ${outExpr(f.desc, `__w.__get_${name}_${f.name}(r)`)}`).join(",\n")}
|
|
2588
|
+
};
|
|
2589
|
+
}
|
|
2590
|
+
function ${P}back_${name}(r, o) {
|
|
2591
|
+
if (r == null || o == null) return;
|
|
2592
|
+
${desc.fields.map((f) => `\to.${f.name} = ${outExpr(f.desc, `__w.__get_${name}_${f.name}(r)`)};`).join("\n")}
|
|
2593
|
+
}`);
|
|
2594
|
+
}
|
|
2595
|
+
|
|
2596
|
+
for (const [key, desc] of needs.boxes) {
|
|
2597
|
+
out.push(`function ${P}boxin_${key}(b) {
|
|
2598
|
+
if (b == null) return null;
|
|
2599
|
+
const v = b.value !== undefined ? b.value : ${zeroExpr(desc.base)};
|
|
2600
|
+
return __w.__box_new_${key}(${inExpr(desc.base, "v")});
|
|
2601
|
+
}
|
|
2602
|
+
function ${P}boxout_${key}(r) {
|
|
2603
|
+
if (r == null) return null;
|
|
2604
|
+
return { "value": ${outExpr(desc.base, `__w.__box_get_${key}(r)`)} };
|
|
2605
|
+
}
|
|
2606
|
+
function ${P}boxback_${key}(r, b) {
|
|
2607
|
+
if (r == null || b == null) return;
|
|
2608
|
+
b.value = ${outExpr(desc.base, `__w.__box_get_${key}(r)`)};
|
|
2609
|
+
}`);
|
|
2610
|
+
}
|
|
2611
|
+
|
|
1515
2612
|
for (const [key, desc] of needs.slices) {
|
|
1516
2613
|
out.push(`function ${P}slin_${key}(arr) {
|
|
1517
2614
|
if (arr == null) return __w.__slice_nil_${key}();
|
|
@@ -1530,8 +2627,34 @@ function ${P}slout_${key}(s) {
|
|
|
1530
2627
|
function ${P}slview_${key}(s) {
|
|
1531
2628
|
if (s == null || __w.__slice_is_nil_${key}(s)) return null;
|
|
1532
2629
|
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")}));
|
|
2630
|
+
} `);
|
|
2631
|
+
}
|
|
2632
|
+
|
|
2633
|
+
for (const [key, desc] of needs.maps) {
|
|
2634
|
+
out.push(`function ${P}mapin_${key}(m) {
|
|
2635
|
+
if (m == null) return null;
|
|
2636
|
+
const entries = m instanceof Map ? m.entries() : Object.entries(m);
|
|
2637
|
+
const size = m instanceof Map ? m.size : Object.keys(m).length;
|
|
2638
|
+
const r = __w.__map_new_${key}(size);
|
|
2639
|
+
for (const [k, v] of entries) {
|
|
2640
|
+
__w.__map_set_${key}(r, ${desc.keyDesc.k === "bool" ? `(k === "true" || k === true ? 1 : 0)` : inExpr(desc.keyDesc, "k")}, ${inExpr(desc.valDesc, "v")});
|
|
2641
|
+
}
|
|
2642
|
+
return r;
|
|
2643
|
+
}
|
|
2644
|
+
function ${P}mapout_${key}(m) {
|
|
2645
|
+
if (m == null) return null;
|
|
2646
|
+
const ks = ${P}slout_${desc.keySliceDesc.key}(__w.__map_keys_${key}(m));
|
|
2647
|
+
const vs = ${P}slout_${desc.valSliceDesc.key}(__w.__map_values_${key}(m));
|
|
2648
|
+
const out = new Map();
|
|
2649
|
+
if (ks) {
|
|
2650
|
+
for (let i = 0; i < ks.length; i++) {
|
|
2651
|
+
out.set(ks[i], vs[i]);
|
|
2652
|
+
}
|
|
2653
|
+
}
|
|
2654
|
+
return out;
|
|
1533
2655
|
}`);
|
|
1534
2656
|
}
|
|
2657
|
+
|
|
1535
2658
|
for (const [key, desc] of needs.arrays) {
|
|
1536
2659
|
out.push(`function ${P}arrzero_${key}(n) {
|
|
1537
2660
|
const out = new Array(n);
|
|
@@ -1555,7 +2678,7 @@ function ${P}arrout_${key}(a) {
|
|
|
1555
2678
|
function ${P}arrview_${key}(a) {
|
|
1556
2679
|
if (a == null) return null;
|
|
1557
2680
|
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
|
-
}`);
|
|
2681
|
+
} `);
|
|
1559
2682
|
}
|
|
1560
2683
|
|
|
1561
2684
|
for (const [key, desc] of needs.funcs) {
|
|
@@ -1594,6 +2717,59 @@ ${indent(body, "\t")}
|
|
|
1594
2717
|
|
|
1595
2718
|
for (const [key, desc] of needs.ifaces) genIfaceFacade(out, key, desc);
|
|
1596
2719
|
|
|
2720
|
+
if (needs.error) {
|
|
2721
|
+
out.push(`class ${P}Error extends Error {
|
|
2722
|
+
constructor(msg, ref) {
|
|
2723
|
+
super(msg);
|
|
2724
|
+
this.name = "GoError";
|
|
2725
|
+
this._msg = msg;
|
|
2726
|
+
this.__ref = ref;
|
|
2727
|
+
}
|
|
2728
|
+
Error() {
|
|
2729
|
+
return this.message;
|
|
2730
|
+
}
|
|
2731
|
+
}
|
|
2732
|
+
const ${P}err_w2j = new WeakMap();
|
|
2733
|
+
function ${P}errout(r) {
|
|
2734
|
+
if (r == null) return null;
|
|
2735
|
+
let e = ${P}err_w2j.get(r);
|
|
2736
|
+
if (!e) {
|
|
2737
|
+
const msg = __w.__error_msg ? (__w.__error_msg(r) ?? "") : "";
|
|
2738
|
+
e = new ${P}Error(msg, r);
|
|
2739
|
+
${P}err_w2j.set(r, e);
|
|
2740
|
+
}
|
|
2741
|
+
return e;
|
|
2742
|
+
}
|
|
2743
|
+
function ${P}errin(e) {
|
|
2744
|
+
if (e == null) return null;
|
|
2745
|
+
if (e.__ref === undefined) throw new TypeError("GoFront: expected a WASM-owned error");
|
|
2746
|
+
if (!__w.__is_error(e.__ref)) throw new TypeError("GoFront: value does not implement error");
|
|
2747
|
+
return e.__ref;
|
|
2748
|
+
}`);
|
|
2749
|
+
}
|
|
2750
|
+
|
|
2751
|
+
if (needs.time) {
|
|
2752
|
+
out.push(`function ${P}timeout(r) {
|
|
2753
|
+
if (r == null) return null;
|
|
2754
|
+
const ms = __w.__time_unix_milli ? __w.__time_unix_milli(r) : 0;
|
|
2755
|
+
const off = __w.__time_offset ? __w.__time_offset(r) : 0;
|
|
2756
|
+
return { _d: new Date(ms), _offset: off };
|
|
2757
|
+
}
|
|
2758
|
+
function ${P}timeout_ptr(r) {
|
|
2759
|
+
return ${P}timeout(r);
|
|
2760
|
+
}
|
|
2761
|
+
function ${P}timein(v) {
|
|
2762
|
+
if (v == null) return null;
|
|
2763
|
+
const d = v._d ? v._d : (v instanceof Date ? v : new Date(v));
|
|
2764
|
+
const ms = d.getTime();
|
|
2765
|
+
const off = typeof v?._offset === "number" ? v._offset : -d.getTimezoneOffset() * 60;
|
|
2766
|
+
return __w.__time_make ? __w.__time_make(ms, off) : null;
|
|
2767
|
+
}
|
|
2768
|
+
function ${P}timein_ptr(v) {
|
|
2769
|
+
return ${P}timein(v);
|
|
2770
|
+
}`);
|
|
2771
|
+
}
|
|
2772
|
+
|
|
1597
2773
|
if (needs.shared) {
|
|
1598
2774
|
// Zero-copy TypedArray views over the exported memory. Memory never
|
|
1599
2775
|
// grows after the start function (allocation is startup-only), but the
|
|
@@ -1615,7 +2791,15 @@ function ${P}shin(v, Ctor) {
|
|
|
1615
2791
|
}`);
|
|
1616
2792
|
}
|
|
1617
2793
|
|
|
1618
|
-
for (const c of meta.consts)
|
|
2794
|
+
for (const c of meta.consts) {
|
|
2795
|
+
if (c.dynamic) {
|
|
2796
|
+
out.push(
|
|
2797
|
+
`const ${c.name} = ${outExpr(c.desc, `__w.__const_${c.name}()`)};`,
|
|
2798
|
+
);
|
|
2799
|
+
} else {
|
|
2800
|
+
out.push(`const ${c.name} = ${c.value};`);
|
|
2801
|
+
}
|
|
2802
|
+
}
|
|
1619
2803
|
for (const v of meta.vars ?? [])
|
|
1620
2804
|
out.push(
|
|
1621
2805
|
`const ${v.name} = ${P}shout(__w.__var_${v.name}(), ${v.desc.ctor});`,
|