diff options
author | chriseth <chris@ethereum.org> | 2017-08-29 01:48:34 +0800 |
---|---|---|
committer | chriseth <chris@ethereum.org> | 2017-09-06 19:50:49 +0800 |
commit | 5470da4d9adc8ef07aa1c2a758b7062be843cca4 (patch) | |
tree | 824e0012f236a9d959c0eb46e5a1fdb02ece43ec /libsolidity/analysis/ViewPureChecker.cpp | |
parent | 16526ad554256bde3e185c99c1651b0f47725165 (diff) | |
download | dexon-solidity-5470da4d9adc8ef07aa1c2a758b7062be843cca4.tar.gz dexon-solidity-5470da4d9adc8ef07aa1c2a758b7062be843cca4.tar.zst dexon-solidity-5470da4d9adc8ef07aa1c2a758b7062be843cca4.zip |
View-pure checker.
Diffstat (limited to 'libsolidity/analysis/ViewPureChecker.cpp')
-rw-r--r-- | libsolidity/analysis/ViewPureChecker.cpp | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp new file mode 100644 index 00000000..0e2cfacf --- /dev/null +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -0,0 +1,227 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ + +#include <libsolidity/analysis/ViewPureChecker.h> + +using namespace std; +using namespace dev; +using namespace dev::solidity; + +bool ViewPureChecker::check() +{ + vector<ContractDefinition const*> contracts; + + for (auto const& node: m_ast) + { + SourceUnit const* source = dynamic_cast<SourceUnit const*>(node.get()); + solAssert(source, ""); + for (auto const& topLevelNode: source->nodes()) + { + ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(topLevelNode.get()); + if (contract) + contracts.push_back(contract); + } + } + + // Check modifiers first to infer their state mutability. + for (auto const* contract: contracts) + for (ModifierDefinition const* mod: contract->functionModifiers()) + mod->accept(*this); + + for (auto const* contract: contracts) + contract->accept(*this); + + return !m_errors; +} + + + +bool ViewPureChecker::visit(FunctionDefinition const& _funDef) +{ + solAssert(!m_currentFunction, ""); + m_currentFunction = &_funDef; + m_currentBestMutability = StateMutability::Pure; + return true; +} + +void ViewPureChecker::endVisit(FunctionDefinition const& _funDef) +{ + solAssert(m_currentFunction == &_funDef, ""); + if ( + m_currentBestMutability < _funDef.stateMutability() && + _funDef.stateMutability() != StateMutability::Payable && + _funDef.isImplemented() && + !_funDef.isConstructor() && + !_funDef.isFallback() && + !_funDef.isConstructor() && + !_funDef.annotation().superFunction + ) + m_errorReporter.warning( + _funDef.location(), + "Function state mutability can be restricted to " + stateMutabilityToString(m_currentBestMutability) + ); + m_currentFunction = nullptr; +} + +bool ViewPureChecker::visit(ModifierDefinition const&) +{ + solAssert(m_currentFunction == nullptr, ""); + m_currentBestMutability = StateMutability::Pure; + return true; +} + +void ViewPureChecker::endVisit(ModifierDefinition const& _modifierDef) +{ + solAssert(m_currentFunction == nullptr, ""); + m_inferredMutability[&_modifierDef] = m_currentBestMutability; +} + +void ViewPureChecker::endVisit(Identifier const& _identifier) +{ + Declaration const* declaration = _identifier.annotation().referencedDeclaration; + solAssert(declaration, ""); + + StateMutability mutability = StateMutability::Pure; + + bool writes = _identifier.annotation().lValueRequested; + if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(declaration)) + { + if (varDecl->isStateVariable()) + mutability = writes ? StateMutability::NonPayable : StateMutability::View; + } + else if (MagicVariableDeclaration const* magicVar = dynamic_cast<MagicVariableDeclaration const*>(declaration)) + { + switch (magicVar->type()->category()) + { + case Type::Category::Contract: + solAssert(_identifier.name() == "this" || _identifier.name() == "super", ""); + if (!dynamic_cast<ContractType const&>(*magicVar->type()).isSuper()) + // reads the address + mutability = StateMutability::View; + break; + case Type::Category::Integer: + solAssert(_identifier.name() == "now", ""); + mutability = StateMutability::View; + break; + default: + break; + } + } + + reportMutability(mutability, _identifier); +} + +void ViewPureChecker::endVisit(InlineAssembly const& _inlineAssembly) +{ + // @TOOD we can and should analyze it further. + reportMutability(StateMutability::NonPayable, _inlineAssembly); +} + +void ViewPureChecker::reportMutability(StateMutability _mutability, const ASTNode& _node) +{ + if (m_currentFunction && m_currentFunction->stateMutability() < _mutability) + { + m_errors = true; + if (_mutability == StateMutability::View) + m_errorReporter.typeError( + _node.location(), + "Function declared as pure, but this expression reads from the environment or state and thus " + "requires \"view\"." + ); + else if (_mutability == StateMutability::NonPayable) + m_errorReporter.typeError( + _node.location(), + "Function declared as " + + stateMutabilityToString(m_currentFunction->stateMutability()) + + ", but this expression modifies the state and thus " + "requires non-payable (the default) or payable." + ); + else + solAssert(false, ""); + } + if (_mutability >= m_currentBestMutability) + m_currentBestMutability = _mutability; +} + +void ViewPureChecker::endVisit(FunctionCall const& _functionCall) +{ + if (_functionCall.annotation().kind != FunctionCallKind::FunctionCall) + return; + + StateMutability mut = dynamic_cast<FunctionType const&>(*_functionCall.expression().annotation().type).stateMutability(); + // We only require "nonpayable" to call a payble function. + if (mut == StateMutability::Payable) + mut = StateMutability::NonPayable; + reportMutability(mut, _functionCall); +} + +void ViewPureChecker::endVisit(MemberAccess const& _memberAccess) +{ + StateMutability mutability = StateMutability::Pure; + bool writes = _memberAccess.annotation().lValueRequested; + + ASTString const& member = _memberAccess.memberName(); + switch (_memberAccess.expression().annotation().type->category()) + { + case Type::Category::Contract: + case Type::Category::Integer: + if (member == "balance" && !_memberAccess.annotation().referencedDeclaration) + mutability = StateMutability::View; + break; + case Type::Category::Magic: + // we can ignore the kind of magic and only look at the name of the member + if (member != "data" && member != "sig" && member != "blockhash") + mutability = StateMutability::View; + break; + case Type::Category::Struct: + { + if (_memberAccess.expression().annotation().type->dataStoredIn(DataLocation::Storage)) + mutability = writes ? StateMutability::NonPayable : StateMutability::View; + break; + } + case Type::Category::Array: + { + auto const& type = dynamic_cast<ArrayType const&>(*_memberAccess.expression().annotation().type); + if (member == "length" && type.isDynamicallySized() && type.dataStoredIn(DataLocation::Storage)) + mutability = writes ? StateMutability::NonPayable : StateMutability::View; + break; + } + default: + break; + } + reportMutability(mutability, _memberAccess); +} + +void ViewPureChecker::endVisit(IndexAccess const& _indexAccess) +{ + solAssert(_indexAccess.indexExpression(), ""); + + bool writes = _indexAccess.annotation().lValueRequested; + if (_indexAccess.baseExpression().annotation().type->dataStoredIn(DataLocation::Storage)) + reportMutability(writes ? StateMutability::NonPayable : StateMutability::View, _indexAccess); +} + +void ViewPureChecker::endVisit(ModifierInvocation const& _modifier) +{ + solAssert(_modifier.name(), ""); + ModifierDefinition const* mod = dynamic_cast<decltype(mod)>(_modifier.name()->annotation().referencedDeclaration); + solAssert(mod, ""); + solAssert(m_inferredMutability.count(mod), ""); + + reportMutability(m_inferredMutability.at(mod), _modifier); +} + |