service/init-classes/InitClassesWithSideEffects.cpp (113 lines of code) (raw):
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "InitClassesWithSideEffects.h"
#include "MethodUtil.h"
#include "Timer.h"
#include "Walkers.h"
#include <memory>
namespace init_classes {
const InitClasses* InitClassesWithSideEffects::compute(
const DexClass* cls,
const method::ClInitHasNoSideEffectsPredicate& clinit_has_no_side_effects,
const std::unordered_set<DexMethod*>* non_true_virtuals) {
auto res =
m_init_classes.get(cls->get_type(), std::shared_ptr<InitClasses>());
if (res) {
return res.get();
}
InitClasses classes;
const auto* refined_cls = method::clinit_may_have_side_effects(
cls, &clinit_has_no_side_effects, non_true_virtuals);
if (refined_cls == nullptr) {
} else if (refined_cls != cls) {
classes =
*compute(refined_cls, clinit_has_no_side_effects, non_true_virtuals);
} else {
classes.push_back(cls);
auto super_cls = type_class(cls->get_super_class());
if (super_cls) {
const auto super_classes =
compute(super_cls, clinit_has_no_side_effects, non_true_virtuals);
classes.insert(classes.end(), super_classes->begin(),
super_classes->end());
}
}
m_init_classes.update(
cls->get_type(),
[&res, &classes, this](const DexType*,
std::shared_ptr<InitClasses>& value, bool exist) {
if (exist) {
always_assert(classes == *value);
} else {
if (classes.empty()) {
m_trivial_init_classes++;
}
value = std::make_shared<InitClasses>(std::move(classes));
}
res = value;
});
return res.get();
}
InitClassesWithSideEffects::InitClassesWithSideEffects(
const Scope& scope,
bool create_init_class_insns,
const method_override_graph::Graph* method_override_graph)
: m_create_init_class_insns(create_init_class_insns) {
Timer t("InitClassesWithSideEffects");
std::unique_ptr<std::unordered_set<DexMethod*>> non_true_virtuals;
if (method_override_graph) {
non_true_virtuals = std::make_unique<std::unordered_set<DexMethod*>>(
method_override_graph::get_non_true_virtuals(*method_override_graph,
scope));
}
size_t prev_trivial_init_classes;
do {
auto prev_init_classes = std::move(m_init_classes);
prev_trivial_init_classes = m_trivial_init_classes.exchange(0);
method::ClInitHasNoSideEffectsPredicate clinit_has_no_side_effects =
[&](const DexType* type) {
auto it = prev_init_classes.find(type);
if (it != prev_init_classes.end()) {
return it->second->empty();
}
auto cls = type_class(type);
return cls && (cls->is_external() ||
cls->rstate.clinit_has_no_side_effects());
};
ConcurrentSet<DexClass*> added_clinit_has_no_side_effects;
walk::parallel::classes(scope, [&](DexClass* cls) {
if (compute(cls, clinit_has_no_side_effects, non_true_virtuals.get())
->empty() &&
!cls->rstate.clinit_has_no_side_effects()) {
added_clinit_has_no_side_effects.insert(cls);
}
});
for (auto cls : added_clinit_has_no_side_effects) {
cls->rstate.set_clinit_has_no_side_effects();
}
TRACE(ICL, 2,
"InitClassesWithSideEffects: %zu trivial init classes, %zu "
"clinit_has_no_side_effects added",
(size_t)prev_trivial_init_classes,
added_clinit_has_no_side_effects.size());
} while (m_trivial_init_classes > prev_trivial_init_classes);
}
const InitClasses* InitClassesWithSideEffects::get(const DexType* type) const {
auto it = m_init_classes.find(type);
return it == m_init_classes.end() ? &m_empty_init_classes : it->second.get();
}
const DexType* InitClassesWithSideEffects::refine(const DexType* type) const {
auto init_classes = get(type);
return init_classes->empty() ? nullptr : init_classes->front()->get_type();
}
IRInstruction* InitClassesWithSideEffects::create_init_class_insn(
const DexType* type) const {
if (!m_create_init_class_insns) {
return nullptr;
}
type = refine(type);
if (!type) {
return nullptr;
}
return (new IRInstruction(IOPCODE_INIT_CLASS))
->set_type(const_cast<DexType*>(type));
}
} // namespace init_classes