UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

124 lines 5.32 kB
"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