seroval 1.6.10 → 1.6.12

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.
@@ -395,6 +395,29 @@ function getObjectFlag(obj) {
395
395
  return 1;
396
396
  }
397
397
  //#endregion
398
+ //#region src/core/utils/typed-array.ts
399
+ function getTypedArrayConstructor(name) {
400
+ switch (name) {
401
+ case "Int8Array": return Int8Array;
402
+ case "Int16Array": return Int16Array;
403
+ case "Int32Array": return Int32Array;
404
+ case "Uint8Array": return Uint8Array;
405
+ case "Uint16Array": return Uint16Array;
406
+ case "Uint32Array": return Uint32Array;
407
+ case "Uint8ClampedArray": return Uint8ClampedArray;
408
+ case "Float32Array": return Float32Array;
409
+ case "Float64Array": return Float64Array;
410
+ case "BigInt64Array": return BigInt64Array;
411
+ case "BigUint64Array": return BigUint64Array;
412
+ default: throw new SerovalUnknownTypedArrayError(name);
413
+ }
414
+ }
415
+ let typedArrayNameGetter;
416
+ function getTypedArrayName(value) {
417
+ if (!typedArrayNameGetter) typedArrayNameGetter = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(Int8Array.prototype), Symbol.toStringTag).get;
418
+ return typedArrayNameGetter.call(value);
419
+ }
420
+ //#endregion
398
421
  //#region src/core/base-primitives.ts
