@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
132 lines • 6.93 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.processAllArguments = processAllArguments;
exports.patchFunctionCall = patchFunctionCall;
const info_1 = require("../../../../info");
const processor_1 = require("../../../../processor");
const r_function_call_1 = require("../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
const identifier_1 = require("../../../../environments/identifier");
const overwrite_1 = require("../../../../environments/overwrite");
const resolve_by_name_1 = require("../../../../environments/resolve-by-name");
const type_1 = require("../../../../../r-bridge/lang-4.x/ast/model/type");
const vertex_1 = require("../../../../graph/vertex");
const edge_1 = require("../../../../graph/edge");
function forceVertexArgumentValueReferences(rootId, value, graph, env) {
const valueVertex = graph.getVertex(value.entryPoint);
if (!valueVertex) {
return;
}
// link read if it is function definition directly and reference the exit point
if (valueVertex.tag !== vertex_1.VertexType.Value) {
if (valueVertex.tag === vertex_1.VertexType.FunctionDefinition) {
for (const exit of valueVertex.exitPoints) {
graph.addEdge(rootId, exit, edge_1.EdgeType.Reads);
}
}
else {
for (const exit of value.exitPoints) {
graph.addEdge(rootId, exit.nodeId, edge_1.EdgeType.Reads);
}
}
}
const containedSubflowIn = [...graph.vertices(true)]
.filter(([, info]) => (0, vertex_1.isFunctionDefinitionVertex)(info))
.flatMap(([, info]) => info);
// try to resolve them against the current environment
for (const ref of [...value.in, ...containedSubflowIn.flatMap(n => n.subflow.in)]) {
if (ref.name) {
const resolved = ref.name ? (0, resolve_by_name_1.resolveByName)(ref.name, env, ref.type) ?? [] : [];
for (const resolve of resolved) {
graph.addEdge(ref.nodeId, resolve.nodeId, edge_1.EdgeType.Reads);
}
}
}
}
function processAllArguments({ functionName, args, data, finalGraph, functionRootId, forceArgs = [], patchData = d => d }) {
let finalEnv = functionName.environment;
// arg env contains the environments with other args defined
let argEnv = functionName.environment;
const callArgs = [];
const processedArguments = [];
const remainingReadInArgs = [];
let i = -1;
for (const arg of args) {
i++;
data = patchData(data, i);
if (arg === r_function_call_1.EmptyArgument) {
callArgs.push(r_function_call_1.EmptyArgument);
processedArguments.push(undefined);
continue;
}
const processed = (0, processor_1.processDataflowFor)(arg, { ...data, environment: argEnv });
if (arg.type === type_1.RType.Argument && arg.value && (forceArgs === 'all' || forceArgs[i]) && arg.value.type !== type_1.RType.Number && arg.value.type !== type_1.RType.String && arg.value.type !== type_1.RType.Logical) {
forceVertexArgumentValueReferences(functionRootId, processed, processed.graph, argEnv);
}
processedArguments.push(processed);
finalEnv = (0, overwrite_1.overwriteEnvironment)(finalEnv, processed.environment);
finalGraph.mergeWith(processed.graph);
// resolve reads within argument, we resolve before adding the `processed.environment` to avoid cyclic dependencies
for (const ingoing of [...processed.in, ...processed.unknownReferences]) {
// check if it is called directly
const vtx = finalGraph.getVertex(ingoing.nodeId);
const tryToResolve = ingoing.name ? (0, resolve_by_name_1.resolveByName)(ingoing.name, argEnv, vtx?.tag === vertex_1.VertexType.FunctionCall ? identifier_1.ReferenceType.Function : identifier_1.ReferenceType.Unknown) : undefined;
if (tryToResolve === undefined) {
remainingReadInArgs.push(ingoing);
}
else {
/* maybe all targets are not definitely of the current scope and should be still kept */
let assumeItMayHaveAHigherTarget = true;
for (const resolved of tryToResolve) {
if ((0, info_1.happensInEveryBranch)(resolved.controlDependencies)) {
assumeItMayHaveAHigherTarget = false;
}
// When only a single index is referenced, we don't need to reference the whole object
const resolvedInGraphDef = resolved;
const isContainer = checkForContainer(resolvedInGraphDef?.indicesCollection);
if (isContainer || isContainer === undefined) {
finalGraph.addEdge(ingoing.nodeId, resolved.nodeId, edge_1.EdgeType.Reads);
}
}
if (assumeItMayHaveAHigherTarget) {
remainingReadInArgs.push(ingoing);
}
}
}
argEnv = (0, overwrite_1.overwriteEnvironment)(argEnv, processed.environment);
if (arg.type !== type_1.RType.Argument || !arg.name) {
callArgs.push({ nodeId: processed.entryPoint, controlDependencies: undefined, type: identifier_1.ReferenceType.Argument });
}
else {
callArgs.push({ nodeId: processed.entryPoint, name: arg.name.content, controlDependencies: undefined, type: identifier_1.ReferenceType.Argument });
}
finalGraph.addEdge(functionRootId, processed.entryPoint, edge_1.EdgeType.Argument);
}
return { finalEnv, callArgs, remainingReadInArgs, processedArguments };
}
function patchFunctionCall({ nextGraph, rootId, name, data, argumentProcessResult }) {
nextGraph.addVertex({
tag: vertex_1.VertexType.FunctionCall,
id: rootId,
name: name.content,
environment: data.environment,
/* will be overwritten accordingly */
onlyBuiltin: false,
cds: data.controlDependencies,
args: argumentProcessResult.map(arg => arg === undefined ? r_function_call_1.EmptyArgument : { nodeId: arg.entryPoint, controlDependencies: undefined, call: undefined, type: identifier_1.ReferenceType.Argument }),
});
for (const arg of argumentProcessResult) {
if (arg) {
nextGraph.addEdge(rootId, arg.entryPoint, edge_1.EdgeType.Argument);
}
}
}
/**
* Check whether passed {@link indices} are containers or whether their sub-indices are containers.
*/
function checkForContainer(indices) {
return indices?.every((indices) => {
const areSubIndicesContainers = indices.indices.every(index => 'subIndices' in index && checkForContainer(index.subIndices));
return indices.isContainer || areSubIndicesContainers;
});
}
//# sourceMappingURL=common.js.map