json-as 1.4.0 → 1.6.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/CHANGELOG.md +54 -29
- package/README.md +126 -33
- package/assembly/custom/chars.ts +39 -78
- package/assembly/deserialize/error.ts +21 -0
- package/assembly/deserialize/index/arbitrary.ts +50 -11
- package/assembly/deserialize/index/array.ts +2 -1
- package/assembly/deserialize/index/bool.ts +1 -1
- package/assembly/deserialize/index/float.ts +2 -4
- package/assembly/deserialize/index/integer.ts +2 -4
- package/assembly/deserialize/index/object.ts +9 -1
- package/assembly/deserialize/index/string.ts +39 -7
- package/assembly/deserialize/index/unsigned.ts +2 -4
- package/assembly/deserialize/index.ts +0 -1
- package/assembly/deserialize/naive/array/arbitrary.ts +3 -2
- package/assembly/deserialize/naive/array/array.ts +1 -0
- package/assembly/deserialize/naive/array/bool.ts +21 -11
- package/assembly/deserialize/naive/array/box.ts +13 -33
- package/assembly/deserialize/naive/array/float.ts +23 -13
- package/assembly/deserialize/naive/array/integer.ts +34 -22
- package/assembly/deserialize/naive/array/map.ts +1 -1
- package/assembly/deserialize/naive/array/object.ts +2 -1
- package/assembly/deserialize/naive/array/string.ts +29 -16
- package/assembly/deserialize/naive/array/struct.ts +19 -1
- package/assembly/deserialize/naive/bool.ts +18 -10
- package/assembly/deserialize/naive/date.ts +1 -2
- package/assembly/deserialize/naive/float.ts +3 -9
- package/assembly/deserialize/naive/integer.ts +1 -2
- package/assembly/deserialize/naive/map.ts +342 -43
- package/assembly/deserialize/naive/object.ts +250 -55
- package/assembly/deserialize/naive/raw.ts +10 -2
- package/assembly/deserialize/naive/set.ts +2 -5
- package/assembly/deserialize/naive/staticarray.ts +4 -4
- package/assembly/deserialize/naive/string.ts +46 -60
- package/assembly/deserialize/naive/unsigned.ts +1 -2
- package/assembly/deserialize/parseMode.ts +216 -0
- package/assembly/deserialize/simd/array/integer.ts +8 -10
- package/assembly/deserialize/simd/float.ts +38 -34
- package/assembly/deserialize/simd/integer.ts +2 -7
- package/assembly/deserialize/simd/string.ts +84 -39
- package/assembly/deserialize/string-validation.ts +34 -0
- package/assembly/deserialize/swar/array/arbitrary.ts +1 -2
- package/assembly/deserialize/swar/array/array.ts +64 -15
- package/assembly/deserialize/swar/array/bool.ts +15 -5
- package/assembly/deserialize/swar/array/box.ts +1 -2
- package/assembly/deserialize/swar/array/float.ts +399 -30
- package/assembly/deserialize/swar/array/generic.ts +13 -4
- package/assembly/deserialize/swar/array/integer.ts +29 -78
- package/assembly/deserialize/swar/array/map.ts +1 -2
- package/assembly/deserialize/swar/array/object.ts +12 -4
- package/assembly/deserialize/swar/array/raw.ts +1 -2
- package/assembly/deserialize/swar/array/shared.ts +7 -14
- package/assembly/deserialize/swar/array/string.ts +37 -13
- package/assembly/deserialize/swar/array/struct.ts +109 -21
- package/assembly/deserialize/swar/array.ts +1 -3
- package/assembly/deserialize/swar/float.ts +23 -21
- package/assembly/deserialize/swar/integer.ts +2 -7
- package/assembly/deserialize/swar/string.ts +81 -124
- package/assembly/deserialize/swar/typedarray.ts +4 -4
- package/assembly/index.d.ts +35 -24
- package/assembly/index.ts +1775 -281
- package/assembly/serialize/index/arbitrary.ts +70 -4
- package/assembly/serialize/index/bool.ts +1 -1
- package/assembly/serialize/index/float.ts +1 -5
- package/assembly/serialize/index/integer.ts +1 -1
- package/assembly/serialize/index/jsonarray.ts +51 -0
- package/assembly/serialize/index/object.ts +42 -3
- package/assembly/serialize/index/string.ts +1 -2
- package/assembly/serialize/index/typedarray.ts +1 -2
- package/assembly/serialize/index.ts +1 -0
- package/assembly/serialize/naive/array.ts +87 -34
- package/assembly/serialize/naive/bool.ts +0 -1
- package/assembly/serialize/naive/float.ts +15 -29
- package/assembly/serialize/naive/integer.ts +1 -5
- package/assembly/serialize/naive/map.ts +37 -0
- package/assembly/serialize/naive/raw.ts +1 -2
- package/assembly/serialize/naive/set.ts +0 -5
- package/assembly/serialize/naive/staticarray.ts +0 -6
- package/assembly/serialize/naive/string.ts +1 -10
- package/assembly/serialize/naive/typedarray.ts +0 -7
- package/assembly/serialize/simd/string.ts +1 -3
- package/assembly/serialize/swar/string.ts +1 -2
- package/assembly/util/atoi-fast.ts +4 -14
- package/assembly/util/bytes.ts +1 -2
- package/assembly/util/eisel-lemire.ts +179 -0
- package/assembly/util/idofd.ts +1 -2
- package/assembly/util/isSpace.ts +1 -2
- package/assembly/util/itoa-fast.ts +6 -9
- package/assembly/util/nextPowerOf2.ts +1 -2
- package/assembly/util/parsefloat-fast.ts +3 -5
- package/assembly/util/prettyWhitespaceSimd.ts +69 -0
- package/assembly/util/ptrToStr.ts +1 -2
- package/assembly/util/scanValueEndSimd.ts +83 -22
- package/assembly/util/scanValueEndSwar.ts +67 -25
- package/assembly/util/scientific.ts +5 -8
- package/assembly/util/snp.ts +1 -2
- package/assembly/util/swar-int.ts +5 -10
- package/assembly/util/swar.ts +2 -4
- package/assembly/util/validateJson.ts +338 -0
- package/lib/as-bs.ts +51 -51
- package/package.json +25 -8
- package/transform/lib/index.d.ts +6 -0
- package/transform/lib/index.js +874 -171
- package/transform/lib/types.d.ts +3 -1
- package/transform/lib/types.js +6 -0
- package/assembly/deserialize/index/struct.ts +0 -1
- package/assembly/deserialize/naive/struct.ts +0 -21
- package/assembly/util/dragonbox-cache.ts +0 -445
- package/assembly/util/dragonbox.ts +0 -652
package/transform/lib/index.js
CHANGED
|
@@ -50,6 +50,7 @@ function envFlagDefaultTrue(value) {
|
|
|
50
50
|
}
|
|
51
51
|
const USE_FAST_PATH = envFlagDefaultTrue(process.env["JSON_USE_FAST_PATH"]);
|
|
52
52
|
const THROW_FAST_PATH = process.env["JSON_FAST_PATH_THROW"]?.trim() === "1";
|
|
53
|
+
const USE_DEFAULT_OBJECT_SPECIALIZATION = true;
|
|
53
54
|
function parseJSONCacheConfig(value) {
|
|
54
55
|
if (!value)
|
|
55
56
|
return { enabled: false, bytes: 0 };
|
|
@@ -115,6 +116,20 @@ function getSerializeCall(type, realName) {
|
|
|
115
116
|
if (type == "ArrayBuffer") {
|
|
116
117
|
return `JSON.__serialize<ArrayBuffer>(load<ArrayBuffer>(ptr, offsetof<this>(${JSON.stringify(realName)})));\n`;
|
|
117
118
|
}
|
|
119
|
+
if (type == "bool" ||
|
|
120
|
+
type == "boolean" ||
|
|
121
|
+
type == "i8" ||
|
|
122
|
+
type == "i16" ||
|
|
123
|
+
type == "i32" ||
|
|
124
|
+
type == "i64" ||
|
|
125
|
+
type == "isize" ||
|
|
126
|
+
type == "u8" ||
|
|
127
|
+
type == "u16" ||
|
|
128
|
+
type == "u32" ||
|
|
129
|
+
type == "u64" ||
|
|
130
|
+
type == "usize") {
|
|
131
|
+
return `JSON.__serializePrimitiveUnsafe<${type}>(load<${type}>(ptr, offsetof<this>(${JSON.stringify(realName)})));\n`;
|
|
132
|
+
}
|
|
118
133
|
return needsReferenceLoad(type)
|
|
119
134
|
? `JSON.__serialize<${type}>(changetype<${type}>(load<usize>(ptr, offsetof<this>(${JSON.stringify(realName)}))));\n`
|
|
120
135
|
: `JSON.__serialize<${type}>(load<${type}>(ptr, offsetof<this>(${JSON.stringify(realName)})));\n`;
|
|
@@ -287,6 +302,174 @@ export class JSONTransform extends Visitor {
|
|
|
287
302
|
}
|
|
288
303
|
return type;
|
|
289
304
|
}
|
|
305
|
+
getDefaultSchema(owner, type) {
|
|
306
|
+
const name = stripNull(type).trim();
|
|
307
|
+
const matches = (schema) => schema.name == name ||
|
|
308
|
+
schema.name.endsWith("." + name) ||
|
|
309
|
+
name.endsWith("." + schema.name);
|
|
310
|
+
if (matches(owner))
|
|
311
|
+
return owner;
|
|
312
|
+
for (const dep of owner.deps) {
|
|
313
|
+
if (dep && matches(dep))
|
|
314
|
+
return dep;
|
|
315
|
+
}
|
|
316
|
+
if (owner.parent && matches(owner.parent))
|
|
317
|
+
return owner.parent;
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
getDefaultElementType(type) {
|
|
321
|
+
const value = stripNull(type).trim();
|
|
322
|
+
if (value.startsWith("Array<") && value.endsWith(">"))
|
|
323
|
+
return value.slice(6, -1).trim();
|
|
324
|
+
if (value.startsWith("StaticArray<") && value.endsWith(">"))
|
|
325
|
+
return value.slice(12, -1).trim();
|
|
326
|
+
return null;
|
|
327
|
+
}
|
|
328
|
+
getDefaultExpressionJSON(expression, type, owner, visiting) {
|
|
329
|
+
if (expression.kind == NodeKind.Parenthesized) {
|
|
330
|
+
return this.getDefaultExpressionJSON(expression.expression, type, owner, visiting);
|
|
331
|
+
}
|
|
332
|
+
if (expression.kind == NodeKind.Assertion) {
|
|
333
|
+
return this.getDefaultExpressionJSON(expression.expression, type, owner, visiting);
|
|
334
|
+
}
|
|
335
|
+
if (expression.kind == NodeKind.Null)
|
|
336
|
+
return "null";
|
|
337
|
+
if (expression.kind == NodeKind.True)
|
|
338
|
+
return "true";
|
|
339
|
+
if (expression.kind == NodeKind.False)
|
|
340
|
+
return "false";
|
|
341
|
+
if (expression.kind == NodeKind.Literal) {
|
|
342
|
+
const literal = expression;
|
|
343
|
+
if (literal.literalKind == 2)
|
|
344
|
+
return JSON.stringify(literal.value);
|
|
345
|
+
if (literal.literalKind == 1) {
|
|
346
|
+
const value = toString(expression).trim();
|
|
347
|
+
return /^\d+$/.test(value) ? value : null;
|
|
348
|
+
}
|
|
349
|
+
if (literal.literalKind == 5) {
|
|
350
|
+
const elementType = this.getDefaultElementType(type);
|
|
351
|
+
if (!elementType)
|
|
352
|
+
return null;
|
|
353
|
+
const values = [];
|
|
354
|
+
for (const element of literal
|
|
355
|
+
.elementExpressions) {
|
|
356
|
+
const value = this.getDefaultExpressionJSON(element, elementType, owner, visiting);
|
|
357
|
+
if (value == null)
|
|
358
|
+
return null;
|
|
359
|
+
values.push(value);
|
|
360
|
+
}
|
|
361
|
+
return "[" + values.join(",") + "]";
|
|
362
|
+
}
|
|
363
|
+
return null;
|
|
364
|
+
}
|
|
365
|
+
if (expression.kind == NodeKind.UnaryPrefix) {
|
|
366
|
+
const value = toString(expression).trim();
|
|
367
|
+
return /^-\d+$/.test(value) ? value : null;
|
|
368
|
+
}
|
|
369
|
+
if (expression.kind == NodeKind.New) {
|
|
370
|
+
const value = expression;
|
|
371
|
+
if (value.args.length != 0)
|
|
372
|
+
return null;
|
|
373
|
+
const constructed = toString(value.typeName).trim();
|
|
374
|
+
if (constructed == "Array" ||
|
|
375
|
+
constructed.startsWith("Array<") ||
|
|
376
|
+
constructed == "StaticArray" ||
|
|
377
|
+
constructed.startsWith("StaticArray<"))
|
|
378
|
+
return "[]";
|
|
379
|
+
const nested = this.getDefaultSchema(owner, type) ||
|
|
380
|
+
this.getDefaultSchema(owner, constructed);
|
|
381
|
+
return nested ? this.getDefaultObjectJSON(nested, visiting) : null;
|
|
382
|
+
}
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
385
|
+
getImplicitDefaultJSON(member, owner, visiting) {
|
|
386
|
+
if (member.node.type.isNullable)
|
|
387
|
+
return "null";
|
|
388
|
+
const type = stripNull(member.type).trim();
|
|
389
|
+
if (isBoolean(type))
|
|
390
|
+
return "false";
|
|
391
|
+
if (isPrimitive(type) ||
|
|
392
|
+
isEnum(type, this.sources.get(owner.node.range.source), this.parser))
|
|
393
|
+
return "0";
|
|
394
|
+
if (isString(type))
|
|
395
|
+
return '""';
|
|
396
|
+
if (this.getDefaultElementType(type))
|
|
397
|
+
return "[]";
|
|
398
|
+
const nested = this.getDefaultSchema(owner, type);
|
|
399
|
+
return nested ? this.getDefaultObjectJSON(nested, visiting) : null;
|
|
400
|
+
}
|
|
401
|
+
getDefaultObjectJSON(schema, visiting = new Set()) {
|
|
402
|
+
if (visiting.has(schema) ||
|
|
403
|
+
schema.custom ||
|
|
404
|
+
!schema.static ||
|
|
405
|
+
schema.node.isGeneric ||
|
|
406
|
+
schema.members.some((member) => member.flags.size != 0 || member.custom || member.generic))
|
|
407
|
+
return null;
|
|
408
|
+
visiting.add(schema);
|
|
409
|
+
const fields = [];
|
|
410
|
+
for (const member of schema.members) {
|
|
411
|
+
const value = member.node.initializer
|
|
412
|
+
? this.getDefaultExpressionJSON(member.node.initializer, member.type, schema, visiting)
|
|
413
|
+
: this.getImplicitDefaultJSON(member, schema, visiting);
|
|
414
|
+
if (value == null) {
|
|
415
|
+
visiting.delete(schema);
|
|
416
|
+
return null;
|
|
417
|
+
}
|
|
418
|
+
fields.push(JSON.stringify(member.alias || member.name) + ":" + value);
|
|
419
|
+
}
|
|
420
|
+
visiting.delete(schema);
|
|
421
|
+
return "{" + fields.join(",") + "}";
|
|
422
|
+
}
|
|
423
|
+
getDefaultMatcherSource(defaultJSON) {
|
|
424
|
+
const literal = JSON.stringify(defaultJSON);
|
|
425
|
+
if (defaultJSON.length > 256)
|
|
426
|
+
return (`@inline __DESERIALIZE_DEFAULT(srcStart: usize, srcEnd: usize, knownMatch: bool = false): this {\n` +
|
|
427
|
+
` if (!knownMatch && !JSON.Util.matchesLiteral(srcStart, srcEnd, ${literal})) return changetype<this>(0);\n` +
|
|
428
|
+
` return changetype<this>(__new(offsetof<this>(), idof<this>())).__INITIALIZE();\n` +
|
|
429
|
+
`}`);
|
|
430
|
+
const bytes = [];
|
|
431
|
+
for (let i = 0; i < defaultJSON.length; i++) {
|
|
432
|
+
const code = defaultJSON.charCodeAt(i);
|
|
433
|
+
bytes.push(code & 0xff, code >>> 8);
|
|
434
|
+
}
|
|
435
|
+
const mismatches = [];
|
|
436
|
+
let offset = 0;
|
|
437
|
+
for (; offset + 16 <= bytes.length; offset += 16) {
|
|
438
|
+
const lanes = bytes
|
|
439
|
+
.slice(offset, offset + 16)
|
|
440
|
+
.map((value) => (value < 128 ? value : value - 256))
|
|
441
|
+
.join(", ");
|
|
442
|
+
mismatches.push(`v128.xor(v128.load(srcStart, ${offset}), v128(${lanes}))`);
|
|
443
|
+
}
|
|
444
|
+
const checks = [];
|
|
445
|
+
if (mismatches.length != 0) {
|
|
446
|
+
checks.push(` let mismatch = ${mismatches[0]};`);
|
|
447
|
+
for (let i = 1; i < mismatches.length; i++)
|
|
448
|
+
checks.push(` mismatch = v128.or(mismatch, ${mismatches[i]});`);
|
|
449
|
+
checks.push(" if (v128.any_true(mismatch)) return false;");
|
|
450
|
+
}
|
|
451
|
+
const scalar = (start, size) => {
|
|
452
|
+
let value = 0n;
|
|
453
|
+
for (let i = 0; i < size; i++)
|
|
454
|
+
value |= BigInt(bytes[start + i]) << BigInt(i << 3);
|
|
455
|
+
return "0x" + value.toString(16);
|
|
456
|
+
};
|
|
457
|
+
for (; offset + 8 <= bytes.length; offset += 8)
|
|
458
|
+
checks.push(` if (load<u64>(srcStart, ${offset}) != ${scalar(offset, 8)}) return false;`);
|
|
459
|
+
for (; offset + 4 <= bytes.length; offset += 4)
|
|
460
|
+
checks.push(` if (load<u32>(srcStart, ${offset}) != ${scalar(offset, 4)}) return false;`);
|
|
461
|
+
for (; offset + 2 <= bytes.length; offset += 2)
|
|
462
|
+
checks.push(` if (load<u16>(srcStart, ${offset}) != ${scalar(offset, 2)}) return false;`);
|
|
463
|
+
return (`@inline __DESERIALIZE_DEFAULT(srcStart: usize, srcEnd: usize, knownMatch: bool = false): this {\n` +
|
|
464
|
+
` if (!knownMatch) {\n` +
|
|
465
|
+
` if (srcEnd - srcStart != ${bytes.length}) return changetype<this>(0);\n` +
|
|
466
|
+
checks
|
|
467
|
+
.join("\n")
|
|
468
|
+
.replaceAll(" ", " ")
|
|
469
|
+
.replaceAll("return false;", "return changetype<this>(0);") +
|
|
470
|
+
`\n }` +
|
|
471
|
+
`\n return changetype<this>(__new(offsetof<this>(), idof<this>())).__INITIALIZE();\n}`);
|
|
472
|
+
}
|
|
290
473
|
visitClassDeclaration(node) {
|
|
291
474
|
if (!node.decorators?.length)
|
|
292
475
|
return;
|
|
@@ -310,6 +493,7 @@ export class JSONTransform extends Visitor {
|
|
|
310
493
|
}) ??
|
|
311
494
|
false));
|
|
312
495
|
let __hasLazy = false;
|
|
496
|
+
const __lazyValInits = new Map();
|
|
313
497
|
for (let i = node.members.length - 1; i >= 0; i--) {
|
|
314
498
|
const fd = node.members[i];
|
|
315
499
|
if (fd.kind !== NodeKind.FieldDeclaration ||
|
|
@@ -340,7 +524,7 @@ export class JSONTransform extends Visitor {
|
|
|
340
524
|
continue;
|
|
341
525
|
if (hasCustomSerde)
|
|
342
526
|
throwError("Lazy fields (@lazy / JSON.Lazy<T> / @json({ lazy })) are not supported " +
|
|
343
|
-
"on a class with a custom @serializer/@deserializer
|
|
527
|
+
"on a class with a custom @serializer/@deserializer - the custom methods " +
|
|
344
528
|
"bypass the generated (de)serializer, so the deferred slot is never filled. " +
|
|
345
529
|
"Remove the lazy marker or the custom (de)serializer.", fd.range);
|
|
346
530
|
const fname = fd.name.text;
|
|
@@ -356,6 +540,13 @@ export class JSONTransform extends Visitor {
|
|
|
356
540
|
: "null";
|
|
357
541
|
const fdInit = fd.initializer;
|
|
358
542
|
const fieldDefault = fdInit ? toString(fdInit) : null;
|
|
543
|
+
const refDefault = fieldDefault != null
|
|
544
|
+
? fieldDefault
|
|
545
|
+
: !storesScalar && baseT == T && !baseT.startsWith("StaticArray<")
|
|
546
|
+
? baseT == "string" || baseT == "String"
|
|
547
|
+
? '""'
|
|
548
|
+
: `new ${baseT}()`
|
|
549
|
+
: null;
|
|
359
550
|
__hasLazy = true;
|
|
360
551
|
const omitIfDeco = decos?.find((d) => d.name.text === "omitif");
|
|
361
552
|
lazyInner.set("__" + fname + "_lz", {
|
|
@@ -397,8 +588,8 @@ export class JSONTransform extends Visitor {
|
|
|
397
588
|
` this.__${fname}_lz = ((<u64>0xffffffff) << 32) | ${encVal("value")};\n}`,
|
|
398
589
|
]
|
|
399
590
|
: [
|
|
400
|
-
`@alias(${key}) private __${fname}_lz: u64 = ${
|
|
401
|
-
`private __${fname}_val: ${valueType} = ${
|
|
591
|
+
`@alias(${key}) private __${fname}_lz: u64 = ${refDefault != null ? "u64.MAX_VALUE" : "0"};`,
|
|
592
|
+
`private __${fname}_val: ${valueType} = ${refDefault ?? valueDefault};`,
|
|
402
593
|
`get ${fname}(): ${T} {\n` +
|
|
403
594
|
` const __lz = this.__${fname}_lz;\n` +
|
|
404
595
|
` if (__lz != 0 && __lz != u64.MAX_VALUE) {\n` +
|
|
@@ -411,6 +602,9 @@ export class JSONTransform extends Visitor {
|
|
|
411
602
|
` this.__${fname}_lz = u64.MAX_VALUE;\n}`,
|
|
412
603
|
]).map((src) => SimpleParser.parseClassMember(src, node));
|
|
413
604
|
node.members.splice(i, 1, ...lowered);
|
|
605
|
+
if (!packScalar && refDefault != null) {
|
|
606
|
+
__lazyValInits.set(`__${fname}_lz`, ` store<${valueType}>(changetype<usize>(this), ${refDefault}, offsetof<this>(${JSON.stringify(`__${fname}_val`)}));\n`);
|
|
607
|
+
}
|
|
414
608
|
}
|
|
415
609
|
if (__hasLazy) {
|
|
416
610
|
node.members.push(SimpleParser.parseClassMember(`private __src: string = "";`, node), SimpleParser.parseClassMember(`__SET_SRC(s: string): void { this.__src = s; }`, node));
|
|
@@ -569,7 +763,7 @@ export class JSONTransform extends Visitor {
|
|
|
569
763
|
else if (isString(type) || isPrimitive(type)) {
|
|
570
764
|
return types;
|
|
571
765
|
}
|
|
572
|
-
else if (["JSON.Box", "JSON.Obj", "JSON.Value", "JSON.Raw"].includes(type)) {
|
|
766
|
+
else if (["JSON.Box", "JSON.Obj", "JSON.Arr", "JSON.Value", "JSON.Raw"].includes(type)) {
|
|
573
767
|
return types;
|
|
574
768
|
}
|
|
575
769
|
else if (node.isGeneric &&
|
|
@@ -667,7 +861,8 @@ export class JSONTransform extends Visitor {
|
|
|
667
861
|
let SERIALIZE = "__SERIALIZE(ptr: usize): void {\n";
|
|
668
862
|
let INITIALIZE = "@inline __INITIALIZE(): this {\n";
|
|
669
863
|
let DESERIALIZE = "__DESERIALIZE_SLOW<__JSON_T>(srcStart: usize, srcEnd: usize, out: __JSON_T): usize {\n";
|
|
670
|
-
let DESERIALIZE_FAST = "
|
|
864
|
+
let DESERIALIZE_FAST = "__DESERIALIZE_FAST<__JSON_T>(srcStart: usize, srcEnd: usize, out: __JSON_T): usize {\n";
|
|
865
|
+
let fastPrettyTailStart = 0;
|
|
671
866
|
let DESERIALIZE_CUSTOM = "";
|
|
672
867
|
let SERIALIZE_CUSTOM = "";
|
|
673
868
|
if (DEBUG > 0)
|
|
@@ -773,8 +968,7 @@ export class JSONTransform extends Visitor {
|
|
|
773
968
|
if (!deserializer.decorators.some((v) => v.name.text == "inline")) {
|
|
774
969
|
deserializer.decorators.push(Node.createDecorator(Node.createIdentifierExpression("inline", deserializer.range), null, deserializer.range));
|
|
775
970
|
}
|
|
776
|
-
DESERIALIZE_CUSTOM +=
|
|
777
|
-
" @inline __DESERIALIZE_CUSTOM(data: string): this {\n";
|
|
971
|
+
DESERIALIZE_CUSTOM += " __DESERIALIZE_CUSTOM(data: string): this {\n";
|
|
778
972
|
DESERIALIZE_CUSTOM +=
|
|
779
973
|
" return this." + deserializer.name.text + "(data);\n";
|
|
780
974
|
DESERIALIZE_CUSTOM += " }\n";
|
|
@@ -783,6 +977,7 @@ export class JSONTransform extends Visitor {
|
|
|
783
977
|
this.generateEmptyMethods(node);
|
|
784
978
|
return;
|
|
785
979
|
}
|
|
980
|
+
const defaultStringLiterals = new Map();
|
|
786
981
|
for (const member of members) {
|
|
787
982
|
if (!member.type)
|
|
788
983
|
throwError("Fields must be strongly typed", node.range);
|
|
@@ -799,6 +994,18 @@ export class JSONTransform extends Visitor {
|
|
|
799
994
|
mem.value = value;
|
|
800
995
|
mem.node = member;
|
|
801
996
|
mem.byteSize = estimatedSerializedByteSize(mem.type, source, this.parser);
|
|
997
|
+
if (member.initializer?.kind == NodeKind.Literal &&
|
|
998
|
+
member.initializer.literalKind ==
|
|
999
|
+
2 &&
|
|
1000
|
+
isString(stripNull(mem.type))) {
|
|
1001
|
+
const literal = member.initializer;
|
|
1002
|
+
const encoded = JSON.stringify(literal.value);
|
|
1003
|
+
mem.byteSize = encoded.length << 1;
|
|
1004
|
+
defaultStringLiterals.set(mem.name, {
|
|
1005
|
+
expression: toString(member.initializer),
|
|
1006
|
+
encoded,
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
802
1009
|
mem.custom = schema.deps.some((dep) => dep?.name == stripNull(type) && dep.custom);
|
|
803
1010
|
const lzInner = lazyInner.get(name.text);
|
|
804
1011
|
if (lzInner !== undefined) {
|
|
@@ -841,6 +1048,10 @@ export class JSONTransform extends Visitor {
|
|
|
841
1048
|
this.schema.static = false;
|
|
842
1049
|
break;
|
|
843
1050
|
}
|
|
1051
|
+
case "optional": {
|
|
1052
|
+
mem.flags.set(PropertyFlags.Optional, null);
|
|
1053
|
+
break;
|
|
1054
|
+
}
|
|
844
1055
|
case "omitnull": {
|
|
845
1056
|
if (isPrimitive(type)) {
|
|
846
1057
|
throwError("@omitnull cannot be used on primitive types!", member.range);
|
|
@@ -857,11 +1068,11 @@ export class JSONTransform extends Visitor {
|
|
|
857
1068
|
}
|
|
858
1069
|
this.schema.members.push(mem);
|
|
859
1070
|
}
|
|
860
|
-
|
|
861
|
-
this.schema.members = sortMembers(this.schema.members);
|
|
1071
|
+
this.schema.members = sortMembers(this.schema.members);
|
|
862
1072
|
const hasOmitIfMembers = this.schema.members.some((v) => v.flags.has(PropertyFlags.OmitIf));
|
|
863
1073
|
const hasOmitNullMembers = this.schema.members.some((v) => v.flags.has(PropertyFlags.OmitNull));
|
|
864
|
-
const
|
|
1074
|
+
const hasExplicitOptionalMembers = this.schema.members.some((v) => v.flags.has(PropertyFlags.Optional));
|
|
1075
|
+
const hasOptionalMembers = hasOmitIfMembers || hasOmitNullMembers || hasExplicitOptionalMembers;
|
|
865
1076
|
const hasLazyMembers = this.schema.members.some((v) => v.flags.has(PropertyFlags.Lazy));
|
|
866
1077
|
const supportsFastOptionalPath = requestedFastPath && hasOptionalMembers;
|
|
867
1078
|
const hasTypeParams = !!node.typeParameters && node.typeParameters.length > 0;
|
|
@@ -884,8 +1095,24 @@ export class JSONTransform extends Visitor {
|
|
|
884
1095
|
let isRegular = isPure;
|
|
885
1096
|
let isFirst = true;
|
|
886
1097
|
const serValue = (member, realName) => {
|
|
887
|
-
if (!member.flags.has(PropertyFlags.Lazy))
|
|
1098
|
+
if (!member.flags.has(PropertyFlags.Lazy)) {
|
|
1099
|
+
const defaultString = defaultStringLiterals.get(member.name);
|
|
1100
|
+
if (defaultString) {
|
|
1101
|
+
const stores = this.getStores(defaultString.encoded, this.program.options.hasFeature(16)).join("\n");
|
|
1102
|
+
return (`{\n` +
|
|
1103
|
+
` const __str = load<${member.type}>(ptr, offsetof<this>(${JSON.stringify(realName)}));\n` +
|
|
1104
|
+
` if (changetype<usize>(__str) == changetype<usize>(${defaultString.expression})) {\n` +
|
|
1105
|
+
stores
|
|
1106
|
+
.split("\n")
|
|
1107
|
+
.map((line) => ` ${line}\n`)
|
|
1108
|
+
.join("") +
|
|
1109
|
+
` } else {\n` +
|
|
1110
|
+
` JSON.__serialize<${member.type}>(__str);\n` +
|
|
1111
|
+
` }\n` +
|
|
1112
|
+
`}\n`);
|
|
1113
|
+
}
|
|
888
1114
|
return getSerializeCall(member.type, realName);
|
|
1115
|
+
}
|
|
889
1116
|
const T = member.lazyInner;
|
|
890
1117
|
const baseName = realName.slice(0, -3);
|
|
891
1118
|
const baseT = stripNull(T);
|
|
@@ -942,12 +1169,10 @@ export class JSONTransform extends Visitor {
|
|
|
942
1169
|
const aliasName = JSON.stringify(member.alias || member.name);
|
|
943
1170
|
const realName = member.name;
|
|
944
1171
|
if (member.value) {
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
INITIALIZE += ` store<${member.type}>(changetype<usize>(this), ${member.value}, offsetof<this>(${JSON.stringify(member.name)}));\n`;
|
|
950
|
-
}
|
|
1172
|
+
INITIALIZE += ` store<${member.type}>(changetype<usize>(this), ${member.value}, offsetof<this>(${JSON.stringify(member.name)}));\n`;
|
|
1173
|
+
const __valInit = __lazyValInits.get(member.name);
|
|
1174
|
+
if (__valInit)
|
|
1175
|
+
INITIALIZE += __valInit;
|
|
951
1176
|
}
|
|
952
1177
|
else if (member.generic) {
|
|
953
1178
|
INITIALIZE += ` if (isManaged<nonnull<${member.type}>>() || isReference<nonnull<${member.type}>>()) {\n`;
|
|
@@ -974,6 +1199,9 @@ export class JSONTransform extends Visitor {
|
|
|
974
1199
|
INITIALIZE += ` store<${member.type}>(changetype<usize>(this), "", offsetof<this>(${JSON.stringify(member.name)}));\n`;
|
|
975
1200
|
}
|
|
976
1201
|
}
|
|
1202
|
+
else {
|
|
1203
|
+
INITIALIZE += ` store<${member.type}>(changetype<usize>(this), null, offsetof<this>(${JSON.stringify(member.name)}));\n`;
|
|
1204
|
+
}
|
|
977
1205
|
const SIMD_ENABLED = this.program.options.hasFeature(16);
|
|
978
1206
|
if (!isRegular &&
|
|
979
1207
|
!member.flags.has(PropertyFlags.OmitIf) &&
|
|
@@ -1116,7 +1344,10 @@ export class JSONTransform extends Visitor {
|
|
|
1116
1344
|
type.startsWith("JSON.Box<") ||
|
|
1117
1345
|
isEnum(type, this.sources.get(this.schema.node.range.source), this.parser))
|
|
1118
1346
|
sortedMembers.number.push(member);
|
|
1119
|
-
else if (isArray(type)
|
|
1347
|
+
else if (isArray(type) ||
|
|
1348
|
+
type == "JSON.Arr" ||
|
|
1349
|
+
type == "Arr" ||
|
|
1350
|
+
needsReferenceLoad(type))
|
|
1120
1351
|
sortedMembers.array.push(member);
|
|
1121
1352
|
else
|
|
1122
1353
|
sortedMembers.object.push(member);
|
|
@@ -1168,6 +1399,8 @@ export class JSONTransform extends Visitor {
|
|
|
1168
1399
|
const FLOAT_TYPES = ["f32", "f64"];
|
|
1169
1400
|
const INTEGER_TYPES = [...UNSIGNED_INTEGER_TYPES, ...SIGNED_INTEGER_TYPES];
|
|
1170
1401
|
const STRING_FIELD_DESERIALIZER = "__deserializeStringField";
|
|
1402
|
+
const STRING_FIELD_TRUSTED_DESERIALIZER = "__deserializeStringFieldTrusted";
|
|
1403
|
+
const getCustomDeserializeExpression = (type, valueStart, valueEnd) => `changetype<nonnull<${stripNull(type)}>>(0).__DESERIALIZE_CUSTOM(JSON.Util.ptrToStr(${valueStart}, ${valueEnd}))`;
|
|
1171
1404
|
const getArrayValueType = (type) => {
|
|
1172
1405
|
if (!type.startsWith("Array<") && !type.startsWith("StaticArray<"))
|
|
1173
1406
|
return null;
|
|
@@ -1204,7 +1437,32 @@ export class JSONTransform extends Visitor {
|
|
|
1204
1437
|
out.push(` ${srcPtr} = ${valuePtr} + 8;`);
|
|
1205
1438
|
out.push(" } else {");
|
|
1206
1439
|
}
|
|
1207
|
-
|
|
1440
|
+
const declaredDefaultString = defaultStringLiterals.get(member.name);
|
|
1441
|
+
const defaultString = declaredDefaultString?.encoded == '""'
|
|
1442
|
+
? undefined
|
|
1443
|
+
: declaredDefaultString;
|
|
1444
|
+
const stringFieldDeserializer = fastPath && keyOffset && !member.node.type.isNullable
|
|
1445
|
+
? STRING_FIELD_TRUSTED_DESERIALIZER
|
|
1446
|
+
: STRING_FIELD_DESERIALIZER;
|
|
1447
|
+
const stringValuePtr = stringFieldDeserializer == STRING_FIELD_TRUSTED_DESERIALIZER
|
|
1448
|
+
? `${valuePtr} + 2`
|
|
1449
|
+
: valuePtr;
|
|
1450
|
+
const stringFieldCall = stringFieldDeserializer == STRING_FIELD_TRUSTED_DESERIALIZER
|
|
1451
|
+
? stringFieldDeserializer
|
|
1452
|
+
: `${stringFieldDeserializer}<${member.type}>`;
|
|
1453
|
+
if (defaultString) {
|
|
1454
|
+
const encodedBytes = defaultString.encoded.length << 1;
|
|
1455
|
+
const comparisons = getComparisions(defaultString.encoded, valuePtr, "==").join(" && ");
|
|
1456
|
+
out.push(` if (${valuePtr} + ${encodedBytes} <= srcEnd && ${comparisons}) {`);
|
|
1457
|
+
out.push(` store<${member.type}>(${outPtr}, ${defaultString.expression}, ${fieldOffset});`);
|
|
1458
|
+
out.push(` ${srcPtr} = ${valuePtr} + ${encodedBytes};`);
|
|
1459
|
+
out.push(" } else {");
|
|
1460
|
+
out.push(` ${srcPtr} = ${stringFieldCall}(${stringValuePtr}, srcEnd, ${outPtr}, ${fieldOffset});`);
|
|
1461
|
+
out.push(" }");
|
|
1462
|
+
}
|
|
1463
|
+
else {
|
|
1464
|
+
out.push(` ${srcPtr} = ${stringFieldCall}(${stringValuePtr}, srcEnd, ${outPtr}, ${fieldOffset});`);
|
|
1465
|
+
}
|
|
1208
1466
|
if (member.node.type.isNullable) {
|
|
1209
1467
|
out.push(" }");
|
|
1210
1468
|
}
|
|
@@ -1275,37 +1533,11 @@ export class JSONTransform extends Visitor {
|
|
|
1275
1533
|
}
|
|
1276
1534
|
else if (resolvedType == "JSON.Raw") {
|
|
1277
1535
|
out.push("{");
|
|
1278
|
-
out.push(` const valueStart = ${
|
|
1279
|
-
out.push(
|
|
1280
|
-
out.push("
|
|
1281
|
-
out.push(`
|
|
1282
|
-
out.push(`
|
|
1283
|
-
out.push(" if (inString) {");
|
|
1284
|
-
out.push(` if (code == 0x22 && load<u16>(${srcPtr} - 2) != 0x5c) inString = false;`);
|
|
1285
|
-
out.push(` ${srcPtr} += 2;`);
|
|
1286
|
-
out.push(" continue;");
|
|
1287
|
-
out.push(" }");
|
|
1288
|
-
out.push(" if (code == 0x22) {");
|
|
1289
|
-
out.push(" inString = true;");
|
|
1290
|
-
out.push(` ${srcPtr} += 2;`);
|
|
1291
|
-
out.push(" continue;");
|
|
1292
|
-
out.push(" }");
|
|
1293
|
-
out.push(" if (code == 0x7b || code == 0x5b) {");
|
|
1294
|
-
out.push(" depth++;");
|
|
1295
|
-
out.push(` ${srcPtr} += 2;`);
|
|
1296
|
-
out.push(" continue;");
|
|
1297
|
-
out.push(" }");
|
|
1298
|
-
out.push(" if (code == 0x7d || code == 0x5d) {");
|
|
1299
|
-
out.push(" if (depth == 0) break;");
|
|
1300
|
-
out.push(" depth--;");
|
|
1301
|
-
out.push(` ${srcPtr} += 2;`);
|
|
1302
|
-
out.push(" continue;");
|
|
1303
|
-
out.push(" }");
|
|
1304
|
-
out.push(" if (code == 0x2c && depth == 0) break;");
|
|
1305
|
-
out.push(` ${srcPtr} += 2;`);
|
|
1306
|
-
out.push(" }");
|
|
1307
|
-
out.push(` if (inString || depth != 0 || ${srcPtr} <= valueStart) break;`);
|
|
1308
|
-
out.push(` store<${member.type}>(${outPtr}, JSON.Raw.from(JSON.Util.ptrToStr(valueStart, ${srcPtr})), ${fieldOffset});`);
|
|
1536
|
+
out.push(` const valueStart = ${valuePtr};`);
|
|
1537
|
+
out.push(` const valueEnd = JSON.Util.scanValueEnd<JSON.Raw>(valueStart, srcEnd);`);
|
|
1538
|
+
out.push(" if (!valueEnd) break;");
|
|
1539
|
+
out.push(` store<${member.type}>(${outPtr}, JSON.Raw.from(JSON.Util.ptrToStr(valueStart, valueEnd)), ${fieldOffset});`);
|
|
1540
|
+
out.push(` ${srcPtr} = valueEnd;`);
|
|
1309
1541
|
out.push("}");
|
|
1310
1542
|
}
|
|
1311
1543
|
else if (isBoolean(resolvedType)) {
|
|
@@ -1324,6 +1556,24 @@ export class JSONTransform extends Visitor {
|
|
|
1324
1556
|
else if (FLOAT_TYPES.includes(resolvedType)) {
|
|
1325
1557
|
out.push(`${srcPtr} = __deserializeFloatField<${resolvedType}>(${valuePtr}, srcEnd, ${outPtr}, ${fieldOffset});`);
|
|
1326
1558
|
}
|
|
1559
|
+
else if (resolvedSchema?.custom) {
|
|
1560
|
+
out.push("{");
|
|
1561
|
+
if (member.node.type.isNullable) {
|
|
1562
|
+
out.push(` if (load<u64>(${valuePtr}) == 30399761348886638) {`);
|
|
1563
|
+
out.push(` store<${member.type}>(${outPtr}, changetype<${member.type}>(0), ${fieldOffset});`);
|
|
1564
|
+
out.push(` ${srcPtr} = ${valuePtr} + 8;`);
|
|
1565
|
+
out.push(" } else {");
|
|
1566
|
+
}
|
|
1567
|
+
out.push(` const valueStart = ${valuePtr};`);
|
|
1568
|
+
out.push(` const valueEnd = JSON.Util.scanValueEnd<${resolvedType}>(valueStart, srcEnd);`);
|
|
1569
|
+
out.push(" if (!valueEnd) break;");
|
|
1570
|
+
out.push(` store<${member.type}>(${outPtr}, ${getCustomDeserializeExpression(resolvedType, "valueStart", "valueEnd")}, ${fieldOffset});`);
|
|
1571
|
+
out.push(` ${srcPtr} = valueEnd;`);
|
|
1572
|
+
if (member.node.type.isNullable) {
|
|
1573
|
+
out.push(" }");
|
|
1574
|
+
}
|
|
1575
|
+
out.push("}");
|
|
1576
|
+
}
|
|
1327
1577
|
else if (resolvedSchema && !resolvedSchema.custom) {
|
|
1328
1578
|
if (fastPath) {
|
|
1329
1579
|
out.push("{");
|
|
@@ -1337,9 +1587,18 @@ export class JSONTransform extends Visitor {
|
|
|
1337
1587
|
out.push(` if (changetype<usize>(value) == 0) {`);
|
|
1338
1588
|
out.push(` value = changetype<${resolvedType}>(__new(offsetof<nonnull<${resolvedType}>>(), idof<nonnull<${resolvedType}>>()));`);
|
|
1339
1589
|
out.push(` store<${resolvedType}>(${outPtr}, value, ${fieldOffset});`);
|
|
1590
|
+
out.push(` changetype<nonnull<${resolvedType}>>(value).__INITIALIZE();`);
|
|
1340
1591
|
out.push(" }");
|
|
1341
|
-
out.push(`
|
|
1342
|
-
out.push(` if (
|
|
1592
|
+
out.push(` const __fe = changetype<nonnull<${resolvedType}>>(value).__DESERIALIZE_FAST<${resolvedType}>(${valuePtr}, srcEnd, value);`);
|
|
1593
|
+
out.push(` if (__fe) {`);
|
|
1594
|
+
out.push(` ${srcPtr} = __fe;`);
|
|
1595
|
+
out.push(` } else {`);
|
|
1596
|
+
out.push(` const __ve = JSON.Util.scanValueEnd<${resolvedType}>(${valuePtr}, srcEnd);`);
|
|
1597
|
+
out.push(` if (!__ve) break;`);
|
|
1598
|
+
out.push(` changetype<nonnull<${resolvedType}>>(value).__INITIALIZE();`);
|
|
1599
|
+
out.push(` changetype<nonnull<${resolvedType}>>(value).__DESERIALIZE_SLOW<${resolvedType}>(${valuePtr}, __ve, value);`);
|
|
1600
|
+
out.push(` ${srcPtr} = __ve;`);
|
|
1601
|
+
out.push(` }`);
|
|
1343
1602
|
if (member.node.type.isNullable) {
|
|
1344
1603
|
out.push(" }");
|
|
1345
1604
|
}
|
|
@@ -1393,25 +1652,29 @@ export class JSONTransform extends Visitor {
|
|
|
1393
1652
|
out.push(` store<${resolvedType}>(${outPtr}, value, ${fieldOffset});`);
|
|
1394
1653
|
out.push(" }");
|
|
1395
1654
|
out.push(" let index = 0;");
|
|
1655
|
+
out.push(" const reusableLength = value.length;");
|
|
1656
|
+
out.push(" const reusableDataStart = value.dataStart;");
|
|
1396
1657
|
out.push(` ${srcPtr} = ${valuePtr} + 2;`);
|
|
1397
1658
|
out.push(` ${srcPtr} = JSON.Util.skipWhitespace(${srcPtr}, srcEnd);`);
|
|
1398
1659
|
out.push(` if (load<u16>(${srcPtr}) == 0x5d) {`);
|
|
1399
|
-
out.push(" value.length = 0;");
|
|
1660
|
+
out.push(" if (value.length != 0) value.length = 0;");
|
|
1400
1661
|
out.push(` ${srcPtr} += 2;`);
|
|
1401
1662
|
out.push(" } else while (true) {");
|
|
1402
1663
|
out.push(` ${srcPtr} = JSON.Util.skipWhitespace(${srcPtr}, srcEnd);`);
|
|
1403
1664
|
if (elementNullable) {
|
|
1404
|
-
out.push(` if (index >=
|
|
1665
|
+
out.push(` if (index >= reusableLength) value.push(changetype<${valueType} | null>(0));`);
|
|
1666
|
+
out.push(` const slot = index < reusableLength ? reusableDataStart + ((<usize>index) << alignof<${valueType}>()) : value.dataStart + ((<usize>index) << alignof<${valueType}>());`);
|
|
1405
1667
|
out.push(` if (load<u64>(${srcPtr}) == 30399761348886638) {`);
|
|
1406
|
-
out.push(
|
|
1668
|
+
out.push(" store<usize>(slot, 0);");
|
|
1407
1669
|
out.push(` ${srcPtr} += 8;`);
|
|
1408
1670
|
out.push(" } else {");
|
|
1409
|
-
out.push(` ${srcPtr} = ${STRING_FIELD_DESERIALIZER}<${valueType}>(${srcPtr}, srcEnd,
|
|
1671
|
+
out.push(` ${srcPtr} = ${STRING_FIELD_DESERIALIZER}<${valueType}>(${srcPtr}, srcEnd, slot);`);
|
|
1410
1672
|
out.push(" }");
|
|
1411
1673
|
}
|
|
1412
1674
|
else {
|
|
1413
|
-
out.push(' if (index >=
|
|
1414
|
-
out.push(`
|
|
1675
|
+
out.push(' if (index >= reusableLength) value.push("");');
|
|
1676
|
+
out.push(` const slot = index < reusableLength ? reusableDataStart + ((<usize>index) << alignof<${valueType}>()) : value.dataStart + ((<usize>index) << alignof<${valueType}>());`);
|
|
1677
|
+
out.push(` ${srcPtr} = ${STRING_FIELD_DESERIALIZER}<${valueType}>(${srcPtr}, srcEnd, slot);`);
|
|
1415
1678
|
}
|
|
1416
1679
|
out.push(" index++;");
|
|
1417
1680
|
out.push(` ${srcPtr} = JSON.Util.skipWhitespace(${srcPtr}, srcEnd);`);
|
|
@@ -1421,7 +1684,7 @@ export class JSONTransform extends Visitor {
|
|
|
1421
1684
|
out.push(" continue;");
|
|
1422
1685
|
out.push(" }");
|
|
1423
1686
|
out.push(" if (code == 0x5d) {");
|
|
1424
|
-
out.push(" value.length = index;");
|
|
1687
|
+
out.push(" if (index != reusableLength) value.length = index;");
|
|
1425
1688
|
out.push(` ${srcPtr} += 2;`);
|
|
1426
1689
|
out.push(" break;");
|
|
1427
1690
|
out.push(" }");
|
|
@@ -1442,25 +1705,39 @@ export class JSONTransform extends Visitor {
|
|
|
1442
1705
|
out.push(` store<${resolvedType}>(${outPtr}, value, ${fieldOffset});`);
|
|
1443
1706
|
out.push(" }");
|
|
1444
1707
|
out.push(" let index = 0;");
|
|
1708
|
+
out.push(" const reusableLength = value.length;");
|
|
1709
|
+
out.push(" const reusableDataStart = value.dataStart;");
|
|
1445
1710
|
out.push(` ${srcPtr} = ${valuePtr} + 2;`);
|
|
1446
1711
|
out.push(` ${srcPtr} = JSON.Util.skipWhitespace(${srcPtr}, srcEnd);`);
|
|
1447
1712
|
out.push(` if (load<u16>(${srcPtr}) == 0x5d) {`);
|
|
1448
|
-
out.push(" value.length = 0;");
|
|
1713
|
+
out.push(" if (value.length != 0) value.length = 0;");
|
|
1449
1714
|
out.push(` ${srcPtr} += 2;`);
|
|
1450
1715
|
out.push(" } else while (true) {");
|
|
1451
1716
|
out.push(` let item: ${valueType};`);
|
|
1452
|
-
out.push(" if (index <
|
|
1453
|
-
out.push(
|
|
1717
|
+
out.push(" if (index < reusableLength) {");
|
|
1718
|
+
out.push(` const itemSlot = reusableDataStart + ((<usize>index) << alignof<${valueType}>());`);
|
|
1719
|
+
out.push(` item = load<${valueType}>(itemSlot);`);
|
|
1454
1720
|
out.push(" if (changetype<usize>(item) == 0) {");
|
|
1455
1721
|
out.push(` item = changetype<${valueType}>(__new(offsetof<nonnull<${valueType}>>(), idof<nonnull<${valueType}>>()));`);
|
|
1456
|
-
out.push(
|
|
1722
|
+
out.push(` store<${valueType}>(itemSlot, item);`);
|
|
1723
|
+
out.push(` changetype<nonnull<${valueType}>>(item).__INITIALIZE();`);
|
|
1457
1724
|
out.push(" }");
|
|
1458
1725
|
out.push(" } else {");
|
|
1459
1726
|
out.push(` item = changetype<${valueType}>(__new(offsetof<nonnull<${valueType}>>(), idof<nonnull<${valueType}>>()));`);
|
|
1460
1727
|
out.push(" value.push(item);");
|
|
1728
|
+
out.push(` changetype<nonnull<${valueType}>>(item).__INITIALIZE();`);
|
|
1461
1729
|
out.push(" }");
|
|
1462
|
-
out.push(`
|
|
1463
|
-
out.push(`
|
|
1730
|
+
out.push(` const __es = ${srcPtr};`);
|
|
1731
|
+
out.push(` const __ee = changetype<nonnull<${valueType}>>(item).__DESERIALIZE_FAST<${valueType}>(${srcPtr}, srcEnd, item);`);
|
|
1732
|
+
out.push(` if (__ee) {`);
|
|
1733
|
+
out.push(` ${srcPtr} = __ee;`);
|
|
1734
|
+
out.push(` } else {`);
|
|
1735
|
+
out.push(` const __ve = JSON.Util.scanValueEnd<${valueType}>(__es, srcEnd);`);
|
|
1736
|
+
out.push(` if (!__ve) break;`);
|
|
1737
|
+
out.push(` changetype<nonnull<${valueType}>>(item).__INITIALIZE();`);
|
|
1738
|
+
out.push(` changetype<nonnull<${valueType}>>(item).__DESERIALIZE_SLOW<${valueType}>(__es, __ve, item);`);
|
|
1739
|
+
out.push(` ${srcPtr} = __ve;`);
|
|
1740
|
+
out.push(` }`);
|
|
1464
1741
|
out.push(" index++;");
|
|
1465
1742
|
out.push(` ${srcPtr} = JSON.Util.skipWhitespace(${srcPtr}, srcEnd);`);
|
|
1466
1743
|
out.push(` const code = load<u16>(${srcPtr});`);
|
|
@@ -1469,7 +1746,7 @@ export class JSONTransform extends Visitor {
|
|
|
1469
1746
|
out.push(" continue;");
|
|
1470
1747
|
out.push(" }");
|
|
1471
1748
|
out.push(" if (code == 0x5d) {");
|
|
1472
|
-
out.push(" value.length = index;");
|
|
1749
|
+
out.push(" if (reusableLength != index) value.length = index;");
|
|
1473
1750
|
out.push(` ${srcPtr} += 2;`);
|
|
1474
1751
|
out.push(" break;");
|
|
1475
1752
|
out.push(" }");
|
|
@@ -1481,8 +1758,25 @@ export class JSONTransform extends Visitor {
|
|
|
1481
1758
|
out.push("}");
|
|
1482
1759
|
return out;
|
|
1483
1760
|
}
|
|
1484
|
-
|
|
1485
|
-
|
|
1761
|
+
if (valueType == "i64") {
|
|
1762
|
+
out.push(` if (load<u32>(${valuePtr}) == 0x005d005b) {`);
|
|
1763
|
+
out.push(` let value = load<${resolvedType}>(${outPtr}, ${fieldOffset});`);
|
|
1764
|
+
out.push(" if (changetype<usize>(value) == 0) {");
|
|
1765
|
+
out.push(` value = instantiate<nonnull<${resolvedType}>>();`);
|
|
1766
|
+
out.push(` store<${resolvedType}>(${outPtr}, value, ${fieldOffset});`);
|
|
1767
|
+
out.push(" } else if (value.length != 0) {");
|
|
1768
|
+
out.push(" value.length = 0;");
|
|
1769
|
+
out.push(" }");
|
|
1770
|
+
out.push(` ${srcPtr} = ${valuePtr} + 4;`);
|
|
1771
|
+
out.push(" } else {");
|
|
1772
|
+
out.push(` ${srcPtr} = __deserializeArrayField_SWAR<nonnull<${resolvedType}>>(${valuePtr}, srcEnd, ${outPtr}, ${fieldOffset});`);
|
|
1773
|
+
out.push(` if (!${srcPtr}) break;`);
|
|
1774
|
+
out.push(" }");
|
|
1775
|
+
}
|
|
1776
|
+
else {
|
|
1777
|
+
out.push(` ${srcPtr} = __deserializeArrayField_SWAR<nonnull<${resolvedType}>>(${valuePtr}, srcEnd, ${outPtr}, ${fieldOffset});`);
|
|
1778
|
+
out.push(` if (!${srcPtr}) break;`);
|
|
1779
|
+
}
|
|
1486
1780
|
if (member.node.type.isNullable) {
|
|
1487
1781
|
out.push(" }");
|
|
1488
1782
|
}
|
|
@@ -1502,6 +1796,7 @@ export class JSONTransform extends Visitor {
|
|
|
1502
1796
|
}
|
|
1503
1797
|
else if (resolvedType == "JSON.Value" ||
|
|
1504
1798
|
resolvedType == "JSON.Obj" ||
|
|
1799
|
+
resolvedType == "JSON.Arr" ||
|
|
1505
1800
|
isEnum(resolvedType, this.sources.get(this.schema.node.range.source), this.parser)) {
|
|
1506
1801
|
out.push("break;");
|
|
1507
1802
|
}
|
|
@@ -1531,32 +1826,28 @@ export class JSONTransform extends Visitor {
|
|
|
1531
1826
|
}
|
|
1532
1827
|
return calls;
|
|
1533
1828
|
};
|
|
1534
|
-
const chunkFastBlocksOptional = (blocks,
|
|
1535
|
-
|
|
1536
|
-
return blocks.join("");
|
|
1537
|
-
let calls = "";
|
|
1538
|
-
for (let c = 0; c < blocks.length; c += FAST_CHUNK_SIZE) {
|
|
1539
|
-
const name = `__DESERIALIZE_FAST_${tag}_${fastChunkId++}`;
|
|
1540
|
-
const body = blocks
|
|
1541
|
-
.slice(c, c + FAST_CHUNK_SIZE)
|
|
1542
|
-
.join("")
|
|
1543
|
-
.replace(/\bbreak;/g, "return 0;");
|
|
1544
|
-
fastChunkMethods.push(`${name}(srcStart: usize, srcEnd: usize, dst: usize, seenAny: bool): u64 {\n` +
|
|
1545
|
-
(needsKp ? " let kp: usize = 0;\n" : "") +
|
|
1546
|
-
`${body}\n` +
|
|
1547
|
-
` return (<u64>srcStart) | ((<u64>(seenAny ? 1 : 0)) << 32);\n}`);
|
|
1548
|
-
calls +=
|
|
1549
|
-
`${callIndent}{\n` +
|
|
1550
|
-
`${callIndent} const __r = this.${name}(srcStart, srcEnd, dst, seenAny);\n` +
|
|
1551
|
-
`${callIndent} if (__r == 0) break;\n` +
|
|
1552
|
-
`${callIndent} srcStart = <usize>(<u32>__r);\n` +
|
|
1553
|
-
`${callIndent} seenAny = (<u32>(__r >>> 32)) != 0;\n` +
|
|
1554
|
-
`${callIndent}}\n`;
|
|
1555
|
-
}
|
|
1556
|
-
return calls;
|
|
1829
|
+
const chunkFastBlocksOptional = (blocks, _tag, _callIndent, _needsKp) => {
|
|
1830
|
+
return blocks.join("");
|
|
1557
1831
|
};
|
|
1832
|
+
const traceOptionalObject = supportsFastOptionalPath && this.schema.members.length <= 63;
|
|
1833
|
+
const objectTraceHeader = traceOptionalObject
|
|
1834
|
+
? " const __traceToken = JSON.Util.beginObjectTrace(dst, start, " +
|
|
1835
|
+
JSON.stringify(this.schema.name) +
|
|
1836
|
+
");\n" +
|
|
1837
|
+
" const __traceActive = __traceToken != i32.MIN_VALUE;\n" +
|
|
1838
|
+
" const __traceHit = __traceActive && __traceToken < 0;\n" +
|
|
1839
|
+
" const __traceSlot = __traceHit ? ~__traceToken : __traceToken;\n" +
|
|
1840
|
+
" const __traceMask = __traceHit ? JSON.Util.getObjectTraceMask(__traceSlot) : <u64>0;\n" +
|
|
1841
|
+
" const __traceTier = __traceHit ? JSON.Util.getObjectTraceTier(__traceSlot) : <u8>0;\n" +
|
|
1842
|
+
" let __tracePresent: u64 = 0;\n"
|
|
1843
|
+
: "";
|
|
1558
1844
|
DESERIALIZE_FAST += indent + "const start = srcStart;\n";
|
|
1559
1845
|
DESERIALIZE_FAST += indent + "const dst = changetype<usize>(out);\n";
|
|
1846
|
+
if (traceOptionalObject) {
|
|
1847
|
+
DESERIALIZE_FAST += objectTraceHeader;
|
|
1848
|
+
DESERIALIZE_FAST += indent + "if (__traceTier < 2) {\n";
|
|
1849
|
+
indent += " ";
|
|
1850
|
+
}
|
|
1560
1851
|
DESERIALIZE_FAST += indent + "do {\n";
|
|
1561
1852
|
indent += " ";
|
|
1562
1853
|
if (supportsFastOptionalPath) {
|
|
@@ -1570,25 +1861,40 @@ export class JSONTransform extends Visitor {
|
|
|
1570
1861
|
const key = JSON.stringify(member.alias || member.name);
|
|
1571
1862
|
if (key.length <= 2)
|
|
1572
1863
|
throw new Error("Key cannot be empty!");
|
|
1573
|
-
const firstKeySection = key + ":";
|
|
1574
|
-
const nextKeySection = "," + key + ":";
|
|
1575
|
-
const firstKeyOffset = firstKeySection.length << 1;
|
|
1576
|
-
const nextKeyOffset = nextKeySection.length << 1;
|
|
1577
1864
|
const resolvedType = stripNull(member.type);
|
|
1578
1865
|
const inlineStringValue = ["string", "String"].includes(resolvedType);
|
|
1579
|
-
const
|
|
1580
|
-
const
|
|
1866
|
+
const packedQuote = inlineStringValue && !member.node.type.isNullable ? '"' : "";
|
|
1867
|
+
const firstKeySection = key + ":" + packedQuote;
|
|
1868
|
+
const nextKeySection = "," + key + ":" + packedQuote;
|
|
1869
|
+
const firstKeyOffset = firstKeySection.length << 1;
|
|
1870
|
+
const nextKeyOffset = nextKeySection.length << 1;
|
|
1871
|
+
const trustedStringValue = packedQuote.length != 0;
|
|
1872
|
+
const firstValueOffset = trustedStringValue
|
|
1873
|
+
? firstKeyOffset - 2
|
|
1874
|
+
: firstKeyOffset;
|
|
1875
|
+
const nextValueOffset = trustedStringValue
|
|
1876
|
+
? nextKeyOffset - 2
|
|
1877
|
+
: nextKeyOffset;
|
|
1878
|
+
const deserializerFirst = getDeserializer(member.type, "srcStart", "dst", member, inlineStringValue ? firstValueOffset : 0, true);
|
|
1879
|
+
const deserializerNext = getDeserializer(member.type, "srcStart", "dst", member, inlineStringValue ? nextValueOffset : 0, true);
|
|
1581
1880
|
const isOptional = member.flags.has(PropertyFlags.OmitNull) ||
|
|
1582
1881
|
member.flags.has(PropertyFlags.OmitIf);
|
|
1882
|
+
const traceBit = `<u64>1 << ${i}`;
|
|
1883
|
+
const firstMismatch = getComparisions(firstKeySection, "srcStart", "!=").join(" || ");
|
|
1884
|
+
const nextMismatch = getComparisions(nextKeySection, "srcStart", "!=").join(" || ");
|
|
1885
|
+
const firstAbsent = traceOptionalObject
|
|
1886
|
+
? `__traceHit ? (__traceMask & (${traceBit})) == 0 : (${firstMismatch})`
|
|
1887
|
+
: firstMismatch;
|
|
1888
|
+
const nextAbsent = traceOptionalObject
|
|
1889
|
+
? `__traceHit ? (__traceMask & (${traceBit})) == 0 : (${nextMismatch})`
|
|
1890
|
+
: nextMismatch;
|
|
1583
1891
|
if (!deserializerFirst.length || !deserializerNext.length) {
|
|
1584
1892
|
t1opt.push(indent + "break;\n\n");
|
|
1585
1893
|
continue;
|
|
1586
1894
|
}
|
|
1587
1895
|
let blk = indent + "if (!seenAny) {\n";
|
|
1588
1896
|
indent += " ";
|
|
1589
|
-
blk +=
|
|
1590
|
-
indent +
|
|
1591
|
-
`if ( // ${firstKeySection}\n${(indent += " ")}${getComparisions(firstKeySection, "srcStart", "!=").join("\n" + indent + "|| ")}\n${(indent = indent.slice(0, -2))}) {\n`;
|
|
1897
|
+
blk += indent + `if (${firstAbsent}) {\n`;
|
|
1592
1898
|
indent += " ";
|
|
1593
1899
|
if (isOptional) {
|
|
1594
1900
|
blk += indent + "// optional @omitnull field omitted\n";
|
|
@@ -1601,19 +1907,20 @@ export class JSONTransform extends Visitor {
|
|
|
1601
1907
|
indent += " ";
|
|
1602
1908
|
if (!inlineStringValue)
|
|
1603
1909
|
blk += indent + `srcStart += ${firstKeyOffset};\n`;
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1910
|
+
if (!trustedStringValue)
|
|
1911
|
+
blk +=
|
|
1912
|
+
indent +
|
|
1913
|
+
`if (JSON.Util.isSpace(load<u16>(${inlineStringValue ? `srcStart + ${firstValueOffset}` : "srcStart"}))) break;\n`;
|
|
1607
1914
|
blk += indent + deserializerFirst.join("\n" + indent) + "\n";
|
|
1915
|
+
if (traceOptionalObject)
|
|
1916
|
+
blk += indent + `__tracePresent |= ${traceBit};\n`;
|
|
1608
1917
|
blk += indent + "seenAny = true;\n";
|
|
1609
1918
|
indent = indent.slice(0, -2);
|
|
1610
1919
|
blk += indent + "}\n";
|
|
1611
1920
|
indent = indent.slice(0, -2);
|
|
1612
1921
|
blk += indent + "} else {\n";
|
|
1613
1922
|
indent += " ";
|
|
1614
|
-
blk +=
|
|
1615
|
-
indent +
|
|
1616
|
-
`if ( // ${nextKeySection}\n${(indent += " ")}${getComparisions(nextKeySection, "srcStart", "!=").join("\n" + indent + "|| ")}\n${(indent = indent.slice(0, -2))}) {\n`;
|
|
1923
|
+
blk += indent + `if (${nextAbsent}) {\n`;
|
|
1617
1924
|
indent += " ";
|
|
1618
1925
|
if (isOptional) {
|
|
1619
1926
|
blk += indent + "// optional @omitnull field omitted\n";
|
|
@@ -1626,10 +1933,13 @@ export class JSONTransform extends Visitor {
|
|
|
1626
1933
|
indent += " ";
|
|
1627
1934
|
if (!inlineStringValue)
|
|
1628
1935
|
blk += indent + `srcStart += ${nextKeyOffset};\n`;
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1936
|
+
if (!trustedStringValue)
|
|
1937
|
+
blk +=
|
|
1938
|
+
indent +
|
|
1939
|
+
`if (JSON.Util.isSpace(load<u16>(${inlineStringValue ? `srcStart + ${nextValueOffset}` : "srcStart"}))) break;\n`;
|
|
1632
1940
|
blk += indent + deserializerNext.join("\n" + indent) + "\n";
|
|
1941
|
+
if (traceOptionalObject)
|
|
1942
|
+
blk += indent + `__tracePresent |= ${traceBit};\n`;
|
|
1633
1943
|
indent = indent.slice(0, -2);
|
|
1634
1944
|
blk += indent + "}\n";
|
|
1635
1945
|
indent = indent.slice(0, -2);
|
|
@@ -1645,24 +1955,27 @@ export class JSONTransform extends Visitor {
|
|
|
1645
1955
|
const key = JSON.stringify(member.alias || member.name);
|
|
1646
1956
|
if (key.length <= 2)
|
|
1647
1957
|
throw new Error("Key cannot be empty!");
|
|
1648
|
-
const keySection = (i == 0 ? "{" : ",") + key + ":";
|
|
1649
|
-
let blk = indent +
|
|
1650
|
-
`if ( // ${keySection}\n${(indent += " ")}${getComparisions(keySection, "srcStart", "!=").join("\n" + indent + "|| ")}\n${(indent = indent.slice(0, -2))}) break;\n`;
|
|
1651
|
-
const keyOffset = keySection.length << 1;
|
|
1652
1958
|
const resolvedType = stripNull(member.type);
|
|
1653
1959
|
const inlineStringValue = ["string", "String"].includes(resolvedType);
|
|
1960
|
+
const trustedStringValue = inlineStringValue && !member.node.type.isNullable;
|
|
1961
|
+
const keySection = (i == 0 ? "{" : ",") + key + ":" + (trustedStringValue ? '"' : "");
|
|
1962
|
+
let blk = indent +
|
|
1963
|
+
`if (${getComparisions(keySection, "srcStart", "!=").join(" || ")}) break;\n`;
|
|
1964
|
+
const keyOffset = keySection.length << 1;
|
|
1965
|
+
const valueOffset = trustedStringValue ? keyOffset - 2 : keyOffset;
|
|
1654
1966
|
if (!inlineStringValue) {
|
|
1655
1967
|
blk += indent + `srcStart += ${keyOffset};\n\n`;
|
|
1656
1968
|
}
|
|
1657
|
-
const deserializer = getDeserializer(member.type, "srcStart", "dst", member, inlineStringValue ?
|
|
1969
|
+
const deserializer = getDeserializer(member.type, "srcStart", "dst", member, inlineStringValue ? valueOffset : 0, true);
|
|
1658
1970
|
if (!deserializer.length) {
|
|
1659
1971
|
blk += indent + "break;\n\n";
|
|
1660
1972
|
t1blocks.push(blk);
|
|
1661
1973
|
continue;
|
|
1662
1974
|
}
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1975
|
+
if (!trustedStringValue)
|
|
1976
|
+
blk +=
|
|
1977
|
+
indent +
|
|
1978
|
+
`if (JSON.Util.isSpace(load<u16>(${inlineStringValue ? `srcStart + ${valueOffset}` : "srcStart"}))) break;\n`;
|
|
1666
1979
|
blk += indent + deserializer.join("\n" + indent) + "\n\n";
|
|
1667
1980
|
t1blocks.push(blk);
|
|
1668
1981
|
}
|
|
@@ -1671,9 +1984,18 @@ export class JSONTransform extends Visitor {
|
|
|
1671
1984
|
DESERIALIZE_FAST +=
|
|
1672
1985
|
indent + "if (load<u16>(srcStart) !== 0x7d) break; // }\n";
|
|
1673
1986
|
DESERIALIZE_FAST += indent + "srcStart += 2;\n";
|
|
1987
|
+
if (traceOptionalObject)
|
|
1988
|
+
DESERIALIZE_FAST +=
|
|
1989
|
+
indent +
|
|
1990
|
+
"if (__traceActive) JSON.Util.saveObjectTrace(__traceSlot, __tracePresent, 1);\n";
|
|
1674
1991
|
DESERIALIZE_FAST += indent + "return srcStart;\n";
|
|
1675
1992
|
indent = indent.slice(0, -2);
|
|
1676
1993
|
DESERIALIZE_FAST += indent + "} while (false);\n\n";
|
|
1994
|
+
if (traceOptionalObject) {
|
|
1995
|
+
indent = indent.slice(0, -2);
|
|
1996
|
+
DESERIALIZE_FAST += indent + "}\n\n";
|
|
1997
|
+
}
|
|
1998
|
+
fastPrettyTailStart = DESERIALIZE_FAST.length;
|
|
1677
1999
|
const tier2Desers = this.schema.members.map((member) => getDeserializer(member.type, "srcStart", "dst", member, 0, true));
|
|
1678
2000
|
const tier2Ok = tier2Desers.every((d) => d.length && !(d.length === 1 && d[0].trim() === "break;"));
|
|
1679
2001
|
if (tier2Ok && !supportsFastOptionalPath) {
|
|
@@ -1694,9 +2016,7 @@ export class JSONTransform extends Visitor {
|
|
|
1694
2016
|
blk += skip;
|
|
1695
2017
|
blk +=
|
|
1696
2018
|
i2 +
|
|
1697
|
-
`if (
|
|
1698
|
-
getComparisions(key, "srcStart", "!=").join("\n" + i2 + " || ") +
|
|
1699
|
-
`\n${i2}) break;\n`;
|
|
2019
|
+
`if (${getComparisions(key, "srcStart", "!=").join(" || ")}) break;\n`;
|
|
1700
2020
|
blk += i2 + `srcStart += ${keyBytes};\n`;
|
|
1701
2021
|
blk += skip;
|
|
1702
2022
|
blk += i2 + "if (load<u16>(srcStart) != 0x3a) break; // :\n";
|
|
@@ -1725,6 +2045,16 @@ export class JSONTransform extends Visitor {
|
|
|
1725
2045
|
const i3 = " ";
|
|
1726
2046
|
DESERIALIZE_FAST += i1 + "srcStart = start;\n";
|
|
1727
2047
|
DESERIALIZE_FAST += i1 + "do {\n";
|
|
2048
|
+
if (traceOptionalObject) {
|
|
2049
|
+
DESERIALIZE_FAST += i2 + "__tracePresent = 0;\n";
|
|
2050
|
+
DESERIALIZE_FAST +=
|
|
2051
|
+
i2 +
|
|
2052
|
+
"const __traceSeparator = __traceHit ? JSON.Util.getObjectTraceSeparator(__traceSlot) : <usize>0;\n";
|
|
2053
|
+
DESERIALIZE_FAST += i2 + "let __traceCandidateSeparator: usize = 0;\n";
|
|
2054
|
+
DESERIALIZE_FAST += i2 + "let __traceSeparatorStart: usize = 0;\n";
|
|
2055
|
+
DESERIALIZE_FAST += i2 + "let __traceSeparatorSeen = false;\n";
|
|
2056
|
+
DESERIALIZE_FAST += i2 + "let __traceCanonicalPretty = true;\n";
|
|
2057
|
+
}
|
|
1728
2058
|
DESERIALIZE_FAST +=
|
|
1729
2059
|
i2 + "srcStart = JSON.Util.skipWhitespace(srcStart, srcEnd);\n";
|
|
1730
2060
|
DESERIALIZE_FAST += i2 + "if (load<u16>(srcStart) != 0x7b) break; // {\n";
|
|
@@ -1737,23 +2067,91 @@ export class JSONTransform extends Visitor {
|
|
|
1737
2067
|
const member = this.schema.members[i];
|
|
1738
2068
|
const key = JSON.stringify(member.alias || member.name);
|
|
1739
2069
|
const keyBytes = key.length << 1;
|
|
2070
|
+
const traceBit = `<u64>1 << ${i}`;
|
|
2071
|
+
const keyMatches = getComparisions(key, "kp", "==").join(" && ");
|
|
2072
|
+
const presentCondition = traceOptionalObject
|
|
2073
|
+
? `__traceHit ? (__traceMask & (${traceBit})) != 0 : (${keyMatches})`
|
|
2074
|
+
: keyMatches;
|
|
1740
2075
|
let blk = "\n";
|
|
1741
|
-
|
|
1742
|
-
|
|
2076
|
+
if (traceOptionalObject) {
|
|
2077
|
+
blk += i2 + `if (__traceHit && (${traceBit} & __traceMask) != 0) {\n`;
|
|
2078
|
+
blk += i3 + "kp = srcStart;\n";
|
|
2079
|
+
if (multi && i > 0) {
|
|
2080
|
+
blk += i3 + "if (seenAny) {\n";
|
|
2081
|
+
blk += i3 + " if (load<u16>(kp) != 0x2c) break; // ,\n";
|
|
2082
|
+
blk += i3 + " kp += 2;\n";
|
|
2083
|
+
blk += i3 + "}\n";
|
|
2084
|
+
}
|
|
2085
|
+
blk += i3 + "if (__traceTier == 3) kp += __traceSeparator;\n";
|
|
2086
|
+
blk += i3 + "else kp = JSON.Util.skipWhitespace(kp, srcEnd);\n";
|
|
2087
|
+
blk += i2 + "} else if (!__traceHit) {\n";
|
|
2088
|
+
blk += i3 + "__traceSeparatorStart = srcStart;\n";
|
|
2089
|
+
blk += i3 + "kp = JSON.Util.skipWhitespace(srcStart, srcEnd);\n";
|
|
2090
|
+
if (multi && i > 0) {
|
|
2091
|
+
blk += i3 + "if (seenAny && load<u16>(kp) == 0x2c) {\n";
|
|
2092
|
+
blk +=
|
|
2093
|
+
i3 + " if (kp != srcStart) __traceCanonicalPretty = false;\n";
|
|
2094
|
+
blk += i3 + " __traceSeparatorStart = kp + 2;\n";
|
|
2095
|
+
blk +=
|
|
2096
|
+
i3 +
|
|
2097
|
+
" kp = JSON.Util.skipWhitespace(__traceSeparatorStart, srcEnd);\n";
|
|
2098
|
+
blk += i3 + "}\n";
|
|
2099
|
+
}
|
|
2100
|
+
blk += i2 + "}\n";
|
|
2101
|
+
}
|
|
2102
|
+
else {
|
|
2103
|
+
blk += i2 + "kp = JSON.Util.skipWhitespace(srcStart, srcEnd);\n";
|
|
2104
|
+
if (multi && i > 0) {
|
|
2105
|
+
blk +=
|
|
2106
|
+
i2 +
|
|
2107
|
+
"if (seenAny && load<u16>(kp) == 0x2c) kp = JSON.Util.skipWhitespace(kp + 2, srcEnd);\n";
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
blk += i2 + `if (${presentCondition}) {\n`;
|
|
2111
|
+
if (traceOptionalObject) {
|
|
2112
|
+
blk += i3 + "if (__traceHit && __traceTier == 3) {\n";
|
|
2113
|
+
blk += i3 + ` kp += ${keyBytes};\n`;
|
|
2114
|
+
blk += i3 + " if (load<u16>(kp) != 0x3a) break; // :\n";
|
|
2115
|
+
blk += i3 + " if (load<u16>(kp + 2) != 0x20) break; // space\n";
|
|
2116
|
+
blk += i3 + " srcStart = kp + 4;\n";
|
|
2117
|
+
blk += i3 + "} else {\n";
|
|
2118
|
+
blk += i3 + " if (!__traceHit) {\n";
|
|
2119
|
+
blk +=
|
|
2120
|
+
i3 +
|
|
2121
|
+
" const __traceCurrentSeparator = kp - __traceSeparatorStart;\n";
|
|
2122
|
+
blk += i3 + " if (__traceSeparatorSeen) {\n";
|
|
2123
|
+
blk +=
|
|
2124
|
+
i3 +
|
|
2125
|
+
" if (__traceCurrentSeparator != __traceCandidateSeparator) __traceCanonicalPretty = false;\n";
|
|
2126
|
+
blk += i3 + " } else {\n";
|
|
2127
|
+
blk +=
|
|
2128
|
+
i3 + " __traceCandidateSeparator = __traceCurrentSeparator;\n";
|
|
2129
|
+
blk += i3 + " __traceSeparatorSeen = true;\n";
|
|
2130
|
+
blk += i3 + " }\n";
|
|
2131
|
+
blk += i3 + " }\n";
|
|
2132
|
+
blk += i3 + ` kp += ${keyBytes};\n`;
|
|
2133
|
+
blk += i3 + " const __traceKeyEnd = kp;\n";
|
|
2134
|
+
blk += i3 + " kp = JSON.Util.skipWhitespace(kp, srcEnd);\n";
|
|
1743
2135
|
blk +=
|
|
1744
|
-
|
|
1745
|
-
"if (
|
|
2136
|
+
i3 +
|
|
2137
|
+
" if (!__traceHit && kp != __traceKeyEnd) __traceCanonicalPretty = false;\n";
|
|
2138
|
+
blk += i3 + " if (load<u16>(kp) != 0x3a) break; // :\n";
|
|
2139
|
+
blk +=
|
|
2140
|
+
i3 + " srcStart = JSON.Util.skipWhitespace(kp + 2, srcEnd);\n";
|
|
2141
|
+
blk +=
|
|
2142
|
+
i3 +
|
|
2143
|
+
" if (!__traceHit && (load<u16>(kp + 2) != 0x20 || srcStart != kp + 4)) __traceCanonicalPretty = false;\n";
|
|
2144
|
+
blk += i3 + "}\n";
|
|
2145
|
+
}
|
|
2146
|
+
else {
|
|
2147
|
+
blk += i3 + `kp += ${keyBytes};\n`;
|
|
2148
|
+
blk += i3 + "kp = JSON.Util.skipWhitespace(kp, srcEnd);\n";
|
|
2149
|
+
blk += i3 + "if (load<u16>(kp) != 0x3a) break; // :\n";
|
|
2150
|
+
blk += i3 + "srcStart = JSON.Util.skipWhitespace(kp + 2, srcEnd);\n";
|
|
1746
2151
|
}
|
|
1747
|
-
blk +=
|
|
1748
|
-
i2 +
|
|
1749
|
-
`if ( // ${key}\n${i2} ` +
|
|
1750
|
-
getComparisions(key, "kp", "==").join("\n" + i2 + " && ") +
|
|
1751
|
-
`\n${i2}) {\n`;
|
|
1752
|
-
blk += i3 + `kp += ${keyBytes};\n`;
|
|
1753
|
-
blk += i3 + "kp = JSON.Util.skipWhitespace(kp, srcEnd);\n";
|
|
1754
|
-
blk += i3 + "if (load<u16>(kp) != 0x3a) break; // :\n";
|
|
1755
|
-
blk += i3 + "srcStart = JSON.Util.skipWhitespace(kp + 2, srcEnd);\n";
|
|
1756
2152
|
blk += i3 + tier2Desers[i].join("\n" + i3) + "\n";
|
|
2153
|
+
if (traceOptionalObject)
|
|
2154
|
+
blk += i3 + `__tracePresent |= ${traceBit};\n`;
|
|
1757
2155
|
if (multi)
|
|
1758
2156
|
blk += i3 + "seenAny = true;\n";
|
|
1759
2157
|
blk += i2 + "}\n";
|
|
@@ -1765,6 +2163,137 @@ export class JSONTransform extends Visitor {
|
|
|
1765
2163
|
i2 + "srcStart = JSON.Util.skipWhitespace(srcStart, srcEnd);\n";
|
|
1766
2164
|
DESERIALIZE_FAST += i2 + "if (load<u16>(srcStart) != 0x7d) break; // }\n";
|
|
1767
2165
|
DESERIALIZE_FAST += i2 + "srcStart += 2;\n";
|
|
2166
|
+
if (traceOptionalObject)
|
|
2167
|
+
DESERIALIZE_FAST +=
|
|
2168
|
+
i2 +
|
|
2169
|
+
"if (__traceActive) JSON.Util.saveObjectTrace(__traceSlot, __tracePresent, __traceHit ? __traceTier : (__traceCanonicalPretty && __traceSeparatorSeen ? 3 : 2), __traceHit ? __traceSeparator : __traceCandidateSeparator);\n";
|
|
2170
|
+
DESERIALIZE_FAST += i2 + "return srcStart;\n";
|
|
2171
|
+
DESERIALIZE_FAST += i1 + "} while (false);\n\n";
|
|
2172
|
+
}
|
|
2173
|
+
const keyedUnionAlternatives = this.schema.members.filter((member) => member.node.type.isNullable &&
|
|
2174
|
+
this.getSchema(stripNull(member.type)) != null).length;
|
|
2175
|
+
const scalarKeyedSchema = this.schema.members.every((member) => {
|
|
2176
|
+
const type = stripNull(member.type);
|
|
2177
|
+
return (INTEGER_TYPES.includes(type) ||
|
|
2178
|
+
FLOAT_TYPES.includes(type) ||
|
|
2179
|
+
["string", "String"].includes(type) ||
|
|
2180
|
+
isBoolean(type));
|
|
2181
|
+
});
|
|
2182
|
+
const structKeyedSchema = this.schema.members.every((member) => {
|
|
2183
|
+
const type = stripNull(member.type);
|
|
2184
|
+
return (INTEGER_TYPES.includes(type) ||
|
|
2185
|
+
FLOAT_TYPES.includes(type) ||
|
|
2186
|
+
["string", "String"].includes(type) ||
|
|
2187
|
+
isBoolean(type) ||
|
|
2188
|
+
this.getSchema(type) != null);
|
|
2189
|
+
});
|
|
2190
|
+
const keyedFallbackEnabled = tier2Ok &&
|
|
2191
|
+
this.schema.members.length > 0 &&
|
|
2192
|
+
this.schema.members.length <= 24 &&
|
|
2193
|
+
(keyedUnionAlternatives >= 2 ||
|
|
2194
|
+
(scalarKeyedSchema && this.schema.members.length >= 12) ||
|
|
2195
|
+
(structKeyedSchema && hasExplicitOptionalMembers));
|
|
2196
|
+
if (keyedFallbackEnabled) {
|
|
2197
|
+
const i1 = " ";
|
|
2198
|
+
const i2 = " ";
|
|
2199
|
+
const i3 = " ";
|
|
2200
|
+
const keyedResets = this.schema.members.map((member) => {
|
|
2201
|
+
const offset = `offsetof<this>(${JSON.stringify(member.name)})`;
|
|
2202
|
+
if (member.value)
|
|
2203
|
+
return `store<${member.type}>(dst, ${member.value}, ${offset});`;
|
|
2204
|
+
if (member.node.type.isNullable)
|
|
2205
|
+
return `store<${member.type}>(dst, null, ${offset});`;
|
|
2206
|
+
if (this.getSchema(member.type))
|
|
2207
|
+
return (`store<${member.type}>(dst, ` +
|
|
2208
|
+
`changetype<nonnull<${member.type}>>(__new(offsetof<nonnull<${member.type}>>(), idof<nonnull<${member.type}>>())).__INITIALIZE(), ${offset});`);
|
|
2209
|
+
if (["string", "String"].includes(member.type))
|
|
2210
|
+
return `store<${member.type}>(dst, "", ${offset});`;
|
|
2211
|
+
if (member.type.endsWith("[]")) {
|
|
2212
|
+
const element = member.type.slice(0, -2).trim();
|
|
2213
|
+
return `store<${member.type}>(dst, new Array<${element}>(), ${offset});`;
|
|
2214
|
+
}
|
|
2215
|
+
if (member.type.startsWith("Array<") ||
|
|
2216
|
+
member.type.startsWith("Map<") ||
|
|
2217
|
+
member.type.startsWith("Set<"))
|
|
2218
|
+
return `store<${member.type}>(dst, new ${member.type}(), ${offset});`;
|
|
2219
|
+
if (member.type.startsWith("StaticArray<")) {
|
|
2220
|
+
const element = member.type.slice("StaticArray<".length, -1).trim();
|
|
2221
|
+
return `store<${member.type}>(dst, new StaticArray<${element}>(0), ${offset});`;
|
|
2222
|
+
}
|
|
2223
|
+
return `store<${member.type}>(dst, 0, ${offset});`;
|
|
2224
|
+
});
|
|
2225
|
+
DESERIALIZE_FAST += i1 + "srcStart = start;\n";
|
|
2226
|
+
DESERIALIZE_FAST += i1 + "do {\n";
|
|
2227
|
+
DESERIALIZE_FAST +=
|
|
2228
|
+
i2 + "srcStart = JSON.Util.skipWhitespace(srcStart, srcEnd);\n";
|
|
2229
|
+
DESERIALIZE_FAST += i2 + "if (load<u16>(srcStart) != 0x7b) break; // {\n";
|
|
2230
|
+
DESERIALIZE_FAST += i2 + "srcStart += 2;\n";
|
|
2231
|
+
DESERIALIZE_FAST += i2 + "let seen: u32 = 0;\n";
|
|
2232
|
+
DESERIALIZE_FAST += i2 + "let complete = false;\n";
|
|
2233
|
+
DESERIALIZE_FAST += i2 + "while (true) {\n";
|
|
2234
|
+
DESERIALIZE_FAST +=
|
|
2235
|
+
i3 + "srcStart = JSON.Util.skipWhitespace(srcStart, srcEnd);\n";
|
|
2236
|
+
DESERIALIZE_FAST += i3 + "let code = load<u16>(srcStart);\n";
|
|
2237
|
+
DESERIALIZE_FAST += i3 + "if (code == 0x7d) {\n";
|
|
2238
|
+
DESERIALIZE_FAST += i3 + " srcStart += 2;\n";
|
|
2239
|
+
DESERIALIZE_FAST += i3 + " complete = true;\n";
|
|
2240
|
+
DESERIALIZE_FAST += i3 + " break;\n";
|
|
2241
|
+
DESERIALIZE_FAST += i3 + "}\n";
|
|
2242
|
+
DESERIALIZE_FAST += i3 + "if (code != 0x22) break; // opening quote\n";
|
|
2243
|
+
DESERIALIZE_FAST += i3 + "const keyStart = srcStart;\n";
|
|
2244
|
+
DESERIALIZE_FAST +=
|
|
2245
|
+
i3 +
|
|
2246
|
+
"const keyEnd = JSON.Util.scanValueEnd<string>(keyStart, srcEnd);\n";
|
|
2247
|
+
DESERIALIZE_FAST += i3 + "if (!keyEnd) break;\n";
|
|
2248
|
+
DESERIALIZE_FAST +=
|
|
2249
|
+
i3 + "srcStart = JSON.Util.skipWhitespace(keyEnd, srcEnd);\n";
|
|
2250
|
+
DESERIALIZE_FAST += i3 + "if (load<u16>(srcStart) != 0x3a) break; // :\n";
|
|
2251
|
+
DESERIALIZE_FAST +=
|
|
2252
|
+
i3 + "srcStart = JSON.Util.skipWhitespace(srcStart + 2, srcEnd);\n";
|
|
2253
|
+
DESERIALIZE_FAST += i3 + "const keyBytes = keyEnd - keyStart;\n";
|
|
2254
|
+
DESERIALIZE_FAST += i3 + "let matched = false;\n";
|
|
2255
|
+
for (let i = 0; i < this.schema.members.length; i++) {
|
|
2256
|
+
const member = this.schema.members[i];
|
|
2257
|
+
const key = JSON.stringify(member.alias || member.name);
|
|
2258
|
+
const condition = [
|
|
2259
|
+
`keyBytes == ${key.length << 1}`,
|
|
2260
|
+
...getComparisions(key, "keyStart", "=="),
|
|
2261
|
+
].join(" &&\n" + i3 + " ");
|
|
2262
|
+
DESERIALIZE_FAST += i3 + `if (!matched && (${condition})) {\n`;
|
|
2263
|
+
DESERIALIZE_FAST += i3 + " matched = true;\n";
|
|
2264
|
+
DESERIALIZE_FAST +=
|
|
2265
|
+
i3 + " " + tier2Desers[i].join("\n" + i3 + " ") + "\n";
|
|
2266
|
+
DESERIALIZE_FAST += i3 + ` seen |= <u32>1 << ${i};\n`;
|
|
2267
|
+
DESERIALIZE_FAST += i3 + "}\n";
|
|
2268
|
+
}
|
|
2269
|
+
DESERIALIZE_FAST += i3 + "if (!matched) {\n";
|
|
2270
|
+
if (STRICT) {
|
|
2271
|
+
DESERIALIZE_FAST += i3 + " break;\n";
|
|
2272
|
+
}
|
|
2273
|
+
else {
|
|
2274
|
+
DESERIALIZE_FAST +=
|
|
2275
|
+
i3 +
|
|
2276
|
+
" const valueEnd = JSON.Util.scanValueEnd<usize>(srcStart, srcEnd);\n";
|
|
2277
|
+
DESERIALIZE_FAST += i3 + " if (!valueEnd) break;\n";
|
|
2278
|
+
DESERIALIZE_FAST += i3 + " srcStart = valueEnd;\n";
|
|
2279
|
+
}
|
|
2280
|
+
DESERIALIZE_FAST += i3 + "}\n";
|
|
2281
|
+
DESERIALIZE_FAST +=
|
|
2282
|
+
i3 + "srcStart = JSON.Util.skipWhitespace(srcStart, srcEnd);\n";
|
|
2283
|
+
DESERIALIZE_FAST += i3 + "code = load<u16>(srcStart);\n";
|
|
2284
|
+
DESERIALIZE_FAST += i3 + "if (code == 0x7d) {\n";
|
|
2285
|
+
DESERIALIZE_FAST += i3 + " srcStart += 2;\n";
|
|
2286
|
+
DESERIALIZE_FAST += i3 + " complete = true;\n";
|
|
2287
|
+
DESERIALIZE_FAST += i3 + " break;\n";
|
|
2288
|
+
DESERIALIZE_FAST += i3 + "}\n";
|
|
2289
|
+
DESERIALIZE_FAST += i3 + "if (code != 0x2c) break; // comma\n";
|
|
2290
|
+
DESERIALIZE_FAST += i3 + "srcStart += 2;\n";
|
|
2291
|
+
DESERIALIZE_FAST += i2 + "}\n";
|
|
2292
|
+
DESERIALIZE_FAST += i2 + "if (!complete) break;\n";
|
|
2293
|
+
for (let i = 0; i < keyedResets.length; i++) {
|
|
2294
|
+
DESERIALIZE_FAST +=
|
|
2295
|
+
i2 + `if ((seen & (<u32>1 << ${i})) == 0) ${keyedResets[i]}\n`;
|
|
2296
|
+
}
|
|
1768
2297
|
DESERIALIZE_FAST += i2 + "return srcStart;\n";
|
|
1769
2298
|
DESERIALIZE_FAST += i1 + "} while (false);\n\n";
|
|
1770
2299
|
}
|
|
@@ -1797,19 +2326,48 @@ export class JSONTransform extends Visitor {
|
|
|
1797
2326
|
" while (srcEnd > srcStart && JSON.Util.isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2;\n";
|
|
1798
2327
|
DESERIALIZE +=
|
|
1799
2328
|
indent +
|
|
1800
|
-
|
|
2329
|
+
" if (srcStart == srcEnd) return JSON.Util.failProductionParse();\n";
|
|
1801
2330
|
DESERIALIZE +=
|
|
1802
2331
|
indent +
|
|
1803
|
-
" if (load<u16>(srcStart) != 123)
|
|
2332
|
+
" if (load<u16>(srcStart) != 123) return JSON.Util.failProductionParse();\n";
|
|
1804
2333
|
DESERIALIZE +=
|
|
1805
2334
|
indent +
|
|
1806
|
-
" if (load<u16>(srcEnd - 2) != 125)
|
|
2335
|
+
" if (load<u16>(srcEnd - 2) != 125) return JSON.Util.failProductionParse();\n";
|
|
2336
|
+
DESERIALIZE += indent + " const objectStart = srcStart;\n";
|
|
1807
2337
|
DESERIALIZE += indent + " srcStart += 2;\n\n";
|
|
1808
2338
|
DESERIALIZE += indent + " while (srcStart < srcEnd) {\n";
|
|
1809
2339
|
DESERIALIZE += indent + " let code = load<u16>(srcStart);\n";
|
|
2340
|
+
DESERIALIZE += indent + " while (JSON.Util.isSpace(code)) {\n";
|
|
2341
|
+
DESERIALIZE += indent + " srcStart += 2;\n";
|
|
2342
|
+
DESERIALIZE +=
|
|
2343
|
+
indent +
|
|
2344
|
+
" if (srcStart >= srcEnd) return JSON.Util.failProductionParse();\n";
|
|
2345
|
+
DESERIALIZE += indent + " code = load<u16>(srcStart);\n";
|
|
2346
|
+
DESERIALIZE += indent + " }\n";
|
|
2347
|
+
DESERIALIZE += indent + " if (keyStart == 0 && !isKey) {\n";
|
|
2348
|
+
DESERIALIZE += indent + " let prev = srcStart - 2;\n";
|
|
2349
|
+
DESERIALIZE +=
|
|
2350
|
+
indent +
|
|
2351
|
+
" while (prev > objectStart && JSON.Util.isSpace(load<u16>(prev))) prev -= 2;\n";
|
|
2352
|
+
DESERIALIZE += indent + " const prevCode = load<u16>(prev);\n";
|
|
2353
|
+
DESERIALIZE += indent + " if (code == 125) {\n";
|
|
2354
|
+
DESERIALIZE +=
|
|
2355
|
+
indent +
|
|
2356
|
+
" if (prevCode == 44) return JSON.Util.failProductionParse();\n";
|
|
2357
|
+
DESERIALIZE += indent + " return srcStart + 2;\n";
|
|
2358
|
+
DESERIALIZE += indent + " }\n";
|
|
2359
|
+
DESERIALIZE += indent + " if (code == 44) {\n";
|
|
1810
2360
|
DESERIALIZE +=
|
|
1811
2361
|
indent +
|
|
1812
|
-
"
|
|
2362
|
+
" if (prevCode == 123 || prevCode == 44) return JSON.Util.failProductionParse();\n";
|
|
2363
|
+
DESERIALIZE += indent + " } else if (code == 34) {\n";
|
|
2364
|
+
DESERIALIZE +=
|
|
2365
|
+
indent +
|
|
2366
|
+
" if (prevCode != 123 && prevCode != 44) return JSON.Util.failProductionParse();\n";
|
|
2367
|
+
DESERIALIZE += indent + " } else {\n";
|
|
2368
|
+
DESERIALIZE += indent + " return JSON.Util.failProductionParse();\n";
|
|
2369
|
+
DESERIALIZE += indent + " }\n";
|
|
2370
|
+
DESERIALIZE += indent + " }\n";
|
|
1813
2371
|
DESERIALIZE += indent + " if (keyStart == 0) {\n";
|
|
1814
2372
|
DESERIALIZE +=
|
|
1815
2373
|
indent + " if (code == 34 && load<u16>(srcStart - 2) !== 92) {\n";
|
|
@@ -1822,20 +2380,16 @@ export class JSONTransform extends Visitor {
|
|
|
1822
2380
|
' console.log("Key: " + JSON.Util.ptrToStr(keyStart, keyEnd));\n';
|
|
1823
2381
|
DESERIALIZE +=
|
|
1824
2382
|
indent +
|
|
1825
|
-
"
|
|
2383
|
+
" do { srcStart += 2; if (srcStart >= srcEnd) return JSON.Util.failProductionParse(); code = load<u16>(srcStart); } while (JSON.Util.isSpace(code));\n";
|
|
1826
2384
|
DESERIALIZE +=
|
|
1827
2385
|
indent +
|
|
1828
|
-
" if (code !== 58)
|
|
2386
|
+
" if (code !== 58) return JSON.Util.failProductionParse();\n";
|
|
1829
2387
|
DESERIALIZE += indent + " isKey = false;\n";
|
|
1830
2388
|
DESERIALIZE += indent + " } else {\n";
|
|
1831
2389
|
DESERIALIZE += indent + " isKey = true;\n";
|
|
1832
2390
|
DESERIALIZE += indent + " lastIndex = srcStart + 2;\n";
|
|
1833
2391
|
DESERIALIZE += indent + " }\n";
|
|
1834
2392
|
DESERIALIZE += indent + " }\n";
|
|
1835
|
-
if (STRICT)
|
|
1836
|
-
DESERIALIZE +=
|
|
1837
|
-
indent +
|
|
1838
|
-
' else if (!isKey && code != 44 && code != 125) throw new Error("Expected \'\\"\' to start key in JSON object at position " + (srcEnd - srcStart).toString());\n';
|
|
1839
2393
|
DESERIALIZE += indent + " srcStart += 2;\n";
|
|
1840
2394
|
DESERIALIZE += indent + " } else {\n";
|
|
1841
2395
|
const groupMembers = (members) => {
|
|
@@ -1938,8 +2492,18 @@ export class JSONTransform extends Visitor {
|
|
|
1938
2492
|
const getSlowValueStore = (member, valueStart, valueEnd, prefix) => {
|
|
1939
2493
|
if (member.flags.has(PropertyFlags.Lazy))
|
|
1940
2494
|
return getLazyRangeStore(member, valueStart, valueEnd, prefix);
|
|
2495
|
+
const offset = JSON.stringify(member.name);
|
|
2496
|
+
const memberSchema = this.getSchema(member.type);
|
|
2497
|
+
if (memberSchema?.custom) {
|
|
2498
|
+
return (prefix +
|
|
2499
|
+
`store<${member.type}>(changetype<usize>(out), ${getCustomDeserializeExpression(member.type, valueStart, valueEnd)}, offsetof<this>(${offset}));\n`);
|
|
2500
|
+
}
|
|
2501
|
+
if (needsReferenceLoad(member.type)) {
|
|
2502
|
+
return (prefix +
|
|
2503
|
+
`store<${member.type}>(changetype<usize>(out), JSON.__deserialize<${member.type}>(${valueStart}, ${valueEnd}, changetype<usize>(load<${member.type}>(changetype<usize>(out), offsetof<this>(${offset})))), offsetof<this>(${offset}));\n`);
|
|
2504
|
+
}
|
|
1941
2505
|
return (prefix +
|
|
1942
|
-
`store<${member.type}>(changetype<usize>(out), JSON.__deserialize<${member.type}>(${valueStart}, ${valueEnd}), offsetof<this>(${
|
|
2506
|
+
`store<${member.type}>(changetype<usize>(out), JSON.__deserialize<${member.type}>(${valueStart}, ${valueEnd}), offsetof<this>(${offset}));\n`);
|
|
1943
2507
|
};
|
|
1944
2508
|
const getSlowBooleanStore = (member, value, valueStart, valueEnd, prefix) => {
|
|
1945
2509
|
if (member.flags.has(PropertyFlags.Lazy))
|
|
@@ -1965,10 +2529,10 @@ export class JSONTransform extends Visitor {
|
|
|
1965
2529
|
DESERIALIZE += mbElse + "if (code == 34) {\n";
|
|
1966
2530
|
DESERIALIZE += " lastIndex = srcStart;\n";
|
|
1967
2531
|
DESERIALIZE += " srcStart += 2;\n";
|
|
2532
|
+
DESERIALIZE += " let escaped = false;\n";
|
|
1968
2533
|
DESERIALIZE += " while (srcStart < srcEnd) {\n";
|
|
1969
2534
|
DESERIALIZE += " const code = load<u16>(srcStart);\n";
|
|
1970
|
-
DESERIALIZE +=
|
|
1971
|
-
" if (code == 34 && load<u16>(srcStart - 2) !== 92) {\n";
|
|
2535
|
+
DESERIALIZE += " if (code == 34 && !escaped) {\n";
|
|
1972
2536
|
if (DEBUG > 1)
|
|
1973
2537
|
DESERIALIZE +=
|
|
1974
2538
|
' console.log("Value (string, ' +
|
|
@@ -2024,6 +2588,7 @@ export class JSONTransform extends Visitor {
|
|
|
2024
2588
|
}
|
|
2025
2589
|
}, "string");
|
|
2026
2590
|
DESERIALIZE += " }\n";
|
|
2591
|
+
DESERIALIZE += " escaped = code == 92 ? !escaped : false;\n";
|
|
2027
2592
|
DESERIALIZE += " srcStart += 2;\n";
|
|
2028
2593
|
DESERIALIZE += " }\n";
|
|
2029
2594
|
DESERIALIZE += " }\n";
|
|
@@ -2453,6 +3018,11 @@ export class JSONTransform extends Visitor {
|
|
|
2453
3018
|
indentDec();
|
|
2454
3019
|
DESERIALIZE += ` }\n`;
|
|
2455
3020
|
indentDec();
|
|
3021
|
+
DESERIALIZE +=
|
|
3022
|
+
` if (srcStart == srcEnd && lastIndex != 0) {\n` +
|
|
3023
|
+
` const lastCode = load<u16>(lastIndex);\n` +
|
|
3024
|
+
` if (lastCode == 123 || lastCode == 91) return 0;\n` +
|
|
3025
|
+
` }\n`;
|
|
2456
3026
|
DESERIALIZE += ` return srcStart;\n}\n`;
|
|
2457
3027
|
indent = " ";
|
|
2458
3028
|
this.schema.byteSize += 2;
|
|
@@ -2473,11 +3043,6 @@ export class JSONTransform extends Visitor {
|
|
|
2473
3043
|
console.log(INITIALIZE);
|
|
2474
3044
|
console.log(DESERIALIZE_CUSTOM || DESERIALIZE);
|
|
2475
3045
|
}
|
|
2476
|
-
const WIDE_STRUCT_FIELD_LIMIT = 32;
|
|
2477
|
-
if (this.schema.members.length > WIDE_STRUCT_FIELD_LIMIT) {
|
|
2478
|
-
INITIALIZE = INITIALIZE.replace(/^@inline /, "");
|
|
2479
|
-
DESERIALIZE_FAST = DESERIALIZE_FAST.replace(/^@inline /, "");
|
|
2480
|
-
}
|
|
2481
3046
|
const SERIALIZE_METHOD = SimpleParser.parseClassMember(SERIALIZE_CUSTOM || SERIALIZE, node);
|
|
2482
3047
|
const INITIALIZE_METHOD = SimpleParser.parseClassMember(INITIALIZE, node);
|
|
2483
3048
|
const DESERIALIZE_CUSTOM_METHOD = DESERIALIZE_CUSTOM
|
|
@@ -2487,6 +3052,86 @@ export class JSONTransform extends Visitor {
|
|
|
2487
3052
|
const DESERIALIZE_FAST_METHOD = useFastPath
|
|
2488
3053
|
? SimpleParser.parseClassMember(DESERIALIZE_FAST, node)
|
|
2489
3054
|
: null;
|
|
3055
|
+
const mapHeavySource = source
|
|
3056
|
+
.getClasses()
|
|
3057
|
+
.some((candidate) => candidate.members.some((member) => member.kind == NodeKind.FieldDeclaration &&
|
|
3058
|
+
toString(member.type).includes("Map<")));
|
|
3059
|
+
let DESERIALIZE_FAST_PRETTY_METHOD = null;
|
|
3060
|
+
let PRETTY_SPECIALIZED_METHOD = null;
|
|
3061
|
+
if (this.program.options.hasFeature(16) &&
|
|
3062
|
+
useFastPath &&
|
|
3063
|
+
mapHeavySource) {
|
|
3064
|
+
let prettyTail = DESERIALIZE_FAST.slice(fastPrettyTailStart);
|
|
3065
|
+
prettyTail = prettyTail.replaceAll(" if (load<u16>(srcStart) != 0x3a) break; // :\n" +
|
|
3066
|
+
" srcStart += 2;\n" +
|
|
3067
|
+
" srcStart = JSON.Util.skipWhitespace(srcStart, srcEnd);\n", " if (load<u16>(srcStart) != 0x3a) break; // :\n" +
|
|
3068
|
+
" srcStart += 2;\n" +
|
|
3069
|
+
" if (load<u16>(srcStart) == 0x20) srcStart += 2;\n" +
|
|
3070
|
+
" else srcStart = JSON.Util.skipWhitespace(srcStart, srcEnd);\n");
|
|
3071
|
+
const prettyFast = ("__DESERIALIZE_FAST_PRETTY<__JSON_T>(srcStart: usize, srcEnd: usize, out: __JSON_T): usize {\n" +
|
|
3072
|
+
" const start = srcStart;\n" +
|
|
3073
|
+
" const dst = changetype<usize>(out);\n" +
|
|
3074
|
+
objectTraceHeader +
|
|
3075
|
+
prettyTail)
|
|
3076
|
+
.replaceAll("JSON.Util.skipWhitespace(", "JSON.Util.skipPrettyWhitespace(")
|
|
3077
|
+
.replaceAll(".__DESERIALIZE_FAST<", ".__DESERIALIZE_FAST_PRETTY<")
|
|
3078
|
+
.replaceAll(".__DESERIALIZE_FAST(", ".__DESERIALIZE_FAST_PRETTY(");
|
|
3079
|
+
DESERIALIZE_FAST_PRETTY_METHOD = SimpleParser.parseClassMember(prettyFast, node);
|
|
3080
|
+
PRETTY_SPECIALIZED_METHOD = SimpleParser.parseClassMember("__DESERIALIZE_PRETTY_SPECIALIZED(): void {}", node);
|
|
3081
|
+
}
|
|
3082
|
+
else if (this.program.options.hasFeature(16) && useFastPath) {
|
|
3083
|
+
const prettyTail = DESERIALIZE_FAST.slice(fastPrettyTailStart);
|
|
3084
|
+
DESERIALIZE_FAST_PRETTY_METHOD = SimpleParser.parseClassMember("__DESERIALIZE_FAST_PRETTY<__JSON_T>(srcStart: usize, srcEnd: usize, out: __JSON_T): usize {\n" +
|
|
3085
|
+
" const start = srcStart;\n" +
|
|
3086
|
+
" const dst = changetype<usize>(out);\n" +
|
|
3087
|
+
objectTraceHeader +
|
|
3088
|
+
prettyTail +
|
|
3089
|
+
"}", node);
|
|
3090
|
+
}
|
|
3091
|
+
else if (useFastPath) {
|
|
3092
|
+
DESERIALIZE_FAST_PRETTY_METHOD = SimpleParser.parseClassMember("__DESERIALIZE_FAST_PRETTY<__JSON_T>(srcStart: usize, srcEnd: usize, out: __JSON_T): usize {\n" +
|
|
3093
|
+
" return this.__DESERIALIZE_FAST(srcStart, srcEnd, out);\n" +
|
|
3094
|
+
"}", node);
|
|
3095
|
+
}
|
|
3096
|
+
const sourceFreeDeserialize = !DESERIALIZE_CUSTOM &&
|
|
3097
|
+
this.schema.members.every((member) => {
|
|
3098
|
+
const type = stripNull(member.type);
|
|
3099
|
+
return (!member.flags.has(PropertyFlags.Lazy) &&
|
|
3100
|
+
(isPrimitive(type) ||
|
|
3101
|
+
isString(type) ||
|
|
3102
|
+
type == "Date" ||
|
|
3103
|
+
type == "JSON.Raw" ||
|
|
3104
|
+
type == "Raw"));
|
|
3105
|
+
});
|
|
3106
|
+
const SOURCE_FREE_METHOD = sourceFreeDeserialize
|
|
3107
|
+
? SimpleParser.parseClassMember("__DESERIALIZE_SOURCE_FREE(): void {}", node)
|
|
3108
|
+
: null;
|
|
3109
|
+
const FULL_WRITE_METHOD = useFastPath && !supportsFastOptionalPath
|
|
3110
|
+
? SimpleParser.parseClassMember("__DESERIALIZE_FULL_WRITE(): void {}", node)
|
|
3111
|
+
: null;
|
|
3112
|
+
const defaultObjectJSON = USE_DEFAULT_OBJECT_SPECIALIZATION &&
|
|
3113
|
+
this.program.options.hasFeature(16) &&
|
|
3114
|
+
useFastPath &&
|
|
3115
|
+
!supportsFastOptionalPath &&
|
|
3116
|
+
!DESERIALIZE_CUSTOM &&
|
|
3117
|
+
!node.extendsType &&
|
|
3118
|
+
!node.is(2) &&
|
|
3119
|
+
!source.getClasses().some((candidate) => {
|
|
3120
|
+
if (!candidate.extendsType)
|
|
3121
|
+
return false;
|
|
3122
|
+
const base = source.resolveExtendsName(candidate);
|
|
3123
|
+
return base == this.schema.name || base == node.name.text;
|
|
3124
|
+
})
|
|
3125
|
+
? this.getDefaultObjectJSON(this.schema)
|
|
3126
|
+
: null;
|
|
3127
|
+
const DEFAULT_OBJECT_METHOD = defaultObjectJSON
|
|
3128
|
+
? SimpleParser.parseClassMember(this.getDefaultMatcherSource(defaultObjectJSON), node)
|
|
3129
|
+
: null;
|
|
3130
|
+
const DEFAULT_OBJECT_FACTORY_METHOD = defaultObjectJSON
|
|
3131
|
+
? SimpleParser.parseClassMember("@inline __DESERIALIZE_DEFAULT_NEW(): this {\n" +
|
|
3132
|
+
" return changetype<this>(__new(offsetof<this>(), idof<this>())).__INITIALIZE();\n" +
|
|
3133
|
+
"}", node)
|
|
3134
|
+
: null;
|
|
2490
3135
|
if (!node.members.find((v) => v.name.text == "__SERIALIZE"))
|
|
2491
3136
|
node.members.push(SERIALIZE_METHOD);
|
|
2492
3137
|
if (INITIALIZE_METHOD &&
|
|
@@ -2504,6 +3149,26 @@ export class JSONTransform extends Visitor {
|
|
|
2504
3149
|
DESERIALIZE_FAST_METHOD &&
|
|
2505
3150
|
!node.members.find((v) => v.name.text == "__DESERIALIZE_FAST"))
|
|
2506
3151
|
node.members.push(DESERIALIZE_FAST_METHOD);
|
|
3152
|
+
if (!DESERIALIZE_CUSTOM &&
|
|
3153
|
+
DESERIALIZE_FAST_PRETTY_METHOD &&
|
|
3154
|
+
!node.members.find((v) => v.name.text == "__DESERIALIZE_FAST_PRETTY"))
|
|
3155
|
+
node.members.push(DESERIALIZE_FAST_PRETTY_METHOD);
|
|
3156
|
+
if (!DESERIALIZE_CUSTOM &&
|
|
3157
|
+
PRETTY_SPECIALIZED_METHOD &&
|
|
3158
|
+
!node.members.find((v) => v.name.text == "__DESERIALIZE_PRETTY_SPECIALIZED"))
|
|
3159
|
+
node.members.push(PRETTY_SPECIALIZED_METHOD);
|
|
3160
|
+
if (SOURCE_FREE_METHOD &&
|
|
3161
|
+
!node.members.find((v) => v.name.text == "__DESERIALIZE_SOURCE_FREE"))
|
|
3162
|
+
node.members.push(SOURCE_FREE_METHOD);
|
|
3163
|
+
if (FULL_WRITE_METHOD &&
|
|
3164
|
+
!node.members.find((v) => v.name.text == "__DESERIALIZE_FULL_WRITE"))
|
|
3165
|
+
node.members.push(FULL_WRITE_METHOD);
|
|
3166
|
+
if (DEFAULT_OBJECT_METHOD &&
|
|
3167
|
+
!node.members.find((v) => v.name.text == "__DESERIALIZE_DEFAULT"))
|
|
3168
|
+
node.members.push(DEFAULT_OBJECT_METHOD);
|
|
3169
|
+
if (DEFAULT_OBJECT_FACTORY_METHOD &&
|
|
3170
|
+
!node.members.find((v) => v.name.text == "__DESERIALIZE_DEFAULT_NEW"))
|
|
3171
|
+
node.members.push(DEFAULT_OBJECT_FACTORY_METHOD);
|
|
2507
3172
|
if (useFastPath && !DESERIALIZE_CUSTOM) {
|
|
2508
3173
|
for (const chunk of fastChunkMethods) {
|
|
2509
3174
|
const chunkMethod = SimpleParser.parseClassMember(chunk, node);
|
|
@@ -2515,14 +3180,21 @@ export class JSONTransform extends Visitor {
|
|
|
2515
3180
|
}
|
|
2516
3181
|
getSchema(name) {
|
|
2517
3182
|
name = stripNull(name);
|
|
2518
|
-
|
|
3183
|
+
const local = this.schemas
|
|
2519
3184
|
.get(this.schema.node.range.source.internalPath)
|
|
2520
|
-
.find((s) => s.name == name) || null
|
|
3185
|
+
.find((s) => s.name == name) || null;
|
|
3186
|
+
if (local)
|
|
3187
|
+
return local;
|
|
3188
|
+
const exact = this.schema.deps.filter((schema) => schema && schema.name == name);
|
|
3189
|
+
if (exact.length == 1)
|
|
3190
|
+
return exact[0];
|
|
3191
|
+
const qualified = this.schema.deps.filter((schema) => schema && schema.name.endsWith("." + name));
|
|
3192
|
+
return qualified.length == 1 ? qualified[0] : null;
|
|
2521
3193
|
}
|
|
2522
3194
|
generateEmptyMethods(node) {
|
|
2523
|
-
const SERIALIZE_EMPTY = "
|
|
2524
|
-
const INITIALIZE_EMPTY = "
|
|
2525
|
-
const DESERIALIZE_SLOW_EMPTY = "
|
|
3195
|
+
const SERIALIZE_EMPTY = "__SERIALIZE(ptr: usize): void {\n bs.proposeSize(4);\n store<u32>(bs.offset, 8192123);\n bs.offset += 4;\n}";
|
|
3196
|
+
const INITIALIZE_EMPTY = "__INITIALIZE(): this {\n return this;\n}";
|
|
3197
|
+
const DESERIALIZE_SLOW_EMPTY = "__DESERIALIZE_SLOW<__JSON_T>(srcStart: usize, srcEnd: usize, out: __JSON_T): usize {\n return srcEnd;\n}";
|
|
2526
3198
|
if (DEBUG > 0) {
|
|
2527
3199
|
console.log(SERIALIZE_EMPTY);
|
|
2528
3200
|
console.log(INITIALIZE_EMPTY);
|
|
@@ -2617,6 +3289,7 @@ export class JSONTransform extends Visitor {
|
|
|
2617
3289
|
fieldHelper("deserializeUnsignedField", "__deserializeUnsignedField"),
|
|
2618
3290
|
fieldHelper("deserializeFloatField", "__deserializeFloatField"),
|
|
2619
3291
|
fieldHelper("deserializeStringField", "__deserializeStringField"),
|
|
3292
|
+
fieldHelper("deserializeStringFieldTrusted", "__deserializeStringFieldTrusted"),
|
|
2620
3293
|
fieldHelper("deserializeArrayField_SWAR", "__deserializeArrayField_SWAR"),
|
|
2621
3294
|
fieldHelper("deserializeMapField", "__deserializeMapField"),
|
|
2622
3295
|
fieldHelper("deserializeSetField", "__deserializeSetField"),
|
|
@@ -2698,9 +3371,11 @@ export class JSONTransform extends Visitor {
|
|
|
2698
3371
|
"Date",
|
|
2699
3372
|
"JSON.Value",
|
|
2700
3373
|
"JSON.Obj",
|
|
3374
|
+
"JSON.Arr",
|
|
2701
3375
|
"JSON.Raw",
|
|
2702
3376
|
"Value",
|
|
2703
3377
|
"Obj",
|
|
3378
|
+
"Arr",
|
|
2704
3379
|
"Raw",
|
|
2705
3380
|
...this.schemas
|
|
2706
3381
|
.get(this.schema.node.range.source.internalPath)
|
|
@@ -2729,6 +3404,8 @@ var JSONMode;
|
|
|
2729
3404
|
JSONMode[JSONMode["NAIVE"] = 2] = "NAIVE";
|
|
2730
3405
|
})(JSONMode || (JSONMode = {}));
|
|
2731
3406
|
let MODE = JSONMode.SWAR;
|
|
3407
|
+
let MODE_TEXT = "SWAR";
|
|
3408
|
+
const STAGES = process.env["JSON_STAGES"] !== undefined;
|
|
2732
3409
|
export default class Transformer extends Transform {
|
|
2733
3410
|
afterInitialize(program) {
|
|
2734
3411
|
if (program.options.hasFeature(16))
|
|
@@ -2749,7 +3426,23 @@ export default class Transformer extends Transform {
|
|
|
2749
3426
|
}
|
|
2750
3427
|
}
|
|
2751
3428
|
}
|
|
3429
|
+
switch (MODE) {
|
|
3430
|
+
case JSONMode.SWAR:
|
|
3431
|
+
MODE_TEXT = "SWAR";
|
|
3432
|
+
break;
|
|
3433
|
+
case JSONMode.SIMD:
|
|
3434
|
+
MODE_TEXT = "SIMD";
|
|
3435
|
+
break;
|
|
3436
|
+
case JSONMode.NAIVE:
|
|
3437
|
+
MODE_TEXT = "NAIVE";
|
|
3438
|
+
break;
|
|
3439
|
+
}
|
|
3440
|
+
if (STAGES)
|
|
3441
|
+
console.log("[transform]: Finished initializing transformer in " +
|
|
3442
|
+
MODE_TEXT +
|
|
3443
|
+
" mode");
|
|
2752
3444
|
program.registerConstantInteger("JSON_MODE", Type.i32, i64_new(MODE));
|
|
3445
|
+
program.registerConstantInteger("JSON_STRICT", Type.bool, STRICT ? i64_one : i64_zero);
|
|
2753
3446
|
if (JSON_CACHE_CONFIG.enabled) {
|
|
2754
3447
|
program.registerConstantInteger("JSON_CACHE", Type.bool, i64_one);
|
|
2755
3448
|
program.registerConstantInteger("JSON_CACHE_SIZE", Type.u32, i64_new(JSON_CACHE_CONFIG.bytes));
|
|
@@ -2757,6 +3450,8 @@ export default class Transformer extends Transform {
|
|
|
2757
3450
|
}
|
|
2758
3451
|
afterParse(parser) {
|
|
2759
3452
|
const transformer = new JSONTransform();
|
|
3453
|
+
if (STAGES)
|
|
3454
|
+
console.log("[transform]: Walking AST and generating schemas");
|
|
2760
3455
|
const sources = parser.sources
|
|
2761
3456
|
.filter((source) => {
|
|
2762
3457
|
const p = source.internalPath;
|
|
@@ -2812,23 +3507,12 @@ export default class Transformer extends Transform {
|
|
|
2812
3507
|
source.sourceKind = 2;
|
|
2813
3508
|
}
|
|
2814
3509
|
}
|
|
3510
|
+
if (STAGES)
|
|
3511
|
+
console.log("[transform]: Finished generating " +
|
|
3512
|
+
transformer.schemas.size +
|
|
3513
|
+
" schemas");
|
|
2815
3514
|
}
|
|
2816
3515
|
}
|
|
2817
|
-
function sortMembers(members) {
|
|
2818
|
-
return members.sort((a, b) => {
|
|
2819
|
-
const aMove = a.flags.has(PropertyFlags.OmitIf) || a.flags.has(PropertyFlags.OmitNull);
|
|
2820
|
-
const bMove = b.flags.has(PropertyFlags.OmitIf) || b.flags.has(PropertyFlags.OmitNull);
|
|
2821
|
-
if (aMove && !bMove) {
|
|
2822
|
-
return -1;
|
|
2823
|
-
}
|
|
2824
|
-
else if (!aMove && bMove) {
|
|
2825
|
-
return 1;
|
|
2826
|
-
}
|
|
2827
|
-
else {
|
|
2828
|
-
return 0;
|
|
2829
|
-
}
|
|
2830
|
-
});
|
|
2831
|
-
}
|
|
2832
3516
|
function toU16(data, offset = 0) {
|
|
2833
3517
|
return data.charCodeAt(offset + 0);
|
|
2834
3518
|
}
|
|
@@ -3011,6 +3695,8 @@ function lazyTypeCost(type, source, parser) {
|
|
|
3011
3695
|
base === "Value" ||
|
|
3012
3696
|
base === "JSON.Obj" ||
|
|
3013
3697
|
base === "Obj" ||
|
|
3698
|
+
base === "JSON.Arr" ||
|
|
3699
|
+
base === "Arr" ||
|
|
3014
3700
|
base === "JSON.Raw" ||
|
|
3015
3701
|
base === "Raw")
|
|
3016
3702
|
return 15;
|
|
@@ -3076,6 +3762,8 @@ function estimatedSerializedByteSize(type, source, parser) {
|
|
|
3076
3762
|
}
|
|
3077
3763
|
else if (baseType == "JSON.Obj" ||
|
|
3078
3764
|
baseType == "Obj" ||
|
|
3765
|
+
baseType == "JSON.Arr" ||
|
|
3766
|
+
baseType == "Arr" ||
|
|
3079
3767
|
baseType == "JSON.Raw" ||
|
|
3080
3768
|
baseType == "Raw" ||
|
|
3081
3769
|
baseType == "JSON.Value" ||
|
|
@@ -3134,6 +3822,21 @@ export function stripNull(type) {
|
|
|
3134
3822
|
}
|
|
3135
3823
|
return type;
|
|
3136
3824
|
}
|
|
3825
|
+
function sortMembers(members) {
|
|
3826
|
+
return members.sort((a, b) => {
|
|
3827
|
+
const aMove = a.flags.has(PropertyFlags.OmitIf) || a.flags.has(PropertyFlags.OmitNull);
|
|
3828
|
+
const bMove = b.flags.has(PropertyFlags.OmitIf) || b.flags.has(PropertyFlags.OmitNull);
|
|
3829
|
+
if (aMove && !bMove) {
|
|
3830
|
+
return -1;
|
|
3831
|
+
}
|
|
3832
|
+
else if (!aMove && bMove) {
|
|
3833
|
+
return 1;
|
|
3834
|
+
}
|
|
3835
|
+
else {
|
|
3836
|
+
return 0;
|
|
3837
|
+
}
|
|
3838
|
+
});
|
|
3839
|
+
}
|
|
3137
3840
|
function getComparison(data) {
|
|
3138
3841
|
switch (data.length << 1) {
|
|
3139
3842
|
case 2: {
|