@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
124 lines • 5.32 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.cfgAnalyzeDeadCode = cfgAnalyzeDeadCode;
const logic_1 = require("../util/logic");
const semantic_cfg_guided_visitor_1 = require("./semantic-cfg-guided-visitor");
const alias_tracking_1 = require("../dataflow/eval/resolve/alias-tracking");
const log_1 = require("../util/log");
const r_function_call_1 = require("../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
const general_1 = require("../dataflow/eval/values/general");
const r_value_1 = require("../dataflow/eval/values/r-value");
class CfgConditionalDeadCodeRemoval extends semantic_cfg_guided_visitor_1.SemanticCfgGuidedVisitor {
cachedConditions = new Map();
cachedStatements = new Map();
getValue(id) {
const has = this.cachedConditions.get(id);
if (has) {
return has;
}
this.visitNode(id);
return this.cachedConditions.get(id) ?? logic_1.Ternary.Maybe;
}
isUnconditionalJump(id) {
const has = this.cachedStatements.get(id);
if (has) {
return has;
}
this.visitNode(id);
return this.cachedStatements.get(id) ?? false;
}
unableToCalculateValue(id) {
this.cachedConditions.set(id, logic_1.Ternary.Maybe);
}
storeDefiniteValue(id, value) {
this.cachedConditions.set(id, value ? logic_1.Ternary.Always : logic_1.Ternary.Never);
}
startVisitor() {
for (const [from, targets] of this.config.controlFlow.graph.edges()) {
for (const [target, edge] of targets) {
if (edge.label === 1 /* CfgEdgeType.Cd */) {
const og = this.getValue(edge.caused);
if (og === logic_1.Ternary.Always && edge.when === 'FALSE') {
this.config.controlFlow.graph.removeEdge(from, target);
}
else if (og === logic_1.Ternary.Never && edge.when === 'TRUE') {
this.config.controlFlow.graph.removeEdge(from, target);
}
}
else if (edge.label === 0 /* CfgEdgeType.Fd */) {
if (this.isUnconditionalJump(target)) {
this.config.controlFlow.graph.removeEdge(from, target);
}
}
}
}
}
handleValuesFor(id, valueId) {
const values = (0, general_1.valueSetGuard)((0, alias_tracking_1.resolveIdToValue)(valueId, { graph: this.config.dfg, full: true, idMap: this.config.normalizedAst.idMap, resolve: this.config.flowrConfig.solver.variables }));
if (values === undefined || values.elements.length !== 1 || values.elements[0].type != 'logical' || !(0, r_value_1.isValue)(values.elements[0].value)) {
this.unableToCalculateValue(id);
return;
}
/* we should translate this to truthy later */
this.storeDefiniteValue(id, Boolean(values.elements[0].value));
}
handleWithCondition(data) {
const id = data.call.id;
if (data.condition === undefined || data.condition === r_function_call_1.EmptyArgument) {
this.unableToCalculateValue(id);
return;
}
this.handleValuesFor(id, typeof data.condition === 'object' ? data.condition.nodeId : data.condition);
}
getBoolArgValue(data) {
if (data.call.args.length !== 1 || data.call.args[0] === r_function_call_1.EmptyArgument) {
return undefined;
}
const values = (0, general_1.valueSetGuard)((0, alias_tracking_1.resolveIdToValue)(data.call.args[0].nodeId, { graph: this.config.dfg, full: true, idMap: this.config.normalizedAst.idMap, resolve: this.config.flowrConfig.solver.variables }));
if (values === undefined || values.elements.length !== 1 || values.elements[0].type != 'logical' || !(0, r_value_1.isValue)(values.elements[0].value)) {
return undefined;
}
return Boolean(values.elements[0].value);
}
handleFunctionCall(data) {
switch (data.call.origin[0]) {
case 'builtin:return':
case 'builtin:stop':
this.cachedStatements.set(data.call.id, true);
break;
case 'builtin:stopifnot': {
const arg = this.getBoolArgValue(data);
if (arg !== undefined) {
this.cachedStatements.set(data.call.id, !arg);
}
break;
}
}
}
onIfThenElseCall(data) {
this.handleWithCondition(data);
}
onWhileLoopCall(data) {
this.handleWithCondition(data);
}
onDefaultFunctionCall(data) {
this.handleFunctionCall(data);
}
}
/** Breaks unsatisfiable control dependencies */
function cfgAnalyzeDeadCode(cfg, info) {
if (!info.ast || !info.dfg) {
log_1.log.warn('cfgAnalyzeDeadCode called without ast or dfg, skipping dead code analysis');
return cfg;
}
const visitor = new CfgConditionalDeadCodeRemoval({
controlFlow: cfg,
normalizedAst: info.ast,
dfg: info.dfg,
flowrConfig: info.config,
defaultVisitingOrder: 'forward',
});
visitor.start();
return cfg;
}
//# sourceMappingURL=cfg-dead-code.js.map