<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 "KotlinInstanceRewriter.h"
#include "CFGMutation.h"
#include "PassManager.h"
#include "ScopedCFG.h"
#include "Show.h"
#include "Walkers.h"

namespace {
DexField* has_instance_field(DexClass* cls, DexString* instance) {
  for (auto* filed : cls->get_sfields()) {
    if (filed->get_name() == instance && filed->get_type() == cls->get_type()) {
      return filed;
    }
  }
  return nullptr;
}

} // namespace

KotlinInstanceRewriter::Stats KotlinInstanceRewriter::collect_instance_usage(
    const Scope& scope,
    ConcurrentMap<DexFieldRef*,
                  std::set<std::pair<IRInstruction*, DexMethod*>>>&
        concurrent_instance_map,

    std::function<bool(DexClass*)> do_not_consider_type) {
  // Collect all the types which are of Kotlin classes which sets INSTANCE
  // variable.
  // Get all the uses of the INSTANCE variables whose <init> does not have
  // sideeffets
  KotlinInstanceRewriter::Stats stats{};
  walk::parallel::classes(scope, [&](DexClass* cls) {
    if (!can_rename(cls) || !can_delete(cls)) {
      return;
    }
    auto instance = has_instance_field(cls, m_instance);
    if (!instance) {
      return;
    }
    if (do_not_consider_type(cls)) {
      return;
    }
    if (concurrent_instance_map.count(instance)) {
      return;
    }
    std::set<std::pair<IRInstruction*, DexMethod*>> insns;
    concurrent_instance_map.emplace(instance, insns);
  });
  stats.kotlin_new_instance = concurrent_instance_map.size();
  return stats;
}

KotlinInstanceRewriter::Stats KotlinInstanceRewriter::remove_escaping_instance(
    const Scope& scope,
    ConcurrentMap<DexFieldRef*,
                  std::set<std::pair<IRInstruction*, DexMethod*>>>&
        concurrent_instance_map) {
  // Get all the single uses of the INSTANCE variables
  KotlinInstanceRewriter::Stats total_stats =
      walk::parallel::methods<KotlinInstanceRewriter::Stats>(
          scope, [&](DexMethod* method) {
            KotlinInstanceRewriter::Stats stats{};
            auto code = method->get_code();
            if (!code) {
              return stats;
            }

            cfg::ScopedCFG cfg(method->get_code());
            auto iterable = cfg::InstructionIterable(*cfg);
            for (auto it = iterable.begin(); it != iterable.end(); it++) {
              auto insn = it->insn;

              if (!opcode::is_an_sget(insn->opcode()) &&
                  !opcode::is_an_sput(insn->opcode())) {
                continue;
              }

              auto field = insn->get_field();
              if (!concurrent_instance_map.count(field)) {
                continue;
              }
              // If there is more SPUT otherthan the initial one.
              if (opcode::is_an_sput(insn->opcode())) {
                if (method::is_clinit(method) &&
                    method->get_class() == field->get_type()) {
                  continue;
                }
                // Erase if the field is written elsewhere.
                concurrent_instance_map.erase(field);
                continue;
              }

              concurrent_instance_map.update(
                  field,
                  [&](DexFieldRef*,
                      std::set<std::pair<IRInstruction*, DexMethod*>>& s,
                      bool /* exists */) {
                    if (s.size() <= max_no_of_instance) {
                      s.insert(std::make_pair(insn, method));
                    }
                  });
            }
            return stats;
          });
  return total_stats;
}

