service/init-classes/InitClassBackwardAnalysis.h (45 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. */ #pragma once #include <unordered_set> #include <utility> #include "BaseIRAnalyzer.h" #include "ConstantAbstractDomain.h" #include "DexUtil.h" #include "MonotonicFixpointIterator.h" namespace init_classes { using LastInitClassDomain = sparta::ConstantAbstractDomain<const DexType*>; class InitClassBackwardFixpointIterator final : public ir_analyzer::BaseBackwardsIRAnalyzer<LastInitClassDomain> { private: const InitClassesWithSideEffects& m_init_classes_with_side_effects; public: explicit InitClassBackwardFixpointIterator( const InitClassesWithSideEffects& init_classes_with_side_effects, const cfg::ControlFlowGraph& cfg) : ir_analyzer::BaseBackwardsIRAnalyzer<LastInitClassDomain>(cfg), m_init_classes_with_side_effects(init_classes_with_side_effects) {} void analyze_instruction(IRInstruction* insn, LastInitClassDomain* current_state) const override { auto init_class = m_init_classes_with_side_effects.refine( get_init_class_type_demand(insn)); // When an instruction... // 1) has an init-class type demand, or // 2) is an invoke that can run arbitrary code that can trigger other static // initializers, // then we need to overwrite the current state with the // current init class type demand. if (init_class || opcode::is_an_invoke(insn->opcode())) { *current_state = init_class ? LastInitClassDomain(init_class) : LastInitClassDomain::top(); } } LastInitClassDomain analyze_edge( const EdgeId& edge, const LastInitClassDomain& exit_state_at_source) const override { auto env = exit_state_at_source; if (edge->type() != cfg::EDGE_THROW) { return env; } auto last_insn_it = edge->src()->get_last_insn(); always_assert(last_insn_it != edge->src()->end()); auto insn = last_insn_it->insn; if (opcode::is_init_class(insn->opcode())) { // We have a throw-edge from an init-class instruction. We'll pretend that // this didn't happen, as when joining with normal control-flow it would // destroy the knowledge about the actually following init-class domain. return LastInitClassDomain::bottom(); } return env; } }; } // namespace init_classes