diff options
author | chriseth <chris@ethereum.org> | 2018-04-13 03:01:08 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-04-13 03:01:08 +0800 |
commit | 7054defdd6c202d0943c11cb87ac2748b9bdc62b (patch) | |
tree | 8a69eb34b9089b689d5f467b8cb56b4a74ad04b8 /libsolidity | |
parent | 44416d1ac65b2cfae4bb15d39bc84b1a78211baa (diff) | |
parent | 966367305ad511900bedfd9af08114a0b1307399 (diff) | |
download | dexon-solidity-7054defdd6c202d0943c11cb87ac2748b9bdc62b.tar.gz dexon-solidity-7054defdd6c202d0943c11cb87ac2748b9bdc62b.tar.zst dexon-solidity-7054defdd6c202d0943c11cb87ac2748b9bdc62b.zip |
Merge pull request #3364 from ethereum/revertWithReason
Revert with reason
Diffstat (limited to 'libsolidity')
-rw-r--r-- | libsolidity/analysis/DeclarationContainer.cpp | 8 | ||||
-rw-r--r-- | libsolidity/analysis/GlobalContext.cpp | 2 | ||||
-rw-r--r-- | libsolidity/analysis/NameAndTypeResolver.cpp | 9 | ||||
-rw-r--r-- | libsolidity/analysis/TypeChecker.cpp | 7 | ||||
-rw-r--r-- | libsolidity/ast/AST.cpp | 7 | ||||
-rw-r--r-- | libsolidity/ast/AST.h | 13 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerContext.cpp | 20 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerContext.h | 7 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerUtils.cpp | 15 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerUtils.h | 7 | ||||
-rw-r--r-- | libsolidity/codegen/ContractCompiler.cpp | 2 | ||||
-rw-r--r-- | libsolidity/codegen/ExpressionCompiler.cpp | 37 |
12 files changed, 107 insertions, 27 deletions
diff --git a/libsolidity/analysis/DeclarationContainer.cpp b/libsolidity/analysis/DeclarationContainer.cpp index c7ba78d6..786272e4 100644 --- a/libsolidity/analysis/DeclarationContainer.cpp +++ b/libsolidity/analysis/DeclarationContainer.cpp @@ -45,7 +45,8 @@ Declaration const* DeclarationContainer::conflictingDeclaration( if ( dynamic_cast<FunctionDefinition const*>(&_declaration) || - dynamic_cast<EventDefinition const*>(&_declaration) + dynamic_cast<EventDefinition const*>(&_declaration) || + dynamic_cast<MagicVariableDeclaration const*>(&_declaration) ) { // check that all other declarations with the same name are functions or a public state variable or events. @@ -68,6 +69,11 @@ Declaration const* DeclarationContainer::conflictingDeclaration( !dynamic_cast<EventDefinition const*>(declaration) ) return declaration; + if ( + dynamic_cast<MagicVariableDeclaration const*>(&_declaration) && + !dynamic_cast<MagicVariableDeclaration const*>(declaration) + ) + return declaration; // Or, continue. } } diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp index ef1a59fe..756bb540 100644 --- a/libsolidity/analysis/GlobalContext.cpp +++ b/libsolidity/analysis/GlobalContext.cpp @@ -52,7 +52,9 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{ make_shared<MagicVariableDeclaration>("mulmod", make_shared<FunctionType>(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::MulMod, false, StateMutability::Pure)), make_shared<MagicVariableDeclaration>("now", make_shared<IntegerType>(256)), make_shared<MagicVariableDeclaration>("require", make_shared<FunctionType>(strings{"bool"}, strings{}, FunctionType::Kind::Require, false, StateMutability::Pure)), + make_shared<MagicVariableDeclaration>("require", make_shared<FunctionType>(strings{"bool", "string memory"}, strings{}, FunctionType::Kind::Require, false, StateMutability::Pure)), make_shared<MagicVariableDeclaration>("revert", make_shared<FunctionType>(strings(), strings(), FunctionType::Kind::Revert, false, StateMutability::Pure)), + make_shared<MagicVariableDeclaration>("revert", make_shared<FunctionType>(strings{"string memory"}, strings(), FunctionType::Kind::Revert, false, StateMutability::Pure)), make_shared<MagicVariableDeclaration>("ripemd160", make_shared<FunctionType>(strings(), strings{"bytes20"}, FunctionType::Kind::RIPEMD160, true, StateMutability::Pure)), make_shared<MagicVariableDeclaration>("selfdestruct", make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Kind::Selfdestruct)), make_shared<MagicVariableDeclaration>("sha256", make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Kind::SHA256, true, StateMutability::Pure)), diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 2f675135..0a356f04 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -47,7 +47,9 @@ NameAndTypeResolver::NameAndTypeResolver( if (!m_scopes[nullptr]) m_scopes[nullptr].reset(new DeclarationContainer()); for (Declaration const* declaration: _globals) - m_scopes[nullptr]->registerDeclaration(*declaration); + { + solAssert(m_scopes[nullptr]->registerDeclaration(*declaration), "Unable to register global declaration."); + } } bool NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit, ASTNode const* _currentScope) @@ -202,8 +204,9 @@ vector<Declaration const*> NameAndTypeResolver::cleanedDeclarations( solAssert( dynamic_cast<FunctionDefinition const*>(declaration) || dynamic_cast<EventDefinition const*>(declaration) || - dynamic_cast<VariableDeclaration const*>(declaration), - "Found overloading involving something not a function or a variable." + dynamic_cast<VariableDeclaration const*>(declaration) || + dynamic_cast<MagicVariableDeclaration const*>(declaration), + "Found overloading involving something not a function, event or a (magic) variable." ); FunctionTypePointer functionType { declaration->functionType(false) }; diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index b95fee38..f4df4c94 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -2146,10 +2146,9 @@ bool TypeChecker::visit(Identifier const& _identifier) for (Declaration const* declaration: annotation.overloadedDeclarations) { - TypePointer function = declaration->type(); - solAssert(!!function, "Requested type not present."); - auto const* functionType = dynamic_cast<FunctionType const*>(function.get()); - if (functionType && functionType->canTakeArguments(*annotation.argumentTypes)) + FunctionTypePointer functionType = declaration->functionType(true); + solAssert(!!functionType, "Requested type not present."); + if (functionType->canTakeArguments(*annotation.argumentTypes)) candidates.push_back(declaration); } if (candidates.empty()) diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index d8ad009d..80f5d642 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -297,7 +297,7 @@ ContractDefinition::ContractKind FunctionDefinition::inContractKind() const return contractDef->contractKind(); } -shared_ptr<FunctionType> FunctionDefinition::functionType(bool _internal) const +FunctionTypePointer FunctionDefinition::functionType(bool _internal) const { if (_internal) { @@ -338,6 +338,7 @@ shared_ptr<FunctionType> FunctionDefinition::functionType(bool _internal) const TypePointer FunctionDefinition::type() const { + solAssert(visibility() != Declaration::Visibility::External, ""); return make_shared<FunctionType>(*this); } @@ -379,7 +380,7 @@ TypePointer EventDefinition::type() const return make_shared<FunctionType>(*this); } -std::shared_ptr<FunctionType> EventDefinition::functionType(bool _internal) const +FunctionTypePointer EventDefinition::functionType(bool _internal) const { if (_internal) return make_shared<FunctionType>(*this); @@ -484,7 +485,7 @@ TypePointer VariableDeclaration::type() const return annotation().type; } -shared_ptr<FunctionType> VariableDeclaration::functionType(bool _internal) const +FunctionTypePointer VariableDeclaration::functionType(bool _internal) const { if (_internal) return {}; diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index ae253f0c..a53987bf 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -218,7 +218,7 @@ public: /// @param _internal false indicates external interface is concerned, true indicates internal interface is concerned. /// @returns null when it is not accessible as a function. - virtual std::shared_ptr<FunctionType> functionType(bool /*_internal*/) const { return {}; } + virtual FunctionTypePointer functionType(bool /*_internal*/) const { return {}; } protected: virtual Visibility defaultVisibility() const { return Visibility::Public; } @@ -634,7 +634,7 @@ public: /// @param _internal false indicates external interface is concerned, true indicates internal interface is concerned. /// @returns null when it is not accessible as a function. - virtual std::shared_ptr<FunctionType> functionType(bool /*_internal*/) const override; + virtual FunctionTypePointer functionType(bool /*_internal*/) const override; virtual FunctionDefinitionAnnotation& annotation() const override; @@ -703,7 +703,7 @@ public: /// @param _internal false indicates external interface is concerned, true indicates internal interface is concerned. /// @returns null when it is not accessible as a function. - virtual std::shared_ptr<FunctionType> functionType(bool /*_internal*/) const override; + virtual FunctionTypePointer functionType(bool /*_internal*/) const override; virtual VariableDeclarationAnnotation& annotation() const override; @@ -805,7 +805,7 @@ public: bool isAnonymous() const { return m_anonymous; } virtual TypePointer type() const override; - virtual std::shared_ptr<FunctionType> functionType(bool /*_internal*/) const override; + virtual FunctionTypePointer functionType(bool /*_internal*/) const override; virtual EventDefinitionAnnotation& annotation() const override; @@ -831,6 +831,11 @@ public: solAssert(false, "MagicVariableDeclaration used inside real AST."); } + virtual FunctionTypePointer functionType(bool) const override + { + solAssert(m_type->category() == Type::Category::Function, ""); + return std::dynamic_pointer_cast<FunctionType const>(m_type); + } virtual TypePointer type() const override { return m_type; } private: diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 47333046..a35eea73 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -262,12 +262,20 @@ CompilerContext& CompilerContext::appendRevert() return *this << u256(0) << u256(0) << Instruction::REVERT; } -CompilerContext& CompilerContext::appendConditionalRevert() -{ - *this << Instruction::ISZERO; - eth::AssemblyItem afterTag = appendConditionalJump(); - appendRevert(); - *this << afterTag; +CompilerContext& CompilerContext::appendConditionalRevert(bool _forwardReturnData) +{ + if (_forwardReturnData && m_evmVersion.supportsReturndata()) + appendInlineAssembly(R"({ + if condition { + returndatacopy(0, 0, returndatasize()) + revert(0, returndatasize()) + } + })", {"condition"}); + else + appendInlineAssembly(R"({ + if condition { revert(0, 0) } + })", {"condition"}); + *this << Instruction::POP; return *this; } diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 7b663277..098472f7 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -156,8 +156,11 @@ public: CompilerContext& appendConditionalInvalid(); /// Appends a REVERT(0, 0) call CompilerContext& appendRevert(); - /// Appends a conditional REVERT(0, 0) call - CompilerContext& appendConditionalRevert(); + /// Appends a conditional REVERT-call, either forwarding the RETURNDATA or providing the + /// empty string. Consumes the condition. + /// If the current EVM version does not support RETURNDATA, uses REVERT but does not forward + /// the data. + CompilerContext& appendConditionalRevert(bool _forwardReturnData = false); /// Appends a JUMP to a specific tag CompilerContext& appendJumpTo(eth::AssemblyItem const& _tag) { m_asm->appendJump(_tag); return *this; } /// Appends pushing of a new tag and @returns the new tag. diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 79aef7b0..b4550153 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -78,6 +78,20 @@ void CompilerUtils::toSizeAfterFreeMemoryPointer() m_context << Instruction::SWAP1; } +void CompilerUtils::revertWithStringData(Type const& _argumentType) +{ + solAssert(_argumentType.isImplicitlyConvertibleTo(*Type::fromElementaryTypeName("string memory")), ""); + fetchFreeMemoryPointer(); + m_context << (u256(FixedHash<4>::Arith(FixedHash<4>(dev::keccak256("Error(string)")))) << (256 - 32)); + m_context << Instruction::DUP2 << Instruction::MSTORE; + m_context << u256(4) << Instruction::ADD; + // Stack: <string data> <mem pos of encoding start> + abiEncode({_argumentType.shared_from_this()}, {make_shared<ArrayType>(DataLocation::Memory, true)}); + toSizeAfterFreeMemoryPointer(); + m_context << Instruction::REVERT; + m_context.adjustStackOffset(_argumentType.sizeOnStack()); +} + unsigned CompilerUtils::loadFromMemory( unsigned _offset, Type const& _type, @@ -691,6 +705,7 @@ void CompilerUtils::convertType( solAssert(enumType.numberOfMembers() > 0, "empty enum should have caused a parser error."); m_context << u256(enumType.numberOfMembers() - 1) << Instruction::DUP2 << Instruction::GT; if (_asPartOfArgumentDecoding) + // TODO: error message? m_context.appendConditionalRevert(); else m_context.appendConditionalInvalid(); diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index a32c5c6e..476a7559 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -54,6 +54,13 @@ public: /// Stack post: <size> <mem_start> void toSizeAfterFreeMemoryPointer(); + /// Appends code that performs a revert, providing the given string data. + /// Will also append an error signature corresponding to Error(string). + /// @param _argumentType the type of the string argument, will be converted to memory string. + /// Stack pre: string data + /// Stack post: + void revertWithStringData(Type const& _argumentType); + /// Loads data from memory to the stack. /// @param _offset offset in memory (or calldata) /// @param _type data type to load diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 5cb37103..0889ac7c 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -128,6 +128,7 @@ void ContractCompiler::appendCallValueCheck() { // Throw if function is not payable but call contained ether. m_context << Instruction::CALLVALUE; + // TODO: error message? m_context.appendConditionalRevert(); } @@ -327,6 +328,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac m_context << Instruction::STOP; } else + // TODO: error message here? m_context.appendRevert(); for (auto const& it: interfaceFunctions) diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 051db536..ed5af42e 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -610,7 +610,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << Instruction::CREATE; // Check if zero (out of stack or not enough balance). m_context << Instruction::DUP1 << Instruction::ISZERO; - m_context.appendConditionalRevert(); + // TODO: Can we bubble up here? There might be different reasons for failure, I think. + m_context.appendConditionalRevert(true); if (function.valueSet()) m_context << swapInstruction(1) << Instruction::POP; break; @@ -672,8 +673,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) if (function.kind() == FunctionType::Kind::Transfer) { // Check if zero (out of stack or not enough balance). + // TODO: bubble up here, but might also be different error. m_context << Instruction::ISZERO; - m_context.appendConditionalRevert(); + m_context.appendConditionalRevert(true); } break; case FunctionType::Kind::Selfdestruct: @@ -682,8 +684,19 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << Instruction::SELFDESTRUCT; break; case FunctionType::Kind::Revert: - m_context.appendRevert(); + { + if (!arguments.empty()) + { + // function-sel(Error(string)) + encoding + solAssert(arguments.size() == 1, ""); + solAssert(function.parameterTypes().size() == 1, ""); + arguments.front()->accept(*this); + utils().revertWithStringData(*arguments.front()->annotation().type); + } + else + m_context.appendRevert(); break; + } case FunctionType::Kind::SHA3: { TypePointers argumentTypes; @@ -902,16 +915,31 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) { arguments.front()->accept(*this); utils().convertType(*arguments.front()->annotation().type, *function.parameterTypes().front(), false); + if (arguments.size() > 1) + { + // Users probably expect the second argument to be evaluated + // even if the condition is false, as would be the case for an actual + // function call. + solAssert(arguments.size() == 2, ""); + solAssert(function.kind() == FunctionType::Kind::Require, ""); + arguments.at(1)->accept(*this); + utils().moveIntoStack(1, arguments.at(1)->annotation().type->sizeOnStack()); + } + // Stack: <error string (unconverted)> <condition> // jump if condition was met m_context << Instruction::ISZERO << Instruction::ISZERO; auto success = m_context.appendConditionalJump(); if (function.kind() == FunctionType::Kind::Assert) // condition was not met, flag an error m_context.appendInvalid(); + else if (arguments.size() > 1) + utils().revertWithStringData(*arguments.at(1)->annotation().type); else m_context.appendRevert(); // the success branch m_context << success; + if (arguments.size() > 1) + utils().popStackElement(*arguments.at(1)->annotation().type); break; } case FunctionType::Kind::ABIEncode: @@ -1882,6 +1910,7 @@ void ExpressionCompiler::appendExternalFunctionCall( if (funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::CallCode || funKind == FunctionType::Kind::DelegateCall) { m_context << Instruction::DUP1 << Instruction::EXTCODESIZE << Instruction::ISZERO; + // TODO: error message? m_context.appendConditionalRevert(); existenceChecked = true; } @@ -1924,7 +1953,7 @@ void ExpressionCompiler::appendExternalFunctionCall( { //Propagate error condition (if CALL pushes 0 on stack). m_context << Instruction::ISZERO; - m_context.appendConditionalRevert(); + m_context.appendConditionalRevert(true); } utils().popStackSlots(remainsSize); |