@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
179 lines • 6.1 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 as mapping of AST node IDs of a program to abstract values of an abstract domain.
* The Bottom element is defined as empty mapping and the Top element is defined as mapping every existing mapped AST node ID to Top.
* @template Domain - Type of the abstract domain to map the AST node IDs to
* @see {@link NodeId} for the node IDs of the AST nodes
*/
class StateAbstractDomain {
_value;
constructor(value) {
this._value = new Map(value);
}
get value() {
return this._value;
}
bottom() {
return new StateAbstractDomain(new Map());
}
top() {
const result = new StateAbstractDomain(this.value);
for (const [key, value] of result.value) {
result._value.set(key, value.top());
}
return result;
}
equals(other) {
if (this.value === other.value) {
return true;
}
else if (this.value.size !== other.value.size) {
return false;
}
for (const [nodeId, value] of this.value) {
const otherValue = other.value.get(nodeId);
if (otherValue === undefined || !value.equals(otherValue)) {
return false;
}
}
return true;
}
leq(other) {
if (this.value === other.value) {
return true;
}
for (const [nodeId, value] of this.value) {
const otherValue = other.value.get(nodeId);
if (otherValue === undefined || !value.leq(otherValue)) {
return false;
}
}
return true;
}
join(...values) {
const result = new StateAbstractDomain(this.value);
for (const other of values) {
for (const [nodeId, value] of other.value) {
const currValue = result.value.get(nodeId);
if (currValue === undefined) {
result.value.set(nodeId, value);
}
else {
result.value.set(nodeId, currValue.join(value));
}
}
}
return result;
}
meet(...values) {
const result = new StateAbstractDomain(this.value);
for (const other of values) {
for (const [nodeId] of result.value) {
if (!other.value.has(nodeId)) {
result.value.delete(nodeId);
}
}
for (const [nodeId, value] of other.value) {
const currValue = result.value.get(nodeId);
if (currValue !== undefined) {
result.value.set(nodeId, currValue.meet(value));
}
}
}
return result;
}
widen(other) {
const result = new StateAbstractDomain(this.value);
for (const [nodeId, value] of other.value) {
const currValue = result.value.get(nodeId);
if (currValue === undefined) {
result.value.set(nodeId, value);
}
else {
result.value.set(nodeId, currValue.widen(value));
}
}
return result;
}
narrow(other) {
const result = new StateAbstractDomain(this.value);
for (const [nodeId] of this.value) {
if (!other.value.has(nodeId)) {
result.value.delete(nodeId);
}
}
for (const [nodeId, value] of other.value) {
const currValue = result.value.get(nodeId);
if (currValue !== undefined) {
result.value.set(nodeId, currValue.narrow(value));
}
}
return result;
}
concretize(limit = abstract_domain_1.DEFAULT_INFERENCE_LIMIT) {
if (this.value.values().some(value => value.isBottom())) {
return new Set();
}
let states = new Set([new Map()]);
for (const [nodeId, value] of this.value) {
const concreteValues = value.concretize(limit);
if (concreteValues === lattice_1.Top) {
return lattice_1.Top;
}
const newStates = new Set();
for (const state of states) {
for (const concrete of concreteValues) {
if (newStates.size > limit) {
return lattice_1.Top;
}
const map = new Map(state);
map.set(nodeId, concrete);
newStates.add(map);
}
}
states = newStates;
}
return states;
}
abstract(concrete) {
const entry = [...this.value.values()][0];
if (concrete === lattice_1.Top || entry === undefined) {
return new StateAbstractDomain(new Map());
}
const mappings = new Map();
for (const state of concrete) {
for (const [nodeId, value] of state) {
const mapping = mappings.get(nodeId);
if (mapping === undefined) {
mappings.set(nodeId, new Set([value]));
}
else {
mapping.add(value);
}
}
}
const result = new Map();
for (const [nodeId, values] of mappings) {
result.set(nodeId, entry.abstract(values));
}
return new StateAbstractDomain(result);
}
toString() {
return '(' + this.value.entries().toArray().map(([key, value]) => `${key} -> ${value.toString()}`).join(', ') + ')';
}
isTop() {
return this.value.values().every(value => value.isTop());
}
isBottom() {
return this.value.size === 0;
}
isValue() {
return true;
}
}
exports.StateAbstractDomain = StateAbstractDomain;
//# sourceMappingURL=state-abstract-domain.js.map