opt/virtual_merging/VirtualMerging.cpp
// Part 4: For each virtual scope, merge all pairs in order, unless inlining
//         is for some reason not possible, e.g. because of code size
//         constraints. Record set of methods in each class which can be
//         removed.
Part 4: For each virtual scope, merge all pairs in order, unless inlining
is for some reason not possible, e.g. because of code size
constraints. Record set of methods in each class which can be
removed.
Part 4 : For each virtual scope merge all pairs in order unless inlining is for some reason not possible e . g . because of code size constraints . Record set of methods in each class which can be removed .
void VirtualMerging::merge_methods(
    const MergablePairsByVirtualScope& mergable_pairs,
    const MergablePairsByVirtualScope& exp_mergable_pairs,
    ab_test::ABExperimentContext* ab_experiment_context) {
  auto ordering_pair = create_ordering(
      mergable_pairs, m_max_overriding_method_instructions, *m_inliner);
  m_stats += ordering_pair.second;
  const bool is_experiment = !exp_mergable_pairs.empty();
  std::unordered_map<const DexMethod*, DexMethod*> clones;
  auto make_clone = [&clones, is_experiment](DexMethod* m) {
    if (!is_experiment) {
      return m;
    }
    if (clones.count(m) == 0) {
      TRACE(VM, 5, "[VM] Cloning %s", show_deobfuscated(m).c_str());
      clones.emplace(m, DexMethod::make_full_method_from(
                            m, m->get_class(),
                            DexString::make_string(
                                m->str() + "$VirtualMergingTemporaryClone")));
    }
    return m;
  };
  auto stats =
      apply_ordering(*m_inliner, ordering_pair.first, make_clone,
                     m_virtual_methods_to_remove, m_virtual_methods_to_remap);
  m_stats += stats;
  always_assert(m_stats.mergeable_pairs ==
                m_stats.huge_methods + m_stats.uninlinable_methods +
                    m_stats.caller_size_removed_methods +
                    m_stats.removed_virtual_methods);
  if (is_experiment) {
    TRACE(VM, 3, "[VM] Applying experiment.");
    auto exp_mergable_pairs_remapped = exp_mergable_pairs;
    for (auto& p : exp_mergable_pairs_remapped) {
      for (auto& q : p.second) {
        auto check_clone = [&clones](const DexMethod* m) -> const DexMethod* {
          auto it = clones.find(m);
          if (it == clones.end()) {
            return m;
          }
          return it->second;
        };
        q.first = check_clone(q.first);
        q.second = check_clone(q.second);
      }
    }
    auto exp_ordering_pair =
        create_ordering(exp_mergable_pairs_remapped,
                        m_max_overriding_method_instructions, *m_inliner);
    redex_assert(ordering_pair.second == exp_ordering_pair.second);
    std::unordered_map<const DexMethod*, const DexMethod*> clones_rev;
    for (const auto& p : clones) {
      auto it = clones_rev.emplace(p.second, p.first);
      redex_assert(it.second);
    }
    std::unordered_map<DexClass*, std::vector<const DexMethod*>>
        exp_virtual_methods_to_remove;
    std::unordered_map<DexMethod*, DexMethod*> exp_virtual_methods_to_remap;
    auto exp_stats = apply_ordering(
        *m_inliner, exp_ordering_pair.first,
        [&clones_rev](auto* m) {
          always_assert_log(clones_rev.count(m) != 0, "%s not a clone!",
                            SHOW(m));
          return m;
        },
        exp_virtual_methods_to_remove, exp_virtual_methods_to_remap);
    redex_assert(stats == exp_stats);
    check_remove(m_virtual_methods_to_remove, exp_virtual_methods_to_remove,
                 clones);
    check_remap(m_virtual_methods_to_remap, exp_virtual_methods_to_remap,
                clones);
    std::unordered_set<const DexMethod*> all_methods;
    std::transform(ordering_pair.first.begin(), ordering_pair.first.end(),
                   std::inserter(all_methods, all_methods.end()),
                   [](const auto& p) { return p.first; });
    auto remap_keys = get_keys(m_virtual_methods_to_remap);
    std20::erase_if(all_methods, [&remap_keys](const auto* m) {
      return remap_keys.count(const_cast<DexMethod*>(m)) != 0;
    });
    TRACE(VM, 3, "[VM] Registering %zu methods for experiments",
          all_methods.size());
    m_stats.experiment_methods = all_methods.size();
    redex_assert(ab_experiment_context != nullptr);
    for (auto* m_const : all_methods) {
      auto* m = const_cast<DexMethod*>(m_const);
      redex_assert(!m->get_code()->cfg_built());
      m->get_code()->build_cfg();
      ab_experiment_context->try_register_method(m);
      m->get_code()->clear_cfg();
      m->set_code(clones.at(m)->release_code());
      m->get_code()->build_cfg();
    }
    ab_experiment_context->flush();
    for (auto* m_const : all_methods) {
      const_cast<DexMethod*>(m_const)->get_code()->clear_cfg();
    }
  }
}
void VirtualMerging :: merge_methods ( const MergablePairsByVirtualScope & mergable_pairs , const MergablePairsByVirtualScope & exp_mergable_pairs , ab_test :: ABExperimentContext * ab_experiment_context ) { auto ordering_pair = create_ordering ( mergable_pairs , m_max_overriding_method_instructions , * m_inliner ) ; m_stats += ordering_pair . second ; const bool is_experiment = ! exp_mergable_pairs . empty ( ) ; std :: unordered_map < const DexMethod * , DexMethod * > clones ; auto make_clone = [ & clones , is_experiment ] ( DexMethod * m ) { if ( ! is_experiment ) { return m ; } if ( clones . count ( m ) == 0 ) { TRACE ( VM , 5 , " " , show_deobfuscated ( m ) . c_str ( ) ) ; clones . emplace ( m , DexMethod :: make_full_method_from ( m , m -> get_class ( ) , DexString :: make_string ( m -> str ( ) + " " ) ) ) ; } return m ; } ; auto stats = apply_ordering ( * m_inliner , ordering_pair . first , make_clone , m_virtual_methods_to_remove , m_virtual_methods_to_remap ) ; m_stats += stats ; always_assert ( m_stats . mergeable_pairs == m_stats . huge_methods + m_stats . uninlinable_methods + m_stats . caller_size_removed_methods + m_stats . removed_virtual_methods ) ; if ( is_experiment ) { TRACE ( VM , 3 , " " ) ; auto exp_mergable_pairs_remapped = exp_mergable_pairs ; for ( auto & p : exp_mergable_pairs_remapped ) { for ( auto & q : p . second ) { auto check_clone = [ & clones ] ( const DexMethod * m ) -> const DexMethod * { auto it = clones . find ( m ) ; if ( it == clones . end ( ) ) { return m ; } return it -> second ; } ; q . first = check_clone ( q . first ) ; q . second = check_clone ( q . second ) ; } } auto exp_ordering_pair = create_ordering ( exp_mergable_pairs_remapped , m_max_overriding_method_instructions , * m_inliner ) ; redex_assert ( ordering_pair . second == exp_ordering_pair . second ) ; std :: unordered_map < const DexMethod * , const DexMethod * > clones_rev ; for ( const auto & p : clones ) { auto it = clones_rev . emplace ( p . second , p . first ) ; redex_assert ( it . second ) ; } std :: unordered_map < DexClass * , std :: vector < const DexMethod * > > exp_virtual_methods_to_remove ; std :: unordered_map < DexMethod * , DexMethod * > exp_virtual_methods_to_remap ; auto exp_stats = apply_ordering ( * m_inliner , exp_ordering_pair . first , [ & clones_rev ] ( auto * m ) { always_assert_log ( clones_rev . count ( m ) != 0 , " " , SHOW ( m ) ) ; return m ; } , exp_virtual_methods_to_remove , exp_virtual_methods_to_remap ) ; redex_assert ( stats == exp_stats ) ; check_remove ( m_virtual_methods_to_remove , exp_virtual_methods_to_remove , clones ) ; check_remap ( m_virtual_methods_to_remap , exp_virtual_methods_to_remap , clones ) ; std :: unordered_set < const DexMethod * > all_methods ; std :: transform ( ordering_pair . first . begin ( ) , ordering_pair . first . end ( ) , std :: inserter ( all_methods , all_methods . end ( ) ) , [ ] ( const auto & p ) { return p . first ; } ) ; auto remap_keys = get_keys ( m_virtual_methods_to_remap ) ; std20 :: erase_if ( all_methods , [ & remap_keys ] ( const auto * m ) { return remap_keys . count ( const_cast < DexMethod * > ( m ) ) != 0 ; } ) ; TRACE ( VM , 3 , " " , all_methods . size ( ) ) ; m_stats . experiment_methods = all_methods . size ( ) ; redex_assert ( ab_experiment_context != nullptr ) ; for ( auto * m_const : all_methods ) { auto * m = const_cast < DexMethod * > ( m_const ) ; redex_assert ( ! m -> get_code ( ) -> cfg_built ( ) ) ; m -> get_code ( ) -> build_cfg ( ) ; ab_experiment_context -> try_register_method ( m ) ; m -> get_code ( ) -> clear_cfg ( ) ; m -> set_code ( clones . at ( m ) -> release_code ( ) ) ; m -> get_code ( ) -> build_cfg ( ) ; } ab_experiment_context -> flush ( ) ; for ( auto * m_const : all_methods ) { const_cast < DexMethod * > ( m_const ) -> get_code ( ) -> clear_cfg ( ) ; } } }
Part 4: For each virtual scope, merge all pairs in order, unless inlining
is for some reason not possible, e.g.
Part 4 : For each virtual scope merge all pairs in order unless inlining is for some reason not possible e . g .
// Gotta remap everything. // Some methods will be filtered out, so not everything is a clone. // Minimal integrity check. // TODO: Check the orderings. // Go and process things with an experiment now.