<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#include "CallGraph.h"

#include <utility>

#include "ConcurrentContainers.h"
#include "MethodOverrideGraph.h"
#include "Show.h"
#include "Trace.h"
#include "Walkers.h"

namespace mog = method_override_graph;

namespace call_graph {

Graph single_callee_graph(const mog::Graph& method_override_graph,
                          const Scope& scope) {
  return Graph(SingleCalleeStrategy(method_override_graph, scope));
}

Graph complete_call_graph(const mog::Graph& method_override_graph,
                          const Scope& scope) {
  return Graph(CompleteCallGraphStrategy(method_override_graph, scope));
}

Graph multiple_callee_graph(const mog::Graph& method_override_graph,
                            const Scope& scope,
                            uint32_t big_override_threshold) {
  return Graph(MultipleCalleeStrategy(method_override_graph, scope,
                                      big_override_threshold));
}

SingleCalleeStrategy::SingleCalleeStrategy(
    const mog::Graph& method_override_graph, const Scope& scope)
    : m_scope(scope) {
  auto non_virtual_vec =
      mog::get_non_true_virtuals(method_override_graph, scope);
  m_non_virtual.insert(non_virtual_vec.begin(), non_virtual_vec.end());
}

CallSites SingleCalleeStrategy::get_callsites(const DexMethod* method) const {
  CallSites callsites;
  auto* code = const_cast<IRCode*>(method->get_code());
  if (code == nullptr) {
    return callsites;
  }
  for (auto& mie : InstructionIterable(code)) {
    auto insn = mie.insn;
    if (opcode::is_an_invoke(insn->opcode())) {
      auto callee = this->resolve_callee(method, insn);
      if (callee == nullptr || is_definitely_virtual(callee)) {
        continue;
      }
      if (callee->is_concrete()) {
        callsites.emplace_back(callee, code->iterator_to(mie));
      }
    }
  }
  return callsites;
}

RootAndDynamic SingleCalleeStrategy::get_roots() const {
  RootAndDynamic root_and_dynamic;
  auto& roots = root_and_dynamic.roots;
  walk::code(m_scope, [&](DexMethod* method, IRCode& /* code */) {
    if (is_definitely_virtual(method) || root(method) ||
        method::is_clinit(method)) {
      roots.emplace_back(method);
    }
  });
  return root_and_dynamic;
}

bool SingleCalleeStrategy::is_definitely_virtual(DexMethod* method) const {
  return method->is_virtual() && m_non_virtual.count(method) == 0;
}

DexMethod* SingleCalleeStrategy::resolve_callee(const DexMethod* caller,
                                                IRInstruction* invoke) const {
  return resolve_method(invoke->get_method(), opcode_to_search(invoke),
                        m_resolved_refs, caller);
}

MultipleCalleeBaseStrategy::MultipleCalleeBaseStrategy(
    const mog::Graph& method_override_graph, const Scope& scope)
    : SingleCalleeStrategy(method_override_graph, scope),
      m_method_override_graph(method_override_graph) {}

RootAndDynamic MultipleCalleeBaseStrategy::get_roots() const {
  RootAndDynamic root_and_dynamic;
  MethodSet emplaced_methods;
  auto& roots = root_and_dynamic.roots;
  auto& dynamic_methods = root_and_dynamic.dynamic_methods;
  // Gather clinits and root methods, and the methods that override or
  // overriden by the root methods.
  auto add_root_method_overrides = [&](const DexMethod* method) {
    if (!method->get_code() || root(method) || method->is_external()) {
      // No need to add root methods, they will be added anyway.
      return;
    }
    if (!emplaced_methods.count(method)) {
      roots.emplace_back(method);
      emplaced_methods.emplace(method);
    }
  };
  walk::methods(m_scope, [&](DexMethod* method) {
    if (method::is_clinit(method)) {
      roots.emplace_back(method);
      emplaced_methods.emplace(method);
      return;
    }
    if (!root(method) && !(method->is_virtual() &&
                           is_interface(type_class(method->get_class())) &&
                           !can_rename(method))) {
      // For root methods and dynamically added classes, created via
      // Proxy.newProxyInstance, we need to add them and their overrides and
      // overriden to roots.
      return;
    }
    if (method->is_virtual() && is_interface(type_class(method->get_class())) &&
        !can_rename(method)) {
      dynamic_methods.emplace(method);
    }
    if (!emplaced_methods.count(method) && method->get_code()) {
      roots.emplace_back(method);
      emplaced_methods.emplace(method);
    }
    const auto& overriding_methods =
        mog::get_overriding_methods(m_method_override_graph, method);
    for (auto overriding_method : overriding_methods) {
      add_root_method_overrides(overriding_method);
    }
    const auto& overiden_methods =
        mog::get_overridden_methods(m_method_override_graph, method);
    for (auto overiden_method : overiden_methods) {
      add_root_method_overrides(overiden_method);
    }
  });
  // Gather methods that override or implement external or native methods
  // as well.
  for (auto& pair : m_method_override_graph.nodes()) {
    auto method = pair.first;
    if (method->is_external()) {
      dynamic_methods.emplace(method);
      const auto& overriding_methods =
          mog::get_overriding_methods(m_method_override_graph, method);
      for (auto* overriding : overriding_methods) {
        if (overriding->is_external()) {
          dynamic_methods.emplace(overriding);
        }
        if (!overriding->is_external() && !emplaced_methods.count(overriding) &&
            overriding->get_code()) {
          roots.emplace_back(overriding);
          emplaced_methods.emplace(overriding);
        }
      }
    }
    if (is_native(method)) {
      dynamic_methods.emplace(method);
      const auto& overriding_methods =
          mog::get_overriding_methods(m_method_override_graph, method, true);
      for (auto m : overriding_methods) {
        dynamic_methods.emplace(m);
      }
      const auto& overiden_methods =
          mog::get_overridden_methods(m_method_override_graph, method, true);
      for (auto m : overiden_methods) {<commit_msg>add method that might be overiden by external method to dynamic method set<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#include "CallGraph.h"

#include <utility>

#include "ConcurrentContainers.h"
#include "MethodOverrideGraph.h"
#include "Show.h"
#include "Trace.h"
#include "Walkers.h"

namespace mog = method_override_graph;

namespace call_graph {

Graph single_callee_graph(const mog::Graph& method_override_graph,
                          const Scope& scope) {
  return Graph(SingleCalleeStrategy(method_override_graph, scope));
}

Graph complete_call_graph(const mog::Graph& method_override_graph,
                          const Scope& scope) {
  return Graph(CompleteCallGraphStrategy(method_override_graph, scope));
}

Graph multiple_callee_graph(const mog::Graph& method_override_graph,
                            const Scope& scope,
                            uint32_t big_override_threshold) {
  return Graph(MultipleCalleeStrategy(method_override_graph, scope,
                                      big_override_threshold));
}

SingleCalleeStrategy::SingleCalleeStrategy(
    const mog::Graph& method_override_graph, const Scope& scope)
    : m_scope(scope) {
  auto non_virtual_vec =
      mog::get_non_true_virtuals(method_override_graph, scope);
  m_non_virtual.insert(non_virtual_vec.begin(), non_virtual_vec.end());
}

CallSites SingleCalleeStrategy::get_callsites(const DexMethod* method) const {
  CallSites callsites;
  auto* code = const_cast<IRCode*>(method->get_code());
  if (code == nullptr) {
    return callsites;
  }
  for (auto& mie : InstructionIterable(code)) {
    auto insn = mie.insn;
    if (opcode::is_an_invoke(insn->opcode())) {
      auto callee = this->resolve_callee(method, insn);
      if (callee == nullptr || is_definitely_virtual(callee)) {
        continue;
      }
      if (callee->is_concrete()) {
        callsites.emplace_back(callee, code->iterator_to(mie));
      }
    }
  }
  return callsites;
}

RootAndDynamic SingleCalleeStrategy::get_roots() const {
  RootAndDynamic root_and_dynamic;
  auto& roots = root_and_dynamic.roots;
  walk::code(m_scope, [&](DexMethod* method, IRCode& /* code */) {
    if (is_definitely_virtual(method) || root(method) ||
        method::is_clinit(method)) {
      roots.emplace_back(method);
    }
  });
  return root_and_dynamic;
}

bool SingleCalleeStrategy::is_definitely_virtual(DexMethod* method) const {
  return method->is_virtual() && m_non_virtual.count(method) == 0;
}

DexMethod* SingleCalleeStrategy::resolve_callee(const DexMethod* caller,
                                                IRInstruction* invoke) const {
  return resolve_method(invoke->get_method(), opcode_to_search(invoke),
                        m_resolved_refs, caller);
}

MultipleCalleeBaseStrategy::MultipleCalleeBaseStrategy(
    const mog::Graph& method_override_graph, const Scope& scope)
    : SingleCalleeStrategy(method_override_graph, scope),
      m_method_override_graph(method_override_graph) {}

RootAndDynamic MultipleCalleeBaseStrategy::get_roots() const {
  RootAndDynamic root_and_dynamic;
  MethodSet emplaced_methods;
  auto& roots = root_and_dynamic.roots;
  auto& dynamic_methods = root_and_dynamic.dynamic_methods;
  // Gather clinits and root methods, and the methods that override or
  // overriden by the root methods.
  auto add_root_method_overrides = [&](const DexMethod* method) {
    if (!method->get_code() || root(method) || method->is_external()) {
      // No need to add root methods, they will be added anyway.
      return;
    }
    if (!emplaced_methods.count(method)) {
      roots.emplace_back(method);
      emplaced_methods.emplace(method);
    }
  };
  walk::methods(m_scope, [&](DexMethod* method) {
    if (method::is_clinit(method)) {
      roots.emplace_back(method);
      emplaced_methods.emplace(method);
      return;
    }
    if (!root(method) && !(method->is_virtual() &&
                           is_interface(type_class(method->get_class())) &&
                           !can_rename(method))) {
      // For root methods and dynamically added classes, created via
      // Proxy.newProxyInstance, we need to add them and their overrides and
      // overriden to roots.
      return;
    }
    if (method->is_virtual() && is_interface(type_class(method->get_class())) &&
        !can_rename(method)) {
      dynamic_methods.emplace(method);
    }
    if (!emplaced_methods.count(method) && method->get_code()) {
      roots.emplace_back(method);
      emplaced_methods.emplace(method);
    }
    const auto& overriding_methods =
        mog::get_overriding_methods(m_method_override_graph, method);
    for (auto overriding_method : overriding_methods) {
      add_root_method_overrides(overriding_method);
    }
    const auto& overiden_methods =
        mog::get_overridden_methods(m_method_override_graph, method);
    for (auto overiden_method : overiden_methods) {
      add_root_method_overrides(overiden_method);
    }
  });
  // Gather methods that override or implement external or native methods
  // as well.
  for (auto& pair : m_method_override_graph.nodes()) {
    auto method = pair.first;
    if (method->is_external()) {
      dynamic_methods.emplace(method);
      const auto& overriding_methods =
          mog::get_overriding_methods(m_method_override_graph, method);
      for (auto* overriding : overriding_methods) {
        if (overriding->is_external()) {
          dynamic_methods.emplace(overriding);
        }
        if (!overriding->is_external() && !emplaced_methods.count(overriding) &&
            overriding->get_code()) {
          roots.emplace_back(overriding);
          emplaced_methods.emplace(overriding);
        }
      }
      // Internal methods might be overriden by external methods. Add such
      // methods to dynamic methods to avoid return value propagation as well.
      const auto& overiden_methods =
          mog::get_overridden_methods(m_method_override_graph, method, true);
      for (auto m : overiden_methods) {
        dynamic_methods.emplace(m);
      }
    }
    if (is_native(method)) {
      dynamic_methods.emplace(method);
      const auto& overriding_methods =
          mog::get_overriding_methods(m_method_override_graph, method, true);
      for (auto m : overriding_methods) {
        dynamic_methods.emplace(m);
      }
      const auto& overiden_methods =
          mog::get_overridden_methods(m_method_override_graph, method, true);
      for (auto m : overiden_methods) {
{'fraction_non_alphanumeric': 0.10612181479179615, 'fraction_numerical': 0.0004661280298321939, 'mean_word_length': 3.375594833446635, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7211472', 'n_tokens_mistral': 3933, 'n_tokens_neox': 3859, 'n_words': 782}