DexField* resolve_field( const DexType* owner, const DexString* name, const DexType* type, FieldSearch fs) { auto field_eq = [&](const DexField* a) { return a->get_name() == name && a->get_type() == type; }; const DexClass* cls = type_class(owner); while (cls) { if (fs == FieldSearch::Instance || fs == FieldSearch::Any) { for (auto ifield : cls->get_ifields()) { if (field_eq(ifield)) { return ifield; } } } if (fs == FieldSearch::Static || fs == FieldSearch::Any) { for (auto sfield : cls->get_sfields()) { if (field_eq(sfield)) { return sfield; } } } cls = type_class(cls->get_super_class()); } return nullptr; }
std::vector<const DexMethod*> select_from(const VirtualScope* scope, const DexType* type) { std::vector<const DexMethod*> refined_scope; std::unordered_map<const DexType*, DexMethod*> non_child_methods; bool found_root_method = false; for (const auto& method : scope->methods) { if (check_cast(method.first->get_class(), type)) { found_root_method = found_root_method || type == method.first->get_class(); refined_scope.emplace_back(method.first); } else { non_child_methods[method.first->get_class()] = method.first; } } if (!found_root_method) { auto cls = type_class(type); while (cls != nullptr) { const auto super = cls->get_super_class(); const auto& meth = non_child_methods.find(super); if (meth != non_child_methods.end()) { refined_scope.emplace_back(meth->second); return refined_scope; } cls = type_class(super); } } return refined_scope; }
void unmark_keep(const Scope& scope, const std::vector<std::string>& package_list, const std::vector<std::string>& supercls_list) { if (package_list.size() == 0 && supercls_list.size() == 0) { return; } std::unordered_set<const DexType*> superclasses; std::unordered_set<const DexType*> interface_list; for (auto& cls_name : supercls_list) { const DexType* supercls_type = DexType::get_type(cls_name); if (supercls_type) { DexClass* supercls = type_class(supercls_type); if (supercls && is_interface(supercls)) { interface_list.emplace(supercls_type); } else { superclasses.emplace(supercls_type); } } } TypeSystem ts(scope); // Unmark proguard keep rule for interface implementors like // "-keep class * extend xxx". for (const DexType* intf_type : interface_list) { for (const DexType* implementor : ts.get_implementors(intf_type)) { DexClass* implementor_cls = type_class(implementor); if (implementor_cls) { implementor_cls->rstate.force_unset_allowshrinking(); } } } walk::parallel::classes( scope, [&ts, &superclasses, &package_list](DexClass* cls) { // Unmark proguard keep rule for classes under path from package list. for (const auto& package : package_list) { if (strstr(cls->get_name()->c_str(), package.c_str()) != nullptr) { cls->rstate.force_unset_allowshrinking(); return; } } if (!is_interface(cls)) { // Unmark proguard keep for classes that extend class from superclass // list for proguard keep rule like "-keep class * extend xxx". const auto& parents_chain = ts.parent_chain(cls->get_type()); if (parents_chain.size() <= 2) { // The class's direct super class is java.lang.Object, no need // to proceed. return; } // We only need to find class started at the second of parents_chain // because first is java.lang.Object, and end at second to last, // because last one is itself. for (uint32_t index = 1; index < parents_chain.size() - 1; ++index) { if (superclasses.find(parents_chain[index]) != superclasses.end()) { cls->rstate.force_unset_allowshrinking(); return; } } } }); }
/** * The callee contains an invoke to a virtual method we either do not know * or it's not public. Given the caller may not be in the same * hierarchy/package we cannot inline it unless we make the method public. * But we need to make all methods public across the hierarchy and for methods * we don't know we have no idea whether the method was public or not anyway. */ bool MultiMethodInliner::unknown_virtual(DexInstruction* insn, DexMethod* context) { if (insn->opcode() == OPCODE_INVOKE_VIRTUAL || insn->opcode() == OPCODE_INVOKE_VIRTUAL_RANGE) { auto method = static_cast<DexOpcodeMethod*>(insn)->get_method(); auto res_method = resolver(method, MethodSearch::Virtual); if (res_method == nullptr) { // if it's not known to redex but it's a common java/android API method if (method_ok(method->get_class(), method)) { return false; } auto type = method->get_class(); if (type_ok(type)) return false; // the method ref is bound to a type known to redex but the method does // not exist in the hierarchy known to redex. Essentially the method // is from an external type i.e. A.equals(Object) auto cls = type_class(type); while (cls != nullptr) { type = cls->get_super_class(); cls = type_class(type); } if (type_ok(type)) return false; if (method_ok(type, method)) return false; assert(track(method)); info.escaped_virtual++; return true; } if (res_method->is_external() && !is_public(res_method)) { info.non_pub_virtual++; return true; } } return false; }
const std::vector<DexMethod*>& get_vmethods(const DexType* type) { const DexClass* cls = type_class(type); if (cls == nullptr) { always_assert_log( type == get_object_type(), "Unknown type %s\n", SHOW(type)); create_object_class(); cls = type_class(type); } return cls->get_vmethods(); }
void relocate_method(DexMethod* method, DexType* to_type) { auto from_cls = type_class(method->get_class()); auto to_cls = type_class(to_type); from_cls->remove_method(method); DexMethodSpec spec; spec.cls = to_type; method->change(spec, true /* rename on collision */, true /* update deobfuscated name */); to_cls->add_method(method); }
bool is_assignable_to(const DexType* child, const DexType* parent) { // Check class hierarchy auto super = child; while (super != nullptr) { if (parent == super) return true; const auto cls = type_class(super); if (cls == nullptr) break; super = cls->get_super_class(); } // Check interface hierarchy DexClass* parent_cls = type_class(parent); return parent_cls && is_interface(parent_cls) && is_assignable_to_interface(child, parent); }
/** * Return whether the method is in an Enum class. * Enum methods are invoked in different magic ways and should never * be removed. */ bool MultiMethodInliner::is_enum_method(DexMethod* callee) { if (type_class(callee->get_class())->get_super_class() == get_enum_type()) { info.enum_callee++; return true; } return false; }
size_t delete_methods( std::vector<DexClass*>& scope, std::unordered_set<DexMethod*>& removable, std::function<DexMethod*(DexMethod*, MethodSearch search)> resolver) { // if a removable candidate is invoked do not delete walk_opcodes(scope, [](DexMethod* meth) { return true; }, [&](DexMethod* meth, DexInstruction* insn) { if (is_invoke(insn->opcode())) { const auto mop = static_cast<DexOpcodeMethod*>(insn); auto callee = resolver(mop->get_method(), opcode_to_search(insn)); if (callee != nullptr) { removable.erase(callee); } } }); size_t deleted = 0; for (auto callee : removable) { if (!callee->is_concrete()) continue; if (do_not_strip(callee)) continue; auto cls = type_class(callee->get_class()); always_assert_log(cls != nullptr, "%s is concrete but does not have a DexClass\n", SHOW(callee)); if (callee->is_virtual()) { cls->get_vmethods().remove(callee); } else { cls->get_dmethods().remove(callee); } deleted++; TRACE(DELMET, 4, "removing %s\n", SHOW(callee)); } return deleted; }
/* * Check that analyze_enum_clinit returns the correct enum field -> ordinal * mapping. */ TEST_F(RedexTest, OrdinalAnalysis) { always_assert(load_class_file(std::getenv("enum_class_file"))); auto dexfile = std::getenv("dexfile"); std::vector<DexStore> stores; DexMetadata dm; dm.set_id("classes"); DexStore root_store(dm); root_store.add_classes(load_classes_from_dex(dexfile)); auto scope = build_class_scope(root_store.get_dexen()); auto enumA = type_class(DexType::get_type(ENUM_A)); auto enum_field_to_ordinal = optimize_enums::analyze_enum_clinit(enumA); auto enumA_zero = static_cast<DexField*>( DexField::get_field("Lcom/facebook/redextest/EnumA;.TYPE_A_0:Lcom/" "facebook/redextest/EnumA;")); auto enumA_one = static_cast<DexField*>( DexField::get_field("Lcom/facebook/redextest/EnumA;.TYPE_A_1:Lcom/" "facebook/redextest/EnumA;")); auto enumA_two = static_cast<DexField*>( DexField::get_field("Lcom/facebook/redextest/EnumA;.TYPE_A_2:Lcom/" "facebook/redextest/EnumA;")); EXPECT_EQ(enum_field_to_ordinal.at(enumA_zero), 0); EXPECT_EQ(enum_field_to_ordinal.at(enumA_one), 1); EXPECT_EQ(enum_field_to_ordinal.at(enumA_two), 2); }
DexMethod* find_collision_excepting(const DexMethod* except, const DexString* name, const DexProto* proto, const DexClass* cls, bool is_virtual, bool check_direct) { for (auto& method : cls->get_dmethods()) { if (match(name, proto, method) && method != except) return method; } for (auto& method : cls->get_vmethods()) { if (match(name, proto, method) && method != except) return method; } if (!is_virtual) return nullptr; auto super = type_class(cls->get_super_class()); if (super) { auto method = resolve_virtual(super, name, proto); if (method && method != except) return method; } TypeVector children; get_all_children(cls->get_type(), children); for (const auto& child : children) { auto vmethod = check_vmethods(name, proto, child); if (vmethod && vmethod != except) return vmethod; if (check_direct) { auto dmethod = check_dmethods(name, proto, child); if (dmethod && dmethod != except) return dmethod; } } return nullptr; }
std::unique_ptr<intraprocedural::FixpointIterator> analyze_procedure( const DexMethod* method, const WholeProgramState& wps, ArgumentDomain args) { always_assert(method->get_code() != nullptr); auto& code = *method->get_code(); // Currently, our callgraph does not include calls to non-devirtualizable // virtual methods. So those methods may appear unreachable despite being // reachable. if (args.is_bottom()) { args.set_to_top(); } else if (!args.is_top()) { TRACE(ICONSTP, 3, "Have args for %s: %s\n", SHOW(method), SHOW(args)); } auto env = env_with_params(&code, args); DexType* class_under_init{nullptr}; if (is_clinit(method)) { class_under_init = method->get_class(); set_encoded_values(type_class(class_under_init), &env); } TRACE(ICONSTP, 5, "%s\n", SHOW(code.cfg())); auto intra_cp = std::make_unique<intraprocedural::FixpointIterator>( code.cfg(), CombinedAnalyzer(class_under_init, &wps, EnumFieldAnalyzerState(), BoxedBooleanAnalyzerState(), nullptr)); intra_cp->run(env); return intra_cp; }
DexClass* LevelChecker::get_outer_class(const DexClass* cls) { const std::string& cls_name = cls->get_deobfuscated_name(); auto cash_idx = cls_name.find_last_of('$'); if (cash_idx == std::string::npos) { // this is not an inner class return nullptr; } auto slash_idx = cls_name.find_last_of('/'); if (slash_idx == std::string::npos || slash_idx < cash_idx) { // there's a $ in the class name const std::string& outer_name = cls_name.substr(0, cash_idx) + ';'; DexType* outer = DexType::get_type(outer_name); if (outer == nullptr) { TRACE(MMINL, 4, "Can't find outer class! %s -> %s\n", cls_name.c_str(), outer_name.c_str()); return nullptr; } DexClass* outer_cls = type_class(outer); if (cls == nullptr) { TRACE(MMINL, 4, "outer class %s is external?\n", SHOW(outer)); return nullptr; } return outer_cls; } return nullptr; }
DexMethod* resolve_method( const DexClass* cls, const DexString* name, const DexProto* proto, MethodSearch search) { while (cls) { if (search == MethodSearch::Virtual || search == MethodSearch::Any) { for (auto& vmeth : cls->get_vmethods()) { if (match(name, proto, vmeth)) { return vmeth; } } } if (search == MethodSearch::Direct || search == MethodSearch::Static || search == MethodSearch::Any) { for (auto& dmeth : cls->get_dmethods()) { if (match(name, proto, dmeth)) { return dmeth; } } } // direct methods only look up the given class cls = search != MethodSearch::Direct ? type_class(cls->get_super_class()) : nullptr; } return nullptr; }
/** * Remove annotation classes that are marked explicitly with a removable * annotation (specified as "kill_annos" in config). Facebook uses these to * remove DI binding annotations. */ void kill_annotation_classes( Scope& scope, const std::unordered_set<DexType*>& kill_annos ) { // Determine which annotation classes are removable. class_set_t bannotations; for (auto clazz : scope) { if (!(clazz->get_access() & DexAccessFlags::ACC_ANNOTATION)) continue; auto aset = clazz->get_anno_set(); if (aset == nullptr) continue; auto& annos = aset->get_annotations(); for (auto anno : annos) { if (kill_annos.count(anno->type())) { bannotations.insert(clazz); TRACE(CLASSKILL, 5, "removable annotation class %s\n", SHOW(clazz->get_type())); } } } // Annotation classes referenced explicitly can't be removed. walk_code( scope, [](DexMethod*) { return true; }, [&](DexMethod* meth, DexCode* code) { auto opcodes = code->get_instructions(); for (const auto& opcode : opcodes) { if (opcode->has_types()) { auto typeop = static_cast<DexOpcodeType*>(opcode); auto dtexclude = typeop->get_type(); DexClass* exclude = type_class(dtexclude); if (exclude != nullptr && bannotations.count(exclude)) { bannotations.erase(exclude); } } } }); // Do the removal. int annotations_removed_count = 0; if (bannotations.size()) { // We have some annotations we can kill. First let's clear all annotation // references to the classes. annotations_removed_count = clear_annotation_references(scope, bannotations); scope.erase( std::remove_if( scope.begin(), scope.end(), [&](DexClass* cls) { return bannotations.count(cls); }), scope.end()); } TRACE(CLASSKILL, 1, "Annotation classes removed %lu\n", bannotations.size()); TRACE(CLASSKILL, 1, "Method param annotations removed %d\n", annotations_removed_count); }
/** * 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; } } }
bool is_subclass(const DexType* parent, const DexType* child) { auto super = child; while (super != nullptr) { if (parent == super) return true; const auto cls = type_class(super); if (cls == nullptr) break; super = cls->get_super_class(); } return false; }
int show_to(var self, var out, int pos) { if (not type_implements(type_of(self), Show)) { return print_to(out, 0, "<'%s' At 0x%p>", type_of(self), self); } else { Show* ishow = type_class(type_of(self), Show); return ishow->show(self, out, pos); } }
bool check_cast(const DexType* type, const DexType* base_type) { if (type == base_type) return true; const auto cls = type_class(type); if (cls == nullptr) return false; if (check_cast(cls->get_super_class(), base_type)) return true; auto intfs = cls->get_interfaces(); for (auto intf : intfs->get_type_list()) { if (check_cast(intf, base_type)) return true; } return false; }
/** * Builds the ClassScope for type and children. * Calling with get_object_type() builds the ClassScope * for the entire system as redex knows it. */ void ClassScopes::build_class_scopes(const DexType* type) { auto cls = type_class(type); always_assert(cls != nullptr || type == get_object_type()); get_root_scopes(m_sig_map, type, m_scopes); const auto& children_it = m_hierarchy.find(type); if (children_it != m_hierarchy.end()) { for (const auto& child : children_it->second) { build_class_scopes(child); } } }
bool is_assignable_to_interface(const DexType* type, const DexType* iface) { if (type == iface) return true; auto cls = type_class(type); if (cls) { for (auto extends : cls->get_interfaces()->get_type_list()) { if (is_assignable_to_interface(extends, iface)) { return true; } } } return false; }
DexClass* create_class(const DexType* type, const DexType* super_type, const std::string& pkg_name, std::vector<DexField*> fields, const TypeSet& interfaces, bool with_default_ctor, DexAccessFlags access) { DexType* t = const_cast<DexType*>(type); always_assert(!pkg_name.empty()); auto name = std::string(type->get_name()->c_str()); name = pkg_name + "/" + name.substr(1); t->set_name(DexString::make_string(name)); // Create class. ClassCreator creator(t); creator.set_access(access); always_assert(super_type != nullptr); creator.set_super(const_cast<DexType*>(super_type)); for (const auto& itf : interfaces) { creator.add_interface(const_cast<DexType*>(itf)); } for (const auto& field : fields) { creator.add_field(field); } auto cls = creator.create(); // Keeping type-erasure generated classes from being renamed. cls->rstate.set_keep_name(); if (!with_default_ctor) { return cls; } // Create ctor. auto super_ctors = type_class(super_type)->get_ctors(); for (auto super_ctor : super_ctors) { auto mc = new MethodCreator(t, DexString::make_string("<init>"), super_ctor->get_proto(), ACC_PUBLIC | ACC_CONSTRUCTOR); // Call to super.<init> std::vector<Location> args; size_t args_size = super_ctor->get_proto()->get_args()->size(); for (size_t arg_loc = 0; arg_loc < args_size + 1; ++arg_loc) { args.push_back(mc->get_local(arg_loc)); } auto mb = mc->get_main_block(); mb->invoke(OPCODE_INVOKE_DIRECT, super_ctor, args); mb->ret_void(); auto ctor = mc->create(); TRACE(TERA, 4, " default ctor created %s\n", SHOW(ctor)); cls->add_method(ctor); } return cls; }
/** * In some limited cases we can do type erasure on an interface when * implementors of the interface only implement that interface and have no * parent class other than java.lang.Object. We create a base class for those * implementors and use the new base class as root, and proceed with type * erasure as usual. */ void handle_interface_as_root(ModelSpec& spec, Scope& scope, DexStoresVector& stores) { TypeSet interface_roots; for (const auto root : spec.roots) { if (is_interface(type_class(root))) { interface_roots.insert(root); } } for (const auto interface_root : interface_roots) { auto empty_base = create_empty_base_type(spec, interface_root, scope, stores); if (empty_base != nullptr) { TRACE(TERA, 3, "Changing the root from %s to %s.\n", SHOW(interface_root), SHOW(empty_base)); spec.roots.insert(empty_base); add_class(type_class(empty_base), scope, stores); } // Remove interface roots regardless of whether an empty base was added. spec.roots.erase(interface_root); } }
void LevelChecker::init_method(DexMethod* method) { int32_t method_level = get_level(method); if (method_level == -1) { DexClass* cls = type_class(method->get_class()); if (cls == nullptr) { method_level = s_min_level; } else { method_level = cls->rstate.get_api_level(); always_assert(method_level != -1); } } method->rstate.set_api_level(method_level); }
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 method definition. */ EscapeReason OptimizationImpl::check_method_collision(DexType* intf, SingleImplData& data) { for (auto method : data.methoddefs) { auto meth_it = new_methods.find(method); if (meth_it != new_methods.end()) method = meth_it->second; auto proto = get_or_make_proto(intf, data.cls, method->get_proto()); assert(proto != method->get_proto()); DexMethod* collision = find_collision(method->get_name(), proto, type_class(method->get_class()), method->is_virtual()); if (collision) return SIG_COLLISION; } return NO_ESCAPE; }
void delete_unused_bridgees() { for (auto bpair : m_bridges_to_bridgees) { auto bridge = bpair.first; auto bridgee = bpair.second; always_assert_log(bridge->is_virtual(), "bridge: %s\nbridgee: %s", SHOW(bridge), SHOW(bridgee)); // TODO: Bridgee won't necessarily be direct once we expand this // optimization assert(!bridgee->is_virtual()); auto cls = type_class(bridgee->get_class()); cls->get_dmethods().remove(bridgee); } }
/* Allocate space for type, set type entry */ var allocate(var type) { type = cast(type, Type); New* inew = type_class(type, New); var self; if (inew->size <= sizeof(ObjectData)) { self = NULL; } else { self = calloc(1, inew->size); if (self == NULL) { throw(OutOfMemoryError, "Cannot create new '%s', out of memory!", type); } ((ObjectData*)self)->type = type; } return self; }
int clear_annotation_references(Scope& scope, class_set_t& deadclasses) { /* * These annotations show in up method parameter annotations, * but they are still unused. We have to visit all the * method param annotations and remove them. */ int cleared_annos = 0; walk_methods( scope, [&](DexMethod* method) { /* Parameter annotations... */ auto pas = method->get_param_anno(); if (pas == nullptr) return; bool clear_pas = true; for (auto pa : *pas) { DexAnnotationSet* aset = pa.second; if (aset->size() == 0) continue; auto& annos = aset->get_annotations(); auto iter = annos.begin(); while (iter != annos.end()) { auto tokill = iter; DexAnnotation* da = *iter++; DexClass* clazz = type_class(da->type()); if (deadclasses.count(clazz)) { annos.erase(tokill); delete da; } } if (aset->size() != 0) { clear_pas = false; } } if (clear_pas) { for (auto pa : *pas) { delete pa.second; } pas->clear(); cleared_annos++; TRACE(CLASSKILL, 5, "Cleared parameter annotations for method %s\n", SHOW(method)); } }); return cleared_annos; }
/** * Rewrite all methods sigs by creating new ones. Remove old methods and push * the new to the proper class method list. */ void OptimizationImpl::set_method_defs(DexType* intf, SingleImplData& data) { for (auto method : data.methoddefs) { TRACE(INTF, 3, "(MDEF) %s\n", SHOW(method)); auto meth = method; auto meth_it = new_methods.find(meth); if (meth_it != new_methods.end()) { // if a method rewrite existed it must not be on a single impl // given we have escaped to next pass all collisions always_assert(meth_it->second->is_def()); meth = static_cast<DexMethod*>(meth_it->second); TRACE(INTF, 4, "(MDEF) current: %s\n", SHOW(meth)); assert(!single_impls->is_single_impl(method->get_class()) || single_impls->is_escaped(method->get_class())); assert(!single_impls->is_single_impl(meth->get_class()) || single_impls->is_escaped(meth->get_class())); } auto proto = get_or_make_proto(intf, data.cls, meth->get_proto()); TRACE(INTF, 5, "(MDEF) make_method: %s.%s - %s => %s\n", SHOW(meth->get_class()), SHOW(meth->get_name()), SHOW(meth->get_proto()), SHOW(proto)); assert(proto != meth->get_proto()); auto new_meth = static_cast<DexMethod*>(DexMethod::make_method( meth->get_class(), meth->get_name(), proto)); // new_meth may have already existed in RedexContext, so // we need to make sure it isn't concrete. // TODO: this is horrible. After we remove methods, we shouldn't // have these zombies lying around. new_meth->clear_annotations(); new_meth->make_non_concrete(); new_meth->set_deobfuscated_name(meth->get_deobfuscated_name()); new_meth->rstate = meth->rstate; assert(new_meth != meth); setup_method(meth, new_meth); new_methods[method] = new_meth; auto owner = type_class(new_meth->get_class()); owner->remove_method(meth); owner->add_method(new_meth); TRACE(INTF, 3, "(MDEF)\t=> %s\n", SHOW(new_meth)); } }