@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
197 lines • 7.94 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SetUpperBoundDomain = 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 satisfiable_domain_1 = require("./satisfiable-domain");
/**
* The set upper bound abstract domain as sets capturing possible values of the concrete set bounded by a `limit` for the maximum number of inferred values.
* The Bottom element is defined as the{@link Bottom} and the Top element is defined as {@link Top} symbol.
* @template T - Type of the values in the abstract domain
* @template Value - Type of the constraint in the abstract domain (Top, Bottom, or an actual value)
*/
class SetUpperBoundDomain extends abstract_domain_1.AbstractDomain {
limit;
setType;
/**
* @param limit - A limit for the maximum number of elements to store in the set
* @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) {
if (value !== lattice_1.Top && value !== lattice_1.Bottom) {
if (Array.isArray(value)) {
super((value.length > limit ? lattice_1.Top : new setType(value)));
}
else {
super((value.size > limit ? lattice_1.Top : new setType(value)));
}
}
else {
super(value);
}
this.limit = limit;
this.setType = setType;
}
create(value) {
return new SetUpperBoundDomain(value, this.limit, this.setType);
}
static top(limit, setType) {
return new SetUpperBoundDomain(lattice_1.Top, limit, setType);
}
static bottom(limit, setType) {
return new SetUpperBoundDomain(lattice_1.Bottom, limit, setType);
}
static abstract(concrete, limit, setType) {
if (concrete === lattice_1.Top) {
return SetUpperBoundDomain.top(limit, setType);
}
else if (concrete.size === 0) {
return SetUpperBoundDomain.bottom(limit, setType);
}
return new SetUpperBoundDomain(concrete.values().reduce((result, set) => result.union(set)), limit, setType);
}
top() {
return SetUpperBoundDomain.top(this.limit, this.setType);
}
bottom() {
return SetUpperBoundDomain.bottom(this.limit, this.setType);
}
equals(other) {
return this.value === other.value || (this.isValue() && other.isValue() && (0, set_1.setEquals)(this.value, other.value));
}
leq(other) {
return this.value === lattice_1.Bottom || other.value === lattice_1.Top || (this.isValue() && other.isValue() && this.value.isSubsetOf(other.value));
}
join(other) {
const otherValue = other instanceof SetUpperBoundDomain ? other.value : Array.isArray(other) ? new this.setType(other) : other;
if (this.value === lattice_1.Top || otherValue === lattice_1.Top) {
return this.top();
}
else if (this.value === lattice_1.Bottom) {
return this.create(otherValue);
}
else if (otherValue === lattice_1.Bottom) {
return this.create(this.value);
}
else {
return this.create(this.value.union(otherValue));
}
}
meet(other) {
const otherValue = other instanceof SetUpperBoundDomain ? other.value : Array.isArray(other) ? new this.setType(other) : other;
if (this.value === lattice_1.Bottom || otherValue === lattice_1.Bottom) {
return this.bottom();
}
else if (this.value === lattice_1.Top) {
return this.create(otherValue);
}
else if (otherValue === lattice_1.Top) {
return this.create(this.value);
}
else {
return this.create(this.value.intersection(otherValue));
}
}
/**
* Subtracts another abstract value from the current abstract value by removing all elements of the other abstract value from the current abstract value.
*/
subtract(other) {
const otherValue = other instanceof SetUpperBoundDomain ? other.value : Array.isArray(other) ? new this.setType(other) : other;
if (this.value === lattice_1.Top) {
return this.top();
}
else if (this.value === lattice_1.Bottom) {
return this.bottom();
}
else if (otherValue === lattice_1.Top || otherValue === lattice_1.Bottom) {
return this.create(this.value);
}
else {
return this.create(this.value.difference(otherValue));
}
}
widen(other) {
if (this.value === lattice_1.Bottom) {
return this.create(other.value);
}
else if (other.value === lattice_1.Bottom) {
return this.create(this.value);
}
return other.leq(this) ? this.create(this.value) : this.top();
}
narrow(other) {
if (this.value === lattice_1.Bottom || other.value === lattice_1.Bottom) {
return this.bottom();
}
return this.isTop() ? this.create(other.value) : this.create(this.value);
}
concretize(limit) {
if (this.value === lattice_1.Bottom) {
return new Set();
}
else if (this.value === lattice_1.Top || 2 ** (this.value.size) > limit) {
return lattice_1.Top;
}
const subsets = [new this.setType()];
for (const element of this.value.values()) {
const newSubsets = subsets.map(subset => new this.setType([...subset, element]));
for (const subset of newSubsets) {
subsets.push(subset);
}
}
return new Set(subsets);
}
abstract(concrete) {
return SetUpperBoundDomain.abstract(concrete, this.limit, this.setType);
}
satisfies(set, comparator = satisfiable_domain_1.SetComparator.Equal) {
switch (comparator) {
case satisfiable_domain_1.SetComparator.Equal:
case satisfiable_domain_1.SetComparator.SubsetOrEqual: {
if (this.isTop() || (this.isValue() && [...set].length <= this.value.size && [...set].every(value => this.value.has(value)))) {
return logic_1.Ternary.Maybe;
}
return logic_1.Ternary.Never;
}
case satisfiable_domain_1.SetComparator.Subset: {
if (this.isTop() || (this.isValue() && [...set].length < this.value.size && [...set].every(value => this.value.has(value)))) {
return logic_1.Ternary.Maybe;
}
return logic_1.Ternary.Never;
}
default: {
(0, assert_1.assertUnreachable)(comparator);
}
}
}
toJson() {
if (this.value === lattice_1.Top || this.value === lattice_1.Bottom) {
return this.value.description;
}
return this.value.values().toArray();
}
toString() {
if (this.value === lattice_1.Top) {
return lattice_1.TopSymbol;
}
else if (this.value === lattice_1.Bottom) {
return lattice_1.BottomSymbol;
}
const string = this.value.values().map(abstract_domain_1.domainElementToString).toArray().join(', ');
return `{${string}}`;
}
isTop() {
return this.value === lattice_1.Top;
}
isBottom() {
return this.value === lattice_1.Bottom;
}
isValue() {
return this.value !== lattice_1.Top && this.value !== lattice_1.Bottom;
}
}
exports.SetUpperBoundDomain = SetUpperBoundDomain;
//# sourceMappingURL=set-upper-bound-domain.js.map