@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
116 lines • 4.59 kB
JavaScript
"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