concordancejs / concordance

Compare, format, diff and serialize any JavaScript value

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

formatDescriptor() throws on certain deserialized descriptors with deeply-nested self-references

ninevra opened this issue · comments

When formatDescriptor() is called on a deserialized descriptor of an object with a self-referential property at a nesting depth beyond maxDepth, it may throw a PointerLookupError.

MRE:

const value = {};
const actualDescriptor = concordance.describe({
  a: {
    b: value
  },
  c: value
});

const serializedActual = concordance.serialize(actualDescriptor);
const deserializedActual = concordance.deserialize(serializedActual);

concordance.formatDescriptor(deserializedActual, {maxDepth: 1});

This throws:

  PointerLookupError {
    index: 3,
    message: 'Could not deserialize buffer: pointer 3 could not be resolved',
  }

  › mapPointerDescriptor (node_modules/concordance/lib/serialize.js:321:67)
  › node_modules/concordance/lib/serialize.js:336:14
  › deserializeRecord (node_modules/concordance/lib/serialize.js:255:12)
  › recursor (node_modules/concordance/lib/serialize.js:264:12)
  › deserializeComplex (node_modules/concordance/lib/metaDescriptors/property.js:18:17)
  › node_modules/concordance/lib/serialize.js:336:35
  › deserializeRecord (node_modules/concordance/lib/serialize.js:267:10)
  › Object.recursor [as next] (node_modules/concordance/lib/serialize.js:264:12)
  › Object.next [as recursor] (node_modules/concordance/lib/recursorUtils.js:59:44)
  › Object.formatDescriptor (node_modules/concordance/lib/format.js:74:35)

To reproduce the issue, the following must hold:

  • The provided maxDepth is non-zero
  • The deeply-nested self-reference occurs at a depth greater than maxDepth
  • The shallow self-reference occurs at a depth at most maxDepth
  • The name of the property containing the deeply-nested self-reference is lexically less than the name of the property containing the shallow self-reference

I believe the problem to be that the descriptor is deserialized lazily, and the formatter never calls upon the deeply-nested self-referential value, so when the shallow self-reference is called upon, its pointer references a descriptor that hasn't been deserialized yet.

I first encountered this in AVA, where it can cause an error to be thrown when a snapshot assertion comparing against such a value fails.

I'm working on a PR that may fix this presently.

diffDescriptors() is also affected; that's AVA's entry point to this.