UNPKG

@eagleoutice/flowr-dev

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

347 lines 14.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SetRangeDomain = exports.SetRangeTop = void 0; const assert_1 = require("../../util/assert"); const set_1 = require("../../util/collections/set"); const logic_1 = require("../../util/logic"); const abstract_domain_1 = require("./abstract-domain"); const lattice_1 = require("./lattice"); const value_abstract_domain_1 = require("./value-abstract-domain"); /** The Top element of the set range domain with an empty set as must set and {@link Top} as may set */ exports.SetRangeTop = { must: new Set(), may: lattice_1.Top }; /** * The set range abstract domain as range of possible value sets with a set of values that must be present and a set of values that may be present * (similar to an interval-like structure with a lower bound and a difference to the upper bound). * The Bottom element is defined as {@link Bottom} symbol and the Top element is defined as the range `[∅, Top]` where the must set is the empty set and the may set is {@link Top}. * @template T - Type of the values in the sets in the abstract domain * @template Value - Type of the constraint in the abstract domain (Top, Bottom, or an actual value) */ class SetRangeDomain extends abstract_domain_1.AbstractDomain { limit; setType; /** * @param limit - A limit for the maximum number of elements to store in the must set and may set before over-approximation * @param newSet - An optional set constructor for the domain elements if the type `T` is not storable in a HashSet */ constructor(value, limit = abstract_domain_1.DEFAULT_INFERENCE_LIMIT, setType = Set) { limit = typeof limit === 'number' ? { must: limit, may: limit } : limit; if (value !== lattice_1.Bottom) { const mustSet = new setType(value.must); const maySet = value.may === lattice_1.Top ? lattice_1.Top : new setType(value.may); const mustExceeds = mustSet.size > limit.must; const mayExceeds = maySet === lattice_1.Top || maySet.size > limit.may || mustSet.size + maySet.size > limit.must + limit.may; const must = mustExceeds ? new setType(mustSet.values().take(limit.must)) : mustSet; const may = mayExceeds ? lattice_1.Top : mustSet.union(maySet).difference(must); super({ must, may }); } else { super(value); } this.limit = limit; this.setType = setType; } create(value) { return new SetRangeDomain(value, this.limit, this.setType); } /** * The set of values that must be present in the set. */ get must() { return (this.isValue() ? this.value.must : lattice_1.Bottom); } /** * The set of values that may be present in the set (can be {@link Top}). */ get may() { return (this.isValue() ? this.value.may : lattice_1.Bottom); } lower() { return this.must; } upper() { return this.isFinite() ? this.must.union(this.may) : this.may; } from(...values) { if (values.length === 0) { return this.bottom(); } const sets = values.map(value => new Set(value)); const must = sets.reduce((result, set) => result.intersection(set)); const may = sets.reduce((result, set) => result.union(set)).difference(must); return this.create({ must, may }); } static top(limit, setType) { return new this(exports.SetRangeTop, limit, setType); } static bottom(limit, setType) { return new this(lattice_1.Bottom, limit, setType); } static from(values, limit, setType) { values = !Array.isArray(values) ? [values] : values.every(value => value instanceof Set || Array.isArray(value)) ? values : [values]; if (values.length === 0) { return this.bottom(); } const sets = values.map(value => new Set(value)); const must = sets.reduce((result, set) => result.intersection(set)); const may = sets.reduce((result, set) => result.union(set)).difference(must); return new this({ must, may }, limit, setType); } top() { return this.create(exports.SetRangeTop); } bottom() { return this.create(lattice_1.Bottom); } equalsValue(other) { return (0, set_1.setEquals)(this.must, other.must) && (this.may === other.may || (this.may !== lattice_1.Top && other.may !== lattice_1.Top && (0, set_1.setEquals)(this.may, other.may))); } leqValue(other) { const thisLower = this.lower(), thisUpper = this.upper(); const otherLower = other.lower(), otherUpper = other.upper(); return otherLower.isSubsetOf(thisLower) && (otherUpper === lattice_1.Top || (thisUpper !== lattice_1.Top && thisUpper.isSubsetOf(otherUpper))); } joinValue(other) { const thisLower = this.lower(), thisUpper = this.upper(); const otherLower = other.lower(), otherUpper = other.upper(); const joinLower = thisLower.intersection(otherLower); let joinUpper; if (thisUpper === lattice_1.Top || otherUpper === lattice_1.Top) { joinUpper = lattice_1.Top; } else { joinUpper = thisUpper.union(otherUpper); } return this.create({ must: joinLower, may: joinUpper === lattice_1.Top ? lattice_1.Top : joinUpper.difference(joinLower) }); } meetValue(other) { const thisLower = this.lower(), thisUpper = this.upper(); const otherLower = other.lower(), otherUpper = other.upper(); const meetLower = thisLower.union(otherLower); let meetUpper; if (thisUpper === lattice_1.Top) { meetUpper = otherUpper; } else if (otherUpper === lattice_1.Top) { meetUpper = thisUpper; } else { meetUpper = thisUpper.intersection(otherUpper); } if (meetUpper !== lattice_1.Top && !meetLower.isSubsetOf(meetUpper)) { return this.bottom(); } return this.create({ must: meetLower, may: meetUpper === lattice_1.Top ? lattice_1.Top : meetUpper.difference(meetLower) }); } union(other) { other = other instanceof SetRangeDomain ? other : this.create(other); if (!this.isValue() || !other.isValue()) { return this.bottom(); } const thisLower = this.lower(), thisUpper = this.upper(); const otherLower = other.lower(), otherUpper = other.upper(); const unionLower = thisLower.union(otherLower); let unionUpper; if (thisUpper === lattice_1.Top || otherUpper === lattice_1.Top) { unionUpper = lattice_1.Top; } else { unionUpper = thisUpper.union(otherUpper); } return this.create({ must: unionLower, may: unionUpper === lattice_1.Top ? lattice_1.Top : unionUpper.difference(unionLower) }); } intersect(other) { other = other instanceof SetRangeDomain ? other : this.create(other); if (!this.isValue() || !other.isValue()) { return this.bottom(); } const thisLower = this.lower(), thisUpper = this.upper(); const otherLower = other.lower(), otherUpper = other.upper(); const intersectLower = thisLower.intersection(otherLower); let intersectUpper; if (thisUpper === lattice_1.Top) { intersectUpper = otherUpper; } else if (otherUpper === lattice_1.Top) { intersectUpper = thisUpper; } else { intersectUpper = thisUpper.intersection(otherUpper); } return this.create({ must: intersectLower, may: intersectUpper === lattice_1.Top ? lattice_1.Top : intersectUpper.difference(intersectLower) }); } subtract(other) { other = other instanceof SetRangeDomain ? other : this.create(other); if (!this.isValue() || !other.isValue()) { return this.bottom(); } const thisLower = this.lower(), thisUpper = this.upper(); const otherLower = other.lower(), otherUpper = other.upper(); let subtractLower; if (otherUpper === lattice_1.Top) { subtractLower = new Set(); } else { subtractLower = thisLower.difference(otherUpper); } let subtractUpper; if (thisUpper === lattice_1.Top) { subtractUpper = lattice_1.Top; } else if (otherUpper === lattice_1.Top) { subtractUpper = thisUpper.difference(otherLower); } else { subtractUpper = thisUpper.difference(otherUpper); } return this.create({ must: subtractLower, may: subtractUpper === lattice_1.Top ? lattice_1.Top : subtractUpper.difference(subtractLower) }); } widenValue(other) { const thisLower = this.lower(), thisUpper = this.upper(); const otherLower = other.lower(), otherUpper = other.upper(); let widenLower; if (thisLower.isSubsetOf(otherLower)) { widenLower = thisLower; } else { widenLower = new Set(); } let widenUpper; if (thisUpper !== lattice_1.Top && otherUpper !== lattice_1.Top && otherUpper.isSubsetOf(thisUpper)) { widenUpper = thisUpper; } else { widenUpper = lattice_1.Top; } return this.create({ must: widenLower, may: widenUpper === lattice_1.Top ? lattice_1.Top : widenUpper.difference(widenLower) }); } narrowValue(other) { const thisLower = this.lower(), thisUpper = this.upper(); const otherLower = other.lower(), otherUpper = other.upper(); if (this.meetValue(other).isBottom()) { return this.bottom(); } let narrowLower; if (thisLower.size === 0) { narrowLower = otherLower; } else { narrowLower = thisLower; } let narrowUpper; if (thisUpper === lattice_1.Top) { narrowUpper = otherUpper; } else { narrowUpper = thisUpper; } return this.create({ must: narrowLower, may: narrowUpper === lattice_1.Top ? lattice_1.Top : narrowUpper.difference(narrowLower) }); } satisfies(set, comparator = value_abstract_domain_1.SetComparator.Equal) { const lower = this.lower(), upper = this.upper(); if (lower === lattice_1.Bottom || upper === lattice_1.Bottom) { return logic_1.Ternary.Never; } const value = new this.setType(set); switch (comparator) { case value_abstract_domain_1.SetComparator.Equal: { if (lower.isSubsetOf(value) && (upper === lattice_1.Top || value.isSubsetOf(upper))) { return upper !== lattice_1.Top && lower.size === upper.size ? logic_1.Ternary.Always : logic_1.Ternary.Maybe; } return logic_1.Ternary.Never; } case value_abstract_domain_1.SetComparator.SubsetOrEqual: { if (upper === lattice_1.Top || value.isSubsetOf(upper)) { return value.isSubsetOf(lower) ? logic_1.Ternary.Always : logic_1.Ternary.Maybe; } return logic_1.Ternary.Never; } case value_abstract_domain_1.SetComparator.Subset: { if (upper === lattice_1.Top || (value.isSubsetOf(upper) && !(0, set_1.setEquals)(value, upper))) { return value.isSubsetOf(lower) && !(0, set_1.setEquals)(value, lower) ? logic_1.Ternary.Always : logic_1.Ternary.Maybe; } return logic_1.Ternary.Never; } case value_abstract_domain_1.SetComparator.Intersect: { if (this.isInfinite() || (this.isFinite() && [...set].some(value => this.may.has(value)))) { return (this.isValue() && [...set].some(value => this.must.has(value))) ? logic_1.Ternary.Always : logic_1.Ternary.Maybe; } return logic_1.Ternary.Never; } case value_abstract_domain_1.SetComparator.NoIntersect: { return logic_1.TernaryLogic.negate(this.satisfies(set, value_abstract_domain_1.SetComparator.Intersect)); } default: { (0, assert_1.assertUnreachable)(comparator); } } } /** * Extends the must set of the current abstract value down to the empty set. */ widenDown() { if (this.isValue()) { return this.create({ must: new this.setType(), may: this.upper() }); } return this.bottom(); } /** * Extends the may set of the current abstract value up to {@link Top}. */ widenUp() { if (this.isValue()) { return this.create({ must: this.must, may: lattice_1.Top }); } return this.bottom(); } jsonify() { const must = this.must.values().toArray(); const may = this.may === lattice_1.Top ? lattice_1.Top.description : this.may.values().toArray(); return { must, may }; } stringify() { const must = this.must.values().map(abstract_domain_1.AbstractDomain.toString).toArray().join(', '); if (this.may === lattice_1.Top) { return `[{${must}}, ${lattice_1.TopSymbol}]`; } const may = this.may.values().map(abstract_domain_1.AbstractDomain.toString).toArray().join(', '); return `[{${must}}, {${may}}]`; } isTop() { return this.isValue() && this.must.size === 0 && this.may === lattice_1.Top; } isBottom() { return this.value === lattice_1.Bottom; } isValue() { return this.value !== lattice_1.Bottom; } isFinite() { return this.isValue() && this.may !== lattice_1.Top; } isInfinite() { return this.isValue() && this.may === lattice_1.Top; } } exports.SetRangeDomain = SetRangeDomain; //# sourceMappingURL=set-range-domain.js.map