UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

132 lines 6.93 kB
"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