opt/app_module_usage/AppModuleUsage.h (54 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 <cstddef>
#include <map>
#include <set>
#include <string>
#include "ConcurrentContainers.h"
#include "DexClass.h"
#include "Pass.h"
#include "ReflectionAnalysis.h"
namespace app_module_usage {
using StoresReferenced =
std::unordered_map<DexStore*, bool /* used_only_reflectively */>;
using MethodStoresReferenced = ConcurrentMap<DexMethod*, StoresReferenced>;
using Violations = std::map<std::string /* entrypoint */,
std::set<std::string /* module name */>>;
} // namespace app_module_usage
/**
* `AppModuleUsagePass` generates a report of violations of unannotated app
* module references. The `@UsesAppModule` annotation should be present and
* contain the name of the module at the entrypoint of an app module, or there
* is a violation. By default the pass crashes on an occurence of a violation.
*
* When configured to continue with `crash_with_violations` set to false a
* report of all violations is output at
* "redex-app-module-annotation-violations.csv". Each line of the violation
* report is the full descriptor of the unannotated entrypoint to a module,
* followed by the name of the module.
*
* By enabling `output_module_use` the pass also generates
* "redex-app-module-usage.csv" mapping methods to all the app modules used by
* each method, and "redex-app-module-count.csv" mapping app modules to the
* number of places it's referenced.
*/
class AppModuleUsagePass : public Pass {
public:
AppModuleUsagePass() : Pass("AppModuleUsagePass") {}
void bind_config() override {
bind("uses_app_module_annotation_descriptor",
DexType::get_type("Lcom/facebook/redex/annotations/UsesAppModule;"),
m_uses_app_module_annotation);
bind("preexisting_violations_filepath", "",
m_preexisting_violations_filepath);
bind("output_module_use", true, m_output_module_use);
bind("crash_with_violations", false, m_crash_with_violations);
}
// Entrypoint for the AppModuleUsagePass pass
void run_pass(DexStoresVector&, ConfigFiles&, PassManager&) override;
// Returns the names of the modules annotated as used by the given entrypoint
template <typename T>
static std::unordered_set<std::string> get_modules_used(
T* entrypoint, DexType* annotation_type);
private:
void load_preexisting_violations(DexStoresVector&);
app_module_usage::MethodStoresReferenced analyze_method_xstore_references(
const Scope& scope);
ConcurrentMap<DexField*, DexStore*> analyze_field_xstore_references(
const Scope& scope);
// Returns number of violations.
unsigned gather_violations(
const app_module_usage::MethodStoresReferenced& method_store_refs,
const ConcurrentMap<DexField*, DexStore*>& field_store_refs,
app_module_usage::Violations& violations) const;
// returns true if the given entrypoint name is allowed to use the given store
bool access_excused_due_to_preexisting(const std::string& entrypoint_name,
DexStore* store_used) const;
bool access_granted_by_annotation(DexMethod* method, DexStore* target) const;
bool access_granted_by_annotation(DexField* field, DexStore* target) const;
bool access_granted_by_annotation(DexClass* cls, DexStore* target) const;
// Map of violations from entrypoint names to the names of stores used
// by the entrypoint
std::unordered_map<std::string, std::unordered_set<DexStore*>>
m_preexisting_violations;
// To quickly look up wich DexStore ("module") a DexType is from
ConcurrentMap<DexType*, DexStore*> m_type_store_map;
bool m_output_module_use;
bool m_crash_with_violations;
DexType* m_uses_app_module_annotation;
std::string m_preexisting_violations_filepath;
};