@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
154 lines • 6.76 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.mergeDefinitions = mergeDefinitions;
exports.define = define;
const assert_1 = require("../../util/assert");
const environment_1 = require("./environment");
const clone_1 = require("./clone");
const vertex_1 = require("../graph/vertex");
const config_1 = require("../../config");
function defInEnv(newEnvironments, name, definition) {
const existing = newEnvironments.memory.get(name);
// When there are defined indices, merge the definitions
const inGraphDefinition = definition;
if ((0, config_1.getConfig)().solver.pointerTracking &&
existing !== undefined &&
inGraphDefinition.controlDependencies === undefined) {
if (inGraphDefinition.indicesCollection !== undefined) {
newEnvironments.memory.set(name, mergeDefinitions(existing, inGraphDefinition));
return;
}
else if (existing?.flatMap(i => i.indicesCollection ?? []).length > 0) {
// When indices couldn't be resolved, but indices where defined before, just add the definition
existing.push(definition);
return;
}
}
// check if it is maybe or not
if (existing === undefined || definition.controlDependencies === undefined) {
newEnvironments.memory.set(name, [definition]);
}
else {
existing.push(definition);
}
}
/**
* assumes: existing is not undefined, the overwrite has indices
*/
function mergeDefinitions(existing, definition) {
// When new definition is not a single index, e.g., a list redefinition, then reset existing definition
if (definition.indicesCollection?.some(indices => indices.isContainer)) {
return [definition];
}
const existingDefs = existing.filter(assert_1.isNotUndefined);
const overwriteIndices = definition.indicesCollection?.flatMap(indices => indices.indices) ?? [];
// Compare existing and new definitions,
// add new definitions and remove existing definitions that are overwritten by new definition
const newExistingDefs = [];
const hasCache = new Set();
for (const overwriteIndex of overwriteIndices) {
for (const existingDef of existingDefs) {
// empty or missing
if (existingDef.indicesCollection === undefined || existingDef.indicesCollection.length === 0) {
const existingDefPrint = JSON.stringify(existingDef);
if (!hasCache.has(existingDefPrint)) {
newExistingDefs.push(existingDef);
hasCache.add(existingDefPrint);
}
continue;
}
const newIndicesCollection = overwriteContainerIndices(existingDef.indicesCollection, overwriteIndex);
// if indices are now empty list, don't keep empty definition
if (newIndicesCollection.length > 0) {
const obj = {
...existingDef,
indicesCollection: newIndicesCollection,
};
const objHash = JSON.stringify(obj);
if (!hasCache.has(objHash)) {
newExistingDefs.push(obj);
hasCache.add(objHash);
}
}
}
}
// store changed existing definitions and add new one
return [...newExistingDefs, definition];
}
function overwriteContainerIndices(existingIndices, overwriteIndex) {
const newIndicesCollection = [];
for (const indices of existingIndices) {
let newIndices;
// When overwrite index is container itself, then only overwrite sub-index
if ((0, vertex_1.isParentContainerIndex)(overwriteIndex)) {
newIndices = [];
for (const index of indices.indices) {
if ((0, vertex_1.isSameIndex)(index, overwriteIndex) && (0, vertex_1.isParentContainerIndex)(index)) {
const overwriteSubIndices = overwriteIndex.subIndices.flatMap(a => a.indices);
let newSubIndices = index.subIndices;
for (const overwriteSubIndex of overwriteSubIndices) {
newSubIndices = overwriteContainerIndices(newSubIndices, overwriteSubIndex);
}
if (newSubIndices.length > 0) {
newIndices.push({
...index,
subIndices: newSubIndices,
});
}
}
if (!(0, vertex_1.isSameIndex)(index, overwriteIndex) || !(0, vertex_1.isParentContainerIndex)(index)) {
newIndices.push(index);
}
}
}
else if (indices.isContainer) {
// If indices are not a single, e.g., a list, take the whole definition
newIndices = indices.indices;
}
else {
// Filter existing indices with the same name
newIndices = indices.indices.filter(def => !(0, vertex_1.isSameIndex)(def, overwriteIndex));
}
if (indices.isContainer || newIndices.length > 0) {
newIndicesCollection.push({
...indices,
indices: newIndices,
});
}
}
return newIndicesCollection;
}
/**
* Insert the given `definition` --- defined within the given scope --- into the passed along `environments` will take care of propagation.
* Does not modify the passed along `environments` in-place! It returns the new reference.
*/
function define(definition, superAssign, environment) {
const name = definition.name;
(0, assert_1.guard)(name !== undefined, () => `Name must be defined, but isn't for ${JSON.stringify(definition)}`);
let newEnvironment;
if (superAssign) {
newEnvironment = (0, clone_1.cloneEnvironmentInformation)(environment, true);
let current = newEnvironment.current;
let last = undefined;
let found = false;
do {
if (current.memory.has(name)) {
current.memory.set(name, [definition]);
found = true;
break;
}
last = current;
current = current.parent;
} while (current.id !== environment_1.BuiltInEnvironment.id);
if (!found) {
(0, assert_1.guard)(last !== undefined, () => `Could not find global scope for ${name}`);
last.memory.set(name, [definition]);
}
}
else {
newEnvironment = (0, clone_1.cloneEnvironmentInformation)(environment, false);
defInEnv(newEnvironment.current, name, definition);
}
return newEnvironment;
}
//# sourceMappingURL=define.js.map