diff options
Diffstat (limited to 'libsolidity/ast')
-rw-r--r-- | libsolidity/ast/AST.cpp | 14 | ||||
-rw-r--r-- | libsolidity/ast/AST.h | 41 | ||||
-rw-r--r-- | libsolidity/ast/ASTAnnotations.h | 3 | ||||
-rw-r--r-- | libsolidity/ast/ASTJsonConverter.cpp | 20 | ||||
-rw-r--r-- | libsolidity/ast/ASTJsonConverter.h | 2 | ||||
-rw-r--r-- | libsolidity/ast/AST_accept.h | 12 | ||||
-rw-r--r-- | libsolidity/ast/Types.cpp | 302 | ||||
-rw-r--r-- | libsolidity/ast/Types.h | 51 |
8 files changed, 351 insertions, 94 deletions
diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 27220b1f..80f5d642 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -290,7 +290,14 @@ TypeDeclarationAnnotation& EnumDefinition::annotation() const return dynamic_cast<TypeDeclarationAnnotation&>(*m_annotation); } -shared_ptr<FunctionType> FunctionDefinition::functionType(bool _internal) const +ContractDefinition::ContractKind FunctionDefinition::inContractKind() const +{ + auto contractDef = dynamic_cast<ContractDefinition const*>(scope()); + solAssert(contractDef, "Enclosing Scope of FunctionDefinition was not set."); + return contractDef->contractKind(); +} + +FunctionTypePointer FunctionDefinition::functionType(bool _internal) const { if (_internal) { @@ -331,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); } @@ -372,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); @@ -477,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 863ad2fe..a53987bf 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -203,6 +203,7 @@ public: bool isPublic() const { return visibility() >= Visibility::Public; } virtual bool isVisibleInContract() const { return visibility() != Visibility::External; } bool isVisibleInDerivedContracts() const { return isVisibleInContract() && visibility() >= Visibility::Internal; } + bool isVisibleAsLibraryMember() const { return visibility() >= Visibility::Internal; } std::string fullyQualifiedName() const { return sourceUnitName() + ":" + name(); } @@ -217,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; } @@ -424,19 +425,22 @@ public: InheritanceSpecifier( SourceLocation const& _location, ASTPointer<UserDefinedTypeName> const& _baseName, - std::vector<ASTPointer<Expression>> _arguments + std::unique_ptr<std::vector<ASTPointer<Expression>>> _arguments ): - ASTNode(_location), m_baseName(_baseName), m_arguments(_arguments) {} + ASTNode(_location), m_baseName(_baseName), m_arguments(std::move(_arguments)) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; UserDefinedTypeName const& name() const { return *m_baseName; } - std::vector<ASTPointer<Expression>> const& arguments() const { return m_arguments; } + // Returns nullptr if no argument list was given (``C``). + // If an argument list is given (``C(...)``), the arguments are returned + // as a vector of expressions. Note that this vector can be empty (``C()``). + std::vector<ASTPointer<Expression>> const* arguments() const { return m_arguments.get(); } private: ASTPointer<UserDefinedTypeName> m_baseName; - std::vector<ASTPointer<Expression>> m_arguments; + std::unique_ptr<std::vector<ASTPointer<Expression>>> m_arguments; }; /** @@ -606,7 +610,8 @@ public: StateMutability stateMutability() const { return m_stateMutability; } bool isConstructor() const { return m_isConstructor; } - bool isFallback() const { return name().empty(); } + bool isOldStyleConstructor() const { return m_isConstructor && !name().empty(); } + bool isFallback() const { return !m_isConstructor && name().empty(); } bool isPayable() const { return m_stateMutability == StateMutability::Payable; } std::vector<ASTPointer<ModifierInvocation>> const& modifiers() const { return m_functionModifiers; } std::vector<ASTPointer<VariableDeclaration>> const& returnParameters() const { return m_returnParameters->parameters(); } @@ -623,11 +628,13 @@ public: /// arguments separated by commas all enclosed in parentheses without any spaces. std::string externalSignature() const; + ContractDefinition::ContractKind inContractKind() const; + virtual TypePointer type() const override; /// @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; @@ -696,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; @@ -755,19 +762,22 @@ public: ModifierInvocation( SourceLocation const& _location, ASTPointer<Identifier> const& _name, - std::vector<ASTPointer<Expression>> _arguments + std::unique_ptr<std::vector<ASTPointer<Expression>>> _arguments ): - ASTNode(_location), m_modifierName(_name), m_arguments(_arguments) {} + ASTNode(_location), m_modifierName(_name), m_arguments(std::move(_arguments)) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; ASTPointer<Identifier> const& name() const { return m_modifierName; } - std::vector<ASTPointer<Expression>> const& arguments() const { return m_arguments; } + // Returns nullptr if no argument list was given (``mod``). + // If an argument list is given (``mod(...)``), the arguments are returned + // as a vector of expressions. Note that this vector can be empty (``mod()``). + std::vector<ASTPointer<Expression>> const* arguments() const { return m_arguments.get(); } private: ASTPointer<Identifier> m_modifierName; - std::vector<ASTPointer<Expression>> m_arguments; + std::unique_ptr<std::vector<ASTPointer<Expression>>> m_arguments; }; /** @@ -795,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; @@ -821,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/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 3d4236cc..5cbe42bd 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -90,6 +90,9 @@ struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, DocumentedAnnota /// List of contracts this contract creates, i.e. which need to be compiled first. /// Also includes all contracts from @a linearizedBaseContracts. std::set<ContractDefinition const*> contractDependencies; + /// Mapping containing the nodes that define the arguments for base constructors. + /// These can either be inheritance specifiers or modifier invocations. + std::map<FunctionDefinition const*, ASTNode const*> baseConstructorArguments; }; struct FunctionDefinitionAnnotation: ASTAnnotation, DocumentedAnnotation diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 4fef67c3..b8e00b60 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -134,10 +134,10 @@ string ASTJsonConverter::namePathToString(std::vector<ASTString> const& _namePat return boost::algorithm::join(_namePath, "."); } -Json::Value ASTJsonConverter::typePointerToJson(TypePointer _tp) +Json::Value ASTJsonConverter::typePointerToJson(TypePointer _tp, bool _short) { Json::Value typeDescriptions(Json::objectValue); - typeDescriptions["typeString"] = _tp ? Json::Value(_tp->toString()) : Json::nullValue; + typeDescriptions["typeString"] = _tp ? Json::Value(_tp->toString(_short)) : Json::nullValue; typeDescriptions["typeIdentifier"] = _tp ? Json::Value(_tp->identifier()) : Json::nullValue; return typeDescriptions; @@ -268,7 +268,7 @@ bool ASTJsonConverter::visit(InheritanceSpecifier const& _node) { setJsonNode(_node, "InheritanceSpecifier", { make_pair("baseName", toJson(_node.name())), - make_pair("arguments", toJson(_node.arguments())) + make_pair("arguments", _node.arguments() ? toJson(*_node.arguments()) : Json::nullValue) }); return false; } @@ -354,7 +354,7 @@ bool ASTJsonConverter::visit(VariableDeclaration const& _node) make_pair("visibility", Declaration::visibilityToString(_node.visibility())), make_pair("value", _node.value() ? toJson(*_node.value()) : Json::nullValue), make_pair("scope", idOrNull(_node.scope())), - make_pair("typeDescriptions", typePointerToJson(_node.annotation().type)) + make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true)) }; if (m_inEvent) attributes.push_back(make_pair("indexed", _node.isIndexed())); @@ -378,7 +378,7 @@ bool ASTJsonConverter::visit(ModifierInvocation const& _node) { setJsonNode(_node, "ModifierInvocation", { make_pair("modifierName", toJson(*_node.name())), - make_pair("arguments", toJson(_node.arguments())) + make_pair("arguments", _node.arguments() ? toJson(*_node.arguments()) : Json::nullValue) }); return false; } @@ -399,7 +399,7 @@ bool ASTJsonConverter::visit(ElementaryTypeName const& _node) { setJsonNode(_node, "ElementaryTypeName", { make_pair("name", _node.typeName().toString()), - make_pair("typeDescriptions", typePointerToJson(_node.annotation().type)) + make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true)) }); return false; } @@ -410,7 +410,7 @@ bool ASTJsonConverter::visit(UserDefinedTypeName const& _node) make_pair("name", namePathToString(_node.namePath())), make_pair("referencedDeclaration", idOrNull(_node.annotation().referencedDeclaration)), make_pair("contractScope", idOrNull(_node.annotation().contractScope)), - make_pair("typeDescriptions", typePointerToJson(_node.annotation().type)) + make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true)) }); return false; } @@ -425,7 +425,7 @@ bool ASTJsonConverter::visit(FunctionTypeName const& _node) make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.stateMutability() <= StateMutability::View), make_pair("parameterTypes", toJson(*_node.parameterTypeList())), make_pair("returnParameterTypes", toJson(*_node.returnParameterTypeList())), - make_pair("typeDescriptions", typePointerToJson(_node.annotation().type)) + make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true)) }); return false; } @@ -435,7 +435,7 @@ bool ASTJsonConverter::visit(Mapping const& _node) setJsonNode(_node, "Mapping", { make_pair("keyType", toJson(_node.keyType())), make_pair("valueType", toJson(_node.valueType())), - make_pair("typeDescriptions", typePointerToJson(_node.annotation().type)) + make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true)) }); return false; } @@ -445,7 +445,7 @@ bool ASTJsonConverter::visit(ArrayTypeName const& _node) setJsonNode(_node, "ArrayTypeName", { make_pair("baseType", toJson(_node.baseType())), make_pair("length", toJsonOrNull(_node.length())), - make_pair("typeDescriptions", typePointerToJson(_node.annotation().type)) + make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true)) }); return false; } diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h index 88b93699..29712f3b 100644 --- a/libsolidity/ast/ASTJsonConverter.h +++ b/libsolidity/ast/ASTJsonConverter.h @@ -152,7 +152,7 @@ private: } return tmp; } - static Json::Value typePointerToJson(TypePointer _tp); + static Json::Value typePointerToJson(TypePointer _tp, bool _short = false); static Json::Value typePointerToJson(std::shared_ptr<std::vector<TypePointer>> _tps); void appendExpressionAttributes( std::vector<std::pair<std::string, Json::Value>> &_attributes, diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h index 70ee997e..aeff6e4a 100644 --- a/libsolidity/ast/AST_accept.h +++ b/libsolidity/ast/AST_accept.h @@ -94,7 +94,8 @@ void InheritanceSpecifier::accept(ASTVisitor& _visitor) if (_visitor.visit(*this)) { m_baseName->accept(_visitor); - listAccept(m_arguments, _visitor); + if (m_arguments) + listAccept(*m_arguments, _visitor); } _visitor.endVisit(*this); } @@ -104,7 +105,8 @@ void InheritanceSpecifier::accept(ASTConstVisitor& _visitor) const if (_visitor.visit(*this)) { m_baseName->accept(_visitor); - listAccept(m_arguments, _visitor); + if (m_arguments) + listAccept(*m_arguments, _visitor); } _visitor.endVisit(*this); } @@ -262,7 +264,8 @@ void ModifierInvocation::accept(ASTVisitor& _visitor) if (_visitor.visit(*this)) { m_modifierName->accept(_visitor); - listAccept(m_arguments, _visitor); + if (m_arguments) + listAccept(*m_arguments, _visitor); } _visitor.endVisit(*this); } @@ -272,7 +275,8 @@ void ModifierInvocation::accept(ASTConstVisitor& _visitor) const if (_visitor.visit(*this)) { m_modifierName->accept(_visitor); - listAccept(m_arguments, _visitor); + if (m_arguments) + listAccept(*m_arguments, _visitor); } _visitor.endVisit(*this); } diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index c08e0e67..51739cb0 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -28,6 +28,7 @@ #include <libdevcore/CommonData.h> #include <libdevcore/SHA3.h> #include <libdevcore/UTF8.h> +#include <libdevcore/Algorithms.h> #include <boost/algorithm/string/join.hpp> #include <boost/algorithm/string/replace.hpp> @@ -43,6 +44,85 @@ using namespace std; using namespace dev; using namespace dev::solidity; +namespace +{ + +unsigned int mostSignificantBit(bigint const& _number) +{ +#if BOOST_VERSION < 105500 + solAssert(_number > 0, ""); + bigint number = _number; + unsigned int result = 0; + while (number != 0) + { + number >>= 1; + ++result; + } + return --result; +#else + return boost::multiprecision::msb(_number); +#endif +} + +/// Check whether (_base ** _exp) fits into 4096 bits. +bool fitsPrecisionExp(bigint const& _base, bigint const& _exp) +{ + if (_base == 0) + return true; + + solAssert(_base > 0, ""); + + size_t const bitsMax = 4096; + + unsigned mostSignificantBaseBit = mostSignificantBit(_base); + if (mostSignificantBaseBit == 0) // _base == 1 + return true; + if (mostSignificantBaseBit > bitsMax) // _base >= 2 ^ 4096 + return false; + + bigint bitsNeeded = _exp * (mostSignificantBaseBit + 1); + + return bitsNeeded <= bitsMax; +} + +/// Checks whether _mantissa * (X ** _exp) fits into 4096 bits, +/// where X is given indirectly via _log2OfBase = log2(X). +bool fitsPrecisionBaseX( + bigint const& _mantissa, + double _log2OfBase, + uint32_t _exp +) +{ + if (_mantissa == 0) + return true; + + solAssert(_mantissa > 0, ""); + + size_t const bitsMax = 4096; + + unsigned mostSignificantMantissaBit = mostSignificantBit(_mantissa); + if (mostSignificantMantissaBit > bitsMax) // _mantissa >= 2 ^ 4096 + return false; + + bigint bitsNeeded = mostSignificantMantissaBit + bigint(floor(double(_exp) * _log2OfBase)) + 1; + return bitsNeeded <= bitsMax; +} + +/// Checks whether _mantissa * (10 ** _expBase10) fits into 4096 bits. +bool fitsPrecisionBase10(bigint const& _mantissa, uint32_t _expBase10) +{ + double const log2Of10AwayFromZero = 3.3219280948873624; + return fitsPrecisionBaseX(_mantissa, log2Of10AwayFromZero, _expBase10); +} + +/// Checks whether _mantissa * (2 ** _expBase10) fits into 4096 bits. +bool fitsPrecisionBase2(bigint const& _mantissa, uint32_t _expBase2) +{ + return fitsPrecisionBaseX(_mantissa, 1.0, _expBase2); +} + +} + void StorageOffsets::computeOffsets(TypePointers const& _types) { bigint slotOffset = 0; @@ -208,9 +288,9 @@ TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type) case Token::UInt: return make_shared<IntegerType>(256, IntegerType::Modifier::Unsigned); case Token::Fixed: - return make_shared<FixedPointType>(128, 19, FixedPointType::Modifier::Signed); + return make_shared<FixedPointType>(128, 18, FixedPointType::Modifier::Signed); case Token::UFixed: - return make_shared<FixedPointType>(128, 19, FixedPointType::Modifier::Unsigned); + return make_shared<FixedPointType>(128, 18, FixedPointType::Modifier::Unsigned); case Token::Byte: return make_shared<FixedBytesType>(1); case Token::Address: @@ -232,11 +312,22 @@ TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type) TypePointer Type::fromElementaryTypeName(string const& _name) { + string name = _name; + DataLocation location = DataLocation::Storage; + if (boost::algorithm::ends_with(name, " memory")) + { + name = name.substr(0, name.length() - 7); + location = DataLocation::Memory; + } unsigned short firstNum; unsigned short secondNum; Token::Value token; - tie(token, firstNum, secondNum) = Token::fromIdentifierOrKeyword(_name); - return fromElementaryTypeName(ElementaryTypeNameToken(token, firstNum, secondNum)); + tie(token, firstNum, secondNum) = Token::fromIdentifierOrKeyword(name); + auto t = fromElementaryTypeName(ElementaryTypeNameToken(token, firstNum, secondNum)); + if (auto* ref = dynamic_cast<ReferenceType const*>(t.get())) + return ref->copyForLocation(location, true); + else + return t; } TypePointer Type::forLiteral(Literal const& _literal) @@ -304,7 +395,7 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition ); for (FunctionDefinition const* function: library.definedFunctions()) { - if (!function->isVisibleInDerivedContracts() || seenFunctions.count(function)) + if (!function->isVisibleAsLibraryMember() || seenFunctions.count(function)) continue; seenFunctions.insert(function); FunctionType funType(*function, false); @@ -327,7 +418,7 @@ bool isValidShiftAndAmountType(Token::Value _operator, Type const& _shiftAmountT else if (IntegerType const* otherInt = dynamic_cast<decltype(otherInt)>(&_shiftAmountType)) return !otherInt->isAddress(); else if (RationalNumberType const* otherRat = dynamic_cast<decltype(otherRat)>(&_shiftAmountType)) - return otherRat->integerType() && !otherRat->integerType()->isSigned(); + return !otherRat->isFractional() && otherRat->integerType() && !otherRat->integerType()->isSigned(); else return false; } @@ -677,31 +768,39 @@ tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal } else if (expPoint != _literal.value().end()) { - // parse the exponent + // Parse base and exponent. Checks numeric limit. bigint exp = bigint(string(expPoint + 1, _literal.value().end())); if (exp > numeric_limits<int32_t>::max() || exp < numeric_limits<int32_t>::min()) return make_tuple(false, rational(0)); - // parse the base + uint32_t expAbs = bigint(abs(exp)).convert_to<uint32_t>(); + + tuple<bool, rational> base = parseRational(string(_literal.value().begin(), expPoint)); + if (!get<0>(base)) return make_tuple(false, rational(0)); value = get<1>(base); if (exp < 0) { - exp *= -1; + if (!fitsPrecisionBase10(abs(value.denominator()), expAbs)) + return make_tuple(false, rational(0)); value /= boost::multiprecision::pow( bigint(10), - exp.convert_to<int32_t>() + expAbs ); } - else + else if (exp > 0) + { + if (!fitsPrecisionBase10(abs(value.numerator()), expAbs)) + return make_tuple(false, rational(0)); value *= boost::multiprecision::pow( bigint(10), - exp.convert_to<int32_t>() + expAbs ); + } } else { @@ -827,10 +926,10 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ { if (_other->category() == Category::Integer || _other->category() == Category::FixedPoint) { - auto mobile = mobileType(); - if (!mobile) + auto commonType = Type::commonType(shared_from_this(), _other); + if (!commonType) return TypePointer(); - return mobile->binaryOperatorResult(_operator, _other); + return commonType->binaryOperatorResult(_operator, _other); } else if (_other->category() != category()) return TypePointer(); @@ -900,16 +999,49 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ using boost::multiprecision::pow; if (other.isFractional()) return TypePointer(); - else if (abs(other.m_value) > numeric_limits<uint32_t>::max()) - return TypePointer(); // This will need too much memory to represent. - uint32_t exponent = abs(other.m_value).numerator().convert_to<uint32_t>(); - bigint numerator = pow(m_value.numerator(), exponent); - bigint denominator = pow(m_value.denominator(), exponent); - if (other.m_value >= 0) - value = rational(numerator, denominator); + solAssert(other.m_value.denominator() == 1, ""); + bigint const& exp = other.m_value.numerator(); + + // x ** 0 = 1 + // for 0, 1 and -1 the size of the exponent doesn't have to be restricted + if (exp == 0) + value = 1; + else if (m_value.numerator() == 0 || m_value == 1) + value = m_value; + else if (m_value == -1) + { + bigint isOdd = abs(exp) & bigint(1); + value = 1 - 2 * isOdd.convert_to<int>(); + } else - // invert - value = rational(denominator, numerator); + { + if (abs(exp) > numeric_limits<uint32_t>::max()) + return TypePointer(); // This will need too much memory to represent. + + uint32_t absExp = bigint(abs(exp)).convert_to<uint32_t>(); + + // Limit size to 4096 bits + if (!fitsPrecisionExp(abs(m_value.numerator()), absExp) || !fitsPrecisionExp(abs(m_value.denominator()), absExp)) + return TypePointer(); + + static auto const optimizedPow = [](bigint const& _base, uint32_t _exponent) -> bigint { + if (_base == 1) + return 1; + else if (_base == -1) + return 1 - 2 * int(_exponent & 1); + else + return pow(_base, _exponent); + }; + + bigint numerator = optimizedPow(m_value.numerator(), absExp); + bigint denominator = optimizedPow(m_value.denominator(), absExp); + + if (exp >= 0) + value = rational(numerator, denominator); + else + // invert + value = rational(denominator, numerator); + } break; } case Token::SHL: @@ -921,28 +1053,48 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ return TypePointer(); else if (other.m_value > numeric_limits<uint32_t>::max()) return TypePointer(); - uint32_t exponent = other.m_value.numerator().convert_to<uint32_t>(); - value = m_value.numerator() * pow(bigint(2), exponent); + if (m_value.numerator() == 0) + value = 0; + else + { + uint32_t exponent = other.m_value.numerator().convert_to<uint32_t>(); + if (!fitsPrecisionBase2(abs(m_value.numerator()), exponent)) + return TypePointer(); + value = m_value.numerator() * pow(bigint(2), exponent); + } break; } // NOTE: we're using >> (SAR) to denote right shifting. The type of the LValue // determines the resulting type and the type of shift (SAR or SHR). case Token::SAR: { - using boost::multiprecision::pow; + namespace mp = boost::multiprecision; if (fractional) return TypePointer(); else if (other.m_value < 0) return TypePointer(); else if (other.m_value > numeric_limits<uint32_t>::max()) return TypePointer(); - uint32_t exponent = other.m_value.numerator().convert_to<uint32_t>(); - value = rational(m_value.numerator() / pow(bigint(2), exponent), 1); + if (m_value.numerator() == 0) + value = 0; + else + { + uint32_t exponent = other.m_value.numerator().convert_to<uint32_t>(); + if (exponent > mostSignificantBit(mp::abs(m_value.numerator()))) + value = 0; + else + value = rational(m_value.numerator() / mp::pow(bigint(2), exponent), 1); + } break; } default: return TypePointer(); } + + // verify that numerator and denominator fit into 4096 bit after every operation + if (value.numerator() != 0 && max(mostSignificantBit(abs(value.numerator())), mostSignificantBit(abs(value.denominator()))) > 4096) + return TypePointer(); + return make_shared<RationalNumberType>(value); } } @@ -1262,6 +1414,8 @@ bool ContractType::isPayable() const TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const { + if (isSuper()) + return TypePointer{}; return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer(); } @@ -1969,25 +2123,19 @@ bool StructType::recursive() const { if (!m_recursive.is_initialized()) { - set<StructDefinition const*> structsSeen; - function<bool(StructType const*)> check = [&](StructType const* t) -> bool + auto visitor = [&](StructDefinition const& _struct, CycleDetector<StructDefinition>& _cycleDetector) { - StructDefinition const* str = &t->structDefinition(); - if (structsSeen.count(str)) - return true; - structsSeen.insert(str); - for (ASTPointer<VariableDeclaration> const& variable: str->members()) + for (ASTPointer<VariableDeclaration> const& variable: _struct.members()) { Type const* memberType = variable->annotation().type.get(); while (dynamic_cast<ArrayType const*>(memberType)) memberType = dynamic_cast<ArrayType const*>(memberType)->baseType().get(); if (StructType const* innerStruct = dynamic_cast<StructType const*>(memberType)) - if (check(innerStruct)) - return true; + if (_cycleDetector.run(innerStruct->structDefinition())) + return; } - return false; }; - m_recursive = check(this); + m_recursive = (CycleDetector<StructDefinition>(visitor).run(structDefinition()) != nullptr); } return *m_recursive; } @@ -2311,6 +2459,18 @@ vector<string> FunctionType::parameterNames() const return vector<string>(m_parameterNames.cbegin() + 1, m_parameterNames.cend()); } +TypePointers FunctionType::returnParameterTypesWithoutDynamicTypes() const +{ + TypePointers returnParameterTypes = m_returnParameterTypes; + + if (m_kind == Kind::External || m_kind == Kind::CallCode || m_kind == Kind::DelegateCall) + for (auto& param: returnParameterTypes) + if (param->isDynamicallySized() && !param->dataStoredIn(DataLocation::Storage)) + param = make_shared<InaccessibleDynamicType>(); + + return returnParameterTypes; +} + TypePointers FunctionType::parameterTypes() const { if (!bound()) @@ -2355,7 +2515,11 @@ string FunctionType::richIdentifier() const case Kind::ByteArrayPush: id += "bytearraypush"; break; case Kind::ObjectCreation: id += "objectcreation"; break; case Kind::Assert: id += "assert"; break; - case Kind::Require: id += "require";break; + case Kind::Require: id += "require"; break; + case Kind::ABIEncode: id += "abiencode"; break; + case Kind::ABIEncodePacked: id += "abiencodepacked"; break; + case Kind::ABIEncodeWithSelector: id += "abiencodewithselector"; break; + case Kind::ABIEncodeWithSignature: id += "abiencodewithsignature"; break; default: solAssert(false, "Unknown function location."); break; } id += "_" + stateMutabilityToString(m_stateMutability); @@ -2772,18 +2936,9 @@ FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary, bool _bound) kind = Kind::DelegateCall; } - TypePointers returnParameterTypes = m_returnParameterTypes; - if (kind != Kind::Internal) - { - // Alter dynamic types to be non-accessible. - for (auto& param: returnParameterTypes) - if (param->isDynamicallySized()) - param = make_shared<InaccessibleDynamicType>(); - } - return make_shared<FunctionType>( parameterTypes, - returnParameterTypes, + m_returnParameterTypes, m_parameterNames, m_returnParameterNames, kind, @@ -2875,7 +3030,7 @@ MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _current } if (contract.isLibrary()) for (FunctionDefinition const* function: contract.definedFunctions()) - if (function->isVisibleInDerivedContracts()) + if (function->isVisibleAsLibraryMember()) members.push_back(MemberList::Member( function->name(), FunctionType(*function).asMemberFunction(true), @@ -2985,6 +3140,8 @@ string MagicType::richIdentifier() const return "t_magic_message"; case Kind::Transaction: return "t_magic_transaction"; + case Kind::ABI: + return "t_magic_abi"; default: solAssert(false, "Unknown kind of magic"); } @@ -3025,6 +3182,45 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const {"origin", make_shared<IntegerType>(160, IntegerType::Modifier::Address)}, {"gasprice", make_shared<IntegerType>(256)} }); + case Kind::ABI: + return MemberList::MemberMap({ + {"encode", make_shared<FunctionType>( + TypePointers(), + TypePointers{make_shared<ArrayType>(DataLocation::Memory)}, + strings{}, + strings{}, + FunctionType::Kind::ABIEncode, + true, + StateMutability::Pure + )}, + {"encodePacked", make_shared<FunctionType>( + TypePointers(), + TypePointers{make_shared<ArrayType>(DataLocation::Memory)}, + strings{}, + strings{}, + FunctionType::Kind::ABIEncodePacked, + true, + StateMutability::Pure + )}, + {"encodeWithSelector", make_shared<FunctionType>( + TypePointers{make_shared<FixedBytesType>(4)}, + TypePointers{make_shared<ArrayType>(DataLocation::Memory)}, + strings{}, + strings{}, + FunctionType::Kind::ABIEncodeWithSelector, + true, + StateMutability::Pure + )}, + {"encodeWithSignature", make_shared<FunctionType>( + TypePointers{make_shared<ArrayType>(DataLocation::Memory, true)}, + TypePointers{make_shared<ArrayType>(DataLocation::Memory)}, + strings{}, + strings{}, + FunctionType::Kind::ABIEncodeWithSignature, + true, + StateMutability::Pure + )} + }); default: solAssert(false, "Unknown kind of magic."); } @@ -3040,6 +3236,8 @@ string MagicType::toString(bool) const return "msg"; case Kind::Transaction: return "tx"; + case Kind::ABI: + return "abi"; default: solAssert(false, "Unknown kind of magic."); } diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index c20a025f..345f84a1 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -150,6 +150,7 @@ public: /// @name Factory functions /// Factory functions that convert an AST @ref TypeName to a Type. static TypePointer fromElementaryTypeName(ElementaryTypeNameToken const& _type); + /// Converts a given elementary type name with optional suffix " memory" to a type pointer. static TypePointer fromElementaryTypeName(std::string const& _name); /// @} @@ -229,6 +230,9 @@ public: /// i.e. it behaves differently in lvalue context and in value context. virtual bool isValueType() const { return false; } virtual unsigned sizeOnStack() const { return 1; } + /// If it is possible to initialize such a value in memory by just writing zeros + /// of the size memoryHeadSize(). + virtual bool hasSimpleZeroValueInMemory() const { return true; } /// @returns the mobile (in contrast to static) type corresponding to the given type. /// This returns the corresponding IntegerType or FixedPointType for RationalNumberType /// and the pointer type for storage reference types. @@ -399,7 +403,7 @@ private: }; /** - * Integer and fixed point constants either literals or computed. + * Integer and fixed point constants either literals or computed. * Example expressions: 2, 3.14, 2+10.2, ~10. * There is one distinct type per value. */ @@ -411,7 +415,7 @@ public: /// @returns true if the literal is a valid integer. static std::tuple<bool, rational> isValidLiteral(Literal const& _literal); - + explicit RationalNumberType(rational const& _value): m_value(_value) {} @@ -432,7 +436,7 @@ public: /// @returns the smallest integer type that can hold the value or an empty pointer if not possible. std::shared_ptr<IntegerType const> integerType() const; - /// @returns the smallest fixed type that can hold the value or incurs the least precision loss. + /// @returns the smallest fixed type that can hold the value or incurs the least precision loss. /// If the integer part does not fit, returns an empty pointer. std::shared_ptr<FixedPointType const> fixedPointType() const; @@ -442,6 +446,9 @@ public: /// @returns true if the value is negative. bool isNegative() const { return m_value < 0; } + /// @returns true if the value is zero. + bool isZero() const { return m_value == 0; } + private: rational m_value; @@ -568,6 +575,7 @@ public: virtual TypePointer mobileType() const override { return copyForLocation(m_location, true); } virtual bool dataStoredIn(DataLocation _location) const override { return m_location == _location; } + virtual bool hasSimpleZeroValueInMemory() const override { return false; } /// Storage references can be pointers or bound references. In general, local variables are of /// pointer type, state variables are bound references. Assignments to pointers or deleting @@ -692,22 +700,27 @@ public: virtual bool operator==(Type const& _other) const override; virtual unsigned calldataEncodedSize(bool _padded ) const override { + solAssert(!isSuper(), ""); return encodingType()->calldataEncodedSize(_padded); } - virtual unsigned storageBytes() const override { return 20; } - virtual bool canLiveOutsideStorage() const override { return true; } + virtual unsigned storageBytes() const override { solAssert(!isSuper(), ""); return 20; } + virtual bool canLiveOutsideStorage() const override { return !isSuper(); } virtual unsigned sizeOnStack() const override { return m_super ? 0 : 1; } - virtual bool isValueType() const override { return true; } + virtual bool isValueType() const override { return !isSuper(); } virtual std::string toString(bool _short) const override; virtual std::string canonicalName() const override; virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; virtual TypePointer encodingType() const override { + if (isSuper()) + return TypePointer{}; return std::make_shared<IntegerType>(160, IntegerType::Modifier::Address); } virtual TypePointer interfaceType(bool _inLibrary) const override { + if (isSuper()) + return TypePointer{}; return _inLibrary ? shared_from_this() : encodingType(); } @@ -768,7 +781,7 @@ public: virtual std::string canonicalName() const override; virtual std::string signatureInExternalFunction(bool _structsByName) const override; - /// @returns a function that peforms the type conversion between a list of struct members + /// @returns a function that performs the type conversion between a list of struct members /// and a memory struct of this type. FunctionTypePointer constructorType() const; @@ -850,6 +863,7 @@ public: virtual u256 storageSize() const override; virtual bool canLiveOutsideStorage() const override { return false; } virtual unsigned sizeOnStack() const override; + virtual bool hasSimpleZeroValueInMemory() const override { return false; } virtual TypePointer mobileType() const override; /// Converts components to their temporary types and performs some wildcard matching. virtual TypePointer closestTemporaryType(TypePointer const& _targetType) const override; @@ -903,6 +917,10 @@ public: ObjectCreation, ///< array creation using new Assert, ///< assert() Require, ///< require() + ABIEncode, + ABIEncodePacked, + ABIEncodeWithSelector, + ABIEncodeWithSignature, GasLeft ///< gasleft() }; @@ -973,6 +991,9 @@ public: TypePointers parameterTypes() const; std::vector<std::string> parameterNames() const; TypePointers const& returnParameterTypes() const { return m_returnParameterTypes; } + /// @returns the list of return parameter types. All dynamically-sized types (this excludes + /// storage pointers) are replaced by InaccessibleDynamicType instances. + TypePointers returnParameterTypesWithoutDynamicTypes() const; std::vector<std::string> const& returnParameterNames() const { return m_returnParameterNames; } /// @returns the "self" parameter type for a bound function TypePointer const& selfType() const; @@ -991,6 +1012,7 @@ public: virtual bool isValueType() const override { return true; } virtual bool canLiveOutsideStorage() const override { return m_kind == Kind::Internal || m_kind == Kind::External; } virtual unsigned sizeOnStack() const override; + virtual bool hasSimpleZeroValueInMemory() const override { return false; } virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; virtual TypePointer encodingType() const override; virtual TypePointer interfaceType(bool _inLibrary) const override; @@ -1024,7 +1046,7 @@ public: return *m_declaration; } bool hasDeclaration() const { return !!m_declaration; } - /// @returns true if the the result of this function only depends on its arguments + /// @returns true if the result of this function only depends on its arguments /// and it does not modify the state. /// Currently, this will only return true for internal functions like keccak and ecrecover. bool isPure() const; @@ -1034,14 +1056,14 @@ public: ASTPointer<ASTString> documentation() const; /// true iff arguments are to be padded to multiples of 32 bytes for external calls - bool padArguments() const { return !(m_kind == Kind::SHA3 || m_kind == Kind::SHA256 || m_kind == Kind::RIPEMD160); } + bool padArguments() const { return !(m_kind == Kind::SHA3 || m_kind == Kind::SHA256 || m_kind == Kind::RIPEMD160 || m_kind == Kind::ABIEncodePacked); } bool takesArbitraryParameters() const { return m_arbitraryParameters; } bool gasSet() const { return m_gasSet; } bool valueSet() const { return m_valueSet; } bool bound() const { return m_bound; } /// @returns a copy of this type, where gas or value are set manually. This will never set one - /// of the parameters to fals. + /// of the parameters to false. TypePointer copyAndSetGasOrValue(bool _setGas, bool _setValue) const; /// @returns a copy of this function type where all return parameters of dynamic size are @@ -1096,6 +1118,8 @@ public: return _inLibrary ? shared_from_this() : TypePointer(); } virtual bool dataStoredIn(DataLocation _location) const override { return _location == DataLocation::Storage; } + /// Cannot be stored in memory, but just in case. + virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } TypePointer const& keyType() const { return m_keyType; } TypePointer const& valueType() const { return m_valueType; } @@ -1124,6 +1148,7 @@ public: virtual u256 storageSize() const override; virtual bool canLiveOutsideStorage() const override { return false; } virtual unsigned sizeOnStack() const override; + virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } virtual std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; } virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; @@ -1146,6 +1171,7 @@ public: virtual u256 storageSize() const override; virtual bool canLiveOutsideStorage() const override { return false; } virtual unsigned sizeOnStack() const override { return 0; } + virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual std::string toString(bool _short) const override; @@ -1171,6 +1197,7 @@ public: virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } virtual bool canLiveOutsideStorage() const override { return true; } + virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } virtual unsigned sizeOnStack() const override { return 0; } virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override; @@ -1187,7 +1214,7 @@ private: class MagicType: public Type { public: - enum class Kind { Block, Message, Transaction }; + enum class Kind { Block, Message, Transaction, ABI }; virtual Category category() const override { return Category::Magic; } explicit MagicType(Kind _kind): m_kind(_kind) {} @@ -1201,6 +1228,7 @@ public: virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } virtual bool canLiveOutsideStorage() const override { return true; } + virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } virtual unsigned sizeOnStack() const override { return 0; } virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override; @@ -1230,6 +1258,7 @@ public: virtual bool canLiveOutsideStorage() const override { return false; } virtual bool isValueType() const override { return true; } virtual unsigned sizeOnStack() const override { return 1; } + virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } virtual std::string toString(bool) const override { return "inaccessible dynamic type"; } virtual TypePointer decodingType() const override { return std::make_shared<IntegerType>(256); } }; |