void exclude_referenced_bridgees() { walk_code(*m_scope, [](DexMethod*) { return true; }, [&](DexMethod* m, DexCode& code) { exclude_referenced_bridgee(m, code); }); }
/** * 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); }
/** * Walks all the code of the app, finding classes that are reachable from * code. * * Note that as code is changed or removed by Redex, this information will * become stale, so this method should be called periodically, for example * after each pass. */ void recompute_classes_reachable_from_code(const Scope& scope) { for (auto clazz : scope) { clazz->rstate.clear_if_compute(); } std::unordered_set<DexString*> maybetypes; walk_annotations(scope, [&](DexAnnotation* anno) { static DexType* dalviksig = DexType::get_type("Ldalvik/annotation/Signature;"); // Signature annotations contain strings that Jackson uses // to construct the underlying types. We capture the // full list here, and mark them later. (There are many // duplicates, so de-duping before we look it up as a class // makes sense) if (anno->type() == dalviksig) { auto elems = anno->anno_elems(); for (auto const& elem : elems) { auto ev = elem.encoded_value; if (ev->evtype() != DEVT_ARRAY) continue; auto arrayev = static_cast<DexEncodedValueArray*>(ev); auto const& evs = arrayev->evalues(); for (auto strev : *evs) { if (strev->evtype() != DEVT_STRING) continue; auto stringev = static_cast<DexEncodedValueString*>(strev); maybetypes.insert((DexString*)stringev->string()); } } return; } // Class literals in annotations. // // Example: // @JsonDeserialize(using=MyJsonDeserializer.class) // ^^^^ if (anno->runtime_visible()) { auto elems = anno->anno_elems(); for (auto const& dae : elems) { auto evalue = dae.encoded_value; std::vector<DexType*> ltype; evalue->gather_types(ltype); if (ltype.size()) { for (auto dextype : ltype) { mark_reachable_directly(dextype); } } } } }); // Now we process the strings that were in the signature // annotations. // Note: We do not mark these as ref'd by string, because // these cases are handleable for renaming. for (auto dstring : maybetypes) { const char* cstr = dstring->c_str(); int len = strlen(cstr); if (len < 3) continue; if (cstr[0] != 'L') continue; if (cstr[len - 1] == ';') { auto dtype = DexType::get_type(dstring); mark_reachable_directly(dtype); continue; } std::string buf(cstr); buf += ';'; auto dtype = DexType::get_type(buf.c_str()); mark_reachable_directly(dtype); } // Matches methods marked as native walk_methods(scope, [&](DexMethod* meth) { if (meth->get_access() & DexAccessFlags::ACC_NATIVE) { mark_reachable_by_classname(meth->get_class(), true); } }); walk_code(scope, [](DexMethod*) { return true; }, [&](DexMethod* meth, DexCode* code) { auto opcodes = code->get_instructions(); for (const auto& opcode : opcodes) { // Matches any stringref that name-aliases a type. if (opcode->has_strings()) { auto stringop = static_cast<DexOpcodeString*>(opcode); DexString* dsclzref = stringop->get_string(); DexType* dtexclude = get_dextype_from_dotname(dsclzref->c_str()); if (dtexclude == nullptr) continue; mark_reachable_by_classname(dtexclude, true); } if (opcode->has_types()) { // Matches the following instructions (from most to least // common): // check-cast, new-instance, const-class, instance-of // new-instance should not be on this list, and // we should not allow these to operate on themselves. // TODO(snay/dalves) auto typeop = static_cast<DexOpcodeType*>(opcode); mark_reachable_directly(typeop->get_type()); } } }); }