/** * The callee contains a *get/put instruction to an unknown field. * Given the caller may not be in the same hierarchy/package we cannot inline * it unless we make the field public. * But we need to make all fields public across the hierarchy and for fields * we don't know we have no idea whether the field was public or not anyway. */ bool MultiMethodInliner::unknown_field(DexInstruction* insn, DexMethod* context) { if (is_ifield_op(insn->opcode()) || is_sfield_op(insn->opcode())) { auto fop = static_cast<DexOpcodeField*>(insn); auto field = fop->field(); field = resolve_field(field, is_sfield_op(insn->opcode()) ? FieldSearch::Static : FieldSearch::Instance); if (field == nullptr) { info.escaped_field++; return true; } if (!field->is_concrete() && !is_public(field)) { info.non_pub_field++; return true; } } return false; }
void inline_field_values(Scope& fullscope) { std::unordered_set<DexField*> inline_field; std::unordered_set<DexField*> cheap_inline_field; std::vector<DexClass*> scope; uint32_t aflags = ACC_STATIC | ACC_FINAL; for (auto clazz : fullscope) { std::unordered_map<DexField*, bool> blank_statics; get_sput_in_clinit(clazz, blank_statics); auto sfields = clazz->get_sfields(); for (auto sfield : sfields) { if ((sfield->get_access() & aflags) != aflags) continue; if (blank_statics[sfield]) continue; auto value = sfield->get_static_value(); if (value == nullptr && !is_primitive(sfield->get_type())) { continue; } if (value != nullptr && !value->is_evtype_primitive()) { continue; } uint64_t v = value != nullptr ? value->value() : 0; if ((v & 0xffff) == v || (v & 0xffff0000) == v) { cheap_inline_field.insert(sfield); } inline_field.insert(sfield); scope.push_back(clazz); } } std::vector<std::pair<DexMethod*, DexOpcodeField*>> cheap_rewrites; std::vector<std::pair<DexMethod*, DexOpcodeField*>> simple_rewrites; walk_opcodes( fullscope, [](DexMethod* method) { return true; }, [&](DexMethod* method, DexInstruction* insn) { if (insn->has_fields() && is_sfield_op(insn->opcode())) { auto fieldop = static_cast<DexOpcodeField*>(insn); auto field = resolve_field(fieldop->field(), FieldSearch::Static); if (field == nullptr || !field->is_concrete()) return; if (inline_field.count(field) == 0) return; if (cheap_inline_field.count(field) > 0) { cheap_rewrites.push_back(std::make_pair(method, fieldop)); return; } simple_rewrites.push_back(std::make_pair(method, fieldop)); } }); TRACE(FINALINLINE, 1, "Method Re-writes Cheap %lu Simple %lu\n", cheap_rewrites.size(), simple_rewrites.size()); for (auto cheapcase : cheap_rewrites) { inline_cheap_sget(cheapcase.first, cheapcase.second); } for (auto simplecase : simple_rewrites) { inline_sget(simplecase.first, simplecase.second); } MethodTransform::sync_all(); }
void change_visibility(DexMethod* method) { auto code = method->get_code(); always_assert(code != nullptr); editable_cfg_adapter::iterate(code, [](MethodItemEntry& mie) { auto insn = mie.insn; if (insn->has_field()) { auto cls = type_class(insn->get_field()->get_class()); if (cls != nullptr && !cls->is_external()) { set_public(cls); } auto field = resolve_field(insn->get_field(), is_sfield_op(insn->opcode()) ? FieldSearch::Static : FieldSearch::Instance); if (field != nullptr && field->is_concrete()) { set_public(field); set_public(type_class(field->get_class())); // FIXME no point in rewriting opcodes in the method insn->set_field(field); } } else if (insn->has_method()) { auto cls = type_class(insn->get_method()->get_class()); if (cls != nullptr && !cls->is_external()) { set_public(cls); } auto current_method = resolve_method( insn->get_method(), opcode_to_search(insn)); if (current_method != nullptr && current_method->is_concrete()) { set_public(current_method); set_public(type_class(current_method->get_class())); // FIXME no point in rewriting opcodes in the method insn->set_method(current_method); } } else if (insn->has_type()) { auto type = insn->get_type(); auto cls = type_class(type); if (cls != nullptr && !cls->is_external()) { set_public(cls); } } return editable_cfg_adapter::LOOP_CONTINUE; }); std::vector<DexType*> types; if (code->editable_cfg_built()) { code->cfg().gather_catch_types(types); } else { code->gather_catch_types(types); } for (auto type : types) { auto cls = type_class(type); if (cls != nullptr && !cls->is_external()) { set_public(cls); } } }
void MethodBlock::sfield_op(DexOpcode opcode, DexField* field, Location& src_or_dst) { always_assert(is_sfield_op(opcode)); if (is_sget(opcode)) { auto sget = new DexOpcodeField(opcode, field); sget->set_dest(reg_num(src_or_dst)); src_or_dst.type = field->get_class(); push_instruction(sget); } else { auto sput = new DexOpcodeField(opcode, field); sput->set_src(0, reg_num(src_or_dst)); push_instruction(sput); } }
void MethodBlock::sfield_op(IROpcode opcode, DexField* field, Location& src_or_dst) { always_assert(is_sfield_op(opcode)); if (is_sget(opcode)) { auto sget = new IRInstruction(opcode); sget->set_field(field); src_or_dst.type = field->get_class(); push_instruction(sget); push_instruction( (new IRInstruction(opcode::move_result_pseudo_for_sget(opcode))) ->set_dest(src_or_dst.get_reg())); } else { auto sput = new IRInstruction(opcode); sput->set_field(field)->set_src(0, src_or_dst.get_reg()); push_instruction(sput); } }
/** * Change the visibility of members accessed in a callee as they are moved * to the caller context. * We make everything public but we could be more precise and only * relax visibility as needed. */ void MultiMethodInliner::change_visibility(DexMethod* callee) { TRACE(MMINL, 6, "checking visibility usage of members in %s\n", SHOW(callee)); for (auto insn : callee->get_code()->get_instructions()) { if (insn->has_fields()) { auto fop = static_cast<DexOpcodeField*>(insn); auto field = fop->field(); field = resolve_field(field, is_sfield_op(insn->opcode()) ? FieldSearch::Static : FieldSearch::Instance); if (field != nullptr && field->is_concrete()) { TRACE(MMINL, 6, "changing visibility of %s.%s %s\n", SHOW(field->get_class()), SHOW(field->get_name()), SHOW(field->get_type())); set_public(field); set_public(type_class(field->get_class())); fop->rewrite_field(field); } continue; } if (insn->has_methods()) { auto mop = static_cast<DexOpcodeMethod*>(insn); auto method = mop->get_method(); method = resolver(method, opcode_to_search(insn)); if (method != nullptr && method->is_concrete()) { TRACE(MMINL, 6, "changing visibility of %s.%s: %s\n", SHOW(method->get_class()), SHOW(method->get_name()), SHOW(method->get_proto())); set_public(method); set_public(type_class(method->get_class())); mop->rewrite_method(method); } continue; } if (insn->has_types()) { auto type = static_cast<DexOpcodeType*>(insn)->get_type(); auto cls = type_class(type); if (cls != nullptr && !cls->is_external()) { TRACE(MMINL, 6, "changing visibility of %s\n", SHOW(type)); set_public(cls); } continue; } } }