<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 "WholeProgramState.h"

#include "BaseIRAnalyzer.h"
#include "GlobalTypeAnalyzer.h"
#include "Resolver.h"
#include "Walkers.h"

using namespace type_analyzer;

std::ostream& operator<<(std::ostream& out, const DexField* field) {
  out << SHOW(field);
  return out;
}

std::ostream& operator<<(std::ostream& out, const DexMethod* method) {
  out << SHOW(method);
  return out;
}

namespace {

void set_sfields_in_partition(const DexClass* cls,
                              const DexTypeEnvironment& env,
                              DexTypeFieldPartition* field_partition) {
  // Note that we *must* iterate over the list of fields in the class and not
  // the bindings in env here. This ensures that fields whose values are unknown
  // (and therefore implicitly represented by Top in the env) get correctly
  // bound to Top in field_partition (which defaults its bindings to Bottom).
  for (auto& field : cls->get_sfields()) {
    auto value = env.get(field);
    if (!value.is_top()) {
      TRACE(TYPE, 5, "%s has value %s after <clinit>", SHOW(field),
            SHOW(value));
      always_assert(field->get_class() == cls->get_type());
    } else {
      TRACE(TYPE, 5, "%s has unknown value after <clinit>", SHOW(field));
    }
    field_partition->set(field, value);
  }
}

void analyze_clinits(const Scope& scope,
                     const global::GlobalTypeAnalyzer& gta,
                     DexTypeFieldPartition* field_partition) {
  for (DexClass* cls : scope) {
    auto clinit = cls->get_clinit();
    if (!clinit) {
      continue;
    }
    IRCode* code = clinit->get_code();
    auto& cfg = code->cfg();
    auto lta = gta.get_local_analysis(clinit);
    auto env = lta->get_exit_state_at(cfg.exit_block());
    set_sfields_in_partition(cls, env, field_partition);
  }
}

bool returns_reference(const DexMethod* method) {
  auto rtype = method->get_proto()->get_rtype();
  return type::is_object(rtype);
}

bool analyze_gets_helper(const WholeProgramState* whole_program_state,
                         const IRInstruction* insn,
                         DexTypeEnvironment* env) {
  auto field = resolve_field(insn->get_field());
  if (field == nullptr || !type::is_object(field->get_type())) {
    return false;
  }
  auto field_type = whole_program_state->get_field_type(field);
  if (field_type.is_top()) {
    return false;
  }
  env->set(RESULT_REGISTER, field_type);
  return true;
}

} // namespace

namespace type_analyzer {

WholeProgramState::WholeProgramState(
    const Scope& scope,
    const global::GlobalTypeAnalyzer& gta,
    const std::unordered_set<DexMethod*>& non_true_virtuals) {
  // Exclude fields we cannot correctly analyze.
  walk::fields(scope, [&](DexField* field) {
    if (!type::is_object(field->get_type())) {
      return;
    }
    // We assume that a field we cannot delete is marked by a Proguard keep rule
    // or an annotation. The reason behind is that the field is referenced by
    // non-dex code.
    if (!can_delete(field)) {
      return;
    }
    m_known_fields.emplace(field);
  });

  // TODO: revisit this for multiple callee call graph.
  // Put non-root non true virtual methods in known methods.
  for (const auto& non_true_virtual : non_true_virtuals) {
    if (!root(non_true_virtual) && non_true_virtual->get_code() &&
        returns_reference(non_true_virtual)) {
      m_known_methods.emplace(non_true_virtual);<commit_msg>Do not track volatile fields<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 "WholeProgramState.h"

#include "BaseIRAnalyzer.h"
#include "GlobalTypeAnalyzer.h"
#include "Resolver.h"
#include "Walkers.h"

using namespace type_analyzer;

std::ostream& operator<<(std::ostream& out, const DexField* field) {
  out << SHOW(field);
  return out;
}

std::ostream& operator<<(std::ostream& out, const DexMethod* method) {
  out << SHOW(method);
  return out;
}

namespace {

void set_sfields_in_partition(const DexClass* cls,
                              const DexTypeEnvironment& env,
                              DexTypeFieldPartition* field_partition) {
  // Note that we *must* iterate over the list of fields in the class and not
  // the bindings in env here. This ensures that fields whose values are unknown
  // (and therefore implicitly represented by Top in the env) get correctly
  // bound to Top in field_partition (which defaults its bindings to Bottom).
  for (auto& field : cls->get_sfields()) {
    auto value = env.get(field);
    if (!value.is_top()) {
      TRACE(TYPE, 5, "%s has value %s after <clinit>", SHOW(field),
            SHOW(value));
      always_assert(field->get_class() == cls->get_type());
    } else {
      TRACE(TYPE, 5, "%s has unknown value after <clinit>", SHOW(field));
    }
    field_partition->set(field, value);
  }
}

void analyze_clinits(const Scope& scope,
                     const global::GlobalTypeAnalyzer& gta,
                     DexTypeFieldPartition* field_partition) {
  for (DexClass* cls : scope) {
    auto clinit = cls->get_clinit();
    if (!clinit) {
      continue;
    }
    IRCode* code = clinit->get_code();
    auto& cfg = code->cfg();
    auto lta = gta.get_local_analysis(clinit);
    auto env = lta->get_exit_state_at(cfg.exit_block());
    set_sfields_in_partition(cls, env, field_partition);
  }
}

bool returns_reference(const DexMethod* method) {
  auto rtype = method->get_proto()->get_rtype();
  return type::is_object(rtype);
}

bool analyze_gets_helper(const WholeProgramState* whole_program_state,
                         const IRInstruction* insn,
                         DexTypeEnvironment* env) {
  auto field = resolve_field(insn->get_field());
  if (field == nullptr || !type::is_object(field->get_type())) {
    return false;
  }
  auto field_type = whole_program_state->get_field_type(field);
  if (field_type.is_top()) {
    return false;
  }
  env->set(RESULT_REGISTER, field_type);
  return true;
}

} // namespace

namespace type_analyzer {

WholeProgramState::WholeProgramState(
    const Scope& scope,
    const global::GlobalTypeAnalyzer& gta,
    const std::unordered_set<DexMethod*>& non_true_virtuals) {
  // Exclude fields we cannot correctly analyze.
  walk::fields(scope, [&](DexField* field) {
    if (!type::is_object(field->get_type())) {
      return;
    }
    // We assume that a field we cannot delete is marked by a Proguard keep rule
    // or an annotation. The reason behind is that the field is referenced by
    // non-dex code.
    if (!can_delete(field) || field->is_external() || is_volatile(field)) {
      return;
    }
    m_known_fields.emplace(field);
  });

  // TODO: revisit this for multiple callee call graph.
  // Put non-root non true virtual methods in known methods.
  for (const auto& non_true_virtual : non_true_virtuals) {
    if (!root(non_true_virtual) && non_true_virtual->get_code() &&
        returns_reference(non_true_virtual)) {
      m_known_methods.emplace(non_true_virtual);
{'fraction_non_alphanumeric': 0.11690622861054073, 'fraction_numerical': 0.0005475701574264203, 'mean_word_length': 3.2875586854460095, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 42, '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': '6334818', 'n_tokens_mistral': 2288, 'n_tokens_neox': 2244, 'n_words': 637}