UNPKG

@eagleoutice/flowr-dev

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

230 lines 8.48 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AbstractDomain = exports.DEFAULT_INFERENCE_LIMIT = void 0; const assert_1 = require("../../util/assert"); const lattice_1 = require("./lattice"); /** * The default limit of inferred constraints in {@link AbstractDomain|AbstractDomains}. */ exports.DEFAULT_INFERENCE_LIMIT = 50; /** * An abstract domain as complete lattice with a widening and narrowing operator. * All operations of value abstract domains should not modify the domain in-place but return new values using {@link create}. * @template Value - Type of an abstract element of the abstract domain representing possible elements (excludes `Top` and `Bot`) * @template Top - Type of the Top element of the abstract domain representing all possible elements * @template Bot - Type of the Bottom element of the abstract domain representing no possible elements * @template Lift - Type of the current abstract value in the abstract domain (defaults to `Value` or `Top` or `Bot`) */ class AbstractDomain { _value; constructor(value) { this._value = value; } get value() { return this._value; } equals(other) { if (this.value === other.value || (this.isTop() && other.isTop()) || (this.isBottom() && other.isBottom())) { return true; } else if (!this.isValue() || !other.isValue()) { return false; } return this.equalsValue(other); } leq(other) { if (this.isBottom() || other.isTop() || this.equals(other)) { return true; } else if (!this.isValue() || !other.isValue()) { return false; } return this.leqValue(other); } join(other) { other = other instanceof AbstractDomain ? other : this.create(other); if (this.isTop() || other.isTop()) { return this.top(); } else if (this.isBottom()) { return this.create(other.value); } else if (other.isBottom()) { return this.create(this.value); } else if (!this.isValue() || !other.isValue()) { return this.bottom(); } return this.joinValue(other); } /** * Joins the current abstract value with multiple other abstract values. */ joinAll(values) { let result = this.create(this.value); for (const other of values) { result = result.join(other); } return result; } meet(other) { other = other instanceof AbstractDomain ? other : this.create(other); if (this.isTop()) { return this.create(other.value); } else if (other.isTop()) { return this.create(this.value); } else if (this.isBottom() || other.isBottom()) { return this.bottom(); } else if (!this.isValue() || !other.isValue()) { return this.top(); } return this.meetValue(other); } /** * Meets the current abstract value with multiple other abstract values. */ meetAll(values) { let result = this.create(this.value); for (const other of values) { result = result.meet(other); } return result; } /** * Widens the current abstract value with another abstract value as a sound over-approximation of the join (least upper bound) for fixpoint iteration acceleration. */ widen(other) { if (this.isTop() || other.isTop()) { return this.top(); } else if (this.isBottom()) { return this.create(other.value); } else if (other.isBottom()) { return this.create(this.value); } else if (!this.isValue() || !other.isValue()) { return this.bottom(); } return this.widenValue?.(other) ?? (other.leq(this) ? this.create(this.value) : this.top()); } /** * Narrows the current abstract value with another abstract value as a sound over-approximation of the meet (greatest lower bound) to refine the value after widening. */ narrow(other) { if (this.isTop()) { return this.create(other.value); } else if (other.isTop()) { return this.create(this.value); } else if (this.isBottom() || other.isBottom()) { return this.bottom(); } else if (!this.isValue() || !other.isValue()) { return this.top(); } return this.narrowValue?.(other) ?? (this.isTop() ? this.create(other.value) : this.create(this.value)); } transform(transform, bottomDefault, topDefault) { if (bottomDefault !== undefined && this.isBottom()) { return this.create(bottomDefault); } else if (topDefault !== undefined && this.isTop()) { return this.create(topDefault); } return this.create(transform(this.value)); } merge(other, merge, bottomDefault, topDefault) { if (bottomDefault !== undefined) { if (this.isBottom()) { return this.create(bottomDefault(other.value)); } else if (other.isBottom()) { return this.create(bottomDefault(this.value)); } } if (topDefault !== undefined) { if (this.isTop()) { return this.create(topDefault(other.value)); } else if (other.isTop()) { return this.create(topDefault(this.value)); } } return this.create(merge(this.value, other.value)); } toJSON() { if (this.value === lattice_1.Top) { return lattice_1.Top.description; } else if (this.value === lattice_1.Bottom) { return lattice_1.Bottom.description; } return this.jsonify(); } toString() { if (this.value === lattice_1.Top) { return lattice_1.TopSymbol; } else if (this.value === lattice_1.Bottom) { return lattice_1.BottomSymbol; } return this.stringify(); } isNotTop() { return !this.isTop(); } isNotBottom() { return !this.isBottom(); } isNotValue() { return !this.isValue(); } /** * Joins an array of abstract values by joining the first abstract value with the other values in the array. * The provided array of abstract values must not be empty or a default value must be provided! */ static joinAll(values, defaultValue) { (0, assert_1.guard)(values.length > 0 || defaultValue !== undefined, 'Abstract values to join cannot be empty'); return values[0]?.joinAll(values.slice(1)) ?? defaultValue; } /** * Meets an array of abstract values by meeting the first abstract value with the other values in the array. * The provided array of abstract values must not be empty or a default value must be provided! */ static meetAll(values, defaultValue) { (0, assert_1.guard)(values.length > 0 || defaultValue !== undefined, 'Abstract values to meet cannot be empty'); return values[0]?.meetAll(values.slice(1)) ?? defaultValue; } /** * Converts an element of an abstract domain into a string. */ static toString(value) { if (value instanceof Map) { return `{${value.entries().map(([key, value]) => `${AbstractDomain.toString(key)} -> ${AbstractDomain.toString(value)}`).toArray().join(', ')}}`; } else if (value instanceof Set) { return `{${value.values().map(AbstractDomain.toString).toArray().join(', ')}}`; } else if (typeof value === 'object' && value !== null && value.toString !== Object.prototype.toString) { // eslint-disable-next-line @typescript-eslint/no-base-to-string return value.toString(); } else if (Array.isArray(value)) { return `[${value.map(AbstractDomain.toString).join(', ')}]`; } else if (value === lattice_1.Top) { return lattice_1.TopSymbol; } else if (value === lattice_1.Bottom) { return lattice_1.BottomSymbol; } return JSON.stringify(value); } } exports.AbstractDomain = AbstractDomain; //# sourceMappingURL=abstract-domain.js.map