static size_t analyze_read_locations()

in libredex/Purity.cpp [710:849]


static size_t analyze_read_locations(
    const Scope& scope,
    const method_override_graph::Graph* method_override_graph,
    const method::ClInitHasNoSideEffectsPredicate& clinit_has_no_side_effects,
    const std::unordered_set<DexMethodRef*>& pure_methods,
    bool ignore_methods_with_assumenosideeffects,
    bool for_conditional_purity,
    bool compute_locations,
    std::unordered_map<const DexMethod*, CseUnorderedLocationSet>* result,
    const purity::CacheConfig& cache_config) {
  std::unordered_set<const DexMethod*> pure_methods_closure;
  for (auto pure_method_ref : pure_methods) {
    auto pure_method = pure_method_ref->as_def();
    if (pure_method == nullptr) {
      continue;
    }
    pure_methods_closure.insert(pure_method);
    if (pure_method->is_virtual() && method_override_graph) {
      const auto overriding_methods =
          method_override_graph::get_overriding_methods(*method_override_graph,
                                                        pure_method);
      pure_methods_closure.insert(overriding_methods.begin(),
                                  overriding_methods.end());
    }
  }

  return compute_locations_closure(
      scope, method_override_graph,
      [&](DexMethod* method) -> boost::optional<LocationsAndDependencies> {
        auto action = get_base_or_overriding_method_action(
            method, &pure_methods_closure,
            ignore_methods_with_assumenosideeffects);

        if (action == MethodOverrideAction::UNKNOWN) {
          return boost::none;
        }

        LocationsAndDependencies lads;
        if (!process_base_and_overriding_methods(
                method_override_graph, method, &pure_methods_closure,
                ignore_methods_with_assumenosideeffects,
                [&](DexMethod* other_method) {
                  if (other_method != method) {
                    lads.dependencies.insert(other_method);
                  }
                  return true;
                })) {
          return boost::none;
        }

        if (action == MethodOverrideAction::EXCLUDE) {
          return lads;
        }

        bool unknown = false;
        editable_cfg_adapter::iterate_with_iterator(
            method->get_code(), [&](const IRList::iterator& it) {
              auto insn = it->insn;
              auto opcode = insn->opcode();
              switch (opcode) {
              case OPCODE_MONITOR_ENTER:
              case OPCODE_MONITOR_EXIT:
              case OPCODE_FILL_ARRAY_DATA:
              case OPCODE_THROW:
                unknown = true;
                break;
              case IOPCODE_INIT_CLASS:
                unknown = true;
                break;
              case OPCODE_NEW_INSTANCE:
                if (for_conditional_purity ||
                    !clinit_has_no_side_effects(insn->get_type())) {
                  unknown = true;
                }
                break;
              case OPCODE_NEW_ARRAY:
              case OPCODE_FILLED_NEW_ARRAY:
                if (for_conditional_purity) {
                  unknown = true;
                }
                break;
              case OPCODE_INVOKE_SUPER:
                // TODO: Support properly.
                unknown = true;
                break;
              default:
                if (opcode::is_an_aput(opcode) || opcode::is_an_iput(opcode) ||
                    opcode::is_an_sput(opcode)) {
                  unknown = true;
                } else if (opcode::is_an_aget(opcode) ||
                           opcode::is_an_iget(opcode) ||
                           opcode::is_an_sget(opcode)) {
                  auto location = get_read_location(insn);
                  if (location ==
                      CseLocation(
                          CseSpecialLocations::GENERAL_MEMORY_BARRIER)) {
                    unknown = true;
                  } else {
                    if (opcode::is_an_sget(opcode) &&
                        (!clinit_has_no_side_effects(
                            location.get_field()->get_class()))) {
                      unknown = true;
                    } else if (compute_locations) {
                      lads.locations.insert(location);
                    }
                  }
                } else if (opcode::is_an_invoke(opcode)) {
                  auto invoke_method = resolve_method(
                      insn->get_method(), opcode_to_search(opcode), method);
                  if ((invoke_method && opcode::is_invoke_static(opcode) &&
                       (!clinit_has_no_side_effects(
                           invoke_method->get_class()))) ||
                      !process_base_and_overriding_methods(
                          method_override_graph, invoke_method,
                          &pure_methods_closure,
                          ignore_methods_with_assumenosideeffects,
                          [&](DexMethod* other_method) {
                            if (other_method != method) {
                              lads.dependencies.insert(other_method);
                            }
                            return true;
                          })) {
                    unknown = true;
                  }
                }
                break;
              }

              return unknown ? editable_cfg_adapter::LOOP_BREAK
                             : editable_cfg_adapter::LOOP_CONTINUE;
            });

        if (unknown) {
          return boost::none;
        }

        return lads;
      },
      result, cache_config);
}