mirror of
https://github.com/github/codeql-action.git
synced 2025-12-27 01:30:10 +08:00
255 lines
7.0 KiB
JavaScript
255 lines
7.0 KiB
JavaScript
'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
|