diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index abe47e10e..ceb3fbdd0 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -207,6 +207,7 @@ bool SemanticInformation::invalidInPureFunctions(Instruction _instruction) case Instruction::NUMBER: case Instruction::DIFFICULTY: case Instruction::GASLIMIT: + case Instruction::STATICCALL: case Instruction::SLOAD: return true; default: diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index b4b7f3722..60b07297a 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -17,10 +17,86 @@ #include +#include + +#include + +#include + using namespace std; using namespace dev; using namespace dev::solidity; + +class AssemblyViewPureChecker: public boost::static_visitor +{ +public: + explicit AssemblyViewPureChecker(std::function _reportMutability): + m_reportMutability(_reportMutability) {} + + void operator()(assembly::Label const&) { } + void operator()(assembly::Instruction const& _instruction) + { + if (eth::SemanticInformation::invalidInViewFunctions(_instruction.instruction)) + m_reportMutability(StateMutability::NonPayable, _instruction.location); + else if (eth::SemanticInformation::invalidInPureFunctions(_instruction.instruction)) + m_reportMutability(StateMutability::View, _instruction.location); + } + void operator()(assembly::Literal const&) {} + void operator()(assembly::Identifier const&) {} + void operator()(assembly::FunctionalInstruction const& _instr) + { + (*this)(_instr.instruction); + for (auto const& arg: _instr.arguments) + boost::apply_visitor(*this, arg); + } + void operator()(assembly::StackAssignment const&) {} + void operator()(assembly::Assignment const& _assignment) + { + boost::apply_visitor(*this, *_assignment.value); + } + void operator()(assembly::VariableDeclaration const& _varDecl) + { + if (_varDecl.value) + boost::apply_visitor(*this, *_varDecl.value); + } + void operator()(assembly::FunctionDefinition const& _funDef) + { + (*this)(_funDef.body); + } + void operator()(assembly::FunctionCall const& _funCall) + { + for (auto const& arg: _funCall.arguments) + boost::apply_visitor(*this, arg); + } + void operator()(assembly::Switch const& _switch) + { + boost::apply_visitor(*this, *_switch.expression); + for (auto const& _case: _switch.cases) + { + if (_case.value) + (*this)(*_case.value); + (*this)(_case.body); + } + } + void operator()(assembly::ForLoop const& _for) + { + (*this)(_for.pre); + boost::apply_visitor(*this, *_for.condition); + (*this)(_for.body); + (*this)(_for.post); + } + void operator()(assembly::Block const& _block) + { + for (auto const& s: _block.statements) + boost::apply_visitor(*this, s); + } + +private: + std::function m_reportMutability; +}; + + bool ViewPureChecker::check() { vector contracts; @@ -122,16 +198,17 @@ void ViewPureChecker::endVisit(Identifier const& _identifier) } } - reportMutability(mutability, _identifier); + reportMutability(mutability, _identifier.location()); } void ViewPureChecker::endVisit(InlineAssembly const& _inlineAssembly) { - // @TOOD we can and should analyze it further. - reportMutability(StateMutability::NonPayable, _inlineAssembly); + AssemblyViewPureChecker{ + [=](StateMutability _mut, SourceLocation const& _loc) { reportMutability(_mut, _loc); } + }(_inlineAssembly.operations()); } -void ViewPureChecker::reportMutability(StateMutability _mutability, ASTNode const& _node) +void ViewPureChecker::reportMutability(StateMutability _mutability, SourceLocation const& _location) { if (m_currentFunction && m_currentFunction->stateMutability() < _mutability) { @@ -151,11 +228,11 @@ void ViewPureChecker::reportMutability(StateMutability _mutability, ASTNode cons if (m_currentFunction->stateMutability() == StateMutability::View) // Change this to error with 0.5.0 - m_errorReporter.warning(_node.location(), text); + m_errorReporter.warning(_location, text); else if (m_currentFunction->stateMutability() == StateMutability::Pure) { m_errors = true; - m_errorReporter.typeError(_node.location(), text); + m_errorReporter.typeError(_location, text); } else solAssert(false, ""); @@ -173,7 +250,7 @@ void ViewPureChecker::endVisit(FunctionCall const& _functionCall) // We only require "nonpayable" to call a payble function. if (mut == StateMutability::Payable) mut = StateMutability::NonPayable; - reportMutability(mut, _functionCall); + reportMutability(mut, _functionCall.location()); } void ViewPureChecker::endVisit(MemberAccess const& _memberAccess) @@ -210,7 +287,7 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess) default: break; } - reportMutability(mutability, _memberAccess); + reportMutability(mutability, _memberAccess.location()); } void ViewPureChecker::endVisit(IndexAccess const& _indexAccess) @@ -219,7 +296,7 @@ void ViewPureChecker::endVisit(IndexAccess const& _indexAccess) bool writes = _indexAccess.annotation().lValueRequested; if (_indexAccess.baseExpression().annotation().type->dataStoredIn(DataLocation::Storage)) - reportMutability(writes ? StateMutability::NonPayable : StateMutability::View, _indexAccess); + reportMutability(writes ? StateMutability::NonPayable : StateMutability::View, _indexAccess.location()); } void ViewPureChecker::endVisit(ModifierInvocation const& _modifier) @@ -229,6 +306,6 @@ void ViewPureChecker::endVisit(ModifierInvocation const& _modifier) solAssert(mod, ""); solAssert(m_inferredMutability.count(mod), ""); - reportMutability(m_inferredMutability.at(mod), _modifier); + reportMutability(m_inferredMutability.at(mod), _modifier.location()); } diff --git a/libsolidity/analysis/ViewPureChecker.h b/libsolidity/analysis/ViewPureChecker.h index 6aedfa369..ae3035336 100644 --- a/libsolidity/analysis/ViewPureChecker.h +++ b/libsolidity/analysis/ViewPureChecker.h @@ -64,7 +64,7 @@ private: /// Called when an element of mutability @a _mutability is encountered. /// Creates appropriate warnings and errors and sets @a m_currentBestMutability. - void reportMutability(StateMutability _mutability, ASTNode const& _node); + void reportMutability(StateMutability _mutability, SourceLocation const& _location); std::vector> const& m_ast; ErrorReporter& m_errorReporter; diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 800f102ba..6886fcd0b 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -5234,7 +5234,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_storage_variable_access_out_of_functions) char const* text = R"( contract test { uint a; - function f() { + function f() pure { assembly { function g() -> x { x := a_slot } } @@ -5921,7 +5921,7 @@ BOOST_AUTO_TEST_CASE(no_unused_inline_asm) { char const* text = R"( contract C { - function f() { + function f() pure { uint a; assembly { a := 1 diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp index 7099ffd7a..fabd1bee0 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -316,6 +316,68 @@ BOOST_AUTO_TEST_CASE(function_types) CHECK_SUCCESS_NO_WARNINGS(text); } +BOOST_AUTO_TEST_CASE(creation) +{ + string text = R"( + contract D {} + contract C { + function f() { new D(); } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(assembly) +{ + string text = R"( + contract C { + struct S { uint x; } + S s; + function e() pure { + assembly { mstore(keccak256(0, 20), mul(s_slot, 2)) } + } + function f() pure { + uint x; + assembly { x := 7 } + } + function g() view { + assembly { for {} 1 { pop(sload(0)) } { } } + } + function h() view { + assembly { function g() { pop(blockhash(20)) } } + } + function j() { + assembly { pop(call(0, 1, 2, 3, 4, 5, 6)) } + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(assembly_staticcall) +{ + string text = R"( + contract C { + function i() view { + assembly { pop(staticcall(0, 1, 2, 3, 4, 5)) } + } + } + )"; + CHECK_WARNING(text, "only available after the Metropolis"); +} + +BOOST_AUTO_TEST_CASE(assembly_jump) +{ + string text = R"( + contract C { + function k() { + assembly { jump(2) } + } + } + )"; + CHECK_WARNING(text, "low-level EVM features"); +} + BOOST_AUTO_TEST_SUITE_END() }