static bool validate_sget(DexMethod* context, DexOpcodeField* opfield) { auto opcode = opfield->opcode(); switch (opcode) { case OPCODE_SGET_WIDE: unhandled_inline++; return false; case OPCODE_SGET: case OPCODE_SGET_BOOLEAN: case OPCODE_SGET_BYTE: case OPCODE_SGET_CHAR: case OPCODE_SGET_SHORT: return true; default: auto field = resolve_field(opfield->field(), FieldSearch::Static); always_assert_log(field->is_concrete(), "Must be a concrete field"); auto value = field->get_static_value(); always_assert_log( false, "Unexpected field type in inline_*sget %s for field %s value %s in " "method %s\n", SHOW(opfield), SHOW(field), value != nullptr ? value->show().c_str() : "('nullptr')", SHOW(context)); } 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); } } }
/** * Check collisions in field definition. */ EscapeReason OptimizationImpl::check_field_collision(DexType* intf, SingleImplData& data) { for (const auto field : data.fielddefs) { assert(!single_impls->is_escaped(field->get_class())); auto collision = resolve_field(field->get_class(), field->get_name(), data.cls); if (collision) return FIELD_COLLISION; } return NO_ESCAPE; }
void inline_sget(DexMethod* method, DexOpcodeField* opfield) { if (!validate_sget(method, opfield)) return; auto opcode = OPCODE_CONST; auto dest = opfield->dest(); auto field = resolve_field(opfield->field(), FieldSearch::Static); always_assert_log(field->is_concrete(), "Must be a concrete field"); auto value = field->get_static_value(); /* FIXME for sget_wide case */ uint32_t v = value != nullptr ? (uint32_t)value->value() : 0; auto newopcode = (new DexInstruction(opcode))->set_dest(dest)->set_literal(v); replace_opcode(method, opfield, newopcode); }
void analyze_sget(cfg::InstructionIterator it, Environment* env) { auto insn = it->insn; auto op = insn->opcode(); if (op == OPCODE_SGET_OBJECT) { auto field = resolve_field(insn->get_field(), FieldSearch::Static); if (field != nullptr) { Info info; info.array_field = field; env->set(RESULT_REGISTER, Domain(info)); return; } } analyze_default(it, env); }
std::unordered_set<DexField*> get_called_field_defs(Scope& scope) { std::vector<DexField*> field_refs; walk_methods(scope, [&](DexMethod* method) { method->gather_fields(field_refs); }); sort_unique(field_refs); /* Okay, now we have a complete list of field refs * for this particular dex. Map to the def actually invoked. */ std::unordered_set<DexField*> field_defs; for (auto field_ref : field_refs) { auto field_def = resolve_field(field_ref); if (field_def == nullptr || !field_def->is_concrete()) continue; field_defs.insert(field_def); } return field_defs; }
/** * 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; }
/** * 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; } } }
/* * There's no "good way" to differentiate blank vs. non-blank * finals. So, we just scan the code in the CL-init. If * it's sput there, then it's a blank. Lame, agreed, but functional. * */ void get_sput_in_clinit(DexClass* clazz, std::unordered_map<DexField*, bool>& blank_statics) { auto methods = clazz->get_dmethods(); for (auto method : methods) { if (is_clinit(method)) { always_assert_log(is_static(method) && is_constructor(method), "static constructor doesn't have the proper access bits set\n"); auto& code = method->get_code(); auto opcodes = code->get_instructions(); for (auto opcode : opcodes) { if (opcode->has_fields() && is_sput(opcode->opcode())) { auto fieldop = static_cast<DexOpcodeField*>(opcode); auto field = resolve_field(fieldop->field(), FieldSearch::Static); if (field == nullptr || !field->is_concrete()) continue; if (field->get_class() != clazz->get_type()) continue; blank_statics[field] = true; } } } } }
void inline_cheap_sget(DexMethod* method, DexOpcodeField* opfield) { if (!validate_sget(method, opfield)) return; auto dest = opfield->dest(); auto field = resolve_field(opfield->field(), FieldSearch::Static); always_assert_log(field->is_concrete(), "Must be a concrete field"); auto value = field->get_static_value(); /* FIXME for sget_wide case */ uint32_t v = value != nullptr ? (uint32_t)value->value() : 0; auto opcode = [&] { if ((v & 0xffff) == v) { return OPCODE_CONST_16; } else if ((v & 0xffff0000) == v) { return OPCODE_CONST_HIGH16; } always_assert_log(false, "Bad inline_cheap_sget queued up, can't fit to" " CONST_16 or CONST_HIGH16, bailing\n"); }(); auto newopcode = (new DexInstruction(opcode, 0))->set_dest(dest)->set_literal(v); replace_opcode(method, opfield, newopcode); }
TEST(ResolveField, empty) { g_redex = new RedexContext(); create_scope(); // different cases for int A.f1 DexField* fdef = DexField::get_field(DexType::get_type("A"), DexString::get_string("f1"), DexType::get_type("I")); EXPECT_TRUE(fdef != nullptr && fdef->is_def()); DexField* fref = make_field_ref( DexType::get_type("A"), "f1", DexType::get_type("I")); EXPECT_TRUE(fdef->is_def()); EXPECT_TRUE(resolve_field(fref) == fdef); EXPECT_TRUE(resolve_field(fref, FieldSearch::Instance) == fdef); EXPECT_TRUE(resolve_field(DexType::get_type("A"), DexString::get_string("f1"), DexType::get_type("I"), FieldSearch::Static) == nullptr); EXPECT_TRUE(resolve_field(DexType::get_type("D"), DexString::get_string("f1"), DexType::get_type("I"), FieldSearch::Static) == nullptr); EXPECT_TRUE(resolve_field(DexType::get_type("B"), DexString::get_string("f1"), DexType::get_type("I"), FieldSearch::Instance) == fdef); EXPECT_TRUE(resolve_field(DexType::get_type("B"), DexString::get_string("f1"), DexType::get_type("I"), FieldSearch::Static) == nullptr); EXPECT_TRUE(resolve_field(DexType::get_type("C"), DexString::get_string("f1"), DexType::get_type("I")) == fdef); EXPECT_TRUE(resolve_field(DexType::get_type("C"), DexString::get_string("f1"), DexType::get_type("I"), FieldSearch::Static) == nullptr); fref = make_field_ref( DexType::get_type("B"), "f1", DexType::get_type("I")); EXPECT_FALSE(fref->is_def()); EXPECT_TRUE(resolve_field(fref) == fdef); EXPECT_TRUE(resolve_field(fref, FieldSearch::Instance) == fdef); EXPECT_TRUE(resolve_field(fref, FieldSearch::Static) == nullptr); fref = make_field_ref( DexType::get_type("C"), "f1", DexType::get_type("I")); EXPECT_FALSE(fref->is_def()); EXPECT_TRUE(resolve_field(fref) == fdef); EXPECT_TRUE(resolve_field(fref, FieldSearch::Instance) == fdef); EXPECT_TRUE(resolve_field(fref, FieldSearch::Static) == nullptr); // different cases for static String B.f2 fdef = DexField::get_field(DexType::get_type("B"), DexString::get_string("f2"), DexType::get_type("Ljava/lang/String;")); EXPECT_TRUE(fdef != nullptr && fdef->is_def()); fref = make_field_ref( DexType::get_type("A"), "f2", DexType::get_type("Ljava/lang/String;")); EXPECT_FALSE(fref->is_def()); EXPECT_TRUE(resolve_field(fref) == nullptr); EXPECT_TRUE(resolve_field(fref, FieldSearch::Instance) == nullptr); EXPECT_TRUE(resolve_field(fref, FieldSearch::Static) == nullptr); fref = make_field_ref( DexType::get_type("B"), "f2", DexType::get_type("Ljava/lang/String;")); EXPECT_TRUE(fref->is_def()); EXPECT_TRUE(resolve_field(fref) == fdef); EXPECT_TRUE(resolve_field(fref, FieldSearch::Instance) == fdef); fref = make_field_ref( DexType::get_type("C"), "f2", DexType::get_type("Ljava/lang/String;")); EXPECT_FALSE(fref->is_def()); EXPECT_TRUE(resolve_field(fref) == fdef); EXPECT_TRUE(resolve_field(fref, FieldSearch::Static) == fdef); EXPECT_TRUE(resolve_field(fref, FieldSearch::Instance) == nullptr); // different cases for D.f fdef = DexField::get_field(DexType::get_type("D"), DexString::get_string("f"), DexType::get_type("A")); EXPECT_TRUE(fdef != nullptr && fdef->is_def()); EXPECT_TRUE(resolve_field(fdef) == fdef); EXPECT_TRUE(resolve_field(fdef, FieldSearch::Instance) == fdef); // random non existent field fdef = DexField::make_field(DexType::get_type("U"), DexString::get_string("f"), DexType::get_type("I")); EXPECT_FALSE(fdef->is_def()); EXPECT_TRUE(resolve_field(fdef) == nullptr); EXPECT_TRUE(resolve_field(fdef, FieldSearch::Instance) == nullptr); EXPECT_TRUE(resolve_field(fdef, FieldSearch::Static) == nullptr); EXPECT_TRUE(resolve_field(DexType::get_type("E"), DexString::get_string("f1"), DexType::get_type("I"), FieldSearch::Static) == nullptr); EXPECT_TRUE(resolve_field(DexType::get_type("E"), DexString::get_string("f1"), DexType::get_type("Ljava/lang/String;"), FieldSearch::Instance) == nullptr); EXPECT_TRUE(resolve_field(DexType::get_type("E"), DexString::get_string("f1"), DexType::get_type("I")) == nullptr); delete g_redex; }