@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
231 lines • 7.71 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.StateAbstractDomain = void 0;
const abstract_domain_1 = require("./abstract-domain");
const lattice_1 = require("./lattice");
/**
* A state abstract domain that maps AST node IDs of a program to abstract values of an abstract domain.
* The Bottom element is defined as {@link Bottom} symbol and the Top element as empty mapping.
* @template Domain - Type of the value abstract domain to map the AST node IDs to
* @see {@link NodeId} for the node IDs of the AST nodes
*/
class StateAbstractDomain extends abstract_domain_1.AbstractDomain {
domain;
constructor(value, domain) {
if (value === lattice_1.Bottom || value.values().some(entry => entry.isBottom())) {
super(lattice_1.Bottom);
}
else {
super(new Map(value));
}
this.domain = domain;
}
create(value) {
return new StateAbstractDomain(value, this.domain);
}
static top(domain) {
return new this(new Map(), domain);
}
static bottom(domain) {
return new this(lattice_1.Bottom, domain);
}
get(node) {
return this.value === lattice_1.Bottom ? this.domain.bottom() : this.value.get(node);
}
has(node) {
return this.value !== lattice_1.Bottom && this.value.has(node);
}
set(node, value) {
if (this.value !== lattice_1.Bottom) {
this._value.set(node, value);
}
}
remove(node) {
if (this.value !== lattice_1.Bottom) {
this._value.delete(node);
}
}
top() {
return this.create(new Map());
}
bottom() {
return this.create(lattice_1.Bottom);
}
equals(other) {
if (this.value === other.value) {
return true;
}
else if (this.value === lattice_1.Bottom || other.value === lattice_1.Bottom || this.value.size !== other.value.size) {
return false;
}
for (const [key, value] of this.value.entries()) {
const otherValue = other.get(key);
if (otherValue === undefined || !value.equals(otherValue)) {
return false;
}
}
return true;
}
leq(other) {
if (this.value === other.value || this.value === lattice_1.Bottom) {
return true;
}
else if (other.value === lattice_1.Bottom || this.value.size > other.value.size) {
return false;
}
for (const [key, value] of this.value.entries()) {
const otherValue = other.get(key);
if (otherValue === undefined || !value.leq(otherValue)) {
return false;
}
}
return true;
}
join(other) {
if (this.value === lattice_1.Bottom) {
return this.create(other.value);
}
else if (other.value === lattice_1.Bottom) {
return this.create(this.value);
}
const result = this.create(this.value);
for (const [key, value] of other.value.entries()) {
const currValue = result.get(key);
if (currValue === undefined) {
result.set(key, value);
}
else {
result.set(key, currValue.join(value));
}
}
return result;
}
meet(other) {
if (this.value === lattice_1.Bottom || other.value === lattice_1.Bottom) {
return this.bottom();
}
const result = this.create(this.value);
for (const key of result.value.keys()) {
if (!other.has(key)) {
result.remove(key);
}
}
for (const [key, value] of other.value.entries()) {
const currValue = result.get(key);
if (currValue !== undefined) {
result.set(key, currValue.meet(value));
}
}
return result;
}
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);
}
const result = this.create(this.value);
for (const [key, value] of other.value.entries()) {
const currValue = result.get(key);
if (currValue === undefined) {
result.set(key, value);
}
else {
result.set(key, currValue.widen(value));
}
}
return result;
}
narrow(other) {
if (this.value === lattice_1.Bottom || other.value === lattice_1.Bottom) {
return this.bottom();
}
const result = this.create(this.value);
for (const key of result.value.keys()) {
if (!other.has(key)) {
result.remove(key);
}
}
for (const [key, value] of other.value.entries()) {
const currValue = result.get(key);
if (currValue !== undefined) {
result.set(key, currValue.narrow(value));
}
}
return result;
}
concretize(limit) {
if (this.value === lattice_1.Bottom) {
return new Set();
}
let mappings = new Set([new Map()]);
for (const [key, value] of this.value.entries()) {
const concreteValues = value.concretize(limit);
if (concreteValues === lattice_1.Top) {
return lattice_1.Top;
}
const newMappings = new Set();
for (const state of mappings) {
for (const concrete of concreteValues) {
if (newMappings.size > limit) {
return lattice_1.Top;
}
const map = new Map(state);
map.set(key, concrete);
newMappings.add(map);
}
}
mappings = newMappings;
}
return mappings;
}
abstract(concrete) {
if (concrete === lattice_1.Top) {
return this.top();
}
else if (concrete.size === 0) {
return this.bottom();
}
const mapping = new Map();
for (const concreteMapping of concrete) {
for (const [key, value] of concreteMapping) {
const set = mapping.get(key);
if (set === undefined) {
mapping.set(key, new Set([value]));
}
else {
set.add(value);
}
}
}
const result = new Map();
for (const [key, values] of mapping) {
result.set(key, this.domain.abstract(values));
}
return this.create(result);
}
toJson() {
if (this.value === lattice_1.Bottom) {
return this.value.description;
}
return Object.fromEntries(this.value.entries().map(([key, value]) => [key, value.toJson()]));
}
toString() {
if (this.value === lattice_1.Bottom) {
return lattice_1.BottomSymbol;
}
return '(' + this.value.entries().toArray().map(([key, value]) => `${abstract_domain_1.AbstractDomain.toString(key)} -> ${value.toString()}`).join(', ') + ')';
}
isTop() {
return this.value !== lattice_1.Bottom && this.value.size === 0;
}
isBottom() {
return this.value === lattice_1.Bottom;
}
isValue() {
return this.value !== lattice_1.Bottom;
}
}
exports.StateAbstractDomain = StateAbstractDomain;
//# sourceMappingURL=state-abstract-domain.js.map