@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
152 lines • 6.87 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 common_1 = require("../common");
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 type_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/type");
const containers_1 = require("../../../../../../util/containers");
const config_1 = require("../../../../../../config");
const unpack_argument_1 = require("../argument/unpack-argument");
const built_in_access_1 = require("./built-in-access");
const built_in_1 = require("../../../../../environments/built-in");
const identifier_1 = require("../../../../../environments/identifier");
const unknown_replacement_1 = require("../../../../../graph/unknown-replacement");
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, origin: 'default' }).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 = config.activeIndices;
if ((0, config_1.getConfig)().solver.pointerTracking) {
indices ??= constructAccessedIndices(name.content, args);
}
/* 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 */
let res = built_in_1.BuiltInProcessorMapper['builtin:assignment'](name, [args[0], args[args.length - 1]], rootId, data, {
superAssignment: config.assignmentOperator === '<<-',
makeMaybe: indices !== undefined ? false : config.makeMaybe,
indicesCollection: indices,
canBeReplacement: true
});
const createdVert = res.graph.getVertex(rootId);
if (createdVert?.tag === vertex_1.VertexType.FunctionCall) {
createdVert.origin = ['builtin:replacement'];
}
const convertedArgs = config.readIndices ? args.slice(1, -1) : (0, built_in_access_1.symbolArgumentsToStrings)(args.slice(1, -1), 0);
/* 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: convertedArgs,
data,
functionRootId: rootId,
finalGraph: res.graph,
forceArgs: config.forceArgs,
});
(0, common_1.patchFunctionCall)({
nextGraph: res.graph,
data,
rootId,
name,
argumentProcessResult: args.map(a => a === r_function_call_1.EmptyArgument ? undefined : { entryPoint: (0, unpack_argument_1.unpackArgument)(a)?.info.id }),
origin: 'builtin:replacement',
link: config.assignRootId ? { origin: [config.assignRootId] } : undefined
});
const firstArg = (0, unpack_argument_1.unpackArgument)(args[0]);
(0, unknown_replacement_1.handleReplacementOperator)({
operator: name.content,
target: firstArg?.lexeme,
env: res.environment,
id: rootId
});
if (firstArg) {
res.graph.addEdge(firstArg.info.id, rootId, edge_1.EdgeType.DefinedBy | edge_1.EdgeType.Reads);
}
/* 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);
}
}
const fa = (0, unpack_argument_1.unpackArgument)(args[0]);
if (!(0, config_1.getConfig)().solver.pointerTracking && fa) {
res = {
...res,
in: [...res.in, { name: fa.lexeme, type: identifier_1.ReferenceType.Variable, nodeId: fa.info.id, controlDependencies: data.controlDependencies }]
};
}
return res;
}
/**
* Constructs accessed indices of replacement function recursively.
*
* Example:
* ```r
* a$b <- 1
* # results in index with lexeme b as identifier
*
* a[[1]]$b
* # results in index with index 1 as identifier with a sub-index with lexeme b as identifier
* ```
*
* @param operation - Operation of replacement function e.g. '$\<-', '[\<-', '[[\<-'
* @param args - Arguments of the replacement function
* @returns Accessed indices construct
*/
function constructAccessedIndices(operation, args) {
const { accessedArg, accessArg } = (0, containers_1.getAccessOperands)(args);
if (accessedArg === undefined || accessArg?.value === undefined || !isSupportedOperation(operation, accessArg.value)) {
return undefined;
}
const constructIdentifier = getIdentifierBuilder(operation);
const leafIndex = {
identifier: constructIdentifier(accessArg),
nodeId: accessedArg.info.parent ?? ''
};
const accessIndices = {
indices: [leafIndex],
isContainer: false
};
// Check for nested access
let indicesCollection = undefined;
if (accessedArg.value?.type === type_1.RType.Access) {
indicesCollection = (0, containers_1.constructNestedAccess)(accessedArg.value, accessIndices, constructIdentifier);
}
else {
// use access node as reference to get complete line in slice
indicesCollection = [accessIndices];
}
return indicesCollection;
}
function isSupportedOperation(operation, value) {
const isNameBasedAccess = (operation === '$<-' || operation === '@<-') && value.type === type_1.RType.Symbol;
const isNumericalIndexBasedAccess = (operation === '[[<-' || operation === '[<-') && value.type === type_1.RType.Number;
return isNameBasedAccess || isNumericalIndexBasedAccess;
}
function getIdentifierBuilder(operation) {
if (operation === '$<-' || operation == '@<-') {
return (arg) => {
return {
index: undefined,
lexeme: arg.lexeme,
};
};
}
// [[<- and [<-
return (arg) => {
return {
index: Number(arg.lexeme),
};
};
}
//# sourceMappingURL=built-in-replacement.js.map