UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

117 lines 4.68 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getOriginInDfg = getOriginInDfg; const node_id_1 = require("../../r-bridge/lang-4.x/ast/model/processing/node-id"); const vertex_1 = require("../graph/vertex"); const edge_1 = require("../graph/edge"); const linker_1 = require("../internal/linker"); const assert_1 = require("../../util/assert"); /** * Obtain the (dataflow) origin of a given node in the dfg. * @example consider the following code: * ```r * x <- 2 * if(u) { * x <- 3 * } * print(x) * ``` * Requesting the origin of `x` in the `print(x)` node yields two {@link SimpleOriginOrigin|variable origins} for both * definitions of `x`. * Similarly, requesting the origin of `print` returns a {@link BuiltInFunctionOrigin|`BuiltInFunctionOrigin`}. * * This returns undefined only if there is no dataflow correspondence (e.g. in case of unevaluated non-standard eval). */ function getOriginInDfg(dfg, id) { const vtx = dfg.getVertex(id); switch (vtx?.tag) { case undefined: return undefined; case vertex_1.VertexType.Value: return [{ type: 4 /* OriginType.ConstantOrigin */, id }]; case vertex_1.VertexType.FunctionDefinition: return [{ type: 4 /* OriginType.ConstantOrigin */, id }]; case vertex_1.VertexType.VariableDefinition: return getVariableDefinitionOrigin(dfg, vtx); case vertex_1.VertexType.Use: return getVariableUseOrigin(dfg, vtx); case vertex_1.VertexType.FunctionCall: return getCallTarget(dfg, vtx); } } const WantedVariableTypes = edge_1.EdgeType.Reads | edge_1.EdgeType.DefinedByOnCall; const UnwantedVariableTypes = edge_1.EdgeType.NonStandardEvaluation; function getVariableUseOrigin(dfg, use) { // to identify the origins we have to track read edges and definitions on function calls const origins = []; for (const [target, e] of dfg.outgoingEdges(use.id) ?? []) { if (edge_1.DfEdge.doesNotIncludeType(e, WantedVariableTypes) || edge_1.DfEdge.includesType(e, UnwantedVariableTypes)) { continue; } const targetVtx = dfg.getVertex(target); if (!targetVtx) { continue; } if (targetVtx.tag === vertex_1.VertexType.VariableDefinition) { origins.push({ type: 0 /* OriginType.ReadVariableOrigin */, id: target }); } } return origins.length > 0 ? origins : undefined; } function getVariableDefinitionOrigin(dfg, vtx) { const pool = [{ type: 1 /* OriginType.WriteVariableOrigin */, id: vtx.id }]; const outgoingReads = dfg.outgoingEdges(vtx.id) ?? []; for (const [target, e] of outgoingReads) { if (edge_1.DfEdge.includesType(e, edge_1.EdgeType.Reads)) { const targetVtx = dfg.getVertex(target); if (!targetVtx) { continue; } if (targetVtx.tag === vertex_1.VertexType.VariableDefinition) { pool.push({ type: 0 /* OriginType.ReadVariableOrigin */, id: target }); } } } return pool; } function getCallTarget(dfg, call) { // check for built-ins: const builtInTarget = call.origin !== 'unnamed' && call.origin.filter(o => o.startsWith('builtin:')); let origins = builtInTarget ? builtInTarget.map(o => ({ type: 3 /* OriginType.BuiltInFunctionOrigin */, fn: { name: call.name }, id: call.id, proc: o })) : undefined; const targets = new Set((0, linker_1.getAllFunctionCallTargets)(call.id, dfg)); if (targets.size === 0) { // resolve via variable use origin return origins?.length === 0 ? getVariableUseOrigin(dfg, call) : origins; } origins = (origins ?? []).concat([...targets].map(target => { if (node_id_1.NodeId.isBuiltIn(target)) { return { type: 3 /* OriginType.BuiltInFunctionOrigin */, fn: { name: call.name }, id: call.id, proc: target }; } const get = dfg.getVertex(target); if (get?.tag !== vertex_1.VertexType.FunctionDefinition && get?.tag !== vertex_1.VertexType.VariableDefinition) { return undefined; } return { type: get.tag === vertex_1.VertexType.FunctionDefinition ? 2 /* OriginType.FunctionCallOrigin */ : 0 /* OriginType.ReadVariableOrigin */, id: target }; }).filter(assert_1.isNotUndefined)); return origins; } //# sourceMappingURL=dfg-get-origin.js.map