Skip to content
Permalink
9bfb9ba527
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
254 lines (212 sloc) 7.02 KB
'use strict'
const constants = require('../constants')
const ObjectFormatter = require('../formatUtils').ObjectFormatter
const getObjectKeys = require('../getObjectKeys')
const hasLength = require('../hasLength')
const stats = require('../metaDescriptors/stats')
const recursorUtils = require('../recursorUtils')
const DEEP_EQUAL = constants.DEEP_EQUAL
const SHALLOW_EQUAL = constants.SHALLOW_EQUAL
const UNEQUAL = constants.UNEQUAL
function describe (props) {
const isArray = props.stringTag === 'Array'
const object = props.value
return new DescribedObjectValue(Object.assign({
isArray,
isIterable: object[Symbol.iterator] !== undefined,
isList: isArray || hasLength(object),
}, props))
}
exports.describe = describe
function deserialize (state, recursor) {
return new DeserializedObjectValue(state, recursor)
}
exports.deserialize = deserialize
const tag = Symbol('ObjectValue')
exports.tag = tag
class ObjectValue {
constructor (props) {
this.ctor = props.ctor
this.pointer = props.pointer
this.stringTag = props.stringTag
this.isArray = props.isArray === true
this.isIterable = props.isIterable === true
this.isList = props.isList === true
}
compare (expected) {
if (this.tag !== expected.tag) return UNEQUAL
if (this.stringTag !== expected.stringTag || !this.hasSameCtor(expected)) return UNEQUAL
return SHALLOW_EQUAL
}
hasSameCtor (expected) {
return this.ctor === expected.ctor
}
formatShallow (theme, indent) {
return new ObjectFormatter(this, theme, indent)
}
serialize () {
return [
this.ctor, this.pointer, this.stringTag,
this.isArray, this.isIterable, this.isList,
]
}
}
Object.defineProperty(ObjectValue.prototype, 'isComplex', { value: true })
Object.defineProperty(ObjectValue.prototype, 'tag', { value: tag })
exports.ObjectValue = ObjectValue
const DescribedObjectValue = DescribedMixin(ObjectValue)
const DeserializedObjectValue = DeserializedMixin(ObjectValue)
function DescribedMixin (base) {
return class extends base {
constructor (props) {
super(props)
this.value = props.value
this.describeAny = props.describeAny
this.describeItem = props.describeItem
this.describeMapEntry = props.describeMapEntry
this.describeProperty = props.describeProperty
this.iterableState = null
this.listState = null
this.propertyState = null
}
compare (expected) {
return this.value === expected.value
? DEEP_EQUAL
: super.compare(expected)
}
createPropertyRecursor () {
const objectKeys = getObjectKeys(this.value, this.isList ? this.value.length : 0)
const size = objectKeys.size
if (size === 0) return recursorUtils.NOOP_RECURSOR
let index = 0
const next = () => {
if (index === size) return null
const key = objectKeys.keys[index++]
return this.describeProperty(key, this.describeAny(this.value[key]))
}
return { size, next }
}
createListRecursor () {
if (!this.isList) return recursorUtils.NOOP_RECURSOR
const size = this.value.length
if (size === 0) return recursorUtils.NOOP_RECURSOR
let index = 0
const next = () => {
if (index === size) return null
const current = index
index++
return this.describeItem(current, this.describeAny(this.value[current]))
}
return { size, next }
}
createIterableRecursor () {
if (this.isArray || !this.isIterable) return recursorUtils.NOOP_RECURSOR
const iterator = this.value[Symbol.iterator]()
let first = iterator.next()
let done = false
let size = -1
if (first.done) {
if (first.value === undefined) {
size = 0
done = true
} else {
size = 1
}
}
let index = 0
const next = () => {
if (done) return null
while (!done) {
const current = first || iterator.next()
if (current === first) {
first = null
}
if (current.done) {
done = true
}
const item = current.value
if (done && item === undefined) return null
if (this.isList && this.value[index] === item) {
index++
} else {
return this.describeItem(index++, this.describeAny(item))
}
}
}
return { size, next }
}
createRecursor () {
let recursedProperty = false
let recursedList = false
let recursedIterable = false
let recursor = null
return () => {
let retval = null
do {
if (recursor !== null) {
retval = recursor.next()
if (retval === null) {
recursor = null
}
}
while (recursor === null && (!recursedList || !recursedProperty || !recursedIterable)) {
// Prioritize recursing lists
if (!recursedList) {
const replay = recursorUtils.replay(this.listState, () => this.createListRecursor())
this.listState = replay.state
recursor = replay.recursor
recursedList = true
if (recursor !== recursorUtils.NOOP_RECURSOR) {
retval = stats.describeListRecursor(recursor)
}
} else if (!recursedProperty) {
const replay = recursorUtils.replay(this.propertyState, () => this.createPropertyRecursor())
this.propertyState = replay.state
recursor = replay.recursor
recursedProperty = true
if (recursor !== recursorUtils.NOOP_RECURSOR) {
retval = stats.describePropertyRecursor(recursor)
}
} else if (!recursedIterable) {
const replay = recursorUtils.replay(this.iterableState, () => this.createIterableRecursor())
this.iterableState = replay.state
recursor = replay.recursor
recursedIterable = true
if (recursor !== recursorUtils.NOOP_RECURSOR) {
retval = stats.describeIterableRecursor(recursor)
}
}
}
} while (recursor !== null && retval === null)
return retval
}
}
}
}
exports.DescribedMixin = DescribedMixin
function DeserializedMixin (base) {
return class extends base {
constructor (state, recursor) {
super({
ctor: state[0],
pointer: state[1],
stringTag: state[2],
isArray: state[3],
isIterable: state[4],
isList: state[5],
})
this.deserializedRecursor = recursor
this.replayState = null
}
createRecursor () {
if (!this.deserializedRecursor) return () => null
const replay = recursorUtils.replay(this.replayState, () => ({ size: -1, next: this.deserializedRecursor }))
this.replayState = replay.state
return replay.recursor.next
}
hasSameCtor (expected) {
return this.ctor === expected.ctor
}
}
}
exports.DeserializedMixin = DeserializedMixin