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.
Files changed (108) hide show
  1. package/CHANGELOG.md +54 -29
  2. package/README.md +126 -33
  3. package/assembly/custom/chars.ts +39 -78
  4. package/assembly/deserialize/error.ts +21 -0
  5. package/assembly/deserialize/index/arbitrary.ts +50 -11
  6. package/assembly/deserialize/index/array.ts +2 -1
  7. package/assembly/deserialize/index/bool.ts +1 -1
  8. package/assembly/deserialize/index/float.ts +2 -4
  9. package/assembly/deserialize/index/integer.ts +2 -4
  10. package/assembly/deserialize/index/object.ts +9 -1
  11. package/assembly/deserialize/index/string.ts +39 -7
  12. package/assembly/deserialize/index/unsigned.ts +2 -4
  13. package/assembly/deserialize/index.ts +0 -1
  14. package/assembly/deserialize/naive/array/arbitrary.ts +3 -2
  15. package/assembly/deserialize/naive/array/array.ts +1 -0
  16. package/assembly/deserialize/naive/array/bool.ts +21 -11
  17. package/assembly/deserialize/naive/array/box.ts +13 -33
  18. package/assembly/deserialize/naive/array/float.ts +23 -13
  19. package/assembly/deserialize/naive/array/integer.ts +34 -22
  20. package/assembly/deserialize/naive/array/map.ts +1 -1
  21. package/assembly/deserialize/naive/array/object.ts +2 -1
  22. package/assembly/deserialize/naive/array/string.ts +29 -16
  23. package/assembly/deserialize/naive/array/struct.ts +19 -1
  24. package/assembly/deserialize/naive/bool.ts +18 -10
  25. package/assembly/deserialize/naive/date.ts +1 -2
  26. package/assembly/deserialize/naive/float.ts +3 -9
  27. package/assembly/deserialize/naive/integer.ts +1 -2
  28. package/assembly/deserialize/naive/map.ts +342 -43
  29. package/assembly/deserialize/naive/object.ts +250 -55
  30. package/assembly/deserialize/naive/raw.ts +10 -2
  31. package/assembly/deserialize/naive/set.ts +2 -5
  32. package/assembly/deserialize/naive/staticarray.ts +4 -4
  33. package/assembly/deserialize/naive/string.ts +46 -60
  34. package/assembly/deserialize/naive/unsigned.ts +1 -2
  35. package/assembly/deserialize/parseMode.ts +216 -0
  36. package/assembly/deserialize/simd/array/integer.ts +8 -10
  37. package/assembly/deserialize/simd/float.ts +38 -34
  38. package/assembly/deserialize/simd/integer.ts +2 -7
  39. package/assembly/deserialize/simd/string.ts +84 -39
  40. package/assembly/deserialize/string-validation.ts +34 -0
  41. package/assembly/deserialize/swar/array/arbitrary.ts +1 -2
  42. package/assembly/deserialize/swar/array/array.ts +64 -15
  43. package/assembly/deserialize/swar/array/bool.ts +15 -5
  44. package/assembly/deserialize/swar/array/box.ts +1 -2
  45. package/assembly/deserialize/swar/array/float.ts +399 -30
  46. package/assembly/deserialize/swar/array/generic.ts +13 -4
  47. package/assembly/deserialize/swar/array/integer.ts +29 -78
  48. package/assembly/deserialize/swar/array/map.ts +1 -2
  49. package/assembly/deserialize/swar/array/object.ts +12 -4
  50. package/assembly/deserialize/swar/array/raw.ts +1 -2
  51. package/assembly/deserialize/swar/array/shared.ts +7 -14
  52. package/assembly/deserialize/swar/array/string.ts +37 -13
  53. package/assembly/deserialize/swar/array/struct.ts +109 -21
  54. package/assembly/deserialize/swar/array.ts +1 -3
  55. package/assembly/deserialize/swar/float.ts +23 -21
  56. package/assembly/deserialize/swar/integer.ts +2 -7
  57. package/assembly/deserialize/swar/string.ts +81 -124
  58. package/assembly/deserialize/swar/typedarray.ts +4 -4
  59. package/assembly/index.d.ts +35 -24
  60. package/assembly/index.ts +1775 -281
  61. package/assembly/serialize/index/arbitrary.ts +70 -4
  62. package/assembly/serialize/index/bool.ts +1 -1
  63. package/assembly/serialize/index/float.ts +1 -5
  64. package/assembly/serialize/index/integer.ts +1 -1
  65. package/assembly/serialize/index/jsonarray.ts +51 -0
  66. package/assembly/serialize/index/object.ts +42 -3
  67. package/assembly/serialize/index/string.ts +1 -2
  68. package/assembly/serialize/index/typedarray.ts +1 -2
  69. package/assembly/serialize/index.ts +1 -0
  70. package/assembly/serialize/naive/array.ts +87 -34
  71. package/assembly/serialize/naive/bool.ts +0 -1
  72. package/assembly/serialize/naive/float.ts +15 -29
  73. package/assembly/serialize/naive/integer.ts +1 -5
  74. package/assembly/serialize/naive/map.ts +37 -0
  75. package/assembly/serialize/naive/raw.ts +1 -2
  76. package/assembly/serialize/naive/set.ts +0 -5
  77. package/assembly/serialize/naive/staticarray.ts +0 -6
  78. package/assembly/serialize/naive/string.ts +1 -10
  79. package/assembly/serialize/naive/typedarray.ts +0 -7
  80. package/assembly/serialize/simd/string.ts +1 -3
  81. package/assembly/serialize/swar/string.ts +1 -2
  82. package/assembly/util/atoi-fast.ts +4 -14
  83. package/assembly/util/bytes.ts +1 -2
  84. package/assembly/util/eisel-lemire.ts +179 -0
  85. package/assembly/util/idofd.ts +1 -2
  86. package/assembly/util/isSpace.ts +1 -2
  87. package/assembly/util/itoa-fast.ts +6 -9
  88. package/assembly/util/nextPowerOf2.ts +1 -2
  89. package/assembly/util/parsefloat-fast.ts +3 -5
  90. package/assembly/util/prettyWhitespaceSimd.ts +69 -0
  91. package/assembly/util/ptrToStr.ts +1 -2
  92. package/assembly/util/scanValueEndSimd.ts +83 -22
  93. package/assembly/util/scanValueEndSwar.ts +67 -25
  94. package/assembly/util/scientific.ts +5 -8
  95. package/assembly/util/snp.ts +1 -2
  96. package/assembly/util/swar-int.ts +5 -10
  97. package/assembly/util/swar.ts +2 -4
  98. package/assembly/util/validateJson.ts +338 -0
  99. package/lib/as-bs.ts +51 -51
  100. package/package.json +25 -8
  101. package/transform/lib/index.d.ts +6 -0
  102. package/transform/lib/index.js +874 -171
  103. package/transform/lib/types.d.ts +3 -1
  104. package/transform/lib/types.js +6 -0
  105. package/assembly/deserialize/index/struct.ts +0 -1
  106. package/assembly/deserialize/naive/struct.ts +0 -21
  107. package/assembly/util/dragonbox-cache.ts +0 -445
  108. package/assembly/util/dragonbox.ts +0 -652