KotlinInstanceRewriter::Stats KotlinInstanceRewriter::transform(
    ConcurrentMap<DexFieldRef*,
                  std::set<std::pair<IRInstruction*, DexMethod*>>>&
        concurrent_instance_map) {
  // Among the Kotlin classes which sets INSTANCE and whose use is cleary
  // defined, select singles use INSTANCEs. Remove the INSTANCE initialization
  // in <clinit> and replace the SGET INSTANCE with a new INSTANCE.

  KotlinInstanceRewriter::Stats stats = {};
  for (const auto& it : concurrent_instance_map) {
    if (it.second.size() > max_no_of_instance) {
      continue;
    }
    const std::set<std::pair<IRInstruction*, DexMethod*>>& insns = it.second;
    if (insns.empty()) {
      continue;
    }
    stats.kotlin_instances_with_single_use++;
    // Remove instance from cls<commit_msg>KotlinInstanceRewriter change in deterministic order<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 "KotlinInstanceRewriter.h"
#include "CFGMutation.h"
#include "PassManager.h"
#include "ScopedCFG.h"
#include "Show.h"
#include "Walkers.h"

namespace {
DexField* has_instance_field(DexClass* cls, DexString* instance) {
  for (auto* filed : cls->get_sfields()) {
    if (filed->get_name() == instance && filed->get_type() == cls->get_type()) {
      return filed;
    }
  }
  return nullptr;
}

} // namespace

KotlinInstanceRewriter::Stats KotlinInstanceRewriter::collect_instance_usage(
    const Scope& scope,
    ConcurrentMap<DexFieldRef*,
                  std::set<std::pair<IRInstruction*, DexMethod*>>>&
        concurrent_instance_map,

    std::function<bool(DexClass*)> do_not_consider_type) {
  // Collect all the types which are of Kotlin classes which sets INSTANCE
  // variable.
  // Get all the uses of the INSTANCE variables whose <init> does not have
  // sideeffets
  KotlinInstanceRewriter::Stats stats{};
  walk::parallel::classes(scope, [&](DexClass* cls) {
    if (!can_rename(cls) || !can_delete(cls)) {
      return;
    }
    auto instance = has_instance_field(cls, m_instance);
    if (!instance) {
      return;
    }
    if (do_not_consider_type(cls)) {
      return;
    }
    if (concurrent_instance_map.count(instance)) {
      return;
    }
    std::set<std::pair<IRInstruction*, DexMethod*>> insns;
    concurrent_instance_map.emplace(instance, insns);
  });
  stats.kotlin_new_instance = concurrent_instance_map.size();
  return stats;
}

KotlinInstanceRewriter::Stats KotlinInstanceRewriter::remove_escaping_instance(
    const Scope& scope,
    ConcurrentMap<DexFieldRef*,
                  std::set<std::pair<IRInstruction*, DexMethod*>>>&
        concurrent_instance_map) {
  ConcurrentSet<DexFieldRef*> remove_list;
  // Get all the single uses of the INSTANCE variables
  KotlinInstanceRewriter::Stats total_stats =
      walk::parallel::methods<KotlinInstanceRewriter::Stats>(
          scope, [&](DexMethod* method) {
            KotlinInstanceRewriter::Stats stats{};
            auto code = method->get_code();
            if (!code) {
              return stats;
            }

            cfg::ScopedCFG cfg(method->get_code());
            auto iterable = cfg::InstructionIterable(*cfg);
            for (auto it = iterable.begin(); it != iterable.end(); it++) {
              auto insn = it->insn;

              if (!opcode::is_an_sget(insn->opcode()) &&
                  !opcode::is_an_sput(insn->opcode())) {
                continue;
              }

              auto field = insn->get_field();
              if (!concurrent_instance_map.count(field)) {
                continue;
              }
              if (remove_list.count(field)) {
                continue;
              }
              // If there is more SPUT otherthan the initial one.
              if (opcode::is_an_sput(insn->opcode())) {
                if (method::is_clinit(method) &&
                    method->get_class() == field->get_type()) {
                  continue;
                }
                // Erase if the field is written elsewhere.
                remove_list.insert(field);
                continue;
              }

              concurrent_instance_map.update(
                  field,
                  [&](DexFieldRef*,
                      std::set<std::pair<IRInstruction*, DexMethod*>>& s,
                      bool /* exists */) {
                    if (s.size() <= max_no_of_instance) {
                      s.insert(std::make_pair(insn, method));
                    }
                  });
            }
            return stats;
          });
  for (auto* field : remove_list) {
    concurrent_instance_map.erase(field);
  }
  return total_stats;
}

KotlinInstanceRewriter::Stats KotlinInstanceRewriter::transform(
    ConcurrentMap<DexFieldRef*,
                  std::set<std::pair<IRInstruction*, DexMethod*>>>&
        concurrent_instance_map) {
  // Among the Kotlin classes which sets INSTANCE and whose use is cleary
  // defined, select singles use INSTANCEs. Remove the INSTANCE initialization
  // in <clinit> and replace the SGET INSTANCE with a new INSTANCE.

  KotlinInstanceRewriter::Stats stats = {};
  for (const auto& it : concurrent_instance_map) {
    if (it.second.size() > max_no_of_instance) {
      continue;
    }
    const std::set<std::pair<IRInstruction*, DexMethod*>>& insns = it.second;
    if (insns.empty()) {
      continue;
    }
    stats.kotlin_instances_with_single_use++;
    // Remove instance from cls
{'fraction_non_alphanumeric': 0.11848188366842274, 'fraction_numerical': 0.0, 'mean_word_length': 2.355699855699856, 'pattern_counts': {'":': 0, '<': 44, '<?xml version=': 0, '>': 70, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2011848', 'n_tokens_mistral': 2786, 'n_tokens_neox': 2716, 'n_words': 599}