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);
}