diff options
author | chriseth <chris@ethereum.org> | 2016-09-06 18:25:19 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-09-06 18:25:19 +0800 |
commit | f687635e4743a1e12d01fb082a4f8a76a759ab48 (patch) | |
tree | 7e3b917cfcc5ebf44c6770c6d742da0fa9277a12 /libsolidity | |
parent | 171c74843bb231c4663ec0fe7f4252ac03266f53 (diff) | |
parent | dff9633084ebc241d8268c5dbd35a0c5307fd6fc (diff) | |
download | dexon-solidity-f687635e4743a1e12d01fb082a4f8a76a759ab48.tar.gz dexon-solidity-f687635e4743a1e12d01fb082a4f8a76a759ab48.tar.zst dexon-solidity-f687635e4743a1e12d01fb082a4f8a76a759ab48.zip |
Merge pull request #665 from axic/feature/accept-ether
BREAKING: Add payable modifier
Diffstat (limited to 'libsolidity')
-rw-r--r-- | libsolidity/analysis/TypeChecker.cpp | 28 | ||||
-rw-r--r-- | libsolidity/ast/AST.h | 4 | ||||
-rw-r--r-- | libsolidity/ast/Types.cpp | 92 | ||||
-rw-r--r-- | libsolidity/ast/Types.h | 26 | ||||
-rw-r--r-- | libsolidity/codegen/ContractCompiler.cpp | 14 | ||||
-rw-r--r-- | libsolidity/codegen/ExpressionCompiler.cpp | 2 | ||||
-rw-r--r-- | libsolidity/interface/InterfaceHandler.cpp | 2 | ||||
-rw-r--r-- | libsolidity/parsing/Parser.cpp | 7 | ||||
-rw-r--r-- | libsolidity/parsing/Token.h | 2 |
9 files changed, 133 insertions, 44 deletions
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index bc03da01..06e26a76 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -272,6 +272,7 @@ void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contr if ( overriding->visibility() != function->visibility() || overriding->isDeclaredConst() != function->isDeclaredConst() || + overriding->isPayable() != function->isPayable() || overridingType != functionType ) typeError(overriding->location(), "Override changes extended function signature."); @@ -348,7 +349,7 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) typeError(_inheritance.location(), "Libraries cannot be inherited from."); auto const& arguments = _inheritance.arguments(); - TypePointers parameterTypes = ContractType(*base).constructorType()->parameterTypes(); + TypePointers parameterTypes = ContractType(*base).newExpressionType()->parameterTypes(); if (!arguments.empty() && parameterTypes.size() != arguments.size()) { typeError( @@ -416,6 +417,15 @@ bool TypeChecker::visit(StructDefinition const& _struct) bool TypeChecker::visit(FunctionDefinition const& _function) { bool isLibraryFunction = dynamic_cast<ContractDefinition const&>(*_function.scope()).isLibrary(); + if (_function.isPayable()) + { + if (isLibraryFunction) + typeError(_function.location(), "Library functions cannot be payable."); + if (!_function.isConstructor() && !_function.name().empty() && !_function.isPartOfExternalInterface()) + typeError(_function.location(), "Internal functions cannot be payable."); + if (_function.isDeclaredConst()) + typeError(_function.location(), "Functions cannot be constant and payable at the same time."); + } for (ASTPointer<VariableDeclaration> const& var: _function.parameters() + _function.returnParameters()) { if (!type(*var)->canLiveOutsideStorage()) @@ -1256,15 +1266,7 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) "Circular reference for contract creation (cannot create instance of derived or same contract)." ); - auto contractType = make_shared<ContractType>(*contract); - TypePointers parameterTypes = contractType->constructorType()->parameterTypes(); - _newExpression.annotation().type = make_shared<FunctionType>( - parameterTypes, - TypePointers{contractType}, - strings(), - strings(), - FunctionType::Location::Creation - ); + _newExpression.annotation().type = FunctionType::newExpressionType(*contract); } else if (type->category() == Type::Category::Array) { @@ -1328,14 +1330,16 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) fatalTypeError( _memberAccess.location(), "Member \"" + memberName + "\" not found or not visible " - "after argument-dependent lookup in " + exprType->toString() + "after argument-dependent lookup in " + exprType->toString() + + (memberName == "value" ? " - did you forget the \"payable\" modifier?" : "") ); } else if (possibleMembers.size() > 1) fatalTypeError( _memberAccess.location(), "Member \"" + memberName + "\" not unique " - "after argument-dependent lookup in " + exprType->toString() + "after argument-dependent lookup in " + exprType->toString() + + (memberName == "value" ? " - did you forget the \"payable\" modifier?" : "") ); auto& annotation = _memberAccess.annotation(); diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 761d85fe..8fd1584d 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -540,6 +540,7 @@ public: bool _isDeclaredConst, std::vector<ASTPointer<ModifierInvocation>> const& _modifiers, ASTPointer<ParameterList> const& _returnParameters, + bool _isPayable, ASTPointer<Block> const& _body ): CallableDeclaration(_location, _name, _visibility, _parameters, _returnParameters), @@ -547,6 +548,7 @@ public: ImplementationOptional(_body != nullptr), m_isConstructor(_isConstructor), m_isDeclaredConst(_isDeclaredConst), + m_isPayable(_isPayable), m_functionModifiers(_modifiers), m_body(_body) {} @@ -556,6 +558,7 @@ public: bool isConstructor() const { return m_isConstructor; } bool isDeclaredConst() const { return m_isDeclaredConst; } + bool isPayable() const { return m_isPayable; } std::vector<ASTPointer<ModifierInvocation>> const& modifiers() const { return m_functionModifiers; } std::vector<ASTPointer<VariableDeclaration>> const& returnParameters() const { return m_returnParameters->parameters(); } Block const& body() const { return *m_body; } @@ -578,6 +581,7 @@ public: private: bool m_isConstructor; bool m_isDeclaredConst; + bool m_isPayable; std::vector<ASTPointer<ModifierInvocation>> m_functionModifiers; ASTPointer<Block> m_body; }; diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index d86a2caf..a0c1626d 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -362,8 +362,8 @@ MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) cons if (isAddress()) return { {"balance", make_shared<IntegerType >(256)}, - {"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::Bare, true)}, - {"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareCallCode, true)}, + {"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::Bare, true, false, true)}, + {"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareCallCode, true, false, true)}, {"delegatecall", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareDelegateCall, true)}, {"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Location::Send)} }; @@ -1329,16 +1329,10 @@ MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const*) con return members; } -shared_ptr<FunctionType const> const& ContractType::constructorType() const +shared_ptr<FunctionType const> const& ContractType::newExpressionType() const { if (!m_constructorType) - { - FunctionDefinition const* constructor = m_contract.constructor(); - if (constructor) - m_constructorType = make_shared<FunctionType>(*constructor); - else - m_constructorType = make_shared<FunctionType>(TypePointers(), TypePointers()); - } + m_constructorType = FunctionType::newExpressionType(m_contract); return m_constructorType; } @@ -1653,6 +1647,7 @@ TypePointer TupleType::closestTemporaryType(TypePointer const& _targetType) cons FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal): m_location(_isInternal ? Location::Internal : Location::External), m_isConstant(_function.isDeclaredConst()), + m_isPayable(_function.isPayable()), m_declaration(&_function) { TypePointers params; @@ -1737,7 +1732,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl): swap(retParamNames, m_returnParameterNames); } -FunctionType::FunctionType(const EventDefinition& _event): +FunctionType::FunctionType(EventDefinition const& _event): m_location(Location::Event), m_isConstant(true), m_declaration(&_event) { TypePointers params; @@ -1753,6 +1748,35 @@ FunctionType::FunctionType(const EventDefinition& _event): swap(paramNames, m_parameterNames); } +FunctionTypePointer FunctionType::newExpressionType(ContractDefinition const& _contract) +{ + FunctionDefinition const* constructor = _contract.constructor(); + TypePointers parameters; + strings parameterNames; + bool payable = false; + + if (constructor) + { + for (ASTPointer<VariableDeclaration> const& var: constructor->parameters()) + { + parameterNames.push_back(var->name()); + parameters.push_back(var->annotation().type); + } + payable = constructor->isPayable(); + } + return make_shared<FunctionType>( + parameters, + TypePointers{make_shared<ContractType>(_contract)}, + parameterNames, + strings{""}, + Location::Creation, + false, + nullptr, + false, + payable + ); +} + vector<string> FunctionType::parameterNames() const { if (!bound()) @@ -1871,7 +1895,12 @@ FunctionTypePointer FunctionType::interfaceFunctionType() const if (variable && retParamTypes.empty()) return FunctionTypePointer(); - return make_shared<FunctionType>(paramTypes, retParamTypes, m_parameterNames, m_returnParameterNames, m_location, m_arbitraryParameters); + return make_shared<FunctionType>( + paramTypes, retParamTypes, + m_parameterNames, m_returnParameterNames, + m_location, m_arbitraryParameters, + m_declaration, m_isConstant, m_isPayable + ); } MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) const @@ -1889,20 +1918,25 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con { MemberList::MemberMap members; if (m_location != Location::BareDelegateCall && m_location != Location::DelegateCall) - members.push_back(MemberList::Member( - "value", - make_shared<FunctionType>( - parseElementaryTypeVector({"uint"}), - TypePointers{copyAndSetGasOrValue(false, true)}, - strings(), - strings(), - Location::SetValue, - false, - nullptr, - m_gasSet, - m_valueSet - ) - )); + { + if (m_isPayable) + members.push_back(MemberList::Member( + "value", + make_shared<FunctionType>( + parseElementaryTypeVector({"uint"}), + TypePointers{copyAndSetGasOrValue(false, true)}, + strings(), + strings(), + Location::SetValue, + false, + nullptr, + false, + false, + m_gasSet, + m_valueSet + ) + )); + } if (m_location != Location::Creation) members.push_back(MemberList::Member( "gas", @@ -1914,6 +1948,8 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con Location::SetGas, false, nullptr, + false, + false, m_gasSet, m_valueSet ) @@ -2019,6 +2055,8 @@ TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) con m_location, m_arbitraryParameters, m_declaration, + m_isConstant, + m_isPayable, m_gasSet || _setGas, m_valueSet || _setValue, m_bound @@ -2064,6 +2102,8 @@ FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary, bool _bound) location, m_arbitraryParameters, m_declaration, + m_isConstant, + m_isPayable, m_gasSet, m_valueSet, _bound diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 1282e5d8..9173f39a 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -640,9 +640,8 @@ public: bool isSuper() const { return m_super; } ContractDefinition const& contractDefinition() const { return m_contract; } - /// Returns the function type of the constructor. Note that the location part of the function type - /// is not used, as this type cannot be the type of a variable or expression. - FunctionTypePointer const& constructorType() const; + /// Returns the function type of the constructor modified to return an object of the contract's type. + FunctionTypePointer const& newExpressionType() const; /// @returns the identifier of the function with the given name or Invalid256 if such a name does /// not exist. @@ -820,21 +819,32 @@ public: explicit FunctionType(VariableDeclaration const& _varDecl); /// Creates the function type of an event. explicit FunctionType(EventDefinition const& _event); + /// Function type constructor to be used for a plain type (not derived from a declaration). FunctionType( strings const& _parameterTypes, strings const& _returnParameterTypes, Location _location = Location::Internal, - bool _arbitraryParameters = false + bool _arbitraryParameters = false, + bool _constant = false, + bool _payable = false ): FunctionType( parseElementaryTypeVector(_parameterTypes), parseElementaryTypeVector(_returnParameterTypes), strings(), strings(), _location, - _arbitraryParameters + _arbitraryParameters, + nullptr, + _constant, + _payable ) { } + + /// @returns the type of the "new Contract" function, i.e. basically the constructor. + static FunctionTypePointer newExpressionType(ContractDefinition const& _contract); + + /// Detailed constructor, use with care. FunctionType( TypePointers const& _parameterTypes, TypePointers const& _returnParameterTypes, @@ -843,6 +853,8 @@ public: Location _location = Location::Internal, bool _arbitraryParameters = false, Declaration const* _declaration = nullptr, + bool _isConstant = false, + bool _isPayable = false, bool _gasSet = false, bool _valueSet = false, bool _bound = false @@ -856,6 +868,8 @@ public: m_gasSet(_gasSet), m_valueSet(_valueSet), m_bound(_bound), + m_isConstant(_isConstant), + m_isPayable(_isPayable), m_declaration(_declaration) {} @@ -905,6 +919,7 @@ public: } bool hasDeclaration() const { return !!m_declaration; } bool isConstant() const { return m_isConstant; } + bool isPayable() const { return m_isPayable; } /// @return A shared pointer of an ASTString. /// Can contain a nullptr in which case indicates absence of documentation ASTPointer<ASTString> documentation() const; @@ -942,6 +957,7 @@ private: bool const m_valueSet = false; ///< true iff the value to be sent is on the stack bool const m_bound = false; ///< true iff the function is called as arg1.fun(arg2, ..., argn) bool m_isConstant = false; + bool m_isPayable = false; Declaration const* m_declaration = nullptr; }; diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 9d77ccdc..33571bc0 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -242,6 +242,12 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac m_context << notFound; if (fallback) { + if (!fallback->isPayable()) + { + // Throw if function is not payable but call contained ether. + m_context << Instruction::CALLVALUE; + m_context.appendConditionalJumpTo(m_context.errorTag()); + } eth::AssemblyItem returnTag = m_context.pushNewTag(); fallback->accept(*this); m_context << returnTag; @@ -255,7 +261,15 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac FunctionTypePointer const& functionType = it.second; solAssert(functionType->hasDeclaration(), ""); CompilerContext::LocationSetter locationSetter(m_context, functionType->declaration()); + m_context << callDataUnpackerEntryPoints.at(it.first); + if (!functionType->isPayable()) + { + // Throw if function is not payable but call contained ether. + m_context << Instruction::CALLVALUE; + m_context.appendConditionalJumpTo(m_context.errorTag()); + } + eth::AssemblyItem returnTag = m_context.pushNewTag(); m_context << CompilerUtils::dataStartOffset; appendCalldataUnpacker(functionType->parameterTypes()); diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 7b6aa50a..1cc4a50d 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -583,6 +583,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) Location::Bare, false, nullptr, + false, + false, true, true ), diff --git a/libsolidity/interface/InterfaceHandler.cpp b/libsolidity/interface/InterfaceHandler.cpp index d39f8285..6e3ae78f 100644 --- a/libsolidity/interface/InterfaceHandler.cpp +++ b/libsolidity/interface/InterfaceHandler.cpp @@ -52,6 +52,7 @@ string InterfaceHandler::abiInterface(ContractDefinition const& _contractDef) method["type"] = "function"; method["name"] = it.second->declaration().name(); method["constant"] = it.second->isConstant(); + method["payable"] = it.second->isPayable(); method["inputs"] = populateParameters( externalFunctionType->parameterNames(), externalFunctionType->parameterTypeNames(_contractDef.isLibrary()) @@ -81,6 +82,7 @@ string InterfaceHandler::abiInterface(ContractDefinition const& _contractDef) Json::Value method; method["type"] = "fallback"; method["constant"] = externalFunctionType->isConstant(); + method["payable"] = externalFunctionType->isPayable(); abi.append(method); } for (auto const& it: _contractDef.interfaceEvents()) diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 1bffa5d7..0e99d1e7 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -305,6 +305,7 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const* options.allowLocationSpecifier = true; ASTPointer<ParameterList> parameters(parseParameterList(options)); bool isDeclaredConst = false; + bool isPayable = false; Declaration::Visibility visibility(Declaration::Visibility::Default); vector<ASTPointer<ModifierInvocation>> modifiers; while (true) @@ -315,6 +316,11 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const* isDeclaredConst = true; m_scanner->next(); } + else if (m_scanner->currentToken() == Token::Payable) + { + isPayable = true; + m_scanner->next(); + } else if (token == Token::Identifier) modifiers.push_back(parseModifierInvocation()); else if (Token::isVisibilitySpecifier(token)) @@ -354,6 +360,7 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const* isDeclaredConst, modifiers, returnParameters, + isPayable, block ); } diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h index 15d4860f..cc85b610 100644 --- a/libsolidity/parsing/Token.h +++ b/libsolidity/parsing/Token.h @@ -166,6 +166,7 @@ namespace solidity K(Memory, "memory", 0) \ K(Modifier, "modifier", 0) \ K(New, "new", 0) \ + K(Payable, "payable", 0) \ K(Public, "public", 0) \ K(Pragma, "pragma", 0) \ K(Private, "private", 0) \ @@ -229,7 +230,6 @@ namespace solidity K(Let, "let", 0) \ K(Match, "match", 0) \ K(Of, "of", 0) \ - K(Payable, "payable", 0) \ K(Relocatable, "relocatable", 0) \ K(Static, "static", 0) \ K(Switch, "switch", 0) \ |