/* This file is part of solidity. solidity is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. solidity is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with solidity. If not, see . */ // SPDX-License-Identifier: GPL-3.0 /** * Specific AST walkers that collect semantical facts. */ #pragma once #include #include #include #include #include namespace solidity::yul { struct Dialect; /** * Specific AST walker that determines side-effect free-ness and movability of code. * Enters into function definitions. */ class SideEffectsCollector: public ASTWalker { public: explicit SideEffectsCollector( Dialect const& _dialect, std::map const* _functionSideEffects = nullptr ): m_dialect(_dialect), m_functionSideEffects(_functionSideEffects) {} SideEffectsCollector( Dialect const& _dialect, Expression const& _expression, std::map const* _functionSideEffects = nullptr ); SideEffectsCollector(Dialect const& _dialect, Statement const& _statement); SideEffectsCollector( Dialect const& _dialect, Block const& _ast, std::map const* _functionSideEffects = nullptr ); using ASTWalker::operator(); void operator()(FunctionCall const& _functionCall) override; bool movable() const { return m_sideEffects.movable; } bool sideEffectFree(bool _allowMSizeModification = false) const { if (_allowMSizeModification) return sideEffectFreeIfNoMSize(); else return m_sideEffects.sideEffectFree; } bool sideEffectFreeIfNoMSize() const { return m_sideEffects.sideEffectFreeIfNoMSize; } bool invalidatesStorage() const { return m_sideEffects.invalidatesStorage; } bool invalidatesMemory() const { return m_sideEffects.invalidatesMemory; } private: Dialect const& m_dialect; std::map const* m_functionSideEffects = nullptr; SideEffects m_sideEffects; }; /** * This class can be used to determine the side-effects of user-defined functions. * * It is given a dialect and a mapping that represents the direct calls from user-defined * functions to other user-defined functions and built-in functions. */ class SideEffectsPropagator { public: static std::map sideEffects( Dialect const& _dialect, CallGraph const& _directCallGraph ); }; /** * Class that can be used to find out if certain code contains the MSize instruction. * * Note that this is a purely syntactic property meaning that even if this is false, * the code can still contain calls to functions that contain the msize instruction. * * The only safe way to determine this is by passing the full AST. */ class MSizeFinder: public ASTWalker { public: static bool containsMSize(Dialect const& _dialect, Block const& _ast); using ASTWalker::operator(); void operator()(FunctionCall const& _funCall) override; private: MSizeFinder(Dialect const& _dialect): m_dialect(_dialect) {} Dialect const& m_dialect; bool m_msizeFound = false; }; /** * Class that can be used to find out if the given function contains the ``leave`` statement. * * Returns true even in the case where the function definition contains another function definition * that contains the leave statement. */ class LeaveFinder: public ASTWalker { public: static bool containsLeave(FunctionDefinition const& _fun) { LeaveFinder f; f(_fun); return f.m_leaveFound; } using ASTWalker::operator(); void operator()(Leave const&) override { m_leaveFound = true; } private: LeaveFinder() = default; bool m_leaveFound = false; }; /** * Specific AST walker that determines whether an expression is movable * and collects the referenced variables. * Can only be used on expressions. */ class MovableChecker: public SideEffectsCollector { public: explicit MovableChecker( Dialect const& _dialect, std::map const* _functionSideEffects = nullptr ): SideEffectsCollector(_dialect, _functionSideEffects) {} MovableChecker(Dialect const& _dialect, Expression const& _expression); void operator()(Identifier const& _identifier) override; /// Disallow visiting anything apart from Expressions (this throws). void visit(Statement const&) override; using ASTWalker::visit; std::set const& referencedVariables() const { return m_variableReferences; } private: /// Which variables the current expression references. std::set m_variableReferences; }; /** * Helper class to find "irregular" control flow. * This includes termination, break and continue. */ class TerminationFinder { public: // TODO check all uses of TerminationFinder! enum class ControlFlow { FlowOut, Break, Continue, Terminate, Leave }; TerminationFinder(Dialect const& _dialect): m_dialect(_dialect) {} /// @returns the index of the first statement in the provided sequence /// that is an unconditional ``break``, ``continue``, ``leave`` or a /// call to a terminating builtin function. /// If control flow can continue at the end of the list, /// returns `FlowOut` and ``size_t(-1)``. /// The function might return ``FlowOut`` even though control /// flow cannot actually continue. std::pair firstUnconditionalControlFlowChange( std::vector const& _statements ); /// @returns the control flow type of the given statement. /// This function could return FlowOut even if control flow never continues. ControlFlow controlFlowKind(Statement const& _statement); /// @returns true if the expression statement is a direct /// call to a builtin terminating function like /// ``stop``, ``revert`` or ``return``. bool isTerminatingBuiltin(ExpressionStatement const& _exprStmnt); private: Dialect const& m_dialect; }; }