@eagleoutice/flowr-dev
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
76 lines • 4.3 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.processReplacementFunction = processReplacementFunction;
const info_1 = require("../../../../../info");
const known_call_handling_1 = require("../known-call-handling");
const log_1 = require("../../../../../../util/log");
const built_in_assignment_1 = require("./built-in-assignment");
const common_1 = require("../common");
const assert_1 = require("../../../../../../util/assert");
const r_function_call_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
const logger_1 = require("../../../../../logger");
const vertex_1 = require("../../../../../graph/vertex");
const graph_1 = require("../../../../../graph/graph");
const edge_1 = require("../../../../../graph/edge");
const dfg_1 = require("../../../../../../util/mermaid/dfg");
const type_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/type");
const list_access_1 = require("../../../../../../util/list-access");
const config_1 = require("../../../../../../config");
function processReplacementFunction(name,
/** The last one has to be the value */
args, rootId, data, config) {
if (args.length < 2) {
logger_1.dataflowLogger.warn(`Replacement ${name.content} has less than 2 arguments, skipping`);
return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data }).information;
}
/* we only get here if <-, <<-, ... or whatever is part of the replacement is not overwritten */
(0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Replacement ${name.content} with ${JSON.stringify(args)}, processing`);
let indices = undefined;
if (name.content === '$<-' && (0, config_1.getConfig)().solver.pointerTracking) {
const nonEmptyArgs = args.filter(arg => arg !== r_function_call_1.EmptyArgument);
const accessedArg = nonEmptyArgs.find(arg => arg.info.role === "accessed" /* RoleInParent.Accessed */);
const accessArg = nonEmptyArgs.find(arg => arg.info.role === "index-access" /* RoleInParent.IndexAccess */);
if (accessArg !== undefined && accessedArg != undefined) {
const leafIndex = { lexeme: accessArg.lexeme, nodeId: accessedArg.info.parent ?? '' };
const accessIndices = {
indices: [leafIndex],
isContainer: false
};
// Check for nested access
if (accessedArg.value?.type === type_1.RType.Access) {
indices = (0, list_access_1.constructNestedAccess)(accessedArg.value, accessIndices);
}
else {
// use access node as reference to get complete line in slice
indices = [accessIndices];
}
}
}
/* we assign the first argument by the last for now and maybe mark as maybe!, we can keep the symbol as we now know we have an assignment */
const res = (0, built_in_assignment_1.processAssignment)(name, [args[0], args[args.length - 1]], rootId, data, {
superAssignment: config.assignmentOperator === '<<-',
makeMaybe: indices !== undefined ? false : config.makeMaybe,
indicesCollection: indices
});
/* now, we soft-inject other arguments, so that calls like `x[y] <- 3` are linked correctly */
const { callArgs } = (0, common_1.processAllArguments)({
functionName: (0, info_1.initializeCleanDataflowInformation)(rootId, data),
args: args.slice(1, -1),
data,
functionRootId: rootId,
finalGraph: res.graph,
forceArgs: config.forceArgs,
});
const fn = res.graph.getVertex(rootId, true);
(0, assert_1.guard)(fn?.tag === vertex_1.VertexType.FunctionCall && fn.args.length === 2, () => `Function ${rootId} not found in graph or not 2-arg fn-call (${JSON.stringify(fn)}) ${(0, dfg_1.graphToMermaidUrl)(res.graph)}`);
fn.args = [fn.args[0], ...callArgs, fn.args[1]];
/* a replacement reads all of its call args as well, at least as far as I am aware of */
for (const arg of callArgs) {
const ref = (0, graph_1.getReferenceOfArgument)(arg);
if (ref !== undefined) {
res.graph.addEdge(rootId, ref, edge_1.EdgeType.Reads);
}
}
return res;
}
//# sourceMappingURL=built-in-replacement.js.map