/** * 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; }
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; }
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; }
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; }
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); }
Field* Class::lookup_field_recursive(const String* name, const String* desc) { // Step 1: lookup in self Field* field = lookup_field(name, desc); if(field) return field; // Step 2: lookup in direct superinterfaces recursively for(uint16 in = 0; in < m_num_superinterfaces; in++) { field = get_superinterface(in)->lookup_field_recursive(name, desc); if(field) return field; } // Step 3: lookup in super classes recursively if(has_super_class()) { field = get_super_class()->lookup_field_recursive(name, desc); } return field; } // Class::lookup_field_recursive
// Can "other_clss" access the field or method "member"? bool Class::can_access_member(Class_Member *member) { Class* member_clss = member->get_class(); // check access permissions if(member->is_public() || (this == member_clss)) { // no problemo return true; } else if(member->is_private()) { // IllegalAccessError return false; } else if(member->is_protected()) { // When a member is protected, it can be accessed by classes // in the same runtime package if(m_package == member_clss->m_package) return true; // Otherwise, when this class is not in the same package, // the class containing the member (member_clss) must be // a superclass of this class // ppervov: FIXME: this can be made a method of struct Class // smth. like: //if(!is_extending_class(member_clss)) { // // IllegalAccessError // return false; //} //return true; Class* c; for(c = get_super_class(); c != NULL; c = c->get_super_class()) { if(c == member_clss) break; } if(c == NULL) { // IllegalAccessError return false; } return true; } else { // When a member has default (or package private) access, // it can only be accessed by classes in the same package if(m_package == member_clss->m_package) return true; return false; } } // Class::can_access_member
bool Class::load_ancestors(Global_Env* env) { m_state = ST_LoadingAncestors; const String* superName = get_super_class_name(); if(superName == NULL) { if(env->InBootstrap() || get_name() != env->JavaLangClass_String) { // This class better be java.lang.Object if(get_name() != env->JavaLangObject_String) { // ClassFormatError std::stringstream ss; ss << get_name()->bytes << ": class does not have superclass " << "but the class is not java.lang.Object"; REPORT_FAILED_CLASS_CLASS(m_class_loader, this, "java/lang/ClassFormatError", ss.str().c_str()); return false; } } } else { // Load super class Class* superClass; m_super_class.name = NULL; superClass = m_class_loader->LoadVerifyAndPrepareClass(env, superName); if(superClass == NULL) { assert(exn_raised()); return false; } if(superClass->is_interface()) { REPORT_FAILED_CLASS_CLASS(m_class_loader, this, "java/lang/IncompatibleClassChangeError", "class " << m_name->bytes << " has interface " << superClass->get_name()->bytes << " as super class"); return false; } if(superClass->is_final()) { REPORT_FAILED_CLASS_CLASS(m_class_loader, this, "java/lang/VerifyError", m_name->bytes << " cannot inherit from final class " << superClass->get_name()->bytes); return false; } // super class was successfully loaded m_super_class.clss = superClass; if(m_super_class.cp_index) { m_const_pool.resolve_entry(m_super_class.cp_index, superClass); } // if it's an interface, its superclass must be java/lang/Object if(is_interface()) { if((env->JavaLangObject_Class != NULL) && (superClass != env->JavaLangObject_Class)) { std::stringstream ss; ss << get_name()->bytes << ": interface superclass is not java.lang.Object"; REPORT_FAILED_CLASS_CLASS(m_class_loader, this, "java/lang/ClassFormatError", ss.str().c_str()); return false; } } // Update the cha_first_child and cha_next_sibling fields. m_cha_first_child = NULL; if(has_super_class()) { m_cha_next_sibling = get_super_class()->m_cha_first_child; get_super_class()->m_cha_first_child = this; } } // // load in super interfaces // for(unsigned i = 0; i < m_num_superinterfaces; i++ ) { const String* intfc_name = m_superinterfaces[i].name; Class* intfc = m_class_loader->LoadVerifyAndPrepareClass(env, intfc_name); if(intfc == NULL) { assert(exn_raised()); return false; } if(!intfc->is_interface()) { REPORT_FAILED_CLASS_CLASS(m_class_loader, this, "java/lang/IncompatibleClassChangeError", get_name()->bytes << ": " << intfc->get_name()->bytes << " is not an interface"); return false; } // superinterface was successfully loaded m_superinterfaces[i].clss = intfc; if(m_superinterfaces[i].cp_index != 0) { // there are no constant pool entries for array classes m_const_pool.resolve_entry(m_superinterfaces[i].cp_index, intfc); } } // class, superclass, and superinterfaces successfully loaded m_state = ST_Loaded; if(!is_array()) m_package = m_class_loader->ProvidePackage(env, m_name, NULL); return true; }
void search_hierarchy_for_matches(DexMethod* bridge, DexMethod* bridgee) { /* * Direct reference. The only one if it's non-virtual. */ auto clstype = bridgee->get_class(); auto name = bridgee->get_name(); auto proto = bridgee->get_proto(); TRACE(BRIDGE, 5, " %s %s %s\n", SHOW(clstype), SHOW(name), SHOW(proto)); m_potential_bridgee_refs.emplace(MethodRef(clstype, name, proto), bridge); if (!bridgee->is_virtual()) return; /* * Search super classes * * A bridge method in a derived class may be referred to using the name * of a super class if a method with a matching signature is defined in * that super class. * * To build the set of potential matches, we accumulate potential refs in * maybe_refs, and when we find a matching signature in a super class, we * add everything in maybe_refs to the set. */ std::vector<std::pair<MethodRef, DexMethod*>> maybe_refs; for (auto super = type_class(type_class(clstype)->get_super_class()); super != nullptr; super = type_class(super->get_super_class())) { maybe_refs.emplace_back( MethodRef(super->get_type(), name, proto), bridge); for (auto vmethod : super->get_vmethods()) { if (signature_matches(bridgee, vmethod)) { for (auto DEBUG_ONLY refp : maybe_refs) { TRACE(BRIDGE, 5, " %s %s %s\n", SHOW(std::get<0>(refp.first)), SHOW(std::get<1>(refp.first)), SHOW(std::get<2>(refp.first))); } m_potential_bridgee_refs.insert(maybe_refs.begin(), maybe_refs.end()); maybe_refs.clear(); } } } /* * Search sub classes * * Easy. Any subclass can refer to the bridgee. */ TypeVector subclasses; get_all_children(clstype, subclasses); for (auto subclass : subclasses) { m_potential_bridgee_refs.emplace(MethodRef(subclass, name, proto), bridge); TRACE(BRIDGE, 5, " %s %s %s\n", SHOW(subclass), SHOW(name), SHOW(proto)); } }