source/ClassHierarchies.cpp (97 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 <Show.h>
#include <Walkers.h>
#include <mariana-trench/Assert.h>
#include <mariana-trench/ClassHierarchies.h>
#include <mariana-trench/JsonValidation.h>
#include <mariana-trench/Log.h>
namespace marianatrench {
namespace {
class Graph {
public:
void add_edge(const DexType* child, const DexType* parent) {
extends_.update(
parent,
[=](const DexType* /* parent */,
std::unordered_set<const DexType*>& children,
bool /* exists */) { children.insert(child); });
}
std::unordered_set<const DexType*> extends(const DexType* parent) const {
std::unordered_set<const DexType*> result;
std::vector<const DexType*> worklist;
worklist.push_back(parent);
while (!worklist.empty()) {
const DexType* klass = worklist.back();
worklist.pop_back();
auto children = extends_.find(klass);
if (children != extends_.end()) {
for (const auto* child : children->second) {
if (result.insert(child).second) {
worklist.push_back(child);
}
}
}
}
return result;
}
private:
// Map a class to all classes that *directly* extend it.
ConcurrentMap<const DexType*, std::unordered_set<const DexType*>> extends_;
};
} // namespace
ClassHierarchies::ClassHierarchies(
const Options& options,
const DexStoresVector& stores) {
Graph graph;
// Compute the class hierarchy graph.
for (const auto& scope : DexStoreClassesIterator(stores)) {
walk::parallel::classes(scope, [&](const DexClass* klass) {
const DexType* super = klass->get_super_class();
if (super != type::java_lang_Object()) {
graph.add_edge(/* child */ klass->get_type(), /* parent */ super);
}
for (DexType* interface : *klass->get_interfaces()) {
graph.add_edge(/* child */ klass->get_type(), /* parent */ interface);
}
});
}
// Record the results.
for (const auto& scope : DexStoreClassesIterator(stores)) {
walk::parallel::classes(scope, [&](const DexClass* klass) {
auto extends = graph.extends(klass->get_type());
if (!extends.empty()) {
extends_.emplace(
klass->get_type(),
std::make_unique<std::unordered_set<const DexType*>>(
std::move(extends)));
}
});
}
if (options.dump_class_hierarchies()) {
auto class_hierarchies_path = options.class_hierarchies_output_path();
LOG(1,
"Writing class hierarchies to `{}`",
class_hierarchies_path.native());
JsonValidation::write_json_file(class_hierarchies_path, to_json());
}
}
const std::unordered_set<const DexType*>& ClassHierarchies::extends(
const DexType* klass) const {
mt_assert(klass != type::java_lang_Object());
const auto* extends = extends_.get(klass, /* default */ nullptr);
if (extends != nullptr) {
return *extends;
} else {
return empty_type_set_;
}
}
Json::Value ClassHierarchies::to_json() const {
auto extends_value = Json::Value(Json::objectValue);
for (auto [klass, extends] : extends_) {
auto ClassHierarchies_value = Json::Value(Json::arrayValue);
for (const auto* extend : *extends) {
ClassHierarchies_value.append(Json::Value(show(extend)));
}
extends_value[show(klass)] = ClassHierarchies_value;
}
auto value = Json::Value(Json::objectValue);
value["extends"] = extends_value;
return value;
}
} // namespace marianatrench