@@ -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 the custom methods " +
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 = ${fieldDefault != null ? "u64.MAX_VALUE" : "0"};`,
401
- `private __${fname}_val: ${valueType} = ${fieldDefault ?? valueDefault};`,
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 = "@inline __DESERIALIZE_FAST<__JSON_T>(srcStart: usize, srcEnd: usize, out: __JSON_T): usize {\n";
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
- if (!this.schema.static)
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 hasOptionalMembers = hasOmitIfMembers || hasOmitNullMembers;
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
- if (member.value != "null" &&
946
- member.value != "0" &&
947
- member.value != "0.0" &&
948
- member.value != "false") {
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
- out.push(` ${srcPtr} = ${STRING_FIELD_DESERIALIZER}<${member.type}>(${valuePtr}, srcEnd, ${outPtr}, ${fieldOffset});`);
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 = ${srcPtr};`);
1279
- out.push(" let depth: i32 = 0;");
1280
- out.push(" let inString = false;");
1281
- out.push(` while (${srcPtr} < srcEnd) {`);
1282
- out.push(` const code = load<u16>(${srcPtr});`);
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(` ${srcPtr} = changetype<nonnull<${resolvedType}>>(value).__DESERIALIZE_FAST<${resolvedType}>(${valuePtr}, srcEnd, value);`);
1342
- out.push(` if (!${srcPtr}) break;`);
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 >= value.length) value.push(changetype<${valueType} | null>(0));`);
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(` store<usize>(value.dataStart + ((<usize>index) << alignof<${valueType}>()), 0);`);
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, value.dataStart + ((<usize>index) << alignof<${valueType}>()));`);
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 >= value.length) value.push("");');
1414
- out.push(` ${srcPtr} = ${STRING_FIELD_DESERIALIZER}<${valueType}>(${srcPtr}, srcEnd, value.dataStart + ((<usize>index) << alignof<${valueType}>()));`);
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 < value.length) {");
1453
- out.push(" item = unchecked(value[index]);");
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(" unchecked((value[index] = item));");
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(` ${srcPtr} = changetype<nonnull<${valueType}>>(item).__DESERIALIZE_FAST<${valueType}>(${srcPtr}, srcEnd, item);`);
1463
- out.push(` if (!${srcPtr}) break;`);
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
- out.push(` ${srcPtr} = __deserializeArrayField_SWAR<${resolvedType}>(${valuePtr}, srcEnd, ${outPtr}, ${fieldOffset});`);
1485
- out.push(` if (!${srcPtr}) break;`);
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, tag, callIndent, needsKp) => {
1535
- if (blocks.length <= FAST_CHUNK_SIZE)
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 deserializerFirst = getDeserializer(member.type, "srcStart", "dst", member, inlineStringValue ? firstKeyOffset : 0, true);
1580
- const deserializerNext = getDeserializer(member.type, "srcStart", "dst", member, inlineStringValue ? nextKeyOffset : 0, true);
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
- blk +=
1605
- indent +
1606
- `if (JSON.Util.isSpace(load<u16>(${inlineStringValue ? `srcStart + ${firstKeyOffset}` : "srcStart"}))) break;\n`;
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
- blk +=
1630
- indent +
1631
- `if (JSON.Util.isSpace(load<u16>(${inlineStringValue ? `srcStart + ${nextKeyOffset}` : "srcStart"}))) break;\n`;
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 ? keyOffset : 0, true);
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
- blk +=
1664
- indent +
1665
- `if (JSON.Util.isSpace(load<u16>(${inlineStringValue ? `srcStart + ${keyOffset}` : "srcStart"}))) break;\n`;
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 ( // ${key}\n${i2} ` +
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
- blk += i2 + "kp = JSON.Util.skipWhitespace(srcStart, srcEnd);\n";
1742
- if (multi && i > 0) {
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
- i2 +
1745
- "if (seenAny && load<u16>(kp) == 0x2c) kp = JSON.Util.skipWhitespace(kp + 2, srcEnd);\n";
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
- ' if (srcStart - srcEnd == 0) throw new Error("Input string had zero length or was all whitespace");\n';
2329
+ " if (srcStart == srcEnd) return JSON.Util.failProductionParse();\n";
1801
2330
  DESERIALIZE +=
1802
2331
  indent +
1803
- " if (load<u16>(srcStart) != 123) throw new Error(\"Expected '{' at start of object at position \" + (srcEnd - srcStart).toString());\n";
2332
+ " if (load<u16>(srcStart) != 123) return JSON.Util.failProductionParse();\n";
1804
2333
  DESERIALIZE +=
1805
2334
  indent +
1806
- " if (load<u16>(srcEnd - 2) != 125) throw new Error(\"Expected '}' at end of object at position \" + (srcEnd - srcStart).toString());\n";
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
- " while (JSON.Util.isSpace(code)) code = load<u16>(srcStart += 2);\n";
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
- " while (JSON.Util.isSpace((code = load<u16>((srcStart += 2))))) {}\n";
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) throw new Error(\"Expected ':' after key at position \" + (srcEnd - srcStart).toString());\n";
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>(${JSON.stringify(member.name)}));\n`);
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
- return (this.schemas
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 = "@inline __SERIALIZE(ptr: usize): void {\n bs.proposeSize(4);\n store<u32>(bs.offset, 8192123);\n bs.offset += 4;\n}";
2524
- const INITIALIZE_EMPTY = "@inline __INITIALIZE(): this {\n return this;\n}";
2525
- const DESERIALIZE_SLOW_EMPTY = "@inline __DESERIALIZE_SLOW<__JSON_T>(srcStart: usize, srcEnd: usize, out: __JSON_T): usize {\n return srcEnd;\n}";
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: {