libredex/Transform.cpp (93 lines of code) (raw):
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "Transform.h"
#include <stack>
#include "ControlFlow.h"
namespace {
using RegMap = transform::RegMap;
void remap_debug(DexDebugInstruction& dbgop, const RegMap& reg_map) {
switch (dbgop.opcode()) {
case DBG_START_LOCAL:
case DBG_START_LOCAL_EXTENDED:
case DBG_END_LOCAL:
case DBG_RESTART_LOCAL: {
auto it = reg_map.find(dbgop.uvalue());
if (it == reg_map.end()) return;
dbgop.set_uvalue(it->second);
break;
}
default:
break;
}
}
void remap_dest(IRInstruction* inst, const RegMap& reg_map) {
if (!inst->has_dest()) return;
auto it = reg_map.find(inst->dest());
if (it == reg_map.end()) return;
inst->set_dest(it->second);
}
void remap_srcs(IRInstruction* inst, const RegMap& reg_map) {
for (unsigned i = 0; i < inst->srcs_size(); i++) {
auto it = reg_map.find(inst->src(i));
if (it == reg_map.end()) continue;
inst->set_src(i, it->second);
}
}
} // anonymous namespace
namespace transform {
void remap_registers(IRInstruction* insn, const RegMap& reg_map) {
remap_dest(insn, reg_map);
remap_srcs(insn, reg_map);
}
void remap_registers(MethodItemEntry& mei, const RegMap& reg_map) {
switch (mei.type) {
case MFLOW_OPCODE:
remap_registers(mei.insn, reg_map);
break;
case MFLOW_DEBUG:
remap_debug(*mei.dbgop, reg_map);
break;
default:
break;
}
}
void remap_registers(IRCode* code, const RegMap& reg_map) {
if (!code->editable_cfg_built()) {
for (auto& mei : *code) {
remap_registers(mei, reg_map);
}
} else {
for (auto& mei : cfg::InstructionIterable(code->cfg())) {
remap_registers(mei, reg_map);
}
}
}
static size_t remove_block(IRCode* code, cfg::Block* b) {
size_t insns_removed{0};
for (auto& mei : InstructionIterable(b)) {
code->remove_opcode(mei.insn);
++insns_removed;
}
return insns_removed;
}
/**
* TODO: The method is deprecated since it doesn't delete the edges associated
* with the deleted blocks, use ControlFlowGraph::remove_unreachable_blocks()
* instead.
*/
size_t remove_unreachable_blocks(IRCode* code) {
auto& cfg = code->cfg();
const auto& blocks = cfg.blocks();
size_t insns_removed{0};
// remove unreachable blocks
const auto& visited = cfg.visit();
for (size_t i = 1; i < blocks.size(); ++i) {
auto& b = blocks.at(i);
if (visited.test(b->id())) {
continue;
}
// Remove all successor edges. Note that we don't need to try and remove
// predecessors since by definition, unreachable blocks have no preds
cfg.delete_succ_edges(b);
insns_removed += remove_block(code, b);
}
return insns_removed;
}
MethodItemEntry* find_active_catch(IRCode* code, IRList::iterator pos) {
while (++pos != code->end() && pos->type != MFLOW_TRY)
;
return pos != code->end() && pos->tentry->type == TRY_END
? pos->tentry->catch_start
: nullptr;
}
} // namespace transform