UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

116 lines 4.59 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getOriginInDfg = getOriginInDfg; const vertex_1 = require("../graph/vertex"); const edge_1 = require("../graph/edge"); const linker_1 = require("../internal/linker"); const assert_1 = require("../../util/assert"); const built_in_1 = require("../environments/built-in"); /** * 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, { types }] of dfg.outgoingEdges(use.id) ?? []) { if ((0, edge_1.edgeDoesNotIncludeType)(types, WantedVariableTypes) || (0, edge_1.edgeIncludesType)(types, 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, { types }] of outgoingReads) { if ((0, edge_1.edgeIncludesType)(types, 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) { return origins; } origins = (origins ?? []).concat([...targets].map(target => { if ((0, built_in_1.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