Skip to content

Commit

Permalink
Fully deserialize as necessary to look up pointers
Browse files Browse the repository at this point in the history
Fixes #66.

Co-authored-by: Mark Wubben <[email protected]>
  • Loading branch information
ninevra and novemberborn committed Feb 13, 2021
1 parent 2bf27e0 commit 2182889
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 4 deletions.
16 changes: 16 additions & 0 deletions lib/recursorUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,22 @@ function fork (recursor) {
}
exports.fork = fork

function consumeDeep (recursor) {
const stack = [recursor]
while (stack.length > 0) {
const subject = stack[stack.length - 1]()
if (subject === null) {
stack.pop()
continue
}

if (typeof subject.createRecursor === 'function') {
stack.push(subject.createRecursor())
}
}
}
exports.consumeDeep = consumeDeep

function map (recursor, mapFn) {
return () => {
const next = recursor()
Expand Down
29 changes: 25 additions & 4 deletions lib/serialize.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ const promiseValue = require('./complexValues/promise')
const regexpValue = require('./complexValues/regexp')
const setValue = require('./complexValues/set')
const typedArrayValue = require('./complexValues/typedArray')

const encoder = require('./encoder')

const itemDescriptor = require('./metaDescriptors/item')
const mapEntryDescriptor = require('./metaDescriptors/mapEntry')
const pointerDescriptor = require('./metaDescriptors/pointer')
const propertyDescriptor = require('./metaDescriptors/property')
const statsDescriptors = require('./metaDescriptors/stats')

const pluginRegistry = require('./pluginRegistry')

const bigIntValue = require('./primitiveValues/bigInt')
Expand All @@ -33,6 +35,8 @@ const stringValue = require('./primitiveValues/string')
const symbolValue = require('./primitiveValues/symbol')
const undefinedValue = require('./primitiveValues/undefined')

const recursorUtils = require('./recursorUtils')

// Increment if encoding layout, descriptor IDs, or value types change. Previous
// Concordance versions will not be able to decode buffers generated by a newer
// version, so changing this value will require a major version bump of
Expand Down Expand Up @@ -317,12 +321,27 @@ function deserialize (buffer, options) {
const descriptorsByPointerIndex = new Map()
const mapPointerDescriptor = descriptor => {
if (descriptor.isPointer === true) {
if (!descriptorsByPointerIndex.has(descriptor.index)) throw new PointerLookupError(descriptor.index)
if (descriptorsByPointerIndex.has(descriptor.index)) {
return descriptorsByPointerIndex.get(descriptor.index)
}

if (typeof rootDescriptor.createRecursor === 'function') {
// The descriptor we're pointing to may be elsewhere in the serialized
// structure. Consume the entire structure and check again.
recursorUtils.consumeDeep(rootDescriptor.createRecursor())

if (descriptorsByPointerIndex.has(descriptor.index)) {
return descriptorsByPointerIndex.get(descriptor.index)
}
}

throw new PointerLookupError(descriptor.index)
}

return descriptorsByPointerIndex.get(descriptor.index)
} else if (descriptor.isComplex === true) {
if (descriptor.isComplex === true) {
descriptorsByPointerIndex.set(descriptor.pointer, descriptor)
}

return descriptor
}

Expand All @@ -335,6 +354,8 @@ function deserialize (buffer, options) {
return mapPointerDescriptor(deserializeDescriptor(state, recursor))
}
}
return deserializeRecord(decoded.rootRecord, getDescriptorDeserializer, buffer)

const rootDescriptor = deserializeRecord(decoded.rootRecord, getDescriptorDeserializer, buffer)
return rootDescriptor
}
exports.deserialize = deserialize
22 changes: 22 additions & 0 deletions test/diff.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const test = require('ava')

const concordance = require('..')
const { diff: _diff } = require('../lib/diff')

const { theme, normalizedTheme, checkThemeUsage } = require('./_instrumentedTheme')
Expand Down Expand Up @@ -525,3 +526,24 @@ test('objects: effectively resets depth when formatting differences', t => {
}
t.snapshot(_diff(o1, o2, { maxDepth: 1, theme }))
})

// See <https://github.com/concordancejs/concordance/issues/66>.
test('diff pointers hidden behind maxDepth', t => {
const value = {}
const descriptor = concordance.describe({
// `value` is encoded in the serialization of `a.b`. `c` is encoded as a
// pointer to the encoded `value`.
a: {
b: value,
},
c: value,
})
const serialized = concordance.serialize(descriptor)

t.notThrows(() => {
// `maxDepth: 1` means that `a.b` is not normally deserialized, and so the
// `c` pointer cannot be resolved, unless the resolution logic first
// deserializes the descriptor in its entirety.
concordance.diffDescriptors(concordance.deserialize(serialized), concordance.describe(undefined), { maxDepth: 1 })
})
})
22 changes: 22 additions & 0 deletions test/format.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const test = require('ava')

const concordance = require('..')
const { format: _format } = require('../lib/format')
const { theme, normalizedTheme, checkThemeUsage } = require('./_instrumentedTheme')
const customErrorPlugin = require('./fixtures/customErrorPlugin')
Expand Down Expand Up @@ -434,3 +435,24 @@ test('format with given plugin', t => {
const actual2 = _format(new Error('error'), { plugins, theme })
t.snapshot(actual2)
})

// See <https://github.com/concordancejs/concordance/issues/66>.
test('format pointers hidden behind maxDepth', t => {
const value = {}
const descriptor = concordance.describe({
// `value` is encoded in the serialization of `a.b`. `c` is encoded as a
// pointer to the encoded `value`.
a: {
b: value,
},
c: value,
})
const serialized = concordance.serialize(descriptor)

t.notThrows(() => {
// `maxDepth: 1` means that `a.b` is not normally deserialized, and so the
// `c` pointer cannot be resolved, unless the resolution logic first
// deserializes the descriptor in its entirety.
concordance.formatDescriptor(concordance.deserialize(serialized), { maxDepth: 1 })
})
})

0 comments on commit 2182889

Please sign in to comment.