399
422
  function createNumberNode(value) {
400
423
  switch (value) {
@@ -442,11 +465,11 @@ function createBoxedNode(id, boxed) {
442
465
  const MAX_TYPED_ARRAY_LENGTH = 1e6;
443
466
  function createTypedArrayNode(id, current, buffer) {
444
467
  if (current.length > MAX_TYPED_ARRAY_LENGTH) throw new SerovalUnsupportedTypeError(current);
445
- return createSerovalNode(15, id, void 0, current.constructor.name, void 0, void 0, void 0, void 0, buffer, current.byteOffset, void 0, current.length);
468
+ return createSerovalNode(15, id, void 0, getTypedArrayName(current), void 0, void 0, void 0, void 0, buffer, current.byteOffset, void 0, current.length);
446
469
  }
447
470
  function createBigIntTypedArrayNode(id, current, buffer) {
448
471
  if (current.length > MAX_TYPED_ARRAY_LENGTH) throw new SerovalUnsupportedTypeError(current);
449
- return createSerovalNode(16, id, void 0, current.constructor.name, void 0, void 0, void 0, void 0, buffer, current.byteOffset, void 0, current.length);
472
+ return createSerovalNode(16, id, void 0, getTypedArrayName(current), void 0, void 0, void 0, void 0, buffer, current.byteOffset, void 0, current.length);
450
473
  }
451
474
  function createDataViewNode(id, current, buffer) {
452
475
  if (current.byteLength > MAX_TYPED_ARRAY_LENGTH) throw new SerovalUnsupportedTypeError(current);
@@ -980,11 +1003,18 @@ function parseAsyncIteratorFactory(ctx) {
980
1003
  function createObjectNode(id, current, empty, record) {
981
1004
  return createSerovalNode(empty ? 11 : 10, id, void 0, void 0, void 0, record, void 0, void 0, void 0, void 0, getObjectFlag(current), void 0);
982
1005
  }
983
- function createMapNode(ctx, id, k, v) {
1006
+ /**
1007
+ * Gets the sentinel node for a Map. Call it before parsing the entries, so
1008
+ * that an outer Map declares the sentinel before any Map inside it uses it.
1009
+ */
1010
+ function parseMapSentinel(ctx) {
1011
+ return parseSpecialReference(ctx, 0);
1012
+ }
1013
+ function createMapNode(id, sentinel, k, v) {
984
1014
  return createSerovalNode(8, id, void 0, void 0, void 0, void 0, {
985
1015
  k,
986
1016
  v
987
- }, void 0, parseSpecialReference(ctx, 0), void 0, void 0, void 0);
1017
+ }, void 0, sentinel, void 0, void 0, void 0);
988
1018
  }
989
1019
  function createPromiseConstructorNode(ctx, id, resolver) {
990
1020
  return createSerovalNode(22, id, resolver, void 0, void 0, void 0, void 0, void 0, parseSpecialReference(ctx, 1), void 0, void 0, void 0);
@@ -992,8 +1022,7 @@ function createPromiseConstructorNode(ctx, id, resolver) {
992
1022
  function getArrayBufferView(ctx, current) {
993
1023
  if (!ctx.compactArrayBufferViews) return current;
994
1024
  const buffer = new Uint8Array(current.buffer, current.byteOffset, current.byteLength).slice().buffer;
995
- const Constructor = current.constructor;
996
- return new Constructor(buffer);
1025
+ return new (current instanceof DataView ? DataView : getTypedArrayConstructor(getTypedArrayName(current)))(buffer);
997
1026
  }
998
1027
  function encodeArrayBuffer(current) {
999
1028
  if (typeof Buffer !== "undefined") return Buffer.from(current).toString("base64");
@@ -1088,13 +1117,14 @@ async function parseAggregateError$1(ctx, depth, id, current) {
1088
1117
  return createAggregateErrorNode(id, current, options ? await parseProperties$1(ctx, depth, options) : void 0);
1089
1118
  }
1090
1119
  async function parseMap$1(ctx, depth, id, current) {
1120
+ const sentinel = parseMapSentinel(ctx.base);
1091
1121
  const keyNodes = [];
1092
1122
  const valueNodes = [];
1093
1123
  for (const [key, value] of current.entries()) {
1094
1124
  keyNodes.push(await parseAsync(ctx, depth, key));
1095
1125
  valueNodes.push(await parseAsync(ctx, depth, value));
1096
1126
  }
1097
- return createMapNode(ctx.base, id, keyNodes, valueNodes);
1127
+ return createMapNode(id, sentinel, keyNodes, valueNodes);
1098
1128
  }
1099
1129
  async function parseSet$1(ctx, depth, id, current) {
1100
1130
  const items = [];
@@ -1279,24 +1309,6 @@ function resolvePlugins(plugins) {
1279
1309
  }
1280
1310
  }
1281
1311
  //#endregion
1282
- //#region src/core/utils/typed-array.ts
1283
- function getTypedArrayConstructor(name) {
1284
- switch (name) {
1285
- case "Int8Array": return Int8Array;
1286
- case "Int16Array": return Int16Array;
1287
- case "Int32Array": return Int32Array;
1288
- case "Uint8Array": return Uint8Array;
1289
- case "Uint16Array": return Uint16Array;
1290
- case "Uint32Array": return Uint32Array;
1291
- case "Uint8ClampedArray": return Uint8ClampedArray;
1292
- case "Float32Array": return Float32Array;
1293
- case "Float64Array": return Float64Array;
1294
- case "BigInt64Array": return BigInt64Array;
1295
- case "BigUint64Array": return BigUint64Array;
1296
- default: throw new SerovalUnknownTypedArrayError(name);
1297
- }
1298
- }
1299
- //#endregion
1300
1312
  //#region src/core/utils/valid-properties.ts
1301
1313
  function isValidKey(key) {
1302
1314
  switch (key) {
@@ -1766,30 +1778,36 @@ function getAssignmentExpression(assignment) {
1766
1778
  case 4: return "Object.defineProperty(" + assignment.s + ",\"__proto__\",{value:" + assignment.k + ",configurable:!0,enumerable:!0,writable:!0})";
1767
1779
  }
1768
1780
  }
1781
+ const MAX_ASSIGNMENT_CHAIN_LENGTH = 100;
1769
1782
  function mergeAssignments(assignments) {
1770
1783
  const newAssignments = [];
1771
1784
  let current = assignments[0];
1785
+ let chainLength = 1;
1772
1786
  for (let i = 1, len = assignments.length, item, prev = current; i < len; i++) {
1773
1787
  item = assignments[i];
1774
- if (item.t === 0 && item.v === prev.v) current = {
1788
+ if (chainLength === MAX_ASSIGNMENT_CHAIN_LENGTH) {
1789
+ newAssignments.push(current);
1790
+ current = item;
1791
+ chainLength = 0;
1792
+ } else if (item.t === 0 && prev.t === 0 && item.v === prev.v) current = {
1775
1793
  t: 0,
1776
1794
  s: item.s,
1777
1795
  k: void 0,
1778
1796
  v: getAssignmentExpression(current)
1779
1797
  };
1780
- else if (item.t === 2 && item.s === prev.s) current = {
1798
+ else if (item.t === 2 && prev.t === 2 && item.s === prev.s) current = {
1781
1799
  t: 2,
1782
1800
  s: getAssignmentExpression(current),
1783
1801
  k: item.k,
1784
1802
  v: item.v
1785
1803
  };
1786
- else if (item.t === 1 && item.s === prev.s) current = {
1804
+ else if (item.t === 1 && prev.t === 1 && item.s === prev.s) current = {
1787
1805
  t: 1,
1788
1806
  s: getAssignmentExpression(current),
1789
1807
  k: void 0,
1790
1808
  v: item.v
1791
1809
  };
1792
- else if (item.t === 3 && item.s === prev.s) current = {
1810
+ else if (item.t === 3 && prev.t === 2 && item.s === prev.s) current = {
1793
1811
  t: 3,
1794
1812
  s: getAssignmentExpression(current),
1795
1813
  k: item.k,
@@ -1798,7 +1816,9 @@ function mergeAssignments(assignments) {
1798
1816
  else {
1799
1817
  newAssignments.push(current);
1800
1818
  current = item;
1819
+ chainLength = 0;
1801
1820
  }
1821
+ chainLength++;
1802
1822
  prev = item;
1803
1823
  }
1804
1824
  newAssignments.push(current);
@@ -1993,6 +2013,22 @@ function isIndexedValueInStack(ctx, node) {
1993
2013
  return node.t === 4 && ctx.stack.includes(node.i);
1994
2014
  }
1995
2015
  /**
2016
+ * Checks if the node, or any node inside it, is read from more than one
2017
+ * place. Such a node must not be created inside a deferred assignment,
2018
+ * because the places that read it may run before the assignment does.
2019
+ */
2020
+ function hasMarkedNode(ctx, node) {
2021
+ if (Array.isArray(node)) {
2022
+ for (let i = 0, len = node.length; i < len; i++) if (hasMarkedNode(ctx, node[i])) return true;
2023
+ return false;
2024
+ }
2025
+ if (node == null || typeof node !== "object") return false;
2026
+ const current = node;
2027
+ if (current.t !== 4 && typeof current.i === "number" && isSerializerRefMarked(ctx, current.i)) return true;
2028
+ for (const key in current) if (hasMarkedNode(ctx, current[key])) return true;
2029
+ return false;
2030
+ }
2031
+ /**
1996
2032
  * Produces an assignment expression. `id` generates a reference
1997
2033
  * parameter (through `getRefParam`) and has the option to
1998
2034
  * return the reference parameter directly or assign a value to
@@ -2202,7 +2238,8 @@ function serializeMapEntry(ctx, id, key, val, sentinel) {
2202
2238
  createSetAssignment(ctx, id, keyRef, getRefParam(ctx, val.i));
2203
2239
  return "";
2204
2240
  }
2205
- if (val.t !== 4 && val.i != null && isSerializerRefMarked(base, val.i)) {
2241
+ if (val.t !== 4 && val.i != null && hasMarkedNode(base, val)) {
2242
+ markSerializerRef(base, val.i);
2206
2243
  const serialized = "(" + serialize$1(ctx, val) + ",[" + sentinel + "," + sentinel + "])";
2207
2244
  createSetAssignment(ctx, id, keyRef, getRefParam(ctx, val.i));
2208
2245
  createDeleteAssignment(ctx, id, sentinel);
@@ -2217,7 +2254,8 @@ function serializeMapEntry(ctx, id, key, val, sentinel) {
2217
2254
  if (isIndexedValueInStack(base, val)) {
2218
2255
  const valueRef = getRefParam(ctx, val.i);
2219
2256
  markSerializerRef(base, id);
2220
- if (key.t !== 4 && key.i != null && isSerializerRefMarked(base, key.i)) {
2257
+ if (key.t !== 4 && key.i != null && hasMarkedNode(base, key)) {
2258
+ markSerializerRef(base, key.i);
2221
2259
  const serialized = "(" + serialize$1(ctx, key) + ",[" + sentinel + "," + sentinel + "])";
2222
2260
  createSetAssignment(ctx, id, getRefParam(ctx, key.i), valueRef);
2223
2261
  createDeleteAssignment(ctx, id, sentinel);
@@ -2613,13 +2651,14 @@ function parseAggregateError(ctx, depth, id, current) {
2613
2651
  return createAggregateErrorNode(id, current, options ? parseProperties(ctx, depth, options) : void 0);
2614
2652
  }
2615
2653
  function parseMap(ctx, depth, id, current) {
2654
+ const sentinel = parseMapSentinel(ctx.base);
2616
2655
  const keyNodes = [];
2617
2656
  const valueNodes = [];
2618
2657
  for (const [key, value] of current.entries()) {
2619
2658
  keyNodes.push(parseSOS(ctx, depth, key));
2620
2659
  valueNodes.push(parseSOS(ctx, depth, value));
2621
2660
  }
2622
- return createMapNode(ctx.base, id, keyNodes, valueNodes);
2661
+ return createMapNode(id, sentinel, keyNodes, valueNodes);
2623
2662
  }
2624
2663
  function parseSet(ctx, depth, id, current) {
2625
2664
  const items = [];
package/dist/dev/index.js CHANGED
@@ -394,6 +394,29 @@ function getObjectFlag(obj) {
394
394
  return 1;
395
395
  }
396
396
  //#endregion
397
+ //#region src/core/utils/typed-array.ts
398
+ function getTypedArrayConstructor(name) {
399
+ switch (name) {
400
+ case "Int8Array": return Int8Array;
401
+ case "Int16Array": return Int16Array;
402
+ case "Int32Array": return Int32Array;
403
+ case "Uint8Array": return Uint8Array;
404
+ case "Uint16Array": return Uint16Array;
405
+ case "Uint32Array": return Uint32Array;
406
+ case "Uint8ClampedArray": return Uint8ClampedArray;
407
+ case "Float32Array": return Float32Array;
408
+ case "Float64Array": return Float64Array;
409
+ case "BigInt64Array": return BigInt64Array;
410
+ case "BigUint64Array": return BigUint64Array;
411
+ default: throw new SerovalUnknownTypedArrayError(name);
412
+ }
413
+ }
414
+ let typedArrayNameGetter;
415
+ function getTypedArrayName(value) {
416
+ if (!typedArrayNameGetter) typedArrayNameGetter = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(Int8Array.prototype), Symbol.toStringTag).get;
417
+ return typedArrayNameGetter.call(value);
418
+ }
419
+ //#endregion
397
420
  //#region src/core/base-primitives.ts
398
421
  function createNumberNode(value) {
399
422
  switch (value) {
@@ -441,11 +464,11 @@ function createBoxedNode(id, boxed) {
441
464
  const MAX_TYPED_ARRAY_LENGTH = 1e6;
442
465
  function createTypedArrayNode(id, current, buffer) {
443
466
  if (current.length > MAX_TYPED_ARRAY_LENGTH) throw new SerovalUnsupportedTypeError(current);
444
- return createSerovalNode(15, id, void 0, current.constructor.name, void 0, void 0, void 0, void 0, buffer, current.byteOffset, void 0, current.length);
467
+ return createSerovalNode(15, id, void 0, getTypedArrayName(current), void 0, void 0, void 0, void 0, buffer, current.byteOffset, void 0, current.length);
445
468
  }
446
469
  function createBigIntTypedArrayNode(id, current, buffer) {
447
470
  if (current.length > MAX_TYPED_ARRAY_LENGTH) throw new SerovalUnsupportedTypeError(current);
448
- return createSerovalNode(16, id, void 0, current.constructor.name, void 0, void 0, void 0, void 0, buffer, current.byteOffset, void 0, current.length);
471
+ return createSerovalNode(16, id, void 0, getTypedArrayName(current), void 0, void 0, void 0, void 0, buffer, current.byteOffset, void 0, current.length);
449
472
  }
450
473
  function createDataViewNode(id, current, buffer) {
451
474
  if (current.byteLength > MAX_TYPED_ARRAY_LENGTH) throw new SerovalUnsupportedTypeError(current);
@@ -979,11 +1002,18 @@ function parseAsyncIteratorFactory(ctx) {
979
1002
  function createObjectNode(id, current, empty, record) {
980
1003
  return createSerovalNode(empty ? 11 : 10, id, void 0, void 0, void 0, record, void 0, void 0, void 0, void 0, getObjectFlag(current), void 0);
981
1004
  }
982
- function createMapNode(ctx, id, k, v) {
1005
+ /**
1006
+ * Gets the sentinel node for a Map. Call it before parsing the entries, so
1007
+ * that an outer Map declares the sentinel before any Map inside it uses it.
1008
+ */
1009
+ function parseMapSentinel(ctx) {
1010
+ return parseSpecialReference(ctx, 0);
1011
+ }
1012
+ function createMapNode(id, sentinel, k, v) {
983
1013
  return createSerovalNode(8, id, void 0, void 0, void 0, void 0, {
984
1014
  k,
985
1015
  v
986
- }, void 0, parseSpecialReference(ctx, 0), void 0, void 0, void 0);
1016
+ }, void 0, sentinel, void 0, void 0, void 0);
987
1017
  }
988
1018
  function createPromiseConstructorNode(ctx, id, resolver) {
989
1019
  return createSerovalNode(22, id, resolver, void 0, void 0, void 0, void 0, void 0, parseSpecialReference(ctx, 1), void 0, void 0, void 0);
@@ -991,8 +1021,7 @@ function createPromiseConstructorNode(ctx, id, resolver) {
991
1021
  function getArrayBufferView(ctx, current) {
992
1022
  if (!ctx.compactArrayBufferViews) return current;
993
1023
  const buffer = new Uint8Array(current.buffer, current.byteOffset, current.byteLength).slice().buffer;
994
- const Constructor = current.constructor;
995
- return new Constructor(buffer);
1024
+ return new (current instanceof DataView ? DataView : getTypedArrayConstructor(getTypedArrayName(current)))(buffer);
996
1025
  }
997
1026
  function encodeArrayBuffer(current) {
998
1027
  if (typeof Buffer !== "undefined") return Buffer.from(current).toString("base64");
@@ -1087,13 +1116,14 @@ async function parseAggregateError$1(ctx, depth, id, current) {
1087
1116
  return createAggregateErrorNode(id, current, options ? await parseProperties$1(ctx, depth, options) : void 0);
1088
1117
  }
1089
1118
  async function parseMap$1(ctx, depth, id, current) {
1119
+ const sentinel = parseMapSentinel(ctx.base);
1090
1120
  const keyNodes = [];
1091
1121
  const valueNodes = [];
1092
1122
  for (const [key, value] of current.entries()) {
1093
1123
  keyNodes.push(await parseAsync(ctx, depth, key));
1094
1124
  valueNodes.push(await parseAsync(ctx, depth, value));
1095
1125
  }
1096
- return createMapNode(ctx.base, id, keyNodes, valueNodes);
1126
+ return createMapNode(id, sentinel, keyNodes, valueNodes);
1097
1127
  }
1098
1128
  async function parseSet$1(ctx, depth, id, current) {
1099
1129
  const items = [];
@@ -1278,24 +1308,6 @@ function resolvePlugins(plugins) {
1278
1308
  }
1279
1309
  }
1280
1310
  //#endregion
1281
- //#region src/core/utils/typed-array.ts
1282
- function getTypedArrayConstructor(name) {
1283
- switch (name) {
1284
- case "Int8Array": return Int8Array;
1285
- case "Int16Array": return Int16Array;
1286
- case "Int32Array": return Int32Array;
1287
- case "Uint8Array": return Uint8Array;
1288
- case "Uint16Array": return Uint16Array;
1289
- case "Uint32Array": return Uint32Array;
1290
- case "Uint8ClampedArray": return Uint8ClampedArray;
1291
- case "Float32Array": return Float32Array;
1292
- case "Float64Array": return Float64Array;
1293
- case "BigInt64Array": return BigInt64Array;
1294
- case "BigUint64Array": return BigUint64Array;
1295
- default: throw new SerovalUnknownTypedArrayError(name);
1296
- }
1297
- }
1298
- //#endregion
1299
1311
  //#region src/core/utils/valid-properties.ts
1300
1312
  function isValidKey(key) {
1301
1313
  switch (key) {
@@ -1765,30 +1777,36 @@ function getAssignmentExpression(assignment) {
1765
1777
  case 4: return "Object.defineProperty(" + assignment.s + ",\"__proto__\",{value:" + assignment.k + ",configurable:!0,enumerable:!0,writable:!0})";
1766
1778
  }
1767
1779
  }
1780
+ const MAX_ASSIGNMENT_CHAIN_LENGTH = 100;
1768
1781
  function mergeAssignments(assignments) {
1769
1782
  const newAssignments = [];
1770
1783
  let current = assignments[0];
1784
+ let chainLength = 1;
1771
1785
  for (let i = 1, len = assignments.length, item, prev = current; i < len; i++) {
1772
1786
  item = assignments[i];
1773
- if (item.t === 0 && item.v === prev.v) current = {
1787
+ if (chainLength === MAX_ASSIGNMENT_CHAIN_LENGTH) {
1788
+ newAssignments.push(current);
1789
+ current = item;
1790
+ chainLength = 0;
1791
+ } else if (item.t === 0 && prev.t === 0 && item.v === prev.v) current = {
1774
1792
  t: 0,
1775
1793
  s: item.s,
1776
1794
  k: void 0,
1777
1795
  v: getAssignmentExpression(current)
1778
1796
  };
1779
- else if (item.t === 2 && item.s === prev.s) current = {
1797
+ else if (item.t === 2 && prev.t === 2 && item.s === prev.s) current = {
1780
1798
  t: 2,
1781
1799
  s: getAssignmentExpression(current),
1782
1800
  k: item.k,
1783
1801
  v: item.v
1784
1802
  };
1785
- else if (item.t === 1 && item.s === prev.s) current = {
1803
+ else if (item.t === 1 && prev.t === 1 && item.s === prev.s) current = {
1786
1804
  t: 1,
1787
1805
  s: getAssignmentExpression(current),
1788
1806
  k: void 0,
1789
1807
  v: item.v
1790
1808
  };
1791
- else if (item.t === 3 && item.s === prev.s) current = {
1809
+ else if (item.t === 3 && prev.t === 2 && item.s === prev.s) current = {
1792
1810
  t: 3,
1793
1811
  s: getAssignmentExpression(current),
1794
1812
  k: item.k,
@@ -1797,7 +1815,9 @@ function mergeAssignments(assignments) {
1797
1815
  else {
1798
1816
  newAssignments.push(current);
1799
1817
  current = item;
1818
+ chainLength = 0;
1800
1819
  }
1820
+ chainLength++;
1801
1821
  prev = item;
1802
1822
  }
1803
1823
  newAssignments.push(current);
@@ -1992,6 +2012,22 @@ function isIndexedValueInStack(ctx, node) {
1992
2012
  return node.t === 4 && ctx.stack.includes(node.i);
1993
2013
  }
1994
2014
  /**
2015
+ * Checks if the node, or any node inside it, is read from more than one
2016
+ * place. Such a node must not be created inside a deferred assignment,
2017
+ * because the places that read it may run before the assignment does.
2018
+ */
2019
+ function hasMarkedNode(ctx, node) {
2020
+ if (Array.isArray(node)) {
2021
+ for (let i = 0, len = node.length; i < len; i++) if (hasMarkedNode(ctx, node[i])) return true;
2022
+ return false;
2023
+ }
2024
+ if (node == null || typeof node !== "object") return false;
2025
+ const current = node;
2026
+ if (current.t !== 4 && typeof current.i === "number" && isSerializerRefMarked(ctx, current.i)) return true;
2027
+ for (const key in current) if (hasMarkedNode(ctx, current[key])) return true;
2028
+ return false;
2029
+ }
2030
+ /**
1995
2031
  * Produces an assignment expression. `id` generates a reference
1996
2032
  * parameter (through `getRefParam`) and has the option to
1997
2033
  * return the reference parameter directly or assign a value to
@@ -2201,7 +2237,8 @@ function serializeMapEntry(ctx, id, key, val, sentinel) {
2201
2237
  createSetAssignment(ctx, id, keyRef, getRefParam(ctx, val.i));
2202
2238
  return "";
2203
2239
  }
2204
- if (val.t !== 4 && val.i != null && isSerializerRefMarked(base, val.i)) {
2240
+ if (val.t !== 4 && val.i != null && hasMarkedNode(base, val)) {
2241
+ markSerializerRef(base, val.i);
2205
2242
  const serialized = "(" + serialize$1(ctx, val) + ",[" + sentinel + "," + sentinel + "])";
2206
2243
  createSetAssignment(ctx, id, keyRef, getRefParam(ctx, val.i));
2207
2244
  createDeleteAssignment(ctx, id, sentinel);
@@ -2216,7 +2253,8 @@ function serializeMapEntry(ctx, id, key, val, sentinel) {
2216
2253
  if (isIndexedValueInStack(base, val)) {
2217
2254
  const valueRef = getRefParam(ctx, val.i);
2218
2255
  markSerializerRef(base, id);
2219
- if (key.t !== 4 && key.i != null && isSerializerRefMarked(base, key.i)) {
2256
+ if (key.t !== 4 && key.i != null && hasMarkedNode(base, key)) {
2257
+ markSerializerRef(base, key.i);
2220
2258
  const serialized = "(" + serialize$1(ctx, key) + ",[" + sentinel + "," + sentinel + "])";
2221
2259
  createSetAssignment(ctx, id, getRefParam(ctx, key.i), valueRef);
2222
2260
  createDeleteAssignment(ctx, id, sentinel);
@@ -2612,13 +2650,14 @@ function parseAggregateError(ctx, depth, id, current) {
2612
2650
  return createAggregateErrorNode(id, current, options ? parseProperties(ctx, depth, options) : void 0);
2613
2651
  }
2614
2652
  function parseMap(ctx, depth, id, current) {
2653
+ const sentinel = parseMapSentinel(ctx.base);
2615
2654
  const keyNodes = [];
2616
2655
  const valueNodes = [];
2617
2656
  for (const [key, value] of current.entries()) {
2618
2657
  keyNodes.push(parseSOS(ctx, depth, key));
2619
2658
  valueNodes.push(parseSOS(ctx, depth, value));
2620
2659
  }
2621
- return createMapNode(ctx.base, id, keyNodes, valueNodes);
2660
+ return createMapNode(id, sentinel, keyNodes, valueNodes);
2622
2661
  }
2623
2662
  function parseSet(ctx, depth, id, current) {
2624
2663
  const items = [];
package/dist/index.cjs CHANGED
@@ -388,6 +388,29 @@ function getObjectFlag(obj) {
388
388
  return 1;
389
389
  }
390
390
  //#endregion
391
+ //#region src/core/utils/typed-array.ts
392
+ function getTypedArrayConstructor(name) {
393
+ switch (name) {
394
+ case "Int8Array": return Int8Array;
395
+ case "Int16Array": return Int16Array;
396
+ case "Int32Array": return Int32Array;
397
+ case "Uint8Array": return Uint8Array;
398
+ case "Uint16Array": return Uint16Array;
399
+ case "Uint32Array": return Uint32Array;
400
+ case "Uint8ClampedArray": return Uint8ClampedArray;
401
+ case "Float32Array": return Float32Array;
402
+ case "Float64Array": return Float64Array;
403
+ case "BigInt64Array": return BigInt64Array;
404
+ case "BigUint64Array": return BigUint64Array;
405
+ default: throw new SerovalUnknownTypedArrayError(name);
406
+ }
407
+ }
408
+ let typedArrayNameGetter;
409
+ function getTypedArrayName(value) {
410
+ if (!typedArrayNameGetter) typedArrayNameGetter = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(Int8Array.prototype), Symbol.toStringTag).get;
411
+ return typedArrayNameGetter.call(value);
412
+ }
413
+ //#endregion
391
414
  //#region src/core/base-primitives.ts
392
415
  function createNumberNode(value) {
393
416
  switch (value) {
@@ -435,11 +458,11 @@ function createBoxedNode(id, boxed) {
435
458
  const MAX_TYPED_ARRAY_LENGTH = 1e6;
436
459
  function createTypedArrayNode(id, current, buffer) {
437
460
  if (current.length > MAX_TYPED_ARRAY_LENGTH) throw new SerovalUnsupportedTypeError(current);
438
- return createSerovalNode(15, id, void 0, current.constructor.name, void 0, void 0, void 0, void 0, buffer, current.byteOffset, void 0, current.length);
461
+ return createSerovalNode(15, id, void 0, getTypedArrayName(current), void 0, void 0, void 0, void 0, buffer, current.byteOffset, void 0, current.length);
439
462
  }
440
463
  function createBigIntTypedArrayNode(id, current, buffer) {
441
464
  if (current.length > MAX_TYPED_ARRAY_LENGTH) throw new SerovalUnsupportedTypeError(current);
442
- return createSerovalNode(16, id, void 0, current.constructor.name, void 0, void 0, void 0, void 0, buffer, current.byteOffset, void 0, current.length);
465
+ return createSerovalNode(16, id, void 0, getTypedArrayName(current), void 0, void 0, void 0, void 0, buffer, current.byteOffset, void 0, current.length);
443
466
  }
444
467
  function createDataViewNode(id, current, buffer) {
445
468
  if (current.byteLength > MAX_TYPED_ARRAY_LENGTH) throw new SerovalUnsupportedTypeError(current);
@@ -973,11 +996,18 @@ function parseAsyncIteratorFactory(ctx) {
973
996
  function createObjectNode(id, current, empty, record) {
974
997
  return createSerovalNode(empty ? 11 : 10, id, void 0, void 0, void 0, record, void 0, void 0, void 0, void 0, getObjectFlag(current), void 0);
975
998
  }
976
- function createMapNode(ctx, id, k, v) {
999
+ /**
1000
+ * Gets the sentinel node for a Map. Call it before parsing the entries, so
1001
+ * that an outer Map declares the sentinel before any Map inside it uses it.
1002
+ */
1003
+ function parseMapSentinel(ctx) {
1004
+ return parseSpecialReference(ctx, 0);
1005
+ }
1006
+ function createMapNode(id, sentinel, k, v) {
977
1007
  return createSerovalNode(8, id, void 0, void 0, void 0, void 0, {
978
1008
  k,
979
1009
  v
980
- }, void 0, parseSpecialReference(ctx, 0), void 0, void 0, void 0);
1010
+ }, void 0, sentinel, void 0, void 0, void 0);
981
1011
  }
982
1012
  function createPromiseConstructorNode(ctx, id, resolver) {
983
1013
  return createSerovalNode(22, id, resolver, void 0, void 0, void 0, void 0, void 0, parseSpecialReference(ctx, 1), void 0, void 0, void 0);
@@ -985,8 +1015,7 @@ function createPromiseConstructorNode(ctx, id, resolver) {
985
1015
  function getArrayBufferView(ctx, current) {
986
1016
  if (!ctx.compactArrayBufferViews) return current;
987
1017
  const buffer = new Uint8Array(current.buffer, current.byteOffset, current.byteLength).slice().buffer;
988
- const Constructor = current.constructor;
989
- return new Constructor(buffer);
1018
+ return new (current instanceof DataView ? DataView : getTypedArrayConstructor(getTypedArrayName(current)))(buffer);
990
1019
  }
991
1020
  function encodeArrayBuffer(current) {
992
1021
  if (typeof Buffer !== "undefined") return Buffer.from(current).toString("base64");
@@ -1081,13 +1110,14 @@ async function parseAggregateError$1(ctx, depth, id, current) {
1081
1110
  return createAggregateErrorNode(id, current, options ? await parseProperties$1(ctx, depth, options) : void 0);
1082
1111
  }
1083
1112
  async function parseMap$1(ctx, depth, id, current) {
1113
+ const sentinel = parseMapSentinel(ctx.base);
1084
1114
  const keyNodes = [];
1085
1115
  const valueNodes = [];
1086
1116
  for (const [key, value] of current.entries()) {
1087
1117
  keyNodes.push(await parseAsync(ctx, depth, key));
1088
1118
  valueNodes.push(await parseAsync(ctx, depth, value));
1089
1119
  }
1090
- return createMapNode(ctx.base, id, keyNodes, valueNodes);
1120
+ return createMapNode(id, sentinel, keyNodes, valueNodes);
1091
1121
  }
1092
1122
  async function parseSet$1(ctx, depth, id, current) {
1093
1123
  const items = [];
@@ -1272,24 +1302,6 @@ function resolvePlugins(plugins) {
1272
1302
  }
1273
1303
  }
1274
1304
  //#endregion
1275
- //#region src/core/utils/typed-array.ts
1276
- function getTypedArrayConstructor(name) {
1277
- switch (name) {
1278
- case "Int8Array": return Int8Array;
1279
- case "Int16Array": return Int16Array;
1280
- case "Int32Array": return Int32Array;
1281
- case "Uint8Array": return Uint8Array;
1282
- case "Uint16Array": return Uint16Array;
1283
- case "Uint32Array": return Uint32Array;
1284
- case "Uint8ClampedArray": return Uint8ClampedArray;
1285
- case "Float32Array": return Float32Array;
1286
- case "Float64Array": return Float64Array;
1287
- case "BigInt64Array": return BigInt64Array;
1288
- case "BigUint64Array": return BigUint64Array;
1289
- default: throw new SerovalUnknownTypedArrayError(name);
1290
- }
1291
- }
1292
- //#endregion
1293
1305
  //#region src/core/utils/valid-properties.ts
1294
1306
  function isValidKey(key) {
1295
1307
  switch (key) {
@@ -1759,30 +1771,36 @@ function getAssignmentExpression(assignment) {
1759
1771
  case 4: return "Object.defineProperty(" + assignment.s + ",\"__proto__\",{value:" + assignment.k + ",configurable:!0,enumerable:!0,writable:!0})";
1760
1772
  }
1761
1773
  }
1774
+ const MAX_ASSIGNMENT_CHAIN_LENGTH = 100;
1762
1775
  function mergeAssignments(assignments) {
1763
1776
  const newAssignments = [];
1764
1777
  let current = assignments[0];
1778
+ let chainLength = 1;
1765
1779
  for (let i = 1, len = assignments.length, item, prev = current; i < len; i++) {
1766
1780
  item = assignments[i];
1767
- if (item.t === 0 && item.v === prev.v) current = {
1781
+ if (chainLength === MAX_ASSIGNMENT_CHAIN_LENGTH) {
1782
+ newAssignments.push(current);
1783
+ current = item;
1784
+ chainLength = 0;
1785
+ } else if (item.t === 0 && prev.t === 0 && item.v === prev.v) current = {
1768
1786
  t: 0,
1769
1787
  s: item.s,
1770
1788
  k: void 0,
1771
1789
  v: getAssignmentExpression(current)
1772
1790
  };
1773
- else if (item.t === 2 && item.s === prev.s) current = {
1791
+ else if (item.t === 2 && prev.t === 2 && item.s === prev.s) current = {
1774
1792
  t: 2,
1775
1793
  s: getAssignmentExpression(current),
1776
1794
  k: item.k,
1777
1795
  v: item.v
1778
1796
  };
1779
- else if (item.t === 1 && item.s === prev.s) current = {
1797
+ else if (item.t === 1 && prev.t === 1 && item.s === prev.s) current = {
1780
1798
  t: 1,
1781
1799
  s: getAssignmentExpression(current),
1782
1800
  k: void 0,
1783
1801
  v: item.v
1784
1802
  };
1785
- else if (item.t === 3 && item.s === prev.s) current = {
1803
+ else if (item.t === 3 && prev.t === 2 && item.s === prev.s) current = {
1786
1804
  t: 3,
1787
1805
  s: getAssignmentExpression(current),
1788
1806
  k: item.k,
@@ -1791,7 +1809,9 @@ function mergeAssignments(assignments) {
1791
1809
  else {
1792
1810
  newAssignments.push(current);
1793
1811
  current = item;
1812
+ chainLength = 0;
1794
1813
  }
1814
+ chainLength++;
1795
1815
  prev = item;
1796
1816
  }
1797
1817
  newAssignments.push(current);
@@ -1986,6 +2006,22 @@ function isIndexedValueInStack(ctx, node) {
1986
2006
  return node.t === 4 && ctx.stack.includes(node.i);
1987
2007
  }
1988
2008
  /**
2009
+ * Checks if the node, or any node inside it, is read from more than one
2010
+ * place. Such a node must not be created inside a deferred assignment,
2011
+ * because the places that read it may run before the assignment does.
2012
+ */
2013
+ function hasMarkedNode(ctx, node) {
2014
+ if (Array.isArray(node)) {
2015
+ for (let i = 0, len = node.length; i < len; i++) if (hasMarkedNode(ctx, node[i])) return true;
2016
+ return false;
2017
+ }
2018
+ if (node == null || typeof node !== "object") return false;
2019
+ const current = node;
2020
+ if (current.t !== 4 && typeof current.i === "number" && isSerializerRefMarked(ctx, current.i)) return true;
2021
+ for (const key in current) if (hasMarkedNode(ctx, current[key])) return true;
2022
+ return false;
2023
+ }
2024
+ /**
1989
2025
  * Produces an assignment expression. `id` generates a reference
1990
2026
  * parameter (through `getRefParam`) and has the option to
1991
2027
  * return the reference parameter directly or assign a value to
@@ -2195,7 +2231,8 @@ function serializeMapEntry(ctx, id, key, val, sentinel) {
2195
2231
  createSetAssignment(ctx, id, keyRef, getRefParam(ctx, val.i));
2196
2232
  return "";
2197
2233
  }
2198
- if (val.t !== 4 && val.i != null && isSerializerRefMarked(base, val.i)) {
2234
+ if (val.t !== 4 && val.i != null && hasMarkedNode(base, val)) {
2235
+ markSerializerRef(base, val.i);
2199
2236
  const serialized = "(" + serialize$1(ctx, val) + ",[" + sentinel + "," + sentinel + "])";
2200
2237
  createSetAssignment(ctx, id, keyRef, getRefParam(ctx, val.i));
2201
2238
  createDeleteAssignment(ctx, id, sentinel);
@@ -2210,7 +2247,8 @@ function serializeMapEntry(ctx, id, key, val, sentinel) {
2210
2247
  if (isIndexedValueInStack(base, val)) {
2211
2248
  const valueRef = getRefParam(ctx, val.i);
2212
2249
  markSerializerRef(base, id);
2213
- if (key.t !== 4 && key.i != null && isSerializerRefMarked(base, key.i)) {
2250
+ if (key.t !== 4 && key.i != null && hasMarkedNode(base, key)) {
2251
+ markSerializerRef(base, key.i);
2214
2252
  const serialized = "(" + serialize$1(ctx, key) + ",[" + sentinel + "," + sentinel + "])";
2215
2253
  createSetAssignment(ctx, id, getRefParam(ctx, key.i), valueRef);
2216
2254
  createDeleteAssignment(ctx, id, sentinel);
@@ -2606,13 +2644,14 @@ function parseAggregateError(ctx, depth, id, current) {
2606
2644
  return createAggregateErrorNode(id, current, options ? parseProperties(ctx, depth, options) : void 0);
2607
2645
  }
2608
2646
  function parseMap(ctx, depth, id, current) {
2647
+ const sentinel = parseMapSentinel(ctx.base);
2609
2648
  const keyNodes = [];
2610
2649
  const valueNodes = [];
2611
2650
  for (const [key, value] of current.entries()) {
2612
2651
  keyNodes.push(parseSOS(ctx, depth, key));
2613
2652
  valueNodes.push(parseSOS(ctx, depth, value));
2614
2653
  }
2615
- return createMapNode(ctx.base, id, keyNodes, valueNodes);
2654
+ return createMapNode(id, sentinel, keyNodes, valueNodes);
2616
2655
  }
2617
2656
  function parseSet(ctx, depth, id, current) {
2618
2657
  const items = [];
package/dist/index.js CHANGED
@@ -387,6 +387,29 @@ function getObjectFlag(obj) {
387
387
  return 1;
388
388
  }
389
389
  //#endregion
390
+ //#region src/core/utils/typed-array.ts
391
+ function getTypedArrayConstructor(name) {
392
+ switch (name) {
393
+ case "Int8Array": return Int8Array;
394
+ case "Int16Array": return Int16Array;
395
+ case "Int32Array": return Int32Array;
396
+ case "Uint8Array": return Uint8Array;
397
+ case "Uint16Array": return Uint16Array;
398
+ case "Uint32Array": return Uint32Array;
399
+ case "Uint8ClampedArray": return Uint8ClampedArray;
400
+ case "Float32Array": return Float32Array;
401
+ case "Float64Array": return Float64Array;
402
+ case "BigInt64Array": return BigInt64Array;
403
+ case "BigUint64Array": return BigUint64Array;
404
+ default: throw new SerovalUnknownTypedArrayError(name);
405
+ }
406
+ }
407
+ let typedArrayNameGetter;
408
+ function getTypedArrayName(value) {
409
+ if (!typedArrayNameGetter) typedArrayNameGetter = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(Int8Array.prototype), Symbol.toStringTag).get;
410
+ return typedArrayNameGetter.call(value);
411
+ }
412
+ //#endregion
390
413
  //#region src/core/base-primitives.ts
391
414
  function createNumberNode(value) {
392
415
  switch (value) {
@@ -434,11 +457,11 @@ function createBoxedNode(id, boxed) {
434
457
  const MAX_TYPED_ARRAY_LENGTH = 1e6;
435
458
  function createTypedArrayNode(id, current, buffer) {
436
459
  if (current.length > MAX_TYPED_ARRAY_LENGTH) throw new SerovalUnsupportedTypeError(current);
437
- return createSerovalNode(15, id, void 0, current.constructor.name, void 0, void 0, void 0, void 0, buffer, current.byteOffset, void 0, current.length);
460
+ return createSerovalNode(15, id, void 0, getTypedArrayName(current), void 0, void 0, void 0, void 0, buffer, current.byteOffset, void 0, current.length);
438
461
  }
439
462
  function createBigIntTypedArrayNode(id, current, buffer) {
440
463
  if (current.length > MAX_TYPED_ARRAY_LENGTH) throw new SerovalUnsupportedTypeError(current);
441
- return createSerovalNode(16, id, void 0, current.constructor.name, void 0, void 0, void 0, void 0, buffer, current.byteOffset, void 0, current.length);
464
+ return createSerovalNode(16, id, void 0, getTypedArrayName(current), void 0, void 0, void 0, void 0, buffer, current.byteOffset, void 0, current.length);
442
465
  }
443
466
  function createDataViewNode(id, current, buffer) {
444
467
  if (current.byteLength > MAX_TYPED_ARRAY_LENGTH) throw new SerovalUnsupportedTypeError(current);
@@ -972,11 +995,18 @@ function parseAsyncIteratorFactory(ctx) {
972
995
  function createObjectNode(id, current, empty, record) {
973
996
  return createSerovalNode(empty ? 11 : 10, id, void 0, void 0, void 0, record, void 0, void 0, void 0, void 0, getObjectFlag(current), void 0);
974
997
  }
975
- function createMapNode(ctx, id, k, v) {
998
+ /**
999
+ * Gets the sentinel node for a Map. Call it before parsing the entries, so
1000
+ * that an outer Map declares the sentinel before any Map inside it uses it.
1001
+ */
1002
+ function parseMapSentinel(ctx) {
1003
+ return parseSpecialReference(ctx, 0);
1004
+ }
1005
+ function createMapNode(id, sentinel, k, v) {
976
1006
  return createSerovalNode(8, id, void 0, void 0, void 0, void 0, {
977
1007
  k,
978
1008
  v
979
- }, void 0, parseSpecialReference(ctx, 0), void 0, void 0, void 0);
1009
+ }, void 0, sentinel, void 0, void 0, void 0);
980
1010
  }
981
1011
  function createPromiseConstructorNode(ctx, id, resolver) {
982
1012
  return createSerovalNode(22, id, resolver, void 0, void 0, void 0, void 0, void 0, parseSpecialReference(ctx, 1), void 0, void 0, void 0);
@@ -984,8 +1014,7 @@ function createPromiseConstructorNode(ctx, id, resolver) {
984
1014
  function getArrayBufferView(ctx, current) {
985
1015
  if (!ctx.compactArrayBufferViews) return current;
986
1016
  const buffer = new Uint8Array(current.buffer, current.byteOffset, current.byteLength).slice().buffer;
987
- const Constructor = current.constructor;
988
- return new Constructor(buffer);
1017
+ return new (current instanceof DataView ? DataView : getTypedArrayConstructor(getTypedArrayName(current)))(buffer);
989
1018
  }
990
1019
  function encodeArrayBuffer(current) {
991
1020
  if (typeof Buffer !== "undefined") return Buffer.from(current).toString("base64");
@@ -1080,13 +1109,14 @@ async function parseAggregateError$1(ctx, depth, id, current) {
1080
1109
  return createAggregateErrorNode(id, current, options ? await parseProperties$1(ctx, depth, options) : void 0);
1081
1110
  }
1082
1111
  async function parseMap$1(ctx, depth, id, current) {
1112
+ const sentinel = parseMapSentinel(ctx.base);
1083
1113
  const keyNodes = [];
1084
1114
  const valueNodes = [];
1085
1115
  for (const [key, value] of current.entries()) {
1086
1116
  keyNodes.push(await parseAsync(ctx, depth, key));
1087
1117
  valueNodes.push(await parseAsync(ctx, depth, value));
1088
1118
  }
1089
- return createMapNode(ctx.base, id, keyNodes, valueNodes);
1119
+ return createMapNode(id, sentinel, keyNodes, valueNodes);
1090
1120
  }
1091
1121
  async function parseSet$1(ctx, depth, id, current) {
1092
1122
  const items = [];
@@ -1271,24 +1301,6 @@ function resolvePlugins(plugins) {
1271
1301
  }
1272
1302
  }
1273
1303
  //#endregion
1274
- //#region src/core/utils/typed-array.ts
1275
- function getTypedArrayConstructor(name) {
1276
- switch (name) {
1277
- case "Int8Array": return Int8Array;
1278
- case "Int16Array": return Int16Array;
1279
- case "Int32Array": return Int32Array;
1280
- case "Uint8Array": return Uint8Array;
1281
- case "Uint16Array": return Uint16Array;
1282
- case "Uint32Array": return Uint32Array;
1283
- case "Uint8ClampedArray": return Uint8ClampedArray;
1284
- case "Float32Array": return Float32Array;
1285
- case "Float64Array": return Float64Array;
1286
- case "BigInt64Array": return BigInt64Array;
1287
- case "BigUint64Array": return BigUint64Array;
1288
- default: throw new SerovalUnknownTypedArrayError(name);
1289
- }
1290
- }
1291
- //#endregion
1292
1304
  //#region src/core/utils/valid-properties.ts
1293
1305
  function isValidKey(key) {
1294
1306
  switch (key) {
@@ -1758,30 +1770,36 @@ function getAssignmentExpression(assignment) {
1758
1770
  case 4: return "Object.defineProperty(" + assignment.s + ",\"__proto__\",{value:" + assignment.k + ",configurable:!0,enumerable:!0,writable:!0})";
1759
1771
  }
1760
1772
  }
1773
+ const MAX_ASSIGNMENT_CHAIN_LENGTH = 100;
1761
1774
  function mergeAssignments(assignments) {
1762
1775
  const newAssignments = [];
1763
1776
  let current = assignments[0];
1777
+ let chainLength = 1;
1764
1778
  for (let i = 1, len = assignments.length, item, prev = current; i < len; i++) {
1765
1779
  item = assignments[i];
1766
- if (item.t === 0 && item.v === prev.v) current = {
1780
+ if (chainLength === MAX_ASSIGNMENT_CHAIN_LENGTH) {
1781
+ newAssignments.push(current);
1782
+ current = item;
1783
+ chainLength = 0;
1784
+ } else if (item.t === 0 && prev.t === 0 && item.v === prev.v) current = {
1767
1785
  t: 0,
1768
1786
  s: item.s,
1769
1787
  k: void 0,
1770
1788
  v: getAssignmentExpression(current)
1771
1789
  };
1772
- else if (item.t === 2 && item.s === prev.s) current = {
1790
+ else if (item.t === 2 && prev.t === 2 && item.s === prev.s) current = {
1773
1791
  t: 2,
1774
1792
  s: getAssignmentExpression(current),
1775
1793
  k: item.k,
1776
1794
  v: item.v
1777
1795
  };
1778
- else if (item.t === 1 && item.s === prev.s) current = {
1796
+ else if (item.t === 1 && prev.t === 1 && item.s === prev.s) current = {
1779
1797
  t: 1,
1780
1798
  s: getAssignmentExpression(current),
1781
1799
  k: void 0,
1782
1800
  v: item.v
1783
1801
  };
1784
- else if (item.t === 3 && item.s === prev.s) current = {
1802
+ else if (item.t === 3 && prev.t === 2 && item.s === prev.s) current = {
1785
1803
  t: 3,
1786
1804
  s: getAssignmentExpression(current),
1787
1805
  k: item.k,
@@ -1790,7 +1808,9 @@ function mergeAssignments(assignments) {
1790
1808
  else {
1791
1809
  newAssignments.push(current);
1792
1810
  current = item;
1811
+ chainLength = 0;
1793
1812
  }
1813
+ chainLength++;
1794
1814
  prev = item;
1795
1815
  }
1796
1816
  newAssignments.push(current);
@@ -1985,6 +2005,22 @@ function isIndexedValueInStack(ctx, node) {
1985
2005
  return node.t === 4 && ctx.stack.includes(node.i);
1986
2006
  }
1987
2007
  /**
2008
+ * Checks if the node, or any node inside it, is read from more than one
2009
+ * place. Such a node must not be created inside a deferred assignment,
2010
+ * because the places that read it may run before the assignment does.
2011
+ */
2012
+ function hasMarkedNode(ctx, node) {
2013
+ if (Array.isArray(node)) {
2014
+ for (let i = 0, len = node.length; i < len; i++) if (hasMarkedNode(ctx, node[i])) return true;
2015
+ return false;
2016
+ }
2017
+ if (node == null || typeof node !== "object") return false;
2018
+ const current = node;
2019
+ if (current.t !== 4 && typeof current.i === "number" && isSerializerRefMarked(ctx, current.i)) return true;
2020
+ for (const key in current) if (hasMarkedNode(ctx, current[key])) return true;
2021
+ return false;
2022
+ }
2023
+ /**
1988
2024
  * Produces an assignment expression. `id` generates a reference
1989
2025
  * parameter (through `getRefParam`) and has the option to
1990
2026
  * return the reference parameter directly or assign a value to
@@ -2194,7 +2230,8 @@ function serializeMapEntry(ctx, id, key, val, sentinel) {
2194
2230
  createSetAssignment(ctx, id, keyRef, getRefParam(ctx, val.i));
2195
2231
  return "";
2196
2232
  }
2197
- if (val.t !== 4 && val.i != null && isSerializerRefMarked(base, val.i)) {
2233
+ if (val.t !== 4 && val.i != null && hasMarkedNode(base, val)) {
2234
+ markSerializerRef(base, val.i);
2198
2235
  const serialized = "(" + serialize$1(ctx, val) + ",[" + sentinel + "," + sentinel + "])";
2199
2236
  createSetAssignment(ctx, id, keyRef, getRefParam(ctx, val.i));
2200
2237
  createDeleteAssignment(ctx, id, sentinel);
@@ -2209,7 +2246,8 @@ function serializeMapEntry(ctx, id, key, val, sentinel) {
2209
2246
  if (isIndexedValueInStack(base, val)) {
2210
2247
  const valueRef = getRefParam(ctx, val.i);
2211
2248
  markSerializerRef(base, id);
2212
- if (key.t !== 4 && key.i != null && isSerializerRefMarked(base, key.i)) {
2249
+ if (key.t !== 4 && key.i != null && hasMarkedNode(base, key)) {
2250
+ markSerializerRef(base, key.i);
2213
2251
  const serialized = "(" + serialize$1(ctx, key) + ",[" + sentinel + "," + sentinel + "])";
2214
2252
  createSetAssignment(ctx, id, getRefParam(ctx, key.i), valueRef);
2215
2253
  createDeleteAssignment(ctx, id, sentinel);
@@ -2605,13 +2643,14 @@ function parseAggregateError(ctx, depth, id, current) {
2605
2643
  return createAggregateErrorNode(id, current, options ? parseProperties(ctx, depth, options) : void 0);
2606
2644
  }
2607
2645
  function parseMap(ctx, depth, id, current) {
2646
+ const sentinel = parseMapSentinel(ctx.base);
2608
2647
  const keyNodes = [];
2609
2648
  const valueNodes = [];
2610
2649
  for (const [key, value] of current.entries()) {
2611
2650
  keyNodes.push(parseSOS(ctx, depth, key));
2612
2651
  valueNodes.push(parseSOS(ctx, depth, value));
2613
2652
  }
2614
- return createMapNode(ctx.base, id, keyNodes, valueNodes);
2653
+ return createMapNode(id, sentinel, keyNodes, valueNodes);
2615
2654
  }
2616
2655
  function parseSet(ctx, depth, id, current) {
2617
2656
  const items = [];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "seroval",
3
3
  "type": "module",
4
- "version": "1.6.10",
4
+ "version": "1.6.12",
5
5
  "files": [
6
6
  "dist"
7
7
  ],