gofront 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -13
- package/package.json +8 -37
- package/src/backend/js/expressions.js +1341 -148
- package/src/backend/js/fmt-runtime.js +573 -0
- package/src/backend/js/index.js +375 -57
- package/src/backend/js/runtime.js +1460 -133
- package/src/backend/js/statements.js +900 -86
- package/src/backend/js/stdlib/bits.js +87 -14
- package/src/backend/js/stdlib/builder.js +41 -6
- package/src/backend/js/stdlib/bytes.js +253 -7
- package/src/backend/js/stdlib/errors.js +2 -1
- package/src/backend/js/stdlib/fmt.js +349 -10
- package/src/backend/js/stdlib/index.js +8 -3
- package/src/backend/js/stdlib/json.js +247 -52
- package/src/backend/js/stdlib/maps.js +0 -8
- package/src/backend/js/stdlib/math.js +17 -1
- package/src/backend/js/stdlib/simd.js +10 -0
- package/src/backend/js/stdlib/slices.js +36 -6
- package/src/backend/js/stdlib/strconv.js +10 -5
- package/src/backend/js/stdlib/strings.js +83 -4
- package/src/backend/js/stdlib/testing.js +7 -0
- package/src/backend/js/stdlib/time.js +79 -55
- package/src/backend/js/stdlib/unicode.js +1 -1
- package/src/backend/js/stdlib/utf8.js +1 -1
- package/src/backend/js/templ.js +38 -76
- package/src/backend/js/type-info.js +272 -0
- package/src/backend/shared/fmt-types.js +188 -0
- package/src/backend/wasm/boundary.js +436 -80
- package/src/backend/wasm/emit-builtins.js +303 -13
- package/src/backend/wasm/emit-exprs.js +1397 -203
- package/src/backend/wasm/emit-maps.js +61 -13
- package/src/backend/wasm/emit-stmts.js +1359 -164
- package/src/backend/wasm/emit.js +512 -77
- package/src/backend/wasm/encode.js +43 -13
- package/src/backend/wasm/globals.js +452 -0
- package/src/backend/wasm/index.js +194 -2489
- package/src/backend/wasm/method-expr.js +381 -0
- package/src/backend/wasm/module-emitter.js +2240 -0
- package/src/backend/wasm/monomorph.js +62 -4
- package/src/backend/wasm/prescan.js +397 -0
- package/src/backend/wasm/stdlib/bytes.js +1804 -127
- package/src/backend/wasm/stdlib/core.js +1574 -199
- package/src/backend/wasm/stdlib/index.js +0 -5
- package/src/backend/wasm/stdlib/io.js +1567 -150
- package/src/backend/wasm/stdlib/packages.js +2 -0
- package/src/backend/wasm/stdlib/simd.js +293 -28
- package/src/backend/wasm/stdlib/time.js +1041 -12
- package/src/backend/wasm/types.js +200 -15
- package/src/backend/wasm/variadic.js +93 -0
- package/src/backend/wasm/wat.js +47 -20
- package/src/{cli-core.js → cli/cli-core.js} +236 -10
- package/src/{test-runner.js → cli/test-runner.js} +332 -39
- package/src/{vendor.js → cli/vendor.js} +3 -4
- package/src/compiler.js +107 -269
- package/src/index.js +15 -169
- package/src/{lexer.js → lexer/lexer.js} +102 -55
- package/src/{tokens.js → lexer/tokens.js} +1 -0
- package/src/link/sourcemap.js +97 -0
- package/src/link/stdlib.js +515 -0
- package/src/link/wasm.js +294 -0
- package/src/lower/bounds.js +394 -0
- package/src/lower/boxing.js +1 -1
- package/src/lower/captures.js +176 -32
- package/src/lower/index.js +26 -18
- package/src/lower/json-codecs.js +1398 -0
- package/src/lower/ownership.js +22 -1
- package/src/lower/range.js +86 -1
- package/src/lower/reachability.js +132 -0
- package/src/parser/declarations.js +58 -13
- package/src/parser/expressions.js +29 -7
- package/src/parser/index.js +2 -1
- package/src/parser/statements.js +37 -22
- package/src/{templ-parser.js → parser/templ.js} +138 -15
- package/src/parser/types.js +69 -9
- package/src/resolver.js +22 -1
- package/src/typechecker/assignability.js +176 -17
- package/src/typechecker/const-fold.js +356 -0
- package/src/typechecker/constants.js +256 -0
- package/src/typechecker/expressions.js +931 -122
- package/src/typechecker/index.js +459 -67
- package/src/typechecker/init-order.js +330 -0
- package/src/typechecker/resolve.js +451 -56
- package/src/typechecker/statements.js +90 -9
- package/src/typechecker/stdlib/core.js +333 -55
- package/src/typechecker/stdlib/excluded.js +898 -0
- package/src/typechecker/stdlib/extended.js +322 -26
- package/src/typechecker/{stdlib.js → stdlib/index.js} +5 -5
- package/src/typechecker/stdlib/simd.js +65 -15
- package/src/typechecker/termination.js +1 -1
- package/src/typechecker/types.js +149 -25
- package/stdlib/bytes/iter.go +192 -0
- package/stdlib/bytes/search.go +240 -0
- package/stdlib/bytes/split.go +218 -0
- package/stdlib/cmp/cmp.go +65 -0
- package/stdlib/encoding/json/errors.go +31 -0
- package/stdlib/encoding/json/indent.go +71 -0
- package/stdlib/encoding/json/number.go +12 -0
- package/stdlib/encoding/json/stream.go +173 -0
- package/stdlib/errors/overlay.go +12 -0
- package/stdlib/hash/hash.go +53 -0
- package/stdlib/internal/jsonrt/dec.go +738 -0
- package/stdlib/internal/jsonrt/enc.go +366 -0
- package/stdlib/internal/jsonrt/indent.go +135 -0
- package/stdlib/internal/jsonrt/number.go +27 -0
- package/stdlib/internal/jsonrt/reader.go +73 -0
- package/stdlib/internal/jsonrt/scanner.go +649 -0
- package/stdlib/io/io.go +346 -0
- package/stdlib/iter/iter.go +18 -0
- package/stdlib/maps/iter.go +65 -0
- package/stdlib/math/extra.go +270 -0
- package/stdlib/slices/extra.go +73 -0
- package/stdlib/slices/iter.go +113 -0
- package/stdlib/sort/sort.go +561 -0
- package/stdlib/strconv/quote.go +341 -0
- package/stdlib/strings/extra.go +35 -0
- package/stdlib/strings/iter.go +122 -0
- package/stdlib/sync/extra.go +105 -0
- package/stdlib/unicode/unicode.go +112 -0
- package/stdlib/unicode/utf8/bytes.go +239 -0
- package/src/backend/wasm/const-fold.js +0 -205
- package/src/backend/wasm/emit-stdlib.js +0 -13
- /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
- /package/src/{colors.js → cli/colors.js} +0 -0
- /package/src/{dev-server.js → cli/dev-server.js} +0 -0
- /package/src/{minifier.js → cli/minifier.js} +0 -0
- /package/src/{project-config.js → cli/project-config.js} +0 -0
- /package/src/{pwa.js → cli/pwa.js} +0 -0
- /package/src/{templ-lexer.js → lexer/templ-lexer.js} +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/backend/js/stdlib/json.js
|
|
2
2
|
// CodeGen for Go encoding/json package with compile-time static encoders and decoders.
|
|
3
3
|
|
|
4
|
+
import { someChild } from "../../../lower/walk.js";
|
|
4
5
|
import { typedArrayConstructorForElem } from "../../../typechecker/types.js";
|
|
5
6
|
|
|
6
7
|
function safeIdent(name) {
|
|
@@ -33,6 +34,18 @@ function isRawMessageType(t) {
|
|
|
33
34
|
);
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
const NUMBER_NAMES = new Set(["json.Number", "encoding/json.Number"]);
|
|
38
|
+
|
|
39
|
+
function isNumberType(t) {
|
|
40
|
+
if (!t) return false;
|
|
41
|
+
const name = t.name ?? "";
|
|
42
|
+
if (NUMBER_NAMES.has(name)) return true;
|
|
43
|
+
if (name.startsWith("Number$$") || name.endsWith(".Number")) return true;
|
|
44
|
+
return (
|
|
45
|
+
t.kind === "named" && name === "Number" && unwrapNamed(t)?.name === "string"
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
36
49
|
function isByteSliceType(t) {
|
|
37
50
|
const base = unwrapNamed(t);
|
|
38
51
|
return (
|
|
@@ -77,6 +90,27 @@ const QUOTABLE_KINDS = new Set([
|
|
|
77
90
|
// reflect's names for GoFront's basic aliases, used in decode error messages.
|
|
78
91
|
const BASIC_DISPLAY = { any: "interface {}", byte: "uint8", rune: "int32" };
|
|
79
92
|
|
|
93
|
+
// Unmarshal targets whose argument errors depend on the static type alone:
|
|
94
|
+
// pointers can be nil, interfaces are judged by their dynamic value.
|
|
95
|
+
// Only statically concrete non-pointer kinds are rejected up front; anything
|
|
96
|
+
// else (type parameters, unknown kinds) is judged by its dynamic value.
|
|
97
|
+
const TARGET_KIND = { pointer: "ptr" };
|
|
98
|
+
const VALUE_TARGET_KINDS = new Set([
|
|
99
|
+
"slice",
|
|
100
|
+
"array",
|
|
101
|
+
"map",
|
|
102
|
+
"struct",
|
|
103
|
+
"func",
|
|
104
|
+
"chan",
|
|
105
|
+
]);
|
|
106
|
+
|
|
107
|
+
const COMPOSITE_KINDS = new Set(["slice", "array", "map"]);
|
|
108
|
+
const COMPOSITE_DECODERS = {
|
|
109
|
+
slice: "_getSliceDecoder",
|
|
110
|
+
array: "_getArrayDecoder",
|
|
111
|
+
map: "_getMapDecoder",
|
|
112
|
+
};
|
|
113
|
+
|
|
80
114
|
// Basic kinds decoded by the typed scalar decoders.
|
|
81
115
|
const SCALAR_KINDS = QUOTABLE_KINDS;
|
|
82
116
|
|
|
@@ -94,11 +128,13 @@ export const jsonMethods = {
|
|
|
94
128
|
this._markJsonPtrRecvMarshalers();
|
|
95
129
|
const args = a();
|
|
96
130
|
if (fn === "Marshal") {
|
|
131
|
+
this._markJsonAnyEncoders();
|
|
97
132
|
const argType = expr.args[0]?._type;
|
|
98
133
|
const enc = this._getJsonEncoder(argType);
|
|
99
134
|
return `__json_marshal(${enc}, ${args[0]})`;
|
|
100
135
|
}
|
|
101
136
|
if (fn === "MarshalIndent") {
|
|
137
|
+
this._markJsonAnyEncoders();
|
|
102
138
|
const argType = expr.args[0]?._type;
|
|
103
139
|
const enc = this._getJsonEncoder(argType);
|
|
104
140
|
return `__json_marshal_indent(${enc}, ${args[0]}, ${args[1]}, ${args[2]})`;
|
|
@@ -106,7 +142,7 @@ export const jsonMethods = {
|
|
|
106
142
|
if (fn === "Unmarshal") {
|
|
107
143
|
const targetType = expr.args[1]?._type;
|
|
108
144
|
const dec = this._getJsonDecoder(targetType);
|
|
109
|
-
return `__json_unmarshal(${dec}, ${args[0]}, ${args[1]})`;
|
|
145
|
+
return `__json_unmarshal(${dec}, ${args[0]}, ${args[1]}, ${this._jsonTargetArgs(targetType)})`;
|
|
110
146
|
}
|
|
111
147
|
if (fn === "Valid") {
|
|
112
148
|
return `__json_valid(${args[0]})`;
|
|
@@ -114,18 +150,27 @@ export const jsonMethods = {
|
|
|
114
150
|
return undefined;
|
|
115
151
|
},
|
|
116
152
|
|
|
153
|
+
// Name and static kind of an Unmarshal/Decode target, for the argument
|
|
154
|
+
// errors Go reports from the type alone ("ptr", "val" or none for interfaces).
|
|
155
|
+
_jsonTargetArgs(targetType) {
|
|
156
|
+
const base = unwrapNamed(targetType);
|
|
157
|
+
const key = base?.kind === "basic" ? base.name : base?.kind;
|
|
158
|
+
if (!targetType || key === "nil") return '"", ""';
|
|
159
|
+
const isValue =
|
|
160
|
+
base?.kind === "basic" ? key !== "any" : VALUE_TARGET_KINDS.has(key);
|
|
161
|
+
const kind = TARGET_KIND[key] ?? (isValue ? "val" : "");
|
|
162
|
+
return `${JSON.stringify(this._jsonDisplayType(targetType))}, ${JSON.stringify(kind)}`;
|
|
163
|
+
},
|
|
164
|
+
|
|
117
165
|
_getJsonEncoder(type, addr = false) {
|
|
118
166
|
if (!type) return "__json_enc_any";
|
|
167
|
+
if (isNumberType(type)) return "__json_enc_number";
|
|
119
168
|
if (isRawMessageType(type)) {
|
|
120
169
|
const fnName = "__json_enc_RawMessage";
|
|
121
170
|
if (!this._jsonCodecs.has(fnName)) {
|
|
122
171
|
this._jsonCodecs.set(
|
|
123
172
|
fnName,
|
|
124
|
-
declareCodec(
|
|
125
|
-
fnName,
|
|
126
|
-
"v",
|
|
127
|
-
` if (v === null || v === undefined) return "null";\n var s = (typeof v === "string" ? v : new TextDecoder().decode(v)).trim();\n return s.length > 0 ? s : "null";`,
|
|
128
|
-
),
|
|
173
|
+
declareCodec(fnName, "v", ` return __json_enc_raw(v);`),
|
|
129
174
|
);
|
|
130
175
|
}
|
|
131
176
|
return fnName;
|
|
@@ -170,7 +215,7 @@ export const jsonMethods = {
|
|
|
170
215
|
return fnName;
|
|
171
216
|
}
|
|
172
217
|
|
|
173
|
-
if (base?.kind === "slice") {
|
|
218
|
+
if (base?.kind === "slice" || base?.kind === "array") {
|
|
174
219
|
const elemEnc = this._getJsonEncoder(base.elem, true);
|
|
175
220
|
const fnName = `__json_enc_slice_${safeIdent(typeCodecName(base.elem, "elem").split(".").pop())}`;
|
|
176
221
|
if (!this._jsonCodecs.has(fnName)) {
|
|
@@ -222,8 +267,95 @@ export const jsonMethods = {
|
|
|
222
267
|
}
|
|
223
268
|
},
|
|
224
269
|
|
|
270
|
+
// Structs reach the dynamic `any` encoder as bare instances, so each package
|
|
271
|
+
// struct without a MarshalJSON method gets a hidden hook that runs its static
|
|
272
|
+
// encoder (tags, omitempty, promoted fields) instead of listing its properties.
|
|
273
|
+
// A non-generic struct declared in this package that has no MarshalJSON.
|
|
274
|
+
_isPlainPackageStruct(name, type) {
|
|
275
|
+
const isStruct =
|
|
276
|
+
type?.kind === "named" && type.underlying?.kind === "struct";
|
|
277
|
+
const generic = type?.typeArgs?.length || type?._genericOf;
|
|
278
|
+
if (!isStruct || generic || /[.$[]/.test(name) || type.name !== name)
|
|
279
|
+
return false;
|
|
280
|
+
return !this._jsonMarshalerInfo(type) && this.structNames?.has(name);
|
|
281
|
+
},
|
|
282
|
+
|
|
283
|
+
_markJsonAnyEncoders() {
|
|
284
|
+
this._jsonAnyMarked = true;
|
|
285
|
+
},
|
|
286
|
+
|
|
287
|
+
_isJsonEncoderName(recvName) {
|
|
288
|
+
return (
|
|
289
|
+
recvName === "json.Encoder" ||
|
|
290
|
+
recvName === "encoding/json.Encoder" ||
|
|
291
|
+
recvName?.startsWith("Encoder$$")
|
|
292
|
+
);
|
|
293
|
+
},
|
|
294
|
+
|
|
295
|
+
// True when `node` contains a call that marks the any-encoders when
|
|
296
|
+
// generated (`json.Marshal`, `json.MarshalIndent`, `Encoder.Encode`).
|
|
297
|
+
// Decided before any body is generated, so a function-local struct can get
|
|
298
|
+
// its hook right after its class even when the call comes later.
|
|
299
|
+
_usesJsonAnyMarshal(node) {
|
|
300
|
+
if (Array.isArray(node))
|
|
301
|
+
return node.some((n) => this._usesJsonAnyMarshal(n));
|
|
302
|
+
if (!node || typeof node !== "object") return false;
|
|
303
|
+
if (node.kind === "CallExpr" && node.func?.kind === "SelectorExpr") {
|
|
304
|
+
const { expr, field } = node.func;
|
|
305
|
+
if (
|
|
306
|
+
expr?.kind === "Ident" &&
|
|
307
|
+
expr.name === "json" &&
|
|
308
|
+
(field === "Marshal" || field === "MarshalIndent")
|
|
309
|
+
)
|
|
310
|
+
return true;
|
|
311
|
+
if (
|
|
312
|
+
field === "Encode" &&
|
|
313
|
+
this._isJsonEncoderName(this._resolveRecvName(expr?._type))
|
|
314
|
+
)
|
|
315
|
+
return true;
|
|
316
|
+
}
|
|
317
|
+
return someChild(node, (n) => this._usesJsonAnyMarshal(n));
|
|
318
|
+
},
|
|
319
|
+
|
|
320
|
+
_jsonAnyHook(cls, enc) {
|
|
321
|
+
return `Object.defineProperty(${cls}.prototype, "__json_enc", { value: function() { return ${enc}(this); }, configurable: true, writable: true });`;
|
|
322
|
+
},
|
|
323
|
+
|
|
324
|
+
// Runs once all function bodies are generated; package-level structs get
|
|
325
|
+
// their hook at top level (function-local ones get it in _noteLocalTypeDecl).
|
|
326
|
+
_emitJsonAnyHooks() {
|
|
327
|
+
if (!this._jsonAnyMarked || this._jsonAnyHooked) return;
|
|
328
|
+
this._jsonAnyHooked = true;
|
|
329
|
+
const local = this._localTypeNames;
|
|
330
|
+
for (const [name, type] of this.checker?.types ?? []) {
|
|
331
|
+
if (local.has(name) || !this._isPlainPackageStruct(name, type)) continue;
|
|
332
|
+
const cls = safeIdent(name);
|
|
333
|
+
this._jsonCodecs.set(
|
|
334
|
+
`__json_anyenc_${cls}`,
|
|
335
|
+
this._jsonAnyHook(cls, this._getJsonEncoder(type, false)),
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
},
|
|
339
|
+
|
|
340
|
+
// A function-local struct class is only in scope inside its function, so
|
|
341
|
+
// its hook goes right after the class, and only when the program marshals.
|
|
342
|
+
_noteLocalTypeDecl(stmt) {
|
|
343
|
+
this._localTypeNames.add(stmt.name);
|
|
344
|
+
if (!this._jsonAnyUsed || stmt.type.kind !== "StructType") return;
|
|
345
|
+
const type = this.checker?.types.get(stmt.name);
|
|
346
|
+
if (!this._isPlainPackageStruct(stmt.name, type)) return;
|
|
347
|
+
const enc = this._getJsonEncoder(type, false);
|
|
348
|
+
this.line(this._jsonAnyHook(safeIdent(stmt.name), enc));
|
|
349
|
+
},
|
|
350
|
+
|
|
225
351
|
_jsonTypeName(type) {
|
|
226
352
|
const name = type?.name ?? "value";
|
|
353
|
+
const at = name.indexOf("$$");
|
|
354
|
+
if (at >= 0) {
|
|
355
|
+
const orig = name.slice(0, at);
|
|
356
|
+
const modPath = name.slice(at + 2).replaceAll("$", "/");
|
|
357
|
+
return `${modPath}.${orig}`;
|
|
358
|
+
}
|
|
227
359
|
if (name.includes(".")) return name;
|
|
228
360
|
const pkg = this.checker?.pkgName;
|
|
229
361
|
return pkg ? `${pkg}.${name}` : name;
|
|
@@ -387,7 +519,7 @@ export const jsonMethods = {
|
|
|
387
519
|
case "float32":
|
|
388
520
|
return "__json_float32";
|
|
389
521
|
case "float64":
|
|
390
|
-
return "
|
|
522
|
+
return "__json_float64";
|
|
391
523
|
default:
|
|
392
524
|
return "__json_enc_any";
|
|
393
525
|
}
|
|
@@ -407,11 +539,12 @@ export const jsonMethods = {
|
|
|
407
539
|
const keyLiteral = JSON.stringify(`${JSON.stringify(f.jsonKey)}:`);
|
|
408
540
|
const encodeVal = this._genFieldEncoderExpr(f, addr);
|
|
409
541
|
const isStruct = unwrapNamed(f.type)?.kind === "struct";
|
|
542
|
+
const isMap = unwrapNamed(f.type)?.kind === "map";
|
|
410
543
|
|
|
411
544
|
this._emitFieldEncoderAccess(f, lines, (valName) => {
|
|
412
545
|
const encoded = encodeVal(valName);
|
|
413
546
|
if (f.omitempty) {
|
|
414
|
-
return `if (!__json_is_zero(${valName}, ${isStruct})) parts.push(${keyLiteral} + ${encoded});`;
|
|
547
|
+
return `if (!__json_is_zero(${valName}, ${isStruct}, ${isMap})) parts.push(${keyLiteral} + ${encoded});`;
|
|
415
548
|
}
|
|
416
549
|
return `parts.push(${keyLiteral} + ${encoded});`;
|
|
417
550
|
});
|
|
@@ -451,13 +584,13 @@ export const jsonMethods = {
|
|
|
451
584
|
if (nilChecks.length > 0) {
|
|
452
585
|
lines.push(` if (!(${nilChecks.join(" || ")})) {`);
|
|
453
586
|
lines.push(
|
|
454
|
-
` var val = (
|
|
587
|
+
` var val = (${conds.join(" && ")} ? ${leaf} : v.${f.goName});`,
|
|
455
588
|
);
|
|
456
589
|
lines.push(` if (val !== undefined) { ${onVal("val")} }`);
|
|
457
590
|
lines.push(` }`);
|
|
458
591
|
} else {
|
|
459
592
|
lines.push(
|
|
460
|
-
` { var val = (
|
|
593
|
+
` { var val = (${conds.join(" && ")} ? ${leaf} : v.${f.goName}); if (val !== undefined) { ${onVal("val")} } }`,
|
|
461
594
|
);
|
|
462
595
|
}
|
|
463
596
|
},
|
|
@@ -465,9 +598,9 @@ export const jsonMethods = {
|
|
|
465
598
|
_genFieldEncoderExpr(f, addr = false) {
|
|
466
599
|
const quotedKind = this._jsonQuotedKind(f);
|
|
467
600
|
if (quotedKind) return this._genQuotedFieldEncoderExpr(f, quotedKind);
|
|
601
|
+
if (isNumberType(f.type)) return (v) => `__json_enc_number(${v})`;
|
|
468
602
|
if (isRawMessageType(f.type)) {
|
|
469
|
-
return (v) =>
|
|
470
|
-
`((typeof ${v} === "string" ? ${v} : new TextDecoder().decode(${v})).trim() || "null")`;
|
|
603
|
+
return (v) => `__json_enc_raw(${v})`;
|
|
471
604
|
}
|
|
472
605
|
// Marshalers and pointers go through the shared encoders.
|
|
473
606
|
const special =
|
|
@@ -488,7 +621,7 @@ export const jsonMethods = {
|
|
|
488
621
|
const enc = this._getJsonEncoder(f.type, addr);
|
|
489
622
|
return (v) => `${enc}(${v})`;
|
|
490
623
|
}
|
|
491
|
-
if (base?.kind === "slice") {
|
|
624
|
+
if (base?.kind === "slice" || base?.kind === "array") {
|
|
492
625
|
const enc = this._getJsonEncoder(base.elem, true);
|
|
493
626
|
return (v) => `__json_enc_slice(${v}, ${enc})`;
|
|
494
627
|
}
|
|
@@ -523,8 +656,7 @@ export const jsonMethods = {
|
|
|
523
656
|
case "float32":
|
|
524
657
|
return (v) => `__json_float32(${v})`;
|
|
525
658
|
case "float64":
|
|
526
|
-
return (v) =>
|
|
527
|
-
`(!isFinite(${v}) ? (() => { throw new Error("json: unsupported value: " + ${v}); })() : String(${v}))`;
|
|
659
|
+
return (v) => `__json_float64(${v})`;
|
|
528
660
|
default:
|
|
529
661
|
return (v) => `__json_enc_any(${v})`;
|
|
530
662
|
}
|
|
@@ -536,6 +668,10 @@ export const jsonMethods = {
|
|
|
536
668
|
targetType.kind === "pointer" ? targetType.base : targetType;
|
|
537
669
|
if (!baseType) return "__json_dec_any_target";
|
|
538
670
|
|
|
671
|
+
if (isNumberType(baseType)) {
|
|
672
|
+
return "__json_dec_number";
|
|
673
|
+
}
|
|
674
|
+
|
|
539
675
|
if (this._jsonUnmarshalerInfo(baseType)) {
|
|
540
676
|
return this._getUnmarshalerDecoder(baseType);
|
|
541
677
|
}
|
|
@@ -584,8 +720,8 @@ export const jsonMethods = {
|
|
|
584
720
|
return fnName;
|
|
585
721
|
}
|
|
586
722
|
|
|
587
|
-
|
|
588
|
-
if (
|
|
723
|
+
const composite = COMPOSITE_DECODERS[base?.kind];
|
|
724
|
+
if (composite) return this[composite](base);
|
|
589
725
|
// Top-level scalar targets (`json.Unmarshal(b, &n)`) are type-checked too.
|
|
590
726
|
if (base?.kind === "basic" && SCALAR_KINDS.has(base.name))
|
|
591
727
|
return this._getScalarDecoder(baseType, base);
|
|
@@ -614,6 +750,37 @@ export const jsonMethods = {
|
|
|
614
750
|
return fnName;
|
|
615
751
|
},
|
|
616
752
|
|
|
753
|
+
// Arrays decode into a fresh array of the fixed length: surplus JSON elements
|
|
754
|
+
// are dropped and missing ones are zero. JSON null leaves the array alone.
|
|
755
|
+
_getArrayDecoder(base) {
|
|
756
|
+
const elemTypeName = typeCodecName(base.elem, "elem");
|
|
757
|
+
const fnName = `__json_dec_array${base.size}_${safeIdent(elemTypeName.split(".").pop())}`;
|
|
758
|
+
if (!this._jsonCodecs.has(fnName)) {
|
|
759
|
+
this._jsonCodecs.set(
|
|
760
|
+
fnName,
|
|
761
|
+
declareCodec(
|
|
762
|
+
fnName,
|
|
763
|
+
"parsed, target, box",
|
|
764
|
+
this._arrayDecoderBody(base),
|
|
765
|
+
),
|
|
766
|
+
);
|
|
767
|
+
}
|
|
768
|
+
return fnName;
|
|
769
|
+
},
|
|
770
|
+
|
|
771
|
+
_arrayDecoderBody(base) {
|
|
772
|
+
const elemDec = this._genElemDecoder(base.elem);
|
|
773
|
+
const typedCtor = typedArrayConstructorForElem(base.elem);
|
|
774
|
+
const display = JSON.stringify(this._jsonDisplayType(base));
|
|
775
|
+
const init = `new ${typedCtor ?? "Array"}(${base.size})`;
|
|
776
|
+
// Typed arrays start zeroed; plain arrays need their zero values filled in.
|
|
777
|
+
const zero = this.zeroValueForType(base.elem);
|
|
778
|
+
const fill = typedCtor
|
|
779
|
+
? ""
|
|
780
|
+
: `\n for (var z = parsed.length; z < ${base.size}; z++) out[z] = ${zero};`;
|
|
781
|
+
return ` if (parsed === null) return null;\n if (!Array.isArray(parsed)) return __json_err("cannot unmarshal " + __json_type(parsed) + " into " + __json_ctx(${display}));\n var out = ${init};\n for (var i = 0; i < parsed.length && i < ${base.size}; i++) {\n var item = ${elemDec("parsed[i]", "__json_raw_child(parsed, i)")};\n if (item && item._msg) return item;\n out[i] = item;\n }${fill}\n if (box) box.value = out;\n return null;`;
|
|
782
|
+
},
|
|
783
|
+
|
|
617
784
|
_getMapDecoder(base) {
|
|
618
785
|
const valType = base.value ?? base.elem;
|
|
619
786
|
const valTypeName = typeCodecName(valType, "val");
|
|
@@ -628,7 +795,7 @@ export const jsonMethods = {
|
|
|
628
795
|
declareCodec(
|
|
629
796
|
fnName,
|
|
630
797
|
"parsed, target, box",
|
|
631
|
-
` if (parsed === null) { if (box) box.value = null; return null; }\n if (typeof parsed !== "object" || Array.isArray(parsed)) return __json_err("cannot unmarshal " + __json_type(parsed) + " into " + __json_ctx(${display}));\n var out =
|
|
798
|
+
` if (parsed === null) { if (box) box.value = null; return null; }\n if (typeof parsed !== "object" || Array.isArray(parsed)) return __json_err("cannot unmarshal " + __json_type(parsed) + " into " + __json_ctx(${display}));\n var out = __json_newmap();\n for (var k in parsed) {\n if (Object.prototype.hasOwnProperty.call(parsed, k)) {\n var item = ${valDec("parsed[k]", "__json_raw_child(parsed, k)")};\n if (item && item._msg) return item;\n out[k] = item;\n }\n }\n if (box) box.value = out;\n return null;`,
|
|
632
799
|
),
|
|
633
800
|
);
|
|
634
801
|
}
|
|
@@ -653,14 +820,16 @@ export const jsonMethods = {
|
|
|
653
820
|
|
|
654
821
|
// Go-style display of a type in decode error messages (reflect's Type.String()).
|
|
655
822
|
_jsonDisplayType(t) {
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
823
|
+
const show = (x) => this._jsonDisplayType(x);
|
|
824
|
+
const byKind = {
|
|
825
|
+
pointer: () => `*${show(t.base)}`,
|
|
826
|
+
named: () => this._jsonTypeName(t),
|
|
827
|
+
basic: () => BASIC_DISPLAY[t.name] ?? t.name,
|
|
828
|
+
slice: () => `[]${show(t.elem)}`,
|
|
829
|
+
array: () => `[${t.size}]${show(t.elem)}`,
|
|
830
|
+
map: () => `map[${show(t.key)}]${show(t.value ?? t.elem)}`,
|
|
831
|
+
};
|
|
832
|
+
return byKind[t?.kind]?.() ?? "interface {}";
|
|
664
833
|
},
|
|
665
834
|
|
|
666
835
|
// Decodes one parsed JSON scalar into `kind`; yields the value or an error (`_msg`).
|
|
@@ -683,6 +852,9 @@ export const jsonMethods = {
|
|
|
683
852
|
|
|
684
853
|
_genElemDecoder(elemType) {
|
|
685
854
|
if (!elemType) return (v) => `__json_dec_any(${v})`;
|
|
855
|
+
if (isNumberType(elemType))
|
|
856
|
+
return (v, rawExpr) =>
|
|
857
|
+
`__json_number_decode(${v}, "json.Number", ${rawExpr})`;
|
|
686
858
|
const unmarshaler = this._unmarshalerElemDecoder(elemType);
|
|
687
859
|
if (unmarshaler) return unmarshaler;
|
|
688
860
|
if (isRawMessageType(elemType)) {
|
|
@@ -697,27 +869,39 @@ export const jsonMethods = {
|
|
|
697
869
|
return (v, rawExpr) =>
|
|
698
870
|
this._jsonScalarDecodeExpr(base.name, display, v, rawExpr);
|
|
699
871
|
}
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
if (innerBase?.kind === "struct") {
|
|
703
|
-
const dec = this._getJsonDecoder(elemType.base);
|
|
704
|
-
const structName = elemType.base.name?.split(".").pop() ?? "Object";
|
|
705
|
-
return (v) =>
|
|
706
|
-
`(${v} === null ? null : (() => { var inst = new ${structName}(); var err = ${dec}(${v}, inst); return err || inst; })())`;
|
|
707
|
-
}
|
|
708
|
-
if (innerBase?.kind === "basic") {
|
|
709
|
-
const basicDec = this._genElemDecoder(elemType.base);
|
|
710
|
-
return (v, rawExpr) =>
|
|
711
|
-
`(${v} === null ? null : (() => { var item = ${basicDec(v, rawExpr)}; return item && item._msg ? item : { value: item, __p: true }; })())`;
|
|
712
|
-
}
|
|
713
|
-
}
|
|
872
|
+
const ptrElem = this._pointerElemDecoder(elemType);
|
|
873
|
+
if (ptrElem) return ptrElem;
|
|
714
874
|
if (base?.kind === "struct") {
|
|
715
875
|
const dec = this._getJsonDecoder(elemType);
|
|
716
876
|
const structName = elemType.name?.split(".").pop() ?? "Object";
|
|
717
877
|
return (v) =>
|
|
718
878
|
`(() => { var inst = new ${structName}(); var err = ${dec}(${v}, inst); return err || inst; })()`;
|
|
719
879
|
}
|
|
720
|
-
return (
|
|
880
|
+
return this._compositeElemDecoder(elemType, base);
|
|
881
|
+
},
|
|
882
|
+
|
|
883
|
+
// `*Struct` and `*scalar` elements; null stays nil.
|
|
884
|
+
_pointerElemDecoder(elemType) {
|
|
885
|
+
if (elemType?.kind !== "pointer" || !elemType.base) return null;
|
|
886
|
+
const innerBase = unwrapNamed(elemType.base);
|
|
887
|
+
if (innerBase?.kind === "struct") {
|
|
888
|
+
const dec = this._getJsonDecoder(elemType.base);
|
|
889
|
+
const structName = elemType.base.name?.split(".").pop() ?? "Object";
|
|
890
|
+
return (v) =>
|
|
891
|
+
`(${v} === null ? null : (() => { var inst = new ${structName}(); var err = ${dec}(${v}, inst); return err || inst; })())`;
|
|
892
|
+
}
|
|
893
|
+
if (innerBase?.kind !== "basic") return null;
|
|
894
|
+
const basicDec = this._genElemDecoder(elemType.base);
|
|
895
|
+
return (v, rawExpr) =>
|
|
896
|
+
`(${v} === null ? null : (() => { var item = ${basicDec(v, rawExpr)}; return item && item._msg ? item : { value: item, __p: true }; })())`;
|
|
897
|
+
},
|
|
898
|
+
|
|
899
|
+
// Slices, arrays and maps decode through their own codec into a fresh box.
|
|
900
|
+
_compositeElemDecoder(elemType, base) {
|
|
901
|
+
if (!COMPOSITE_KINDS.has(base?.kind)) return (v) => `__json_dec_any(${v})`;
|
|
902
|
+
const dec = this._getJsonDecoder(elemType);
|
|
903
|
+
return (v) =>
|
|
904
|
+
`(() => { var b = { value: null }; var err = ${dec}(${v}, null, b); return err || b.value; })()`;
|
|
721
905
|
},
|
|
722
906
|
|
|
723
907
|
_buildStructDecoder(structType, fnName) {
|
|
@@ -736,6 +920,11 @@ export const jsonMethods = {
|
|
|
736
920
|
` return __json_err("cannot unmarshal " + __json_type(parsed) + " into " + __json_ctx(${JSON.stringify(this._jsonDisplayType(structType))}));`,
|
|
737
921
|
);
|
|
738
922
|
lines.push(` }`);
|
|
923
|
+
const knownKeys = fields.flatMap((f) => [f.jsonKey, f.goName]);
|
|
924
|
+
lines.push(
|
|
925
|
+
` var disallowErr = __json_check_unknown(parsed, ${JSON.stringify(knownKeys)});`,
|
|
926
|
+
);
|
|
927
|
+
lines.push(` if (disallowErr) return disallowErr;`);
|
|
739
928
|
// Go reports errors as "Go struct field <innermost struct>.<json key path>".
|
|
740
929
|
lines.push(` __json_structs.push(${JSON.stringify(structName)});`);
|
|
741
930
|
lines.push(` try {`);
|
|
@@ -750,6 +939,9 @@ export const jsonMethods = {
|
|
|
750
939
|
lines.push(` try {`);
|
|
751
940
|
lines.push(` var val = parsed[fieldKey];`);
|
|
752
941
|
lines.push(` var raw = __json_raw_child(parsed, fieldKey);`);
|
|
942
|
+
lines.push(
|
|
943
|
+
` __json_current_offset = __json_raw_end(parsed, fieldKey) || 0;`,
|
|
944
|
+
);
|
|
753
945
|
const quotedKind = this._jsonQuotedKind(f);
|
|
754
946
|
if (quotedKind) {
|
|
755
947
|
const valueType = f.type?.kind === "pointer" ? f.type.base : f.type;
|
|
@@ -809,12 +1001,24 @@ export const jsonMethods = {
|
|
|
809
1001
|
}
|
|
810
1002
|
},
|
|
811
1003
|
|
|
1004
|
+
_emitNumberFieldDecoder(f, lines, rawExpr) {
|
|
1005
|
+
lines.push(
|
|
1006
|
+
` var nv = __json_number_decode(val, "json.Number", ${rawExpr}); if (nv && nv._msg) return nv;`,
|
|
1007
|
+
);
|
|
1008
|
+
const val = f.type?.kind === "pointer" ? "{ value: nv }" : "nv";
|
|
1009
|
+
this._emitAssign(f, val, lines);
|
|
1010
|
+
},
|
|
1011
|
+
|
|
812
1012
|
_emitFieldDecoder(f, structName, lines, rawExpr) {
|
|
813
1013
|
const valueType = f.type?.kind === "pointer" ? f.type.base : f.type;
|
|
814
1014
|
if (this._jsonUnmarshalerInfo(valueType)) {
|
|
815
1015
|
this._emitUnmarshalerField(f, lines, rawExpr);
|
|
816
1016
|
return;
|
|
817
1017
|
}
|
|
1018
|
+
if (isNumberType(valueType)) {
|
|
1019
|
+
this._emitNumberFieldDecoder(f, lines, rawExpr);
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
818
1022
|
if (isRawMessageType(f.type)) {
|
|
819
1023
|
lines.push(
|
|
820
1024
|
` var rawBytes = __json_raw_bytes(${rawExpr}); rawBytes.__raw = true;`,
|
|
@@ -877,16 +1081,7 @@ export const jsonMethods = {
|
|
|
877
1081
|
return;
|
|
878
1082
|
}
|
|
879
1083
|
|
|
880
|
-
if (base?.kind
|
|
881
|
-
const dec = this._getJsonDecoder(f.type);
|
|
882
|
-
lines.push(` var box = { value: null };`);
|
|
883
|
-
lines.push(` var err = ${dec}(val, null, box);`);
|
|
884
|
-
lines.push(` if (err) return err;`);
|
|
885
|
-
this._emitAssign(f, "box.value", lines);
|
|
886
|
-
return;
|
|
887
|
-
}
|
|
888
|
-
|
|
889
|
-
if (base?.kind === "map") {
|
|
1084
|
+
if (COMPOSITE_KINDS.has(base?.kind)) {
|
|
890
1085
|
const dec = this._getJsonDecoder(f.type);
|
|
891
1086
|
lines.push(` var box = { value: null };`);
|
|
892
1087
|
lines.push(` var err = ${dec}(val, null, box);`);
|
|
@@ -11,14 +11,6 @@ export const mapsMethods = {
|
|
|
11
11
|
return `__equal(${a1}, ${b1})`;
|
|
12
12
|
}
|
|
13
13
|
switch (fn) {
|
|
14
|
-
case "Keys": {
|
|
15
|
-
const [m] = a();
|
|
16
|
-
return `Object.keys(${m})`;
|
|
17
|
-
}
|
|
18
|
-
case "Values": {
|
|
19
|
-
const [m] = a();
|
|
20
|
-
return `Object.values(${m})`;
|
|
21
|
-
}
|
|
22
14
|
case "Clone": {
|
|
23
15
|
const [m] = a();
|
|
24
16
|
return `({...${m}})`;
|
|
@@ -9,8 +9,10 @@ const MATH1 = {
|
|
|
9
9
|
Sqrt: "sqrt",
|
|
10
10
|
Cbrt: "cbrt",
|
|
11
11
|
Log: "log",
|
|
12
|
+
Log1p: "log1p",
|
|
12
13
|
Log2: "log2",
|
|
13
14
|
Log10: "log10",
|
|
15
|
+
Expm1: "expm1",
|
|
14
16
|
Sin: "sin",
|
|
15
17
|
Cos: "cos",
|
|
16
18
|
Tan: "tan",
|
|
@@ -37,7 +39,7 @@ const MATH_EXTRA = {
|
|
|
37
39
|
Inf: ([x]) => `(${x} >= 0 ? Infinity : -Infinity)`,
|
|
38
40
|
IsNaN: ([x]) => `Number.isNaN(${x})`,
|
|
39
41
|
IsInf: ([x, y]) =>
|
|
40
|
-
`(${y} > 0 ? ${x} === Infinity : ${y} < 0 ? ${x} === -Infinity :
|
|
42
|
+
`(${y} > 0 ? ${x} === Infinity : ${y} < 0 ? ${x} === -Infinity : Math.abs(${x}) === Infinity)`,
|
|
41
43
|
NaN: () => "NaN",
|
|
42
44
|
Exp2: ([x]) => `Math.pow(2, ${x})`,
|
|
43
45
|
Signbit: ([x]) => `(${x} < 0 || Object.is(${x}, -0))`,
|
|
@@ -46,6 +48,20 @@ const MATH_EXTRA = {
|
|
|
46
48
|
Dim: ([x, y]) => `Math.max(${x} - ${y}, 0)`,
|
|
47
49
|
Remainder: ([x, y]) =>
|
|
48
50
|
`((x, y) => { if (isNaN(x) || isNaN(y) || y === 0 || !isFinite(x)) return NaN; if (!isFinite(y)) return x; const q = x / y; const r = Math.round(q); const diff = Math.abs(q - r); const n = (diff === 0.5 && (r % 2 !== 0)) ? (r < q ? r + 1 : r - 1) : r; return x - n * y; })(${x}, ${y})`,
|
|
51
|
+
Frexp: ([x]) =>
|
|
52
|
+
`((f) => { if (f === 0 || !Number.isFinite(f)) return [f, 0]; const buf = new ArrayBuffer(8); const f64 = new Float64Array(buf); const u32 = new Uint32Array(buf); f64[0] = f; let hi = u32[1]; let exp = (hi >>> 20) & 0x7ff; if (exp === 0) { f64[0] = f * Math.pow(2, 54); hi = u32[1]; exp = ((hi >>> 20) & 0x7ff) - 54; } exp -= 1022; u32[1] = (hi & 0x800fffff) | 0x3fe00000; return [f64[0], exp]; })(${x})`,
|
|
53
|
+
Ldexp: ([frac, exp]) =>
|
|
54
|
+
`((frac, exp) => { if (frac === 0 || !Number.isFinite(frac)) return frac; if (exp > 1023) return frac < 0 ? -Infinity : Infinity; if (exp < -1074) return frac < 0 ? -0 : 0; if (exp >= -1022) return frac * Math.pow(2, exp); return frac * Math.pow(2, exp + 54) * Math.pow(2, -54); })(${frac}, ${exp})`,
|
|
55
|
+
Modf: ([f]) =>
|
|
56
|
+
`((x) => { if (Number.isNaN(x)) return [NaN, NaN]; if (!Number.isFinite(x)) return [x, NaN]; const i = Math.trunc(x); const frac = x === i ? (x < 0 || Object.is(x, -0) ? -0 : 0) : x - i; return [i, frac]; })(${f})`,
|
|
57
|
+
FMA: ([x, y, z]) =>
|
|
58
|
+
`((x, y, z) => { if (Number.isNaN(x) || Number.isNaN(y) || Number.isNaN(z)) return NaN; if ((x === 0 && !Number.isFinite(y)) || (y === 0 && !Number.isFinite(x))) return NaN; const p = x * y; if (!Number.isFinite(p) && !Number.isFinite(z) && ((p > 0 && z < 0) || (p < 0 && z > 0))) return NaN; return p + z; })(${x}, ${y}, ${z})`,
|
|
59
|
+
RoundToEven: ([x]) =>
|
|
60
|
+
`((x) => { if (x === 0 || !Number.isFinite(x)) return x; const t = Math.trunc(x); const f = Math.abs(x - t); if (f < 0.5) return Object.is(t, 0) && (x < 0 || Object.is(x, -0)) ? -0 : t; if (f > 0.5) return x > 0 ? t + 1 : t - 1; const res = t % 2 === 0 ? t : (x > 0 ? t + 1 : t - 1); return Object.is(res, 0) && (x < 0 || Object.is(x, -0)) ? -0 : res; })(${x})`,
|
|
61
|
+
Nextafter: ([x, y]) =>
|
|
62
|
+
`((x, y) => { if (Number.isNaN(x) || Number.isNaN(y)) return NaN; if (x === y) return x; if (x === 0) { const s = Number.MIN_VALUE; return y > 0 ? s : -s; } const buf = new ArrayBuffer(8); const f64 = new Float64Array(buf); const i64 = new BigInt64Array(buf); f64[0] = x; let b = i64[0]; if ((x > 0 && y > x) || (x < 0 && y > x)) b = b + (x > 0 ? 1n : -1n); else b = b + (x > 0 ? -1n : 1n); i64[0] = b; return f64[0]; })(${x}, ${y})`,
|
|
63
|
+
Nextafter32: ([x, y]) =>
|
|
64
|
+
`((x, y) => { if (Number.isNaN(x) || Number.isNaN(y)) return NaN; if (x === y) return x; if (x === 0) { const s = 1.401298464324817e-45; return y > 0 ? s : -s; } const buf = new ArrayBuffer(4); const f32 = new Float32Array(buf); const i32 = new Int32Array(buf); f32[0] = x; let b = i32[0]; if ((x > 0 && y > x) || (x < 0 && y > x)) b = b + (x > 0 ? 1 : -1); else b = b + (x > 0 ? -1 : 1); i32[0] = b; return f32[0]; })(${x}, ${y})`,
|
|
49
65
|
Float64bits: ([x]) =>
|
|
50
66
|
`((f) => { const b = new ArrayBuffer(8); new Float64Array(b)[0] = f; return Number(new BigUint64Array(b)[0]); })(${x})`,
|
|
51
67
|
Float64frombits: ([b]) =>
|
|
@@ -13,10 +13,20 @@ export const simdMethods = {
|
|
|
13
13
|
return `__simd_splat(${args[0]})`;
|
|
14
14
|
case "NewF32x4":
|
|
15
15
|
return `__simd_f32x4(${args[0]}, ${args[1]}, ${args[2]}, ${args[3]})`;
|
|
16
|
+
case "Select":
|
|
17
|
+
return `__simd_select(${args[0]}, ${args[1]}, ${args[2]})`;
|
|
18
|
+
case "Shuffle":
|
|
19
|
+
return `__simd_shuffle(${args.join(", ")})`;
|
|
16
20
|
case "LoadF32x4":
|
|
17
21
|
return `__simd_load(${args[0]}, ${args[1]})`;
|
|
18
22
|
case "StoreF32x4":
|
|
19
23
|
return `__simd_store(${args[0]}, ${args[1]}, ${args[2]})`;
|
|
24
|
+
case "LoadF32x4Slice":
|
|
25
|
+
return `__simd_load_slice(${args[0]}, ${args[1]})`;
|
|
26
|
+
case "StoreF32x4Slice":
|
|
27
|
+
return `__simd_store_slice(${args[0]}, ${args[1]}, ${args[2]})`;
|
|
28
|
+
case "Pad4":
|
|
29
|
+
return `(Math.floor((${args[0]} + 3) / 4) * 4)`;
|
|
20
30
|
default:
|
|
21
31
|
return undefined;
|
|
22
32
|
}
|
|
@@ -34,26 +34,26 @@ const SLICES_DISPATCH = {
|
|
|
34
34
|
`((s, f) => { if (!s || s.length === 0) throw new Error("slices.MinFunc: empty list"); return s.reduce((m, x) => f(x, m) < 0 ? x : m); })(${a[0]}, ${a[1]})`,
|
|
35
35
|
Clone: (a) => `((s) => s ? s.slice() : null)(${a[0]})`,
|
|
36
36
|
Compact: (a) =>
|
|
37
|
-
`((s) => { if (!s || s.length === 0) return s; let k = 1; for (let i = 1; i < s.length; i++) { if (s[i] !== s[k - 1]) s[k++] = s[i]; } return ArrayBuffer.isView(s) ? s.subarray(0, k) : (s.length = k, s); })(${a[0]})`,
|
|
37
|
+
`((s) => { if (!s || s.length === 0) return s; let k = 1; for (let i = 1; i < s.length; i++) { if (s[i] !== s[k - 1]) s[k++] = s[i]; } return s[__sv] !== undefined ? __slice(s, 0, k) : ArrayBuffer.isView(s) ? s.subarray(0, k) : (s.length = k, s); })(${a[0]})`,
|
|
38
38
|
CompactFunc: (a) =>
|
|
39
|
-
`((s, f) => { if (!s || s.length === 0) return s; let k = 1; for (let i = 1; i < s.length; i++) { if (!f(s[i], s[k - 1])) s[k++] = s[i]; } return ArrayBuffer.isView(s) ? s.subarray(0, k) : (s.length = k, s); })(${a[0]}, ${a[1]})`,
|
|
39
|
+
`((s, f) => { if (!s || s.length === 0) return s; let k = 1; for (let i = 1; i < s.length; i++) { if (!f(s[i], s[k - 1])) s[k++] = s[i]; } return s[__sv] !== undefined ? __slice(s, 0, k) : ArrayBuffer.isView(s) ? s.subarray(0, k) : (s.length = k, s); })(${a[0]}, ${a[1]})`,
|
|
40
40
|
Concat: (a) =>
|
|
41
41
|
a.length === 0
|
|
42
42
|
? "null"
|
|
43
43
|
: `((...slices) => { const nonNull = slices.filter(Boolean); if (nonNull.length === 0) return slices[0] === null ? null : []; if (ArrayBuffer.isView(nonNull[0])) { const totalLen = nonNull.reduce((sum, s) => sum + s.length, 0); const out = new nonNull[0].constructor(totalLen); let off = 0; for (const s of nonNull) { out.set(s, off); off += s.length; } return out; } return [].concat(...slices.map(s => s ? (ArrayBuffer.isView(s) ? Array.from(s) : s) : [])); })(${a.map((x) => `(${x})`).join(", ")})`,
|
|
44
44
|
Delete: (a) =>
|
|
45
|
-
`((s, i, j) => { if (!s && i === 0 && j === 0) return s; s = s || []; if (i < 0 || j < i || j > s.length) throw new Error("runtime error: slice bounds out of range"); if (ArrayBuffer.isView(s)) { s.copyWithin(i, j); return s.subarray(0, s.length - (j - i)); } s.splice(i, j - i); return s; })(${a[0]}, ${a[1]}, ${a[2]})`,
|
|
45
|
+
`((s, i, j) => { if (!s && i === 0 && j === 0) return s; s = s || []; if (i < 0 || j < i || j > s.length) throw new Error("runtime error: slice bounds out of range"); if (ArrayBuffer.isView(s)) { s.copyWithin(i, j); return s.subarray(0, s.length - (j - i)); } if (s[__sv] !== undefined) { s.copyWithin(i, j); return __slice(s, 0, s.length - (j - i)); } s.splice(i, j - i); return s; })(${a[0]}, ${a[1]}, ${a[2]})`,
|
|
46
46
|
DeleteFunc: (a) =>
|
|
47
|
-
`((s, f) => { if (!s || s.length === 0) return s; let k = 0; for (let i = 0; i < s.length; i++) { if (!f(s[i])) s[k++] = s[i]; } return ArrayBuffer.isView(s) ? s.subarray(0, k) : (s.length = k, s); })(${a[0]}, ${a[1]})`,
|
|
47
|
+
`((s, f) => { if (!s || s.length === 0) return s; let k = 0; for (let i = 0; i < s.length; i++) { if (!f(s[i])) s[k++] = s[i]; } return s[__sv] !== undefined ? __slice(s, 0, k) : ArrayBuffer.isView(s) ? s.subarray(0, k) : (s.length = k, s); })(${a[0]}, ${a[1]})`,
|
|
48
48
|
Insert: (a, isTyped) => {
|
|
49
49
|
const items = a.slice(2);
|
|
50
50
|
const nilInit = isTyped ? "new Uint8Array(0)" : "[]";
|
|
51
|
-
return `((s, i, ...v) => { if (!s && i === 0) s = ${nilInit}; if (!s || i < 0 || i > s.length) throw new Error("runtime error: slice bounds out of range"); if (ArrayBuffer.isView(s)) { const res = new s.constructor(s.length + v.length); res.set(s.subarray(0, i), 0); res.set(v, i); res.set(s.subarray(i), i + v.length); return res; } s.splice(i, 0, ...v); return s; })(${a[0]}, ${a[1]}${items.length ? `, ${items.join(", ")}` : ""})`;
|
|
51
|
+
return `((s, i, ...v) => { if (!s && i === 0) s = ${nilInit}; if (!s || i < 0 || i > s.length) throw new Error("runtime error: slice bounds out of range"); if (ArrayBuffer.isView(s)) { const res = new s.constructor(s.length + v.length); res.set(s.subarray(0, i), 0); res.set(v, i); res.set(s.subarray(i), i + v.length); return res; } if (s[__sv] !== undefined) s = Array.from(s); s.splice(i, 0, ...v); return s; })(${a[0]}, ${a[1]}${items.length ? `, ${items.join(", ")}` : ""})`;
|
|
52
52
|
},
|
|
53
53
|
Replace: (a, isTyped) => {
|
|
54
54
|
const items = a.slice(3);
|
|
55
55
|
const nilInit = isTyped ? "new Uint8Array(0)" : "[]";
|
|
56
|
-
return `((s, i, j, ...v) => { if (!s && i === 0 && j === 0) s = ${nilInit}; if (!s || i < 0 || j < i || j > s.length) throw new Error("runtime error: slice bounds out of range"); if (ArrayBuffer.isView(s)) { const res = new s.constructor(s.length - (j - i) + v.length); res.set(s.subarray(0, i), 0); res.set(v, i); res.set(s.subarray(j), i + v.length); return res; } s.splice(i, j - i, ...v); return s; })(${a[0]}, ${a[1]}, ${a[2]}${items.length ? `, ${items.join(", ")}` : ""})`;
|
|
56
|
+
return `((s, i, j, ...v) => { if (!s && i === 0 && j === 0) s = ${nilInit}; if (!s || i < 0 || j < i || j > s.length) throw new Error("runtime error: slice bounds out of range"); if (ArrayBuffer.isView(s)) { const res = new s.constructor(s.length - (j - i) + v.length); res.set(s.subarray(0, i), 0); res.set(v, i); res.set(s.subarray(j), i + v.length); return res; } if (s[__sv] !== undefined) s = Array.from(s); s.splice(i, j - i, ...v); return s; })(${a[0]}, ${a[1]}, ${a[2]}${items.length ? `, ${items.join(", ")}` : ""})`;
|
|
57
57
|
},
|
|
58
58
|
Grow: (a, isTyped) => {
|
|
59
59
|
const nilInit = isTyped ? "new Uint8Array(0)" : "[]";
|
|
@@ -62,6 +62,15 @@ const SLICES_DISPATCH = {
|
|
|
62
62
|
Clip: (a) => `((s) => s ? s.slice() : null)(${a[0]})`,
|
|
63
63
|
};
|
|
64
64
|
|
|
65
|
+
const VIEW_AWARE = new Set([
|
|
66
|
+
"Compact",
|
|
67
|
+
"CompactFunc",
|
|
68
|
+
"Delete",
|
|
69
|
+
"DeleteFunc",
|
|
70
|
+
"Insert",
|
|
71
|
+
"Replace",
|
|
72
|
+
]);
|
|
73
|
+
|
|
65
74
|
/** @type {ThisType<CodeGen>} */
|
|
66
75
|
export const slicesMethods = {
|
|
67
76
|
_genSlices(fn, a, expr) {
|
|
@@ -70,8 +79,29 @@ export const slicesMethods = {
|
|
|
70
79
|
const [a1, b1] = a();
|
|
71
80
|
return `__equal(${a1}, ${b1})`;
|
|
72
81
|
}
|
|
82
|
+
// Struct and array elements are values: compare them field by field
|
|
83
|
+
// (`.includes` and `.indexOf` compare object identity).
|
|
84
|
+
if (
|
|
85
|
+
(fn === "Contains" || fn === "Index") &&
|
|
86
|
+
this._sliceElemIsAggregate(expr)
|
|
87
|
+
) {
|
|
88
|
+
this.useHelper("equal");
|
|
89
|
+
const [s1, v1] = a();
|
|
90
|
+
const found = fn === "Contains" ? "true" : "i";
|
|
91
|
+
const missing = fn === "Contains" ? "false" : "-1";
|
|
92
|
+
return `((s, v) => { s = s || []; for (let i = 0; i < s.length; i++) { if (__equal(s[i], v)) return ${found}; } return ${missing}; })(${s1}, ${v1})`;
|
|
93
|
+
}
|
|
73
94
|
const isTyped = isTypedArraySlice(expr?.args?.[0]?._type);
|
|
74
95
|
const gen = SLICES_DISPATCH[fn];
|
|
96
|
+
// These shrink or grow the slice in place; a slice view (`__sv`) has a
|
|
97
|
+
// fixed length, so they reslice it or copy it to a plain array.
|
|
98
|
+
if (VIEW_AWARE.has(fn)) this.useHelper("slice");
|
|
75
99
|
return gen ? gen(a(), isTyped) : undefined;
|
|
76
100
|
},
|
|
101
|
+
|
|
102
|
+
_sliceElemIsAggregate(expr) {
|
|
103
|
+
let t = expr?.args?.[0]?._type;
|
|
104
|
+
while (t?.kind === "named") t = t.underlying;
|
|
105
|
+
return t?.kind === "slice" && Boolean(this._aggregateBase(t.elem));
|
|
106
|
+
},
|
|
77
107
|
};
|