@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
206 lines • 9.07 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.mapDataFrameReplacementFunction = mapDataFrameReplacementFunction;
const config_1 = require("../../../config");
const vertex_1 = require("../../../dataflow/graph/vertex");
const make_argument_1 = require("../../../dataflow/internal/process/functions/call/argument/make-argument");
const r_function_call_1 = require("../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
const type_1 = require("../../../r-bridge/lang-4.x/ast/model/type");
const resolve_args_1 = require("../resolve-args");
const semantics_1 = require("../semantics");
const access_mapper_1 = require("./access-mapper");
const arguments_1 = require("./arguments");
const identifier_1 = require("../../../dataflow/environments/identifier");
const built_in_proc_name_1 = require("../../../dataflow/environments/built-in-proc-name");
/** Mapper for mapping the supported data frame replacement functions to mapper functions */
const DataFrameReplacementFunctionMapper = {
'colnames': mapDataFrameColNamesAssignment,
'names': mapDataFrameColNamesAssignment,
'rownames': mapDataFrameRowNamesAssignment,
'dimnames': mapDataFrameDimNamesAssignment
};
/**
* Maps a concrete data frame replacement function call to abstract data frame operations.
* @param node - The R node of the replacement function call
* @param expression - The assigned expression node of the replacement function call
* @param inference - The data frame shape inference visitor for checking data frame arguments
* @param dfg - The data flow graph for resolving the arguments
* @param ctx - The current flowR analysis context
* @returns The mapped abstract data frame operations for the replacement function call, or `undefined` if the node does not represent a data frame replacement function call
*/
function mapDataFrameReplacementFunction(node, expression, inference, dfg, ctx) {
const parent = hasParentReplacement(node, dfg) ? dfg.idMap?.get(node.info.parent) : undefined;
const resolveInfo = { graph: dfg, idMap: dfg.idMap, full: true, resolve: config_1.VariableResolve.Alias, ctx };
if (node.type === type_1.RType.Access) {
if (node.access.every(arg => arg === r_function_call_1.EmptyArgument)) {
return mapDataFrameContentAssignment(node, expression, inference);
}
else if ((0, access_mapper_1.isStringBasedAccess)(node)) {
return mapDataFrameNamedColumnAssignment(node, expression, inference, resolveInfo);
}
else {
return mapDataFrameIndexColRowAssignment(node, expression, inference, resolveInfo);
}
}
else if (node.type === type_1.RType.FunctionCall && node.named && node.arguments.length === 1 && node.arguments[0] !== r_function_call_1.EmptyArgument) {
const n = identifier_1.Identifier.getName(node.functionName.content);
if (isDataFrameReplacement(n)) {
const mapper = DataFrameReplacementFunctionMapper[n];
return mapper(node.arguments[0], expression, inference, resolveInfo, parent);
}
else {
return mapDataFrameUnknownAssignment(node.arguments[0], expression, inference);
}
}
}
function isDataFrameReplacement(functionName) {
// a check with `functionName in DataFrameReplacementFunctionMapper` would return true for "toString"
return Object.hasOwn(DataFrameReplacementFunctionMapper, functionName);
}
function hasParentReplacement(node, dfg) {
const parentVertex = node.info.parent ? dfg.getVertex(node.info.parent) : undefined;
return (0, vertex_1.isFunctionCallVertex)(parentVertex) && parentVertex.origin.includes(built_in_proc_name_1.BuiltInProcName.Replacement);
}
function mapDataFrameContentAssignment(access, expression, inference) {
const dataFrame = access.accessed;
if (!(0, arguments_1.isDataFrameArgument)(dataFrame, inference)) {
return;
}
if ((0, arguments_1.isRNull)(expression)) {
return [{
operation: 'subsetCols',
operand: dataFrame.info.id,
colnames: [],
type: semantics_1.ConstraintType.OperandModification
}];
}
else {
return [{
operation: 'identity',
operand: dataFrame.info.id,
type: semantics_1.ConstraintType.OperandModification
}];
}
}
function mapDataFrameNamedColumnAssignment(access, expression, inference, info) {
const dataFrame = access.accessed;
if (!(0, arguments_1.isDataFrameArgument)(dataFrame, inference)) {
return;
}
const colname = (0, resolve_args_1.resolveIdToArgValueSymbolName)(access.access[0], info);
if ((0, arguments_1.isRNull)(expression)) {
return [{
operation: 'removeCols',
operand: dataFrame.info.id,
colnames: colname ? [colname] : undefined,
type: semantics_1.ConstraintType.OperandModification,
options: { maybe: true }
}];
}
else {
return [{
operation: 'assignCols',
operand: dataFrame.info.id,
columns: colname ? [colname] : undefined
}];
}
}
function mapDataFrameIndexColRowAssignment(access, expression, inference, info) {
const dataFrame = access.accessed;
const args = access.access;
if (!(0, arguments_1.isDataFrameArgument)(dataFrame, inference) || args.every(arg => arg === r_function_call_1.EmptyArgument)) {
return;
}
const result = [];
const rowArg = args.length < 2 ? undefined : args[0];
const colArg = args.length < 2 ? args[0] : args[1];
if (rowArg !== undefined && rowArg !== r_function_call_1.EmptyArgument) {
const rowValue = (0, resolve_args_1.resolveIdToArgValue)(rowArg, info);
let rows = undefined;
if (typeof rowValue === 'number') {
rows = [rowValue];
}
else if (Array.isArray(rowValue) && rowValue.every(row => typeof row === 'number')) {
rows = rowValue;
}
result.push({
operation: 'assignRows',
operand: dataFrame.info.id,
rows
});
}
if (colArg !== undefined && colArg !== r_function_call_1.EmptyArgument) {
const colValue = (0, resolve_args_1.resolveIdToArgValue)(colArg, info);
let columns = undefined;
if (typeof colValue === 'string') {
columns = [colValue];
}
else if (typeof colValue === 'number') {
columns = [colValue];
}
else if (Array.isArray(colValue) && (colValue.every(col => typeof col === 'string') || colValue.every(col => typeof col === 'number'))) {
columns = colValue;
}
if ((0, arguments_1.isRNull)(expression)) {
result.push({
operation: 'removeCols',
operand: dataFrame.info.id,
colnames: columns?.map(col => typeof col === 'string' ? col : undefined),
type: semantics_1.ConstraintType.OperandModification,
options: { maybe: true }
});
}
else {
result.push({
operation: 'assignCols',
operand: dataFrame.info.id,
columns
});
}
}
return result;
}
function mapDataFrameColNamesAssignment(operand, expression, inference, info, parent) {
if (!(0, arguments_1.isDataFrameArgument)(operand, inference)) {
return;
}
const argument = info.idMap !== undefined ? (0, make_argument_1.toUnnamedArgument)(expression, info.idMap) : r_function_call_1.EmptyArgument;
const assignedNames = (0, resolve_args_1.resolveIdToArgStringVector)(argument, info);
return [{
operation: 'setColNames',
operand: operand.value?.info.id,
colnames: assignedNames,
...(parent !== undefined ? { options: { partial: true } } : {})
}];
}
function mapDataFrameRowNamesAssignment(operand, _expression, inference) {
if (!(0, arguments_1.isDataFrameArgument)(operand, inference)) {
return;
}
return [{
operation: 'identity',
operand: operand.value?.info.id,
type: semantics_1.ConstraintType.OperandModification
}];
}
function mapDataFrameDimNamesAssignment(operand, _expression, inference) {
if (!(0, arguments_1.isDataFrameArgument)(operand, inference)) {
return;
}
return [{
operation: 'setColNames',
operand: operand.value?.info.id,
colnames: undefined
}];
}
function mapDataFrameUnknownAssignment(operand, _expression, inference) {
if (!(0, arguments_1.isDataFrameArgument)(operand, inference)) {
return;
}
return [{
operation: 'unknown',
operand: operand.value?.info.id,
type: semantics_1.ConstraintType.OperandModification
}];
}
//# sourceMappingURL=replacement-mapper.js.map