diff options
Diffstat (limited to 'libsolidity')
-rw-r--r-- | libsolidity/analysis/GlobalContext.cpp | 4 | ||||
-rw-r--r-- | libsolidity/analysis/ReferencesResolver.cpp | 20 | ||||
-rw-r--r-- | libsolidity/analysis/SemVerHandler.cpp | 39 | ||||
-rw-r--r-- | libsolidity/analysis/TypeChecker.cpp | 69 | ||||
-rw-r--r-- | libsolidity/ast/ASTJsonConverter.cpp | 9 | ||||
-rw-r--r-- | libsolidity/ast/Types.cpp | 89 | ||||
-rw-r--r-- | libsolidity/ast/Types.h | 24 | ||||
-rw-r--r-- | libsolidity/codegen/ABIFunctions.cpp | 8 | ||||
-rw-r--r-- | libsolidity/codegen/ExpressionCompiler.cpp | 29 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmAnalysis.cpp | 7 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmCodeGen.h | 2 | ||||
-rw-r--r-- | libsolidity/interface/CompilerStack.cpp | 5 | ||||
-rw-r--r-- | libsolidity/interface/EVMVersion.h | 1 | ||||
-rw-r--r-- | libsolidity/interface/GasEstimator.cpp | 5 | ||||
-rw-r--r-- | libsolidity/interface/Natspec.cpp | 3 | ||||
-rw-r--r-- | libsolidity/interface/SourceReferenceFormatter.cpp | 3 | ||||
-rw-r--r-- | libsolidity/interface/StandardCompiler.cpp | 39 | ||||
-rw-r--r-- | libsolidity/parsing/Scanner.cpp | 4 |
18 files changed, 266 insertions, 94 deletions
diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp index 3ac8fd47..cba2655c 100644 --- a/libsolidity/analysis/GlobalContext.cpp +++ b/libsolidity/analysis/GlobalContext.cpp @@ -56,10 +56,10 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{ 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{"bytes memory"}, strings{"bytes20"}, FunctionType::Kind::RIPEMD160, false, StateMutability::Pure)), - make_shared<MagicVariableDeclaration>("selfdestruct", make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Kind::Selfdestruct)), + make_shared<MagicVariableDeclaration>("selfdestruct", make_shared<FunctionType>(strings{"address payable"}, strings{}, FunctionType::Kind::Selfdestruct)), make_shared<MagicVariableDeclaration>("sha256", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::SHA256, false, StateMutability::Pure)), make_shared<MagicVariableDeclaration>("sha3", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, false, StateMutability::Pure)), - make_shared<MagicVariableDeclaration>("suicide", make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Kind::Selfdestruct)), + make_shared<MagicVariableDeclaration>("suicide", make_shared<FunctionType>(strings{"address payable"}, strings{}, FunctionType::Kind::Selfdestruct)), make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::Transaction)) }) { diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 4b678c3b..81de3c43 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -119,11 +119,19 @@ bool ReferencesResolver::visit(ElementaryTypeName const& _typeName) { // for non-address types this was already caught by the parser solAssert(_typeName.annotation().type->category() == Type::Category::Address, ""); - if (!( - *_typeName.stateMutability() == StateMutability::Payable || - *_typeName.stateMutability() == StateMutability::NonPayable - )) - m_errorReporter.typeError(_typeName.location(), "Address types can only be payable or non-payable."); + switch(*_typeName.stateMutability()) + { + case StateMutability::Payable: + case StateMutability::NonPayable: + _typeName.annotation().type = make_shared<AddressType>(*_typeName.stateMutability()); + break; + default: + m_errorReporter.typeError( + _typeName.location(), + "Address types can only be payable or non-payable." + ); + break; + } } } return true; @@ -238,6 +246,8 @@ void ReferencesResolver::endVisit(ArrayTypeName const& _typeName) RationalNumberType const* lengthType = dynamic_cast<RationalNumberType const*>(lengthTypeGeneric.get()); if (!lengthType || !lengthType->mobileType()) fatalTypeError(length->location(), "Invalid array length, expected integer literal or constant expression."); + else if (lengthType->isZero()) + fatalTypeError(length->location(), "Array with zero length specified."); else if (lengthType->isFractional()) fatalTypeError(length->location(), "Array with fractional length specified."); else if (lengthType->isNegative()) diff --git a/libsolidity/analysis/SemVerHandler.cpp b/libsolidity/analysis/SemVerHandler.cpp index 42186396..29f6d5de 100644 --- a/libsolidity/analysis/SemVerHandler.cpp +++ b/libsolidity/analysis/SemVerHandler.cpp @@ -106,18 +106,22 @@ bool SemVerMatchExpression::MatchComponent::matches(SemVerVersion const& _versio } if (cmp == 0 && !_version.prerelease.empty() && didCompare) cmp = -1; - if (prefix == Token::Assign) + + switch (prefix) + { + case Token::Assign: return cmp == 0; - else if (prefix == Token::LessThan) + case Token::LessThan: return cmp < 0; - else if (prefix == Token::LessThanOrEqual) + case Token::LessThanOrEqual: return cmp <= 0; - else if (prefix == Token::GreaterThan) + case Token::GreaterThan: return cmp > 0; - else if (prefix == Token::GreaterThanOrEqual) + case Token::GreaterThanOrEqual: return cmp >= 0; - else + default: solAssert(false, "Invalid SemVer expression"); + } return false; } } @@ -196,21 +200,22 @@ SemVerMatchExpression::MatchComponent SemVerMatchExpressionParser::parseMatchCom { SemVerMatchExpression::MatchComponent component; Token::Value token = currentToken(); - if ( - token == Token::BitXor || - token == Token::BitNot || - token == Token::LessThan || - token == Token::LessThanOrEqual|| - token == Token::GreaterThan || - token == Token::GreaterThanOrEqual || - token == Token::Assign - ) + + switch (token) { + case Token::BitXor: + case Token::BitNot: + case Token::LessThan: + case Token::LessThanOrEqual: + case Token::GreaterThan: + case Token::GreaterThanOrEqual: + case Token::Assign: component.prefix = token; nextToken(); - } - else + break; + default: component.prefix = Token::Assign; + } component.levelsPresent = 0; while (component.levelsPresent < 3) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 143ac109..3d119c82 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -535,7 +535,7 @@ TypePointers TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall c toString(arguments.size()) + " were provided." ); - if (arguments.size() >= 1 && !type(*arguments.front())->isImplicitlyConvertibleTo(ArrayType(DataLocation::Memory))) + if (arguments.size() >= 1 && !type(*arguments.front())->isImplicitlyConvertibleTo(ArrayType::bytesMemory())) m_errorReporter.typeError( arguments.front()->location(), "Invalid type for argument in function call. " @@ -571,6 +571,9 @@ TypePointers TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall c // data locations. Furthermore, storage can be a little dangerous and // calldata is not really implemented anyway. actualType = ReferenceType::copyForLocationIfReference(DataLocation::Memory, actualType); + // We force address payable for address types. + if (actualType->category() == Type::Category::Address) + actualType = make_shared<AddressType>(StateMutability::Payable); solAssert( !actualType->dataStoredIn(DataLocation::CallData) && !actualType->dataStoredIn(DataLocation::Storage), @@ -1730,16 +1733,54 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) DataLocation dataLoc = DataLocation::Memory; if (auto argRefType = dynamic_cast<ReferenceType const*>(argType.get())) dataLoc = argRefType->location(); - resultType = ReferenceType::copyForLocationIfReference(dataLoc, resultType); - if (!argType->isExplicitlyConvertibleTo(*resultType)) - m_errorReporter.typeError( - _functionCall.location(), - "Explicit type conversion not allowed from \"" + - argType->toString() + - "\" to \"" + - resultType->toString() + - "\"." - ); + if (auto type = dynamic_cast<ReferenceType const*>(resultType.get())) + resultType = type->copyForLocation(dataLoc, type->isPointer()); + if (argType->isExplicitlyConvertibleTo(*resultType)) + { + if (auto argArrayType = dynamic_cast<ArrayType const*>(argType.get())) + { + auto resultArrayType = dynamic_cast<ArrayType const*>(resultType.get()); + solAssert(!!resultArrayType, ""); + solAssert( + argArrayType->location() != DataLocation::Storage || + ((resultArrayType->isPointer() || (argArrayType->isByteArray() && resultArrayType->isByteArray())) && + resultArrayType->location() == DataLocation::Storage), + "Invalid explicit conversion to storage type." + ); + } + } + else + { + if (resultType->category() == Type::Category::Contract && argType->category() == Type::Category::Address) + { + solAssert(dynamic_cast<ContractType const*>(resultType.get())->isPayable(), ""); + solAssert(dynamic_cast<AddressType const*>(argType.get())->stateMutability() < StateMutability::Payable, ""); + SecondarySourceLocation ssl; + if (auto const* identifier = dynamic_cast<Identifier const*>(arguments.front().get())) + if (auto const* variableDeclaration = dynamic_cast<VariableDeclaration const*>(identifier->annotation().referencedDeclaration)) + ssl.append("Did you mean to declare this variable as \"address payable\"?", variableDeclaration->location()); + m_errorReporter.typeError( + _functionCall.location(), ssl, + "Explicit type conversion not allowed from non-payable \"address\" to \"" + + resultType->toString() + + "\", which has a payable fallback function." + ); + } + else + m_errorReporter.typeError( + _functionCall.location(), + "Explicit type conversion not allowed from \"" + + argType->toString() + + "\" to \"" + + resultType->toString() + + "\"." + ); + } + if (resultType->category() == Type::Category::Address) + { + bool payable = argType->isExplicitlyConvertibleTo(AddressType::addressPayable()); + resultType = make_shared<AddressType>(payable ? StateMutability::Payable : StateMutability::NonPayable); + } } _functionCall.annotation().type = resultType; _functionCall.annotation().isPure = isPure; @@ -1998,7 +2039,7 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) if (!contract) m_errorReporter.fatalTypeError(_newExpression.location(), "Identifier is not a contract."); if (contract->contractKind() == ContractDefinition::ContractKind::Interface) - m_errorReporter.fatalTypeError(_newExpression.location(), "Cannot instantiate an interface."); + m_errorReporter.fatalTypeError(_newExpression.location(), "Cannot instantiate an interface."); if (!contract->annotation().unimplementedFunctions.empty()) { SecondarySourceLocation ssl; @@ -2103,7 +2144,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) "after argument-dependent lookup in " + exprType->toString() + (memberName == "value" ? " - did you forget the \"payable\" modifier?" : "."); if (exprType->category() == Type::Category::Contract) - for (auto const& addressMember: AddressType().nativeMembers(nullptr)) + for (auto const& addressMember: AddressType::addressPayable().nativeMembers(nullptr)) if (addressMember.name == memberName) { Identifier const* var = dynamic_cast<Identifier const*>(&_memberAccess.expression()); @@ -2354,7 +2395,7 @@ void TypeChecker::endVisit(Literal const& _literal) if (_literal.looksLikeAddress()) { // Assign type here if it even looks like an address. This prevents double errors for invalid addresses - _literal.annotation().type = make_shared<AddressType>(); + _literal.annotation().type = make_shared<AddressType>(StateMutability::Payable); string msg; if (_literal.value().length() != 42) // "0x" + 40 hex digits diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 4d41376a..cadc5f28 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -394,10 +394,15 @@ bool ASTJsonConverter::visit(EventDefinition const& _node) bool ASTJsonConverter::visit(ElementaryTypeName const& _node) { - setJsonNode(_node, "ElementaryTypeName", { + std::vector<pair<string, Json::Value>> attributes = { make_pair("name", _node.typeName().toString()), make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true)) - }); + }; + + if (_node.stateMutability()) + attributes.emplace_back(make_pair("stateMutability", stateMutabilityToString(*_node.stateMutability()))); + + setJsonNode(_node, "ElementaryTypeName", std::move(attributes)); return false; } diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index a302203b..fd72bf41 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -299,7 +299,7 @@ TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type) case Token::Byte: return make_shared<FixedBytesType>(1); case Token::Address: - return make_shared<AddressType>(); + return make_shared<AddressType>(StateMutability::NonPayable); case Token::Bool: return make_shared<BoolType>(); case Token::Bytes: @@ -340,6 +340,17 @@ TypePointer Type::fromElementaryTypeName(string const& _name) } return ref->copyForLocation(location, true); } + else if (t->category() == Type::Category::Address) + { + if (nameParts.size() == 2) + { + if (nameParts[1] == "payable") + return make_shared<AddressType>(StateMutability::Payable); + else + solAssert(false, "Invalid state mutability for address type: " + nameParts[1]); + } + return make_shared<AddressType>(StateMutability::NonPayable); + } else { solAssert(nameParts.size() == 1, "Storage location suffix only allowed for reference types"); @@ -439,21 +450,48 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition return members; } +AddressType::AddressType(StateMutability _stateMutability): + m_stateMutability(_stateMutability) +{ + solAssert(m_stateMutability == StateMutability::Payable || m_stateMutability == StateMutability::NonPayable, ""); +} + string AddressType::richIdentifier() const { - return "t_address"; + if (m_stateMutability == StateMutability::Payable) + return "t_address_payable"; + else + return "t_address"; +} + +bool AddressType::isImplicitlyConvertibleTo(Type const& _other) const +{ + if (_other.category() != category()) + return false; + AddressType const& other = dynamic_cast<AddressType const&>(_other); + + return other.m_stateMutability <= m_stateMutability; } bool AddressType::isExplicitlyConvertibleTo(Type const& _convertTo) const { + if (auto const* contractType = dynamic_cast<ContractType const*>(&_convertTo)) + return (m_stateMutability >= StateMutability::Payable) || !contractType->isPayable(); return isImplicitlyConvertibleTo(_convertTo) || - _convertTo.category() == Category::Contract || _convertTo.category() == Category::Integer || (_convertTo.category() == Category::FixedBytes && 160 == dynamic_cast<FixedBytesType const&>(_convertTo).numBytes() * 8); } string AddressType::toString(bool) const { + if (m_stateMutability == StateMutability::Payable) + return "address payable"; + else + return "address"; +} + +string AddressType::canonicalName() const +{ return "address"; } @@ -479,17 +517,29 @@ TypePointer AddressType::binaryOperatorResult(Token::Value _operator, TypePointe return Type::commonType(shared_from_this(), _other); } +bool AddressType::operator==(Type const& _other) const +{ + if (_other.category() != category()) + return false; + AddressType const& other = dynamic_cast<AddressType const&>(_other); + return other.m_stateMutability == m_stateMutability; +} + MemberList::MemberMap AddressType::nativeMembers(ContractDefinition const*) const { - return { + MemberList::MemberMap members = { {"balance", make_shared<IntegerType>(256)}, {"call", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareCall, false, StateMutability::Payable)}, {"callcode", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareCallCode, false, StateMutability::Payable)}, {"delegatecall", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareDelegateCall, false)}, - {"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Kind::Send)}, - {"staticcall", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareStaticCall, false, StateMutability::View)}, - {"transfer", make_shared<FunctionType>(strings{"uint"}, strings(), FunctionType::Kind::Transfer)} + {"staticcall", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareStaticCall, false, StateMutability::View)} }; + if (m_stateMutability == StateMutability::Payable) + { + members.emplace_back(MemberList::Member{"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Kind::Send)}); + members.emplace_back(MemberList::Member{"transfer", make_shared<FunctionType>(strings{"uint"}, strings(), FunctionType::Kind::Transfer)}); + } + return members; } namespace @@ -616,7 +666,7 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe return TypePointer(); } - auto commonType = Type::commonType(shared_from_this(), _other); //might be a integer or fixed point + auto commonType = Type::commonType(shared_from_this(), _other); //might be an integer or fixed point if (!commonType) return TypePointer(); @@ -1226,13 +1276,13 @@ u256 RationalNumberType::literalValue(Literal const*) const else { auto fixed = fixedPointType(); - solAssert(fixed, ""); + solAssert(fixed, "Rational number cannot be represented as fixed point type."); int fractionalDigits = fixed->fractionalDigits(); shiftedValue = m_value.numerator() * boost::multiprecision::pow(bigint(10), fractionalDigits) / m_value.denominator(); } // we ignore the literal and hope that the type was correctly determined - solAssert(shiftedValue <= u256(-1), "Integer constant too large."); + solAssert(shiftedValue <= u256(-1), "Number constant too large."); solAssert(shiftedValue >= -(bigint(1) << 255), "Number constant too small."); if (m_value >= rational(0)) @@ -1476,7 +1526,9 @@ bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - return isImplicitlyConvertibleTo(_convertTo) || _convertTo.category() == Category::Address; + if (auto const* addressType = dynamic_cast<AddressType const*>(&_convertTo)) + return isPayable() || (addressType->stateMutability() < StateMutability::Payable); + return isImplicitlyConvertibleTo(_convertTo); } bool ContractType::isPayable() const @@ -2074,8 +2126,11 @@ bool StructType::canBeUsedExternally(bool _inLibrary) const // passed by value and thus the encoding does not differ, but it will disallow // mappings. for (auto const& var: m_struct.members()) + { + solAssert(var->annotation().type, ""); if (!var->annotation().type->canBeUsedExternally(false)) return false; + } } return true; } @@ -2377,7 +2432,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl): else if (auto arrayType = dynamic_cast<ArrayType const*>(returnType.get())) { if (arrayType->isByteArray()) - // Return byte arrays as as whole. + // Return byte arrays as whole. break; returnType = arrayType->baseType(); m_parameterNames.push_back(""); @@ -2589,8 +2644,8 @@ bool FunctionType::operator==(Type const& _other) const bool FunctionType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - if (m_kind == Kind::External && _convertTo.category() == Category::Address) - return true; + if (m_kind == Kind::External && _convertTo == AddressType::address()) + return true; return _convertTo.category() == category(); } @@ -3275,7 +3330,7 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const { case Kind::Block: return MemberList::MemberMap({ - {"coinbase", make_shared<AddressType>()}, + {"coinbase", make_shared<AddressType>(StateMutability::Payable)}, {"timestamp", make_shared<IntegerType>(256)}, {"blockhash", make_shared<FunctionType>(strings{"uint"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, false, StateMutability::View)}, {"difficulty", make_shared<IntegerType>(256)}, @@ -3284,7 +3339,7 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const }); case Kind::Message: return MemberList::MemberMap({ - {"sender", make_shared<AddressType>()}, + {"sender", make_shared<AddressType>(StateMutability::Payable)}, {"gas", make_shared<IntegerType>(256)}, {"value", make_shared<IntegerType>(256)}, {"data", make_shared<ArrayType>(DataLocation::CallData)}, @@ -3292,7 +3347,7 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const }); case Kind::Transaction: return MemberList::MemberMap({ - {"origin", make_shared<AddressType>()}, + {"origin", make_shared<AddressType>(StateMutability::Payable)}, {"gasprice", make_shared<IntegerType>(256)} }); case Kind::ABI: diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 7ee66838..0f3373a1 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -174,7 +174,7 @@ public: /// Will not contain any character which would be invalid as an identifier. std::string identifier() const; - /// More complex identifier strings use "parentheses", where $_ is interpreted as as + /// More complex identifier strings use "parentheses", where $_ is interpreted as /// "opening parenthesis", _$ as "closing parenthesis", _$_ as "comma" and any $ that /// appears as part of a user-supplied identifier is escaped as _$$$_. /// @returns an escaped identifier (will not contain any parenthesis or commas) @@ -319,17 +319,21 @@ protected: class AddressType: public Type { public: + static AddressType& address() { static std::shared_ptr<AddressType> addr(std::make_shared<AddressType>(StateMutability::NonPayable)); return *addr; } + static AddressType& addressPayable() { static std::shared_ptr<AddressType> addr(std::make_shared<AddressType>(StateMutability::Payable)); return *addr; } + virtual Category category() const override { return Category::Address; } - explicit AddressType() - { - } + explicit AddressType(StateMutability _stateMutability); virtual std::string richIdentifier() const override; + virtual bool isImplicitlyConvertibleTo(Type const& _other) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; + virtual bool operator==(Type const& _other) const override; + virtual unsigned calldataEncodedSize(bool _padded = true) const override { return _padded ? 32 : 160 / 8; } virtual unsigned storageBytes() const override { return 160 / 8; } virtual bool isValueType() const override { return true; } @@ -337,11 +341,17 @@ public: virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override; virtual std::string toString(bool _short) const override; + virtual std::string canonicalName() const override; virtual u256 literalValue(Literal const* _literal) const override; virtual TypePointer encodingType() const override { return shared_from_this(); } virtual TypePointer interfaceType(bool) const override { return shared_from_this(); } + + StateMutability stateMutability(void) const { return m_stateMutability; } + +private: + StateMutability m_stateMutability; }; /** @@ -354,6 +364,7 @@ public: { Unsigned, Signed }; + virtual Category category() const override { return Category::Integer; } explicit IntegerType(unsigned _bits, Modifier _modifier = Modifier::Unsigned); @@ -654,6 +665,9 @@ protected: class ArrayType: public ReferenceType { public: + static ArrayType& bytesMemory() { static std::shared_ptr<ArrayType> addr(std::make_shared<ArrayType>(DataLocation::Memory)); return *addr; } + static ArrayType& stringMemory() { static std::shared_ptr<ArrayType> addr(std::make_shared<ArrayType>(DataLocation::Memory, true)); return *addr; } + virtual Category category() const override { return Category::Array; } /// Constructor for a byte array ("bytes") and string. @@ -755,7 +769,7 @@ public: { if (isSuper()) return TypePointer{}; - return std::make_shared<AddressType>(); + return std::make_shared<AddressType>(isPayable() ? StateMutability::Payable : StateMutability::NonPayable); } virtual TypePointer interfaceType(bool _inLibrary) const override { diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index 5e5fe84a..6c27533c 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -242,8 +242,14 @@ string ABIFunctions::cleanupFunction(Type const& _type, bool _revertOnFailure) break; } case Type::Category::Contract: - templ("body", "cleaned := " + cleanupFunction(AddressType()) + "(value)"); + { + AddressType addressType(dynamic_cast<ContractType const&>(_type).isPayable() ? + StateMutability::Payable : + StateMutability::NonPayable + ); + templ("body", "cleaned := " + cleanupFunction(addressType) + "(value)"); break; + } case Type::Category::Enum: { size_t members = dynamic_cast<EnumType const&>(_type).numberOfMembers(); diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 23faeef5..27440289 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -710,9 +710,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) arguments.front()->accept(*this); // Optimization: If type is bytes or string, then do not encode, // but directly compute keccak256 on memory. - if (*argType == ArrayType(DataLocation::Memory) || *argType == ArrayType(DataLocation::Memory, true)) + if (*argType == ArrayType::bytesMemory() || *argType == ArrayType::stringMemory()) { - ArrayUtils(m_context).retrieveLength(ArrayType(DataLocation::Memory)); + ArrayUtils(m_context).retrieveLength(ArrayType::bytesMemory()); m_context << Instruction::SWAP1 << u256(0x20) << Instruction::ADD; } else @@ -1086,7 +1086,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) utils().abiDecode(targetTypes, false); else { - utils().convertType(*firstArgType, ArrayType(DataLocation::Memory)); + utils().convertType(*firstArgType, ArrayType::bytesMemory()); m_context << Instruction::DUP1 << u256(32) << Instruction::ADD; m_context << Instruction::SWAP1 << Instruction::MLOAD; // stack now: <mem_pos> <length> @@ -1228,7 +1228,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) else solAssert(false, "Contract member is neither variable nor function."); m_context << identifier; - /// need to store store it as bytes4 + /// need to store it as bytes4 utils().leftShiftNumberOnStack(224); return false; } @@ -1257,7 +1257,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) identifier = FunctionType(*function).externalIdentifier(); else solAssert(false, "Contract member is neither variable nor function."); - utils().convertType(type, AddressType(), true); + utils().convertType(type, type.isPayable() ? AddressType::addressPayable() : AddressType::address(), true); m_context << identifier; } else @@ -1275,15 +1275,24 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) { utils().convertType( *_memberAccess.expression().annotation().type, - AddressType(), + AddressType::address(), true ); m_context << Instruction::BALANCE; } - else if ((set<string>{"send", "transfer", "call", "callcode", "delegatecall", "staticcall"}).count(member)) + else if ((set<string>{"send", "transfer"}).count(member)) + { + solAssert(dynamic_cast<AddressType const&>(*_memberAccess.expression().annotation().type).stateMutability() == StateMutability::Payable, ""); + utils().convertType( + *_memberAccess.expression().annotation().type, + AddressType(StateMutability::Payable), + true + ); + } + else if ((set<string>{"call", "callcode", "delegatecall", "staticcall"}).count(member)) utils().convertType( *_memberAccess.expression().annotation().type, - AddressType(), + AddressType::address(), true ); else @@ -1294,7 +1303,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) if (member == "selector") { m_context << Instruction::SWAP1 << Instruction::POP; - /// need to store store it as bytes4 + /// need to store it as bytes4 utils().leftShiftNumberOnStack(224); } else @@ -1964,7 +1973,7 @@ void ExpressionCompiler::appendExternalFunctionCall( m_context << dupInstruction(m_context.baseToCurrentStackOffset(contractStackPos)); bool existenceChecked = false; - // Check the the target contract exists (has code) for non-low-level calls. + // Check the target contract exists (has code) for non-low-level calls. if (funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::DelegateCall) { m_context << Instruction::DUP1 << Instruction::EXTCODESIZE << Instruction::ISZERO; diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index 9a0110cf..947b6d05 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -565,8 +565,10 @@ void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocatio // We assume that returndatacopy, returndatasize and staticcall are either all available // or all not available. solAssert(m_evmVersion.supportsReturndata() == m_evmVersion.hasStaticCall(), ""); + // Similarly we assume bitwise shifting and create2 go together. + solAssert(m_evmVersion.hasBitwiseShifting() == m_evmVersion.hasCreate2(), ""); - if (_instr == solidity::Instruction::CREATE2) + if (_instr == solidity::Instruction::EXTCODEHASH) m_errorReporter.warning( _location, "The \"" + @@ -593,7 +595,8 @@ void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocatio else if (( _instr == solidity::Instruction::SHL || _instr == solidity::Instruction::SHR || - _instr == solidity::Instruction::SAR + _instr == solidity::Instruction::SAR || + _instr == solidity::Instruction::CREATE2 ) && !m_evmVersion.hasBitwiseShifting()) m_errorReporter.warning( _location, diff --git a/libsolidity/inlineasm/AsmCodeGen.h b/libsolidity/inlineasm/AsmCodeGen.h index a7d7ead1..277e1879 100644 --- a/libsolidity/inlineasm/AsmCodeGen.h +++ b/libsolidity/inlineasm/AsmCodeGen.h @@ -41,7 +41,7 @@ struct Block; class CodeGenerator { public: - /// Performs code generation and appends generated to to _assembly. + /// Performs code generation and appends generated to _assembly. static void assemble( Block const& _parsedData, AsmAnalysisInfo& _analysisInfo, diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index e800b278..d1001c80 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -128,7 +128,7 @@ bool CompilerStack::addSource(string const& _name, string const& _content, bool bool CompilerStack::parse() { //reset - if(m_stackState != SourcesSet) + if (m_stackState != SourcesSet) return false; m_errorReporter.clear(); ASTNode::resetID(); @@ -988,8 +988,7 @@ Json::Value CompilerStack::gasEstimates(string const& _contractName) const if (eth::AssemblyItems const* items = assemblyItems(_contractName)) { Gas executionGas = gasEstimator.functionalEstimation(*items); - u256 bytecodeSize(runtimeObject(_contractName).bytecode.size()); - Gas codeDepositGas = bytecodeSize * eth::GasCosts::createDataGas; + Gas codeDepositGas{eth::GasMeter::dataGas(runtimeObject(_contractName).bytecode, false)}; Json::Value creation(Json::objectValue); creation["codeDepositCost"] = gasToJson(codeDepositGas); diff --git a/libsolidity/interface/EVMVersion.h b/libsolidity/interface/EVMVersion.h index b68e1f4e..657727ac 100644 --- a/libsolidity/interface/EVMVersion.h +++ b/libsolidity/interface/EVMVersion.h @@ -75,6 +75,7 @@ public: bool supportsReturndata() const { return *this >= byzantium(); } bool hasStaticCall() const { return *this >= byzantium(); } bool hasBitwiseShifting() const { return *this >= constantinople(); } + bool hasCreate2() const { return *this >= constantinople(); } /// Whether we have to retain the costs for the call opcode itself (false), /// or whether we can just forward easily all remaining gas (true). diff --git a/libsolidity/interface/GasEstimator.cpp b/libsolidity/interface/GasEstimator.cpp index a532f86e..e70e23a2 100644 --- a/libsolidity/interface/GasEstimator.cpp +++ b/libsolidity/interface/GasEstimator.cpp @@ -160,8 +160,7 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation( ); } - PathGasMeter meter(_items, m_evmVersion); - return meter.estimateMax(0, state); + return PathGasMeter::estimateMax(_items, m_evmVersion, 0, state); } GasEstimator::GasConsumption GasEstimator::functionalEstimation( @@ -183,7 +182,7 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation( if (parametersSize > 0) state->feedItem(swapInstruction(parametersSize)); - return PathGasMeter(_items, m_evmVersion).estimateMax(_offset, state); + return PathGasMeter::estimateMax(_items, m_evmVersion, _offset, state); } set<ASTNode const*> GasEstimator::finestNodesAtLocation( diff --git a/libsolidity/interface/Natspec.cpp b/libsolidity/interface/Natspec.cpp index a8716862..11dde349 100644 --- a/libsolidity/interface/Natspec.cpp +++ b/libsolidity/interface/Natspec.cpp @@ -83,7 +83,8 @@ Json::Value Natspec::devDocumentation(ContractDefinition const& _contractDef) doc["details"] = Json::Value(dev); auto constructorDefinition(_contractDef.constructor()); - if (constructorDefinition) { + if (constructorDefinition) + { Json::Value constructor(devDocumentation(constructorDefinition->annotation().docTags)); if (!constructor.empty()) // add the constructor, only if we have any documentation to add diff --git a/libsolidity/interface/SourceReferenceFormatter.cpp b/libsolidity/interface/SourceReferenceFormatter.cpp index 4724fc7f..865907e2 100644 --- a/libsolidity/interface/SourceReferenceFormatter.cpp +++ b/libsolidity/interface/SourceReferenceFormatter.cpp @@ -59,7 +59,8 @@ void SourceReferenceFormatter::printSourceLocation(SourceLocation const* _locati line = line.substr(max(0, startColumn - 35), min(startColumn, 35) + min(locationLength + 35, len - startColumn)); if (startColumn + locationLength + 35 < len) line += " ..."; - if (startColumn > 35) { + if (startColumn > 35) + { line = " ... " + line; startColumn = 40; } diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index c1996777..2305da13 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -280,6 +280,8 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) for (auto const& url: sources[sourceName]["urls"]) { + if (!url.isString()) + return formatFatalError("JSONError", "URL must be a string."); ReadCallback::Result result = m_readFile(url.asString()); if (result.success) { @@ -320,7 +322,9 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) if (settings.isMember("evmVersion")) { - boost::optional<EVMVersion> version = EVMVersion::fromString(settings.get("evmVersion", {}).asString()); + if (!settings["evmVersion"].isString()) + return formatFatalError("JSONError", "evmVersion must be a string."); + boost::optional<EVMVersion> version = EVMVersion::fromString(settings["evmVersion"].asString()); if (!version) return formatFatalError("JSONError", "Invalid EVM version requested."); m_compilerStack.setEVMVersion(*version); @@ -329,6 +333,8 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) vector<CompilerStack::Remapping> remappings; for (auto const& remapping: settings.get("remappings", Json::Value())) { + if (!remapping.isString()) + return formatFatalError("JSONError", "Remapping entry must be a string."); if (auto r = CompilerStack::parseRemapping(remapping.asString())) remappings.emplace_back(std::move(*r)); else @@ -336,10 +342,25 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) } m_compilerStack.setRemappings(remappings); - Json::Value optimizerSettings = settings.get("optimizer", Json::Value()); - bool const optimize = optimizerSettings.get("enabled", Json::Value(false)).asBool(); - unsigned const optimizeRuns = optimizerSettings.get("runs", Json::Value(200u)).asUInt(); - m_compilerStack.setOptimiserSettings(optimize, optimizeRuns); + if (settings.isMember("optimizer")) + { + Json::Value optimizerSettings = settings["optimizer"]; + if (optimizerSettings.isMember("enabled")) + { + if (!optimizerSettings["enabled"].isBool()) + return formatFatalError("JSONError", "The \"enabled\" setting must be a boolean."); + + bool const optimize = optimizerSettings["enabled"].asBool(); + unsigned optimizeRuns = 200; + if (optimizerSettings.isMember("runs")) + { + if (!optimizerSettings["runs"].isUInt()) + return formatFatalError("JSONError", "The \"runs\" setting must be an unsigned number."); + optimizeRuns = optimizerSettings["runs"].asUInt(); + } + m_compilerStack.setOptimiserSettings(optimize, optimizeRuns); + } + } map<string, h160> libraries; Json::Value jsonLibraries = settings.get("libraries", Json::Value(Json::objectValue)); @@ -349,9 +370,11 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) { auto const& jsonSourceName = jsonLibraries[sourceName]; if (!jsonSourceName.isObject()) - return formatFatalError("JSONError", "library entry is not a JSON object."); + return formatFatalError("JSONError", "Library entry is not a JSON object."); for (auto const& library: jsonSourceName.getMemberNames()) { + if (!jsonSourceName[library].isString()) + return formatFatalError("JSONError", "Library address must be a string."); string address = jsonSourceName[library].asString(); if (!boost::starts_with(address, "0x")) @@ -607,7 +630,7 @@ string StandardCompiler::compile(string const& _input) noexcept } catch (...) { - return "{\"errors\":\"[{\"type\":\"JSONError\",\"component\":\"general\",\"severity\":\"error\",\"message\":\"Error parsing input JSON.\"}]}"; + return "{\"errors\":[{\"type\":\"JSONError\",\"component\":\"general\",\"severity\":\"error\",\"message\":\"Error parsing input JSON.\"}]}"; } // cout << "Input: " << input.toStyledString() << endl; @@ -620,6 +643,6 @@ string StandardCompiler::compile(string const& _input) noexcept } catch (...) { - return "{\"errors\":\"[{\"type\":\"JSONError\",\"component\":\"general\",\"severity\":\"error\",\"message\":\"Error writing output JSON.\"}]}"; + return "{\"errors\":[{\"type\":\"JSONError\",\"component\":\"general\",\"severity\":\"error\",\"message\":\"Error writing output JSON.\"}]}"; } } diff --git a/libsolidity/parsing/Scanner.cpp b/libsolidity/parsing/Scanner.cpp index c9d5b969..0f6d6996 100644 --- a/libsolidity/parsing/Scanner.cpp +++ b/libsolidity/parsing/Scanner.cpp @@ -780,13 +780,13 @@ Token::Value Scanner::scanNumber(char _charSeen) { addLiteralCharAndAdvance(); // either 0, 0exxx, 0Exxx, 0.xxx or a hex number - if (m_char == 'x' || m_char == 'X') + if (m_char == 'x') { // hex number kind = HEX; addLiteralCharAndAdvance(); if (!isHexDigit(m_char)) - return Token::Illegal; // we must have at least one hex digit after 'x'/'X' + return Token::Illegal; // we must have at least one hex digit after 'x' while (isHexDigit(m_char) || m_char == '_') // We keep the underscores for later validation addLiteralCharAndAdvance(); |