@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
88 lines • 4.44 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DataFrameShapeInferenceVisitor = void 0;
const absint_visitor_1 = require("../absint-visitor");
const dataframe_domain_1 = require("./dataframe-domain");
const access_mapper_1 = require("./mappers/access-mapper");
const function_mapper_1 = require("./mappers/function-mapper");
const replacement_mapper_1 = require("./mappers/replacement-mapper");
const semantics_1 = require("./semantics");
/**
* The control flow graph visitor to infer the shape of data frames using abstract interpretation
*/
class DataFrameShapeInferenceVisitor extends absint_visitor_1.AbstractInterpretationVisitor {
/**
* The abstract data frame operations the function call nodes are mapped to.
*/
operations;
constructor({ trackOperations = true, ...config }) {
super(config);
if (trackOperations) {
this.operations = new Map();
}
}
/**
* Gets the mapped abstract data frame operations for an AST node (this only includes direct function calls, replacement calls, or access operations).
* This requires that the abstract interpretation visitor has been completed, or at least started.
* @param id - The ID of the node to get the mapped abstract operations for
* @returns The mapped abstract data frame operations for the node, or `undefined` if no abstract operation was mapped for the node or storing mapped abstract operations is disabled via the visitor config.
*/
getAbstractOperations(id) {
return id !== undefined ? this.operations?.get(id) : undefined;
}
onFunctionCall({ call }) {
super.onFunctionCall({ call });
const node = this.getNormalizedAst(call.id);
if (node === undefined) {
return;
}
const operations = (0, function_mapper_1.mapDataFrameFunctionCall)(node, this, this.config.dfg, this.config.ctx);
this.applyDataFrameExpression(node, operations);
}
onReplacementCall({ call, target, source }) {
super.onReplacementCall({ call, target, source });
const node = this.getNormalizedAst(call.id);
const targetNode = this.getNormalizedAst(target);
const sourceNode = this.getNormalizedAst(source);
if (node === undefined || targetNode === undefined || sourceNode === undefined) {
return;
}
const operations = (0, replacement_mapper_1.mapDataFrameReplacementFunction)(node, sourceNode, this, this.config.dfg, this.config.ctx);
this.applyDataFrameExpression(node, operations);
}
onAccessCall({ call }) {
super.onAccessCall({ call });
const node = this.getNormalizedAst(call.id);
if (node === undefined) {
return;
}
const operations = (0, access_mapper_1.mapDataFrameAccess)(node, this, this.config.dfg, this.config.ctx);
this.applyDataFrameExpression(node, operations);
}
applyDataFrameExpression(node, operations) {
if (operations === undefined) {
return;
}
else if (this.operations !== undefined) {
this.operations.set(node.info.id, operations);
}
const maxColNames = this.config.ctx.config.abstractInterpretation.dataFrame.maxColNames;
let value = dataframe_domain_1.DataFrameDomain.top(maxColNames);
for (const { operation, operand, type, options, ...args } of operations) {
const operandValue = operand !== undefined ? this.getAbstractValue(operand, this.currentState) : value;
value = (0, semantics_1.applyDataFrameSemantics)(operation, operandValue ?? dataframe_domain_1.DataFrameDomain.top(maxColNames), args, options);
const constraintType = type ?? (0, semantics_1.getConstraintType)(operation);
if (operand !== undefined && constraintType === semantics_1.ConstraintType.OperandModification) {
this.updateState(operand, value);
for (const origin of this.getVariableOrigins(operand)) {
this.updateState(origin, value);
}
}
else if (constraintType === semantics_1.ConstraintType.ResultPostcondition) {
this.updateState(node.info.id, value);
}
}
}
}
exports.DataFrameShapeInferenceVisitor = DataFrameShapeInferenceVisitor;
//# sourceMappingURL=shape-inference.js.map