Merge branch 'develop' into anurag_issue_3667

This commit is contained in:
Anurag Dashputre
2018-08-23 11:56:45 +05:30
committed by GitHub
217 changed files with 3814 additions and 924 deletions
+103 -20
View File
@@ -397,7 +397,7 @@ SourceUnit const& Scopable::sourceUnit() const
{
ASTNode const* s = scope();
solAssert(s, "");
// will not always be a declaratoion
// will not always be a declaration
while (dynamic_cast<Scopable const*>(s) && dynamic_cast<Scopable const*>(s)->scope())
s = dynamic_cast<Scopable const*>(s)->scope();
return dynamic_cast<SourceUnit const&>(*s);
@@ -418,6 +418,7 @@ bool VariableDeclaration::isLocalVariable() const
{
auto s = scope();
return
dynamic_cast<FunctionTypeName const*>(s) ||
dynamic_cast<CallableDeclaration const*>(s) ||
dynamic_cast<Block const*>(s) ||
dynamic_cast<ForStatement const*>(s);
@@ -425,14 +426,18 @@ bool VariableDeclaration::isLocalVariable() const
bool VariableDeclaration::isCallableParameter() const
{
auto const* callable = dynamic_cast<CallableDeclaration const*>(scope());
if (!callable)
return false;
for (auto const& variable: callable->parameters())
if (variable.get() == this)
return true;
if (callable->returnParameterList())
for (auto const& variable: callable->returnParameterList()->parameters())
if (isReturnParameter())
return true;
vector<ASTPointer<VariableDeclaration>> const* parameters = nullptr;
if (auto const* funTypeName = dynamic_cast<FunctionTypeName const*>(scope()))
parameters = &funTypeName->parameterTypes();
else if (auto const* callable = dynamic_cast<CallableDeclaration const*>(scope()))
parameters = &callable->parameters();
if (parameters)
for (auto const& variable: *parameters)
if (variable.get() == this)
return true;
return false;
@@ -445,11 +450,16 @@ bool VariableDeclaration::isLocalOrReturn() const
bool VariableDeclaration::isReturnParameter() const
{
auto const* callable = dynamic_cast<CallableDeclaration const*>(scope());
if (!callable)
return false;
if (callable->returnParameterList())
for (auto const& variable: callable->returnParameterList()->parameters())
vector<ASTPointer<VariableDeclaration>> const* returnParameters = nullptr;
if (auto const* funTypeName = dynamic_cast<FunctionTypeName const*>(scope()))
returnParameters = &funTypeName->returnParameterTypes();
else if (auto const* callable = dynamic_cast<CallableDeclaration const*>(scope()))
if (callable->returnParameterList())
returnParameters = &callable->returnParameterList()->parameters();
if (returnParameters)
for (auto const& variable: *returnParameters)
if (variable.get() == this)
return true;
return false;
@@ -457,15 +467,88 @@ bool VariableDeclaration::isReturnParameter() const
bool VariableDeclaration::isExternalCallableParameter() const
{
auto const* callable = dynamic_cast<CallableDeclaration const*>(scope());
if (!callable || callable->visibility() != Declaration::Visibility::External)
if (!isCallableParameter())
return false;
for (auto const& variable: callable->parameters())
if (variable.get() == this)
return true;
if (auto const* callable = dynamic_cast<CallableDeclaration const*>(scope()))
if (callable->visibility() == Declaration::Visibility::External)
return !isReturnParameter();
return false;
}
bool VariableDeclaration::isInternalCallableParameter() const
{
if (!isCallableParameter())
return false;
if (auto const* funTypeName = dynamic_cast<FunctionTypeName const*>(scope()))
return funTypeName->visibility() == Declaration::Visibility::Internal;
else if (auto const* callable = dynamic_cast<CallableDeclaration const*>(scope()))
return callable->visibility() <= Declaration::Visibility::Internal;
return false;
}
bool VariableDeclaration::isLibraryFunctionParameter() const
{
if (!isCallableParameter())
return false;
if (auto const* funDef = dynamic_cast<FunctionDefinition const*>(scope()))
return dynamic_cast<ContractDefinition const&>(*funDef->scope()).isLibrary();
else
return false;
}
bool VariableDeclaration::isEventParameter() const
{
return dynamic_cast<EventDefinition const*>(scope()) != nullptr;
}
bool VariableDeclaration::hasReferenceOrMappingType() const
{
solAssert(typeName(), "");
solAssert(typeName()->annotation().type, "Can only be called after reference resolution");
TypePointer const& type = typeName()->annotation().type;
return type->category() == Type::Category::Mapping || dynamic_cast<ReferenceType const*>(type.get());
}
set<VariableDeclaration::Location> VariableDeclaration::allowedDataLocations() const
{
using Location = VariableDeclaration::Location;
if (!hasReferenceOrMappingType() || isStateVariable() || isEventParameter())
return set<Location>{ Location::Default };
else if (isStateVariable() && isConstant())
return set<Location>{ Location::Memory };
else if (isExternalCallableParameter())
{
set<Location> locations{ Location::CallData };
if (isLibraryFunctionParameter())
locations.insert(Location::Storage);
return locations;
}
else if (isCallableParameter())
{
set<Location> locations{ Location::Memory };
if (isInternalCallableParameter() || isLibraryFunctionParameter())
locations.insert(Location::Storage);
return locations;
}
else if (isLocalVariable())
{
solAssert(typeName(), "");
solAssert(typeName()->annotation().type, "Can only be called after reference resolution");
if (typeName()->annotation().type->category() == Type::Category::Mapping)
return set<Location>{ Location::Storage };
else
// TODO: add Location::Calldata once implemented for local variables.
return set<Location>{ Location::Memory, Location::Storage };
}
else
// Struct members etc.
return set<Location>{ Location::Default };
}
TypePointer VariableDeclaration::type() const
{
return annotation().type;
@@ -580,7 +663,7 @@ bool Literal::passesAddressChecksum() const
return dev::passesAddressChecksum(value(), true);
}
std::string Literal::getChecksummedAddress() const
string Literal::getChecksummedAddress() const
{
solAssert(isHexNumber(), "Expected hex number");
/// Pad literal to be a proper hex address.
+16
View File
@@ -685,6 +685,8 @@ public:
virtual bool isLValue() const override;
virtual bool isPartOfExternalInterface() const override { return isPublic(); }
/// @returns true iff this variable is the parameter (or return parameter) of a function
/// (or function type name or event) or declared inside a function body.
bool isLocalVariable() const;
/// @returns true if this variable is a parameter or return parameter of a function.
bool isCallableParameter() const;
@@ -693,13 +695,27 @@ public:
/// @returns true if this variable is a local variable or return parameter.
bool isLocalOrReturn() const;
/// @returns true if this variable is a parameter (not return parameter) of an external function.
/// This excludes parameters of external function type names.
bool isExternalCallableParameter() const;
/// @returns true if this variable is a parameter or return parameter of an internal function
/// or a function type of internal visibility.
bool isInternalCallableParameter() const;
/// @returns true iff this variable is a parameter(or return parameter of a library function
bool isLibraryFunctionParameter() const;
/// @returns true if the type of the variable does not need to be specified, i.e. it is declared
/// in the body of a function or modifier.
/// @returns true if this variable is a parameter of an event.
bool isEventParameter() const;
/// @returns true if the type of the variable is a reference or mapping type, i.e.
/// array, struct or mapping. These types can take a data location (and often require it).
/// Can only be called after reference resolution.
bool hasReferenceOrMappingType() const;
bool isStateVariable() const { return m_isStateVariable; }
bool isIndexed() const { return m_isIndexed; }
bool isConstant() const { return m_isConstant; }
Location referenceLocation() const { return m_location; }
/// @returns a set of allowed storage locations for the variable.
std::set<Location> allowedDataLocations() const;
virtual TypePointer type() const override;
+135 -52
View File
@@ -355,13 +355,7 @@ TypePointer Type::forLiteral(Literal const& _literal)
case Token::FalseLiteral:
return make_shared<BoolType>();
case Token::Number:
{
tuple<bool, rational> validLiteral = RationalNumberType::isValidLiteral(_literal);
if (get<0>(validLiteral) == true)
return make_shared<RationalNumberType>(get<1>(validLiteral));
else
return TypePointer();
}
return RationalNumberType::forLiteral(_literal);
case Token::StringLiteral:
return make_shared<StringLiteralType>(_literal);
default:
@@ -400,17 +394,17 @@ TypePointer Type::fullEncodingType(bool _inLibraryCall, bool _encoderV2, bool _p
encodingType = encodingType->interfaceType(_inLibraryCall);
if (encodingType)
encodingType = encodingType->encodingType();
if (auto structType = dynamic_cast<StructType const*>(encodingType.get()))
{
// Structs are fine in the following circumstances:
// - ABIv2 without packed encoding or,
// - storage struct for a library
if (!(
(_encoderV2 && !_packed) ||
(structType->location() == DataLocation::Storage && _inLibraryCall)
))
// Structs are fine in the following circumstances:
// - ABIv2 without packed encoding or,
// - storage struct for a library
if (_inLibraryCall && encodingType->dataStoredIn(DataLocation::Storage))
return encodingType;
TypePointer baseType = encodingType;
while (auto const* arrayType = dynamic_cast<ArrayType const*>(baseType.get()))
baseType = arrayType->baseType();
if (dynamic_cast<StructType const*>(baseType.get()))
if (!_encoderV2 || _packed)
return TypePointer();
}
return encodingType;
}
@@ -627,6 +621,7 @@ MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) cons
{"callcode", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool"}, FunctionType::Kind::BareCallCode, false, StateMutability::Payable)},
{"delegatecall", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool"}, FunctionType::Kind::BareDelegateCall, false)},
{"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Kind::Send)},
{"staticcall", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool"}, FunctionType::Kind::BareStaticCall, false, StateMutability::View)},
{"transfer", make_shared<FunctionType>(strings{"uint"}, strings(), FunctionType::Kind::Transfer)}
};
else
@@ -779,6 +774,30 @@ tuple<bool, rational> RationalNumberType::parseRational(string const& _value)
}
}
TypePointer RationalNumberType::forLiteral(Literal const& _literal)
{
solAssert(_literal.token() == Token::Number, "");
tuple<bool, rational> validLiteral = isValidLiteral(_literal);
if (get<0>(validLiteral))
{
TypePointer compatibleBytesType;
if (_literal.isHexNumber())
{
size_t digitCount = count_if(
_literal.value().begin() + 2, // skip "0x"
_literal.value().end(),
[](unsigned char _c) -> bool { return isxdigit(_c); }
);
// require even number of digits
if (!(digitCount & 1))
compatibleBytesType = make_shared<FixedBytesType>(digitCount / 2);
}
return make_shared<RationalNumberType>(get<1>(validLiteral), compatibleBytesType);
}
return TypePointer();
}
tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal)
{
rational value;
@@ -918,14 +937,7 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const
return false;
}
case Category::FixedBytes:
{
FixedBytesType const& fixedBytes = dynamic_cast<FixedBytesType const&>(_convertTo);
if (isFractional())
return false;
if (integerType())
return fixedBytes.numBytes() * 8 >= integerType()->numBits();
return false;
}
return (m_value == rational(0)) || (m_compatibleBytesType && *m_compatibleBytesType == _convertTo);
default:
return false;
}
@@ -933,11 +945,15 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const
bool RationalNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
TypePointer mobType = mobileType();
return
(mobType && mobType->isExplicitlyConvertibleTo(_convertTo)) ||
(!isFractional() && _convertTo.category() == Category::FixedBytes)
;
if (isImplicitlyConvertibleTo(_convertTo))
return true;
else if (_convertTo.category() != Category::FixedBytes)
{
TypePointer mobType = mobileType();
return (mobType && mobType->isExplicitlyConvertibleTo(_convertTo));
}
else
return false;
}
TypePointer RationalNumberType::unaryOperatorResult(Token::Value _operator) const
@@ -1263,7 +1279,8 @@ shared_ptr<FixedPointType const> RationalNumberType::fixedPointType() const
return shared_ptr<FixedPointType const>();
// This means we round towards zero for positive and negative values.
bigint v = value.numerator() / value.denominator();
if (negative)
if (negative && v != 0)
// modify value to satisfy bit requirements for negative numbers:
// add one bit for sign and decrement because negative numbers can be larger
v = (v - 1) << 1;
@@ -2502,6 +2519,7 @@ string FunctionType::richIdentifier() const
case Kind::BareCall: id += "barecall"; break;
case Kind::BareCallCode: id += "barecallcode"; break;
case Kind::BareDelegateCall: id += "baredelegatecall"; break;
case Kind::BareStaticCall: id += "barestaticcall"; break;
case Kind::Creation: id += "creation"; break;
case Kind::Send: id += "send"; break;
case Kind::Transfer: id += "transfer"; break;
@@ -2533,6 +2551,7 @@ string FunctionType::richIdentifier() const
case Kind::ABIEncodePacked: id += "abiencodepacked"; break;
case Kind::ABIEncodeWithSelector: id += "abiencodewithselector"; break;
case Kind::ABIEncodeWithSignature: id += "abiencodewithsignature"; break;
case Kind::ABIDecode: id += "abidecode"; break;
}
id += "_" + stateMutabilityToString(m_stateMutability);
id += identifierList(m_parameterTypes) + "returns" + identifierList(m_returnParameterTypes);
@@ -2549,28 +2568,10 @@ bool FunctionType::operator==(Type const& _other) const
{
if (_other.category() != category())
return false;
FunctionType const& other = dynamic_cast<FunctionType const&>(_other);
if (
m_kind != other.m_kind ||
m_stateMutability != other.stateMutability() ||
m_parameterTypes.size() != other.m_parameterTypes.size() ||
m_returnParameterTypes.size() != other.m_returnParameterTypes.size()
)
if (!equalExcludingStateMutability(other))
return false;
auto typeCompare = [](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; };
if (
!equal(m_parameterTypes.cbegin(), m_parameterTypes.cend(), other.m_parameterTypes.cbegin(), typeCompare) ||
!equal(m_returnParameterTypes.cbegin(), m_returnParameterTypes.cend(), other.m_returnParameterTypes.cbegin(), typeCompare)
)
return false;
//@todo this is ugly, but cannot be prevented right now
if (m_gasSet != other.m_gasSet || m_valueSet != other.m_valueSet)
return false;
if (bound() != other.bound())
return false;
if (bound() && *selfType() != *other.selfType())
if (m_stateMutability != other.stateMutability())
return false;
return true;
}
@@ -2586,6 +2587,31 @@ bool FunctionType::isExplicitlyConvertibleTo(Type const& _convertTo) const
return _convertTo.category() == category();
}
bool FunctionType::isImplicitlyConvertibleTo(Type const& _convertTo) const
{
if (_convertTo.category() != category())
return false;
FunctionType const& convertTo = dynamic_cast<FunctionType const&>(_convertTo);
if (!equalExcludingStateMutability(convertTo))
return false;
// non-payable should not be convertible to payable
if (m_stateMutability != StateMutability::Payable && convertTo.stateMutability() == StateMutability::Payable)
return false;
// payable should be convertible to non-payable, because you are free to pay 0 ether
if (m_stateMutability == StateMutability::Payable && convertTo.stateMutability() == StateMutability::NonPayable)
return true;
// e.g. pure should be convertible to view, but not the other way around.
if (m_stateMutability > convertTo.stateMutability())
return false;
return true;
}
TypePointer FunctionType::unaryOperatorResult(Token::Value _operator) const
{
if (_operator == Token::Value::Delete)
@@ -2676,6 +2702,7 @@ unsigned FunctionType::sizeOnStack() const
case Kind::BareCall:
case Kind::BareCallCode:
case Kind::BareDelegateCall:
case Kind::BareStaticCall:
case Kind::Internal:
case Kind::ArrayPush:
case Kind::ArrayPop:
@@ -2743,6 +2770,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
case Kind::BareCall:
case Kind::BareCallCode:
case Kind::BareDelegateCall:
case Kind::BareStaticCall:
{
MemberList::MemberMap members;
if (m_kind == Kind::External)
@@ -2843,6 +2871,38 @@ bool FunctionType::hasEqualParameterTypes(FunctionType const& _other) const
);
}
bool FunctionType::hasEqualReturnTypes(FunctionType const& _other) const
{
if (m_returnParameterTypes.size() != _other.m_returnParameterTypes.size())
return false;
return equal(
m_returnParameterTypes.cbegin(),
m_returnParameterTypes.cend(),
_other.m_returnParameterTypes.cbegin(),
[](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; }
);
}
bool FunctionType::equalExcludingStateMutability(FunctionType const& _other) const
{
if (m_kind != _other.m_kind)
return false;
if (!hasEqualParameterTypes(_other) || !hasEqualReturnTypes(_other))
return false;
//@todo this is ugly, but cannot be prevented right now
if (m_gasSet != _other.m_gasSet || m_valueSet != _other.m_valueSet)
return false;
if (bound() != _other.bound())
return false;
solAssert(!bound() || *selfType() == *_other.selfType(), "");
return true;
}
bool FunctionType::isBareCall() const
{
switch (m_kind)
@@ -2850,6 +2910,7 @@ bool FunctionType::isBareCall() const
case Kind::BareCall:
case Kind::BareCallCode:
case Kind::BareDelegateCall:
case Kind::BareStaticCall:
case Kind::ECRecover:
case Kind::SHA256:
case Kind::RIPEMD160:
@@ -2863,6 +2924,17 @@ string FunctionType::externalSignature() const
{
solAssert(m_declaration != nullptr, "External signature of function needs declaration");
solAssert(!m_declaration->name().empty(), "Fallback function has no signature.");
switch (kind())
{
case Kind::Internal:
case Kind::External:
case Kind::CallCode:
case Kind::DelegateCall:
case Kind::Event:
break;
default:
solAssert(false, "Invalid function type for requesting external signature.");
}
bool const inLibrary = dynamic_cast<ContractDefinition const&>(*m_declaration->scope()).isLibrary();
FunctionTypePointer external = interfaceFunctionType();
@@ -2899,7 +2971,8 @@ bool FunctionType::isPure() const
m_kind == Kind::ABIEncode ||
m_kind == Kind::ABIEncodePacked ||
m_kind == Kind::ABIEncodeWithSelector ||
m_kind == Kind::ABIEncodeWithSignature;
m_kind == Kind::ABIEncodeWithSignature ||
m_kind == Kind::ABIDecode;
}
TypePointers FunctionType::parseElementaryTypeVector(strings const& _types)
@@ -2992,6 +3065,7 @@ bool FunctionType::padArguments() const
case Kind::BareCall:
case Kind::BareCallCode:
case Kind::BareDelegateCall:
case Kind::BareStaticCall:
case Kind::SHA256:
case Kind::RIPEMD160:
case Kind::KECCAK256:
@@ -3253,6 +3327,15 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
FunctionType::Kind::ABIEncodeWithSignature,
true,
StateMutability::Pure
)},
{"decode", make_shared<FunctionType>(
TypePointers(),
TypePointers(),
strings{},
strings{},
FunctionType::Kind::ABIDecode,
true,
StateMutability::Pure
)}
});
default:
+24 -8
View File
@@ -423,12 +423,12 @@ public:
virtual Category category() const override { return Category::RationalNumber; }
/// @returns true if the literal is a valid integer.
static std::tuple<bool, rational> isValidLiteral(Literal const& _literal);
static TypePointer forLiteral(Literal const& _literal);
explicit RationalNumberType(rational const& _value):
m_value(_value)
explicit RationalNumberType(rational const& _value, TypePointer const& _compatibleBytesType = TypePointer()):
m_value(_value), m_compatibleBytesType(_compatibleBytesType)
{}
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
@@ -446,7 +446,8 @@ 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,
/// unless the value was truncated, then a suitable type will be chosen to indicate such event.
/// If the integer part does not fit, returns an empty pointer.
std::shared_ptr<FixedPointType const> fixedPointType() const;
@@ -462,6 +463,13 @@ public:
private:
rational m_value;
/// Bytes type to which the rational can be explicitly converted.
/// Empty for all rationals that are not directly parsed from hex literals.
TypePointer m_compatibleBytesType;
/// @returns true if the literal is a valid integer.
static std::tuple<bool, rational> isValidLiteral(Literal const& _literal);
/// @returns true if the literal is a valid rational number.
static std::tuple<bool, rational> parseRational(std::string const& _value);
@@ -896,6 +904,7 @@ public:
BareCall, ///< CALL without function hash
BareCallCode, ///< CALLCODE without function hash
BareDelegateCall, ///< DELEGATECALL without function hash
BareStaticCall, ///< STATICCALL without function hash
Creation, ///< external call using CREATE
Send, ///< CALL, but without data and gas
Transfer, ///< CALL, but without data and throws on error
@@ -926,7 +935,8 @@ public:
ABIEncodePacked,
ABIEncodeWithSelector,
ABIEncodeWithSignature,
GasLeft ///< gasleft()
ABIDecode,
GasLeft, ///< gasleft()
};
virtual Category category() const override { return Category::Function; }
@@ -1005,6 +1015,7 @@ public:
virtual std::string richIdentifier() const override;
virtual bool operator==(Type const& _other) const override;
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override;
@@ -1034,10 +1045,14 @@ public:
/// @param _selfType if the function is bound, this has to be supplied and is the type of the
/// expression the function is called on.
bool canTakeArguments(TypePointers const& _arguments, TypePointer const& _selfType = TypePointer()) const;
/// @returns true if the types of parameters are equal (doesn't check return parameter types)
/// @returns true if the types of parameters are equal (does not check return parameter types)
bool hasEqualParameterTypes(FunctionType const& _other) const;
/// @returns true iff the return types are equal (does not check parameter types)
bool hasEqualReturnTypes(FunctionType const& _other) const;
/// @returns true iff the function type is equal to the given type, ignoring state mutability differences.
bool equalExcludingStateMutability(FunctionType const& _other) const;
/// @returns true if the ABI is used for this call (only meaningful for external calls)
/// @returns true if the ABI is NOT used for this call (only meaningful for external calls)
bool isBareCall() const;
Kind const& kind() const { return m_kind; }
StateMutability stateMutability() const { return m_stateMutability; }
@@ -1076,6 +1091,7 @@ public:
case FunctionType::Kind::BareCall:
case FunctionType::Kind::BareCallCode:
case FunctionType::Kind::BareDelegateCall:
case FunctionType::Kind::BareStaticCall:
return true;
default:
return false;