From 36fe571576a7e5af91d9f95ff0a2252394720a34 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Wed, 4 Mar 2015 11:41:15 +0100 Subject: [PATCH 01/14] start of cleanup --- Token.h | 99 +++++++++++++++++++------------------------------------ Types.cpp | 2 +- 2 files changed, 34 insertions(+), 67 deletions(-) diff --git a/Token.h b/Token.h index 85979b566..7c1de60bb 100644 --- a/Token.h +++ b/Token.h @@ -253,75 +253,42 @@ namespace solidity K(UInt248, "uint248", 0) \ K(UInt256, "uint256", 0) \ K(Hash, "hash", 0) \ - K(Hash8, "hash8", 0) \ - K(Hash16, "hash16", 0) \ - K(Hash24, "hash24", 0) \ - K(Hash32, "hash32", 0) \ - K(Hash40, "hash40", 0) \ - K(Hash48, "hash48", 0) \ - K(Hash56, "hash56", 0) \ - K(Hash64, "hash64", 0) \ - K(Hash72, "hash72", 0) \ - K(Hash80, "hash80", 0) \ - K(Hash88, "hash88", 0) \ - K(Hash96, "hash96", 0) \ - K(Hash104, "hash104", 0) \ - K(Hash112, "hash112", 0) \ - K(Hash120, "hash120", 0) \ - K(Hash128, "hash128", 0) \ - K(Hash136, "hash136", 0) \ - K(Hash144, "hash144", 0) \ - K(Hash152, "hash152", 0) \ - K(Hash160, "hash160", 0) \ - K(Hash168, "hash168", 0) \ - K(Hash176, "hash178", 0) \ - K(Hash184, "hash184", 0) \ - K(Hash192, "hash192", 0) \ - K(Hash200, "hash200", 0) \ - K(Hash208, "hash208", 0) \ - K(Hash216, "hash216", 0) \ - K(Hash224, "hash224", 0) \ - K(Hash232, "hash232", 0) \ - K(Hash240, "hash240", 0) \ - K(Hash248, "hash248", 0) \ - K(Hash256, "hash256", 0) \ + K(Bytes, "bytes", 0) \ + K(Bytes8, "bytes8", 0) \ + K(Bytes16, "bytes16", 0) \ + K(Bytes24, "bytes24", 0) \ + K(Bytes32, "bytes32", 0) \ + K(Bytes40, "bytes40", 0) \ + K(Bytes48, "bytes48", 0) \ + K(Bytes56, "bytes56", 0) \ + K(Bytes64, "bytes64", 0) \ + K(Bytes72, "bytes72", 0) \ + K(Bytes80, "bytes80", 0) \ + K(Bytes88, "bytes88", 0) \ + K(Bytes96, "bytes96", 0) \ + K(Bytes104, "bytes104", 0) \ + K(Bytes112, "bytes112", 0) \ + K(Bytes120, "bytes120", 0) \ + K(Bytes128, "bytes128", 0) \ + K(Bytes136, "bytes136", 0) \ + K(Bytes144, "bytes144", 0) \ + K(Bytes152, "bytes152", 0) \ + K(Bytes160, "bytes160", 0) \ + K(Bytes168, "bytes168", 0) \ + K(Bytes176, "bytes178", 0) \ + K(Bytes184, "bytes184", 0) \ + K(Bytes192, "bytes192", 0) \ + K(Bytes200, "bytes200", 0) \ + K(Bytes208, "bytes208", 0) \ + K(Bytes216, "bytes216", 0) \ + K(Bytes224, "bytes224", 0) \ + K(Bytes232, "bytes232", 0) \ + K(Bytes240, "bytes240", 0) \ + K(Bytes248, "bytes248", 0) \ + K(Bytes256, "bytes256", 0) \ K(Address, "address", 0) \ K(Bool, "bool", 0) \ - K(Bytes, "bytes", 0) \ K(StringType, "string", 0) \ - K(String0, "string0", 0) \ - K(String1, "string1", 0) \ - K(String2, "string2", 0) \ - K(String3, "string3", 0) \ - K(String4, "string4", 0) \ - K(String5, "string5", 0) \ - K(String6, "string6", 0) \ - K(String7, "string7", 0) \ - K(String8, "string8", 0) \ - K(String9, "string9", 0) \ - K(String10, "string10", 0) \ - K(String11, "string11", 0) \ - K(String12, "string12", 0) \ - K(String13, "string13", 0) \ - K(String14, "string14", 0) \ - K(String15, "string15", 0) \ - K(String16, "string16", 0) \ - K(String17, "string17", 0) \ - K(String18, "string18", 0) \ - K(String19, "string19", 0) \ - K(String20, "string20", 0) \ - K(String21, "string21", 0) \ - K(String22, "string22", 0) \ - K(String23, "string23", 0) \ - K(String24, "string24", 0) \ - K(String25, "string25", 0) \ - K(String26, "string26", 0) \ - K(String27, "string27", 0) \ - K(String28, "string28", 0) \ - K(String29, "string29", 0) \ - K(String30, "string30", 0) \ - K(String31, "string31", 0) \ - K(String32, "string32", 0) \ K(Text, "text", 0) \ K(Real, "real", 0) \ K(UReal, "ureal", 0) \ diff --git a/Types.cpp b/Types.cpp index 454d79d9b..73cd09d6f 100644 --- a/Types.cpp +++ b/Types.cpp @@ -39,7 +39,7 @@ TypePointer Type::fromElementaryTypeName(Token::Value _typeToken) { solAssert(Token::isElementaryTypeName(_typeToken), "Elementary type name expected."); - if (Token::Int <= _typeToken && _typeToken <= Token::Hash256) + if (Token::Int <= _typeToken && _typeToken <= Token::Bytes256) { int offset = _typeToken - Token::Int; int bytes = offset % 33; From bede2f2ad7eb023018ab2faac17286fdee82b03c Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Wed, 4 Mar 2015 17:43:40 +0100 Subject: [PATCH 02/14] More changes towards getting rid of HashXX --- ExpressionCompiler.cpp | 4 ++-- Token.h | 4 +--- Types.cpp | 29 +++++++++++------------------ Types.h | 4 ++-- 4 files changed, 16 insertions(+), 25 deletions(-) diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index 129261120..91a59b2d0 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -131,7 +131,7 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con // conversion from string to hash. no need to clean the high bit // only to shift right because of opposite alignment IntegerType const& targetIntegerType = dynamic_cast(_targetType); - solAssert(targetIntegerType.isHash(), "Only conversion between String and Hash is allowed."); + solAssert(targetIntegerType.isBytes(), "Only conversion between String and Bytes is allowed."); solAssert(targetIntegerType.getNumBits() == typeOnStack.getNumBytes() * 8, "The size should be the same."); m_context << (u256(1) << (256 - typeOnStack.getNumBytes() * 8)) << eth::Instruction::SWAP1 << eth::Instruction::DIV; } @@ -164,7 +164,7 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con // only to shift left because of opposite alignment StaticStringType const& targetStringType = dynamic_cast(_targetType); IntegerType const& typeOnStack = dynamic_cast(_typeOnStack); - solAssert(typeOnStack.isHash(), "Only conversion between String and Hash is allowed."); + solAssert(typeOnStack.isBytes(), "Only conversion between String and Bytes is allowed."); solAssert(typeOnStack.getNumBits() == targetStringType.getNumBytes() * 8, "The size should be the same."); m_context << (u256(1) << (256 - typeOnStack.getNumBits())) << eth::Instruction::MUL; } diff --git a/Token.h b/Token.h index 7c1de60bb..d7f56aa42 100644 --- a/Token.h +++ b/Token.h @@ -252,8 +252,6 @@ namespace solidity K(UInt240, "uint240", 0) \ K(UInt248, "uint248", 0) \ K(UInt256, "uint256", 0) \ - K(Hash, "hash", 0) \ - K(Bytes, "bytes", 0) \ K(Bytes8, "bytes8", 0) \ K(Bytes16, "bytes16", 0) \ K(Bytes24, "bytes24", 0) \ @@ -286,10 +284,10 @@ namespace solidity K(Bytes240, "bytes240", 0) \ K(Bytes248, "bytes248", 0) \ K(Bytes256, "bytes256", 0) \ + K(Bytes, "bytes", 0) \ K(Address, "address", 0) \ K(Bool, "bool", 0) \ K(StringType, "string", 0) \ - K(Text, "text", 0) \ K(Real, "real", 0) \ K(UReal, "ureal", 0) \ T(TypesEnd, NULL, 0) /* used as type enum end marker */ \ diff --git a/Types.cpp b/Types.cpp index 73cd09d6f..daf7c03e1 100644 --- a/Types.cpp +++ b/Types.cpp @@ -49,14 +49,12 @@ TypePointer Type::fromElementaryTypeName(Token::Value _typeToken) return make_shared(bytes * 8, modifier == 0 ? IntegerType::Modifier::Signed : modifier == 1 ? IntegerType::Modifier::Unsigned : - IntegerType::Modifier::Hash); + IntegerType::Modifier::Bytes); } else if (_typeToken == Token::Address) return make_shared(0, IntegerType::Modifier::Address); else if (_typeToken == Token::Bool) return make_shared(); - else if (Token::String0 <= _typeToken && _typeToken <= Token::String32) - return make_shared(int(_typeToken) - int(Token::String0)); else if (_typeToken == Token::Bytes) return make_shared(ArrayType::Location::Storage); else @@ -159,8 +157,8 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const return false; if (isAddress()) return convertTo.isAddress(); - else if (isHash()) - return convertTo.isHash(); + else if (isBytes()) + return convertTo.isBytes(); else if (isSigned()) return convertTo.isSigned(); else @@ -169,14 +167,9 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - if (_convertTo.getCategory() == Category::String) - { - StaticStringType const& convertTo = dynamic_cast(_convertTo); - return isHash() && (m_bits == convertTo.getNumBytes() * 8); - } return _convertTo.getCategory() == getCategory() || - _convertTo.getCategory() == Category::Contract || - _convertTo.getCategory() == Category::Enum; + _convertTo.getCategory() == Category::Contract || + _convertTo.getCategory() == Category::Enum; } TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const @@ -190,8 +183,8 @@ TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const // "~" is ok for all other types else if (_operator == Token::BitNot) return shared_from_this(); - // nothing else for hashes - else if (isHash()) + // nothing else for bytes + else if (isBytes()) return TypePointer(); // for non-hash integers, we allow +, -, ++ and -- else if (_operator == Token::Add || _operator == Token::Sub || @@ -214,7 +207,7 @@ string IntegerType::toString() const { if (isAddress()) return "address"; - string prefix = isHash() ? "hash" : (isSigned() ? "int" : "uint"); + string prefix = isBytes() ? "bytes" : (isSigned() ? "int" : "uint"); return prefix + dev::toString(m_bits); } @@ -231,10 +224,10 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe if (Token::isCompareOp(_operator)) return commonType; - // Nothing else can be done with addresses, but hashes can receive bit operators + // Nothing else can be done with addresses, but bytes can receive bit operators if (commonType->isAddress()) return TypePointer(); - else if (commonType->isHash() && !Token::isBitOp(_operator)) + else if (commonType->isBytes() && !Token::isBitOp(_operator)) return TypePointer(); else return commonType; @@ -461,7 +454,7 @@ bool StaticStringType::isExplicitlyConvertibleTo(Type const& _convertTo) const if (_convertTo.getCategory() == Category::Integer) { IntegerType const& convertTo = dynamic_cast(_convertTo); - if (convertTo.isHash() && (m_bytes * 8 == convertTo.getNumBits())) + if (convertTo.isBytes() && (m_bytes * 8 == convertTo.getNumBits())) return true; } diff --git a/Types.h b/Types.h index 6cef8d64a..3d67720c7 100644 --- a/Types.h +++ b/Types.h @@ -165,7 +165,7 @@ class IntegerType: public Type public: enum class Modifier { - Unsigned, Signed, Hash, Address + Unsigned, Signed, Bytes, Address }; virtual Category getCategory() const override { return Category::Integer; } @@ -186,7 +186,7 @@ public: virtual std::string toString() const override; int getNumBits() const { return m_bits; } - bool isHash() const { return m_modifier == Modifier::Hash || m_modifier == Modifier::Address; } + bool isBytes() const { return m_modifier == Modifier::Bytes || m_modifier == Modifier::Address; } bool isAddress() const { return m_modifier == Modifier::Address; } bool isSigned() const { return m_modifier == Modifier::Signed; } From 7d7f37bd5e1221243729edbd6ef8d19fd2ce13eb Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 5 Mar 2015 16:54:55 +0100 Subject: [PATCH 03/14] Replacing StaticStringType with FixedBytesType --- CompilerUtils.cpp | 7 ++-- ExpressionCompiler.cpp | 26 ++++++------- Types.cpp | 84 ++++++++++++++++++++++++++---------------- Types.h | 27 +++++++------- 4 files changed, 84 insertions(+), 60 deletions(-) diff --git a/CompilerUtils.cpp b/CompilerUtils.cpp index 8a26b5d17..e11b6b4f6 100644 --- a/CompilerUtils.cpp +++ b/CompilerUtils.cpp @@ -177,8 +177,9 @@ void CompilerUtils::computeHashStatic(Type const& _type, bool _padToWordBoundari unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries) { - unsigned numBytes = _type.getCalldataEncodedSize(_padToWordBoundaries); - bool leftAligned = _type.getCategory() == Type::Category::String; + unsigned _encodedSize = _type.getCalldataEncodedSize(); + unsigned numBytes = _padToWordBoundaries ? getPaddedSize(_encodedSize) : _encodedSize; + bool leftAligned = _type.getCategory() == Type::Category::FixedBytes; if (numBytes == 0) m_context << eth::Instruction::POP << u256(0); else @@ -202,7 +203,7 @@ unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCallda unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) const { unsigned numBytes = _type.getCalldataEncodedSize(_padToWordBoundaries); - bool leftAligned = _type.getCategory() == Type::Category::String; + bool leftAligned = _type.getCategory() == Type::Category::FixedBytes; if (numBytes == 0) m_context << eth::Instruction::POP; else diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index 91a59b2d0..51e1fd0a9 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -123,23 +123,23 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con Type::Category stackTypeCategory = _typeOnStack.getCategory(); Type::Category targetTypeCategory = _targetType.getCategory(); - if (stackTypeCategory == Type::Category::String) + if (stackTypeCategory == Type::Category::FixedBytes) { - StaticStringType const& typeOnStack = dynamic_cast(_typeOnStack); + FixedBytesType const& typeOnStack = dynamic_cast(_typeOnStack); if (targetTypeCategory == Type::Category::Integer) { - // conversion from string to hash. no need to clean the high bit + // conversion from string to bytes. no need to clean the high bit // only to shift right because of opposite alignment IntegerType const& targetIntegerType = dynamic_cast(_targetType); - solAssert(targetIntegerType.isBytes(), "Only conversion between String and Bytes is allowed."); + solAssert(targetIntegerType.isAddress(), "Only conversion between Address and FixedBytes is allowed."); solAssert(targetIntegerType.getNumBits() == typeOnStack.getNumBytes() * 8, "The size should be the same."); m_context << (u256(1) << (256 - typeOnStack.getNumBytes() * 8)) << eth::Instruction::SWAP1 << eth::Instruction::DIV; } else { - // clear lower-order bytes for conversion to shorter strings - we always clean - solAssert(targetTypeCategory == Type::Category::String, "Invalid type conversion requested."); - StaticStringType const& targetType = dynamic_cast(_targetType); + // clear lower-order bytes for conversion to shorter bytes - we always clean + solAssert(targetTypeCategory == Type::Category::FixedBytes, "Invalid type conversion requested."); + FixedBytesType const& targetType = dynamic_cast(_targetType); if (targetType.getNumBytes() < typeOnStack.getNumBytes()) { if (targetType.getNumBytes() == 0) @@ -158,14 +158,14 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con stackTypeCategory == Type::Category::Contract || stackTypeCategory == Type::Category::IntegerConstant) { - if (targetTypeCategory == Type::Category::String && stackTypeCategory == Type::Category::Integer) + if (targetTypeCategory == Type::Category::FixedBytes && stackTypeCategory == Type::Category::Integer) { - // conversion from hash to string. no need to clean the high bit + // conversion from bytes to string. no need to clean the high bit // only to shift left because of opposite alignment - StaticStringType const& targetStringType = dynamic_cast(_targetType); + FixedBytesType const& targetBytesType = dynamic_cast(_targetType); IntegerType const& typeOnStack = dynamic_cast(_typeOnStack); - solAssert(typeOnStack.isBytes(), "Only conversion between String and Bytes is allowed."); - solAssert(typeOnStack.getNumBits() == targetStringType.getNumBytes() * 8, "The size should be the same."); + solAssert(typeOnStack.isAddress(), "Only conversion between Address and Bytes is allowed."); + solAssert(typeOnStack.getNumBits() == targetBytesType.getNumBytes() * 8, "The size should be the same."); m_context << (u256(1) << (256 - typeOnStack.getNumBits())) << eth::Instruction::MUL; } else if (targetTypeCategory == Type::Category::Enum) @@ -870,7 +870,7 @@ void ExpressionCompiler::endVisit(Literal const& _literal) { case Type::Category::IntegerConstant: case Type::Category::Bool: - case Type::Category::String: + case Type::Category::FixedBytes: m_context << _literal.getType()->literalValue(&_literal); break; default: diff --git a/Types.cpp b/Types.cpp index daf7c03e1..aadd884b5 100644 --- a/Types.cpp +++ b/Types.cpp @@ -46,10 +46,18 @@ TypePointer Type::fromElementaryTypeName(Token::Value _typeToken) if (bytes == 0) bytes = 32; int modifier = offset / 33; - return make_shared(bytes * 8, - modifier == 0 ? IntegerType::Modifier::Signed : - modifier == 1 ? IntegerType::Modifier::Unsigned : - IntegerType::Modifier::Bytes); + switch(modifier) + { + case 0: + return make_shared(bytes * 8, IntegerType::Modifier::Signed); + case 1: + return make_shared(bytes * 8, IntegerType::Modifier::Unsigned); + case 2: + return make_shared(bytes); + default: + solAssert(false, "Unexpected modifier value. Should never happen"); + return TypePointer(); + } } else if (_typeToken == Token::Address) return make_shared(0, IntegerType::Modifier::Address); @@ -121,7 +129,7 @@ TypePointer Type::forLiteral(Literal const& _literal) return make_shared(_literal); case Token::StringLiteral: //@todo put larger strings into dynamic strings - return StaticStringType::smallestTypeForLiteral(_literal.getValue()); + return FixedBytesType::smallestTypeForLiteral(_literal.getValue()); default: return shared_ptr(); } @@ -157,8 +165,6 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const return false; if (isAddress()) return convertTo.isAddress(); - else if (isBytes()) - return convertTo.isBytes(); else if (isSigned()) return convertTo.isSigned(); else @@ -183,10 +189,7 @@ TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const // "~" is ok for all other types else if (_operator == Token::BitNot) return shared_from_this(); - // nothing else for bytes - else if (isBytes()) - return TypePointer(); - // for non-hash integers, we allow +, -, ++ and -- + // for non-address integers, we allow +, -, ++ and -- else if (_operator == Token::Add || _operator == Token::Sub || _operator == Token::Inc || _operator == Token::Dec || _operator == Token::After) @@ -207,7 +210,7 @@ string IntegerType::toString() const { if (isAddress()) return "address"; - string prefix = isBytes() ? "bytes" : (isSigned() ? "int" : "uint"); + string prefix = isSigned() ? "int" : "uint"; return prefix + dev::toString(m_bits); } @@ -224,13 +227,7 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe if (Token::isCompareOp(_operator)) return commonType; - // Nothing else can be done with addresses, but bytes can receive bit operators - if (commonType->isAddress()) - return TypePointer(); - else if (commonType->isBytes() && !Token::isBitOp(_operator)) - return TypePointer(); - else - return commonType; + return TypePointer(); } const MemberList IntegerType::AddressMemberList = @@ -426,50 +423,75 @@ shared_ptr IntegerConstantType::getIntegerType() const : IntegerType::Modifier::Unsigned); } -shared_ptr StaticStringType::smallestTypeForLiteral(string const& _literal) +shared_ptr FixedBytesType::smallestTypeForLiteral(string const& _literal) { if (_literal.length() <= 32) - return make_shared(_literal.length()); - return shared_ptr(); + return make_shared(_literal.length()); + return shared_ptr(); } -StaticStringType::StaticStringType(int _bytes): m_bytes(_bytes) +FixedBytesType::FixedBytesType(int _bytes): m_bytes(_bytes) { solAssert(m_bytes >= 0 && m_bytes <= 32, "Invalid byte number for static string type: " + dev::toString(m_bytes)); } -bool StaticStringType::isImplicitlyConvertibleTo(Type const& _convertTo) const +bool FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) const { if (_convertTo.getCategory() != getCategory()) return false; - StaticStringType const& convertTo = dynamic_cast(_convertTo); + FixedBytesType const& convertTo = dynamic_cast(_convertTo); return convertTo.m_bytes >= m_bytes; } -bool StaticStringType::isExplicitlyConvertibleTo(Type const& _convertTo) const +bool FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const { if (_convertTo.getCategory() == getCategory()) return true; if (_convertTo.getCategory() == Category::Integer) { IntegerType const& convertTo = dynamic_cast(_convertTo); - if (convertTo.isBytes() && (m_bytes * 8 == convertTo.getNumBits())) + if (m_bytes * 8 == convertTo.getNumBits()) return true; } return false; } -bool StaticStringType::operator==(Type const& _other) const +TypePointer FixedBytesType::unaryOperatorResult(Token::Value _operator) const +{ + // "delete" and "~" is okay for FixedBytesType + if (_operator == Token::Delete) + return make_shared(); + else if (_operator == Token::BitNot) + return shared_from_this(); + + return TypePointer(); +} + +TypePointer FixedBytesType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const +{ + auto commonType = dynamic_pointer_cast(Type::commonType(shared_from_this(), _other)); + + if (!commonType) + return TypePointer(); + + // FixedBytes can be compared and have bitwise operators applied to them + if (Token::isCompareOp(_operator) || Token::isBitOp(_operator)) + return commonType; + + return TypePointer(); +} + +bool FixedBytesType::operator==(Type const& _other) const { if (_other.getCategory() != getCategory()) return false; - StaticStringType const& other = dynamic_cast(_other); + FixedBytesType const& other = dynamic_cast(_other); return other.m_bytes == m_bytes; } -u256 StaticStringType::literalValue(const Literal* _literal) const +u256 FixedBytesType::literalValue(const Literal* _literal) const { solAssert(_literal, ""); u256 value = 0; @@ -1117,7 +1139,7 @@ MagicType::MagicType(MagicType::Kind _kind): case Kind::Block: m_members = MemberList({{"coinbase", make_shared(0, IntegerType::Modifier::Address)}, {"timestamp", make_shared(256)}, - {"blockhash", make_shared(strings{"uint"}, strings{"hash"}, FunctionType::Location::BlockHash)}, + {"blockhash", make_shared(strings{"uint"}, strings{"bytes"}, FunctionType::Location::BlockHash)}, {"difficulty", make_shared(256)}, {"number", make_shared(256)}, {"gaslimit", make_shared(256)}}); diff --git a/Types.h b/Types.h index 3d67720c7..6517cf070 100644 --- a/Types.h +++ b/Types.h @@ -77,12 +77,12 @@ public: enum class Category { Integer, IntegerConstant, Bool, Real, Array, - String, Contract, Struct, Function, Enum, + FixedBytes, Contract, Struct, Function, Enum, Mapping, Void, TypeType, Modifier, Magic }; - ///@{ - ///@name Factory functions + /// @{ + /// @name Factory functions /// Factory functions that convert an AST @ref TypeName to a Type. static TypePointer fromElementaryTypeName(Token::Value _typeToken); static TypePointer fromElementaryTypeName(std::string const& _name); @@ -158,14 +158,14 @@ protected: }; /** - * Any kind of integer type including hash and address. + * Any kind of integer type including address. */ class IntegerType: public Type { public: enum class Modifier { - Unsigned, Signed, Bytes, Address + Unsigned, Signed, Address }; virtual Category getCategory() const override { return Category::Integer; } @@ -186,7 +186,6 @@ public: virtual std::string toString() const override; int getNumBits() const { return m_bits; } - bool isBytes() const { return m_modifier == Modifier::Bytes || m_modifier == Modifier::Address; } bool isAddress() const { return m_modifier == Modifier::Address; } bool isSigned() const { return m_modifier == Modifier::Signed; } @@ -232,27 +231,29 @@ private: }; /** - * String type with fixed length, up to 32 bytes. + * Bytes type with fixed length of up to 32 bytes */ -class StaticStringType: public Type +class FixedBytesType: public Type { public: - virtual Category getCategory() const override { return Category::String; } + virtual Category getCategory() const override { return Category::FixedBytes; } - /// @returns the smallest string type for the given literal or an empty pointer + /// @returns the smallest bytes type for the given literal or an empty pointer /// if no type fits. - static std::shared_ptr smallestTypeForLiteral(std::string const& _literal); + static std::shared_ptr smallestTypeForLiteral(std::string const& _literal); - explicit StaticStringType(int _bytes); + explicit FixedBytesType(int _bytes); virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool operator==(Type const& _other) const override; + virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; + virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; virtual unsigned getCalldataEncodedSize(bool _padded) const override { return _padded && m_bytes > 0 ? 32 : m_bytes; } virtual bool isValueType() const override { return true; } - virtual std::string toString() const override { return "string" + dev::toString(m_bytes); } + virtual std::string toString() const override { return "bytes" + dev::toString(m_bytes); } virtual u256 literalValue(Literal const* _literal) const override; int getNumBytes() const { return m_bytes; } From 3ca37cadddf4f58cef5273c6b49e0f55c1162251 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Fri, 6 Mar 2015 17:20:00 +0100 Subject: [PATCH 04/14] Fixes after rebasing on top of develop --- AST.cpp | 2 +- CompilerUtils.cpp | 3 +-- LValue.cpp | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/AST.cpp b/AST.cpp index 79b755e97..46c44995b 100644 --- a/AST.cpp +++ b/AST.cpp @@ -671,7 +671,7 @@ void IndexAccess::checkTypeRequirements() BOOST_THROW_EXCEPTION(createTypeError("Index expression cannot be omitted.")); m_index->expectType(IntegerType(256)); if (type.isByteArray()) - m_type = make_shared(8, IntegerType::Modifier::Hash); + m_type = make_shared(1); else m_type = type.getBaseType(); m_isLValue = type.getLocation() != ArrayType::Location::CallData; diff --git a/CompilerUtils.cpp b/CompilerUtils.cpp index e11b6b4f6..7b078e03e 100644 --- a/CompilerUtils.cpp +++ b/CompilerUtils.cpp @@ -177,8 +177,7 @@ void CompilerUtils::computeHashStatic(Type const& _type, bool _padToWordBoundari unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries) { - unsigned _encodedSize = _type.getCalldataEncodedSize(); - unsigned numBytes = _padToWordBoundaries ? getPaddedSize(_encodedSize) : _encodedSize; + unsigned numBytes = _type.getCalldataEncodedSize(_padToWordBoundaries); bool leftAligned = _type.getCategory() == Type::Category::FixedBytes; if (numBytes == 0) m_context << eth::Instruction::POP << u256(0); diff --git a/LValue.cpp b/LValue.cpp index a036be80b..db3cd56be 100644 --- a/LValue.cpp +++ b/LValue.cpp @@ -234,7 +234,7 @@ void StorageItem::setToZero(SourceLocation const&, bool _removeReference) const } /// Used in StorageByteArrayElement -static IntegerType byteType(8, IntegerType::Modifier::Hash); +static FixedBytesType byteType(1); StorageByteArrayElement::StorageByteArrayElement(CompilerContext& _compilerContext): LValue(_compilerContext, byteType) From 2bddebc3d5fc942dc43364a9c4ddfb897768c93d Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Mon, 9 Mar 2015 13:49:53 +0100 Subject: [PATCH 05/14] Bytes Tokens properly named and NameAndTypeResolution tests work --- Token.h | 64 +++++++++++++++++++++++++++---------------------------- Types.cpp | 7 ++++-- 2 files changed, 37 insertions(+), 34 deletions(-) diff --git a/Token.h b/Token.h index d7f56aa42..2a4d22495 100644 --- a/Token.h +++ b/Token.h @@ -252,39 +252,39 @@ namespace solidity K(UInt240, "uint240", 0) \ K(UInt248, "uint248", 0) \ K(UInt256, "uint256", 0) \ - K(Bytes8, "bytes8", 0) \ - K(Bytes16, "bytes16", 0) \ - K(Bytes24, "bytes24", 0) \ - K(Bytes32, "bytes32", 0) \ - K(Bytes40, "bytes40", 0) \ - K(Bytes48, "bytes48", 0) \ - K(Bytes56, "bytes56", 0) \ - K(Bytes64, "bytes64", 0) \ - K(Bytes72, "bytes72", 0) \ - K(Bytes80, "bytes80", 0) \ - K(Bytes88, "bytes88", 0) \ - K(Bytes96, "bytes96", 0) \ - K(Bytes104, "bytes104", 0) \ - K(Bytes112, "bytes112", 0) \ - K(Bytes120, "bytes120", 0) \ - K(Bytes128, "bytes128", 0) \ - K(Bytes136, "bytes136", 0) \ - K(Bytes144, "bytes144", 0) \ - K(Bytes152, "bytes152", 0) \ - K(Bytes160, "bytes160", 0) \ - K(Bytes168, "bytes168", 0) \ - K(Bytes176, "bytes178", 0) \ - K(Bytes184, "bytes184", 0) \ - K(Bytes192, "bytes192", 0) \ - K(Bytes200, "bytes200", 0) \ - K(Bytes208, "bytes208", 0) \ - K(Bytes216, "bytes216", 0) \ - K(Bytes224, "bytes224", 0) \ - K(Bytes232, "bytes232", 0) \ - K(Bytes240, "bytes240", 0) \ - K(Bytes248, "bytes248", 0) \ - K(Bytes256, "bytes256", 0) \ K(Bytes, "bytes", 0) \ + K(Bytes1, "bytes1", 0) \ + K(Bytes2, "bytes2", 0) \ + K(Bytes3, "bytes3", 0) \ + K(Bytes4, "bytes4", 0) \ + K(Bytes5, "bytes5", 0) \ + K(Bytes6, "bytes6", 0) \ + K(Bytes7, "bytes7", 0) \ + K(Bytes8, "bytes8", 0) \ + K(Bytes9, "bytes9", 0) \ + K(Bytes10, "bytes10", 0) \ + K(Bytes11, "bytes11", 0) \ + K(Bytes12, "bytes12", 0) \ + K(Bytes13, "bytes13", 0) \ + K(Bytes14, "bytes14", 0) \ + K(Bytes15, "bytes15", 0) \ + K(Bytes16, "bytes16", 0) \ + K(Bytes17, "bytes17", 0) \ + K(Bytes18, "bytes18", 0) \ + K(Bytes19, "bytes19", 0) \ + K(Bytes20, "bytes20", 0) \ + K(Bytes21, "bytes21", 0) \ + K(Bytes22, "bytes22", 0) \ + K(Bytes23, "bytes23", 0) \ + K(Bytes24, "bytes24", 0) \ + K(Bytes25, "bytes25", 0) \ + K(Bytes26, "bytes26", 0) \ + K(Bytes27, "bytes27", 0) \ + K(Bytes28, "bytes28", 0) \ + K(Bytes29, "bytes29", 0) \ + K(Bytes30, "bytes30", 0) \ + K(Bytes31, "bytes31", 0) \ + K(Bytes32, "bytes32", 0) \ K(Address, "address", 0) \ K(Bool, "bool", 0) \ K(StringType, "string", 0) \ diff --git a/Types.cpp b/Types.cpp index aadd884b5..9f307cbc6 100644 --- a/Types.cpp +++ b/Types.cpp @@ -39,7 +39,7 @@ TypePointer Type::fromElementaryTypeName(Token::Value _typeToken) { solAssert(Token::isElementaryTypeName(_typeToken), "Elementary type name expected."); - if (Token::Int <= _typeToken && _typeToken <= Token::Bytes256) + if (Token::Int <= _typeToken && _typeToken <= Token::Bytes32) { int offset = _typeToken - Token::Int; int bytes = offset % 33; @@ -226,8 +226,11 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe // All integer types can be compared if (Token::isCompareOp(_operator)) return commonType; + // Nothing else can be done with addresses, but hashes can receive bit operators + if (commonType->isAddress()) + return TypePointer(); - return TypePointer(); + return commonType; } const MemberList IntegerType::AddressMemberList = From 73ce24ae75554b18a342e1e510f37f99057fdb1d Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Mon, 9 Mar 2015 17:48:33 +0100 Subject: [PATCH 06/14] Most EndToEndTests are now compliant with the Bytes renaming --- CompilerStack.cpp | 8 ++++---- ExpressionCompiler.cpp | 2 -- GlobalContext.cpp | 18 +++++++++--------- Token.h | 3 ++- Types.cpp | 13 ++++++++++--- 5 files changed, 25 insertions(+), 19 deletions(-) diff --git a/CompilerStack.cpp b/CompilerStack.cpp index a878bb61a..55ec0cb59 100644 --- a/CompilerStack.cpp +++ b/CompilerStack.cpp @@ -41,14 +41,14 @@ namespace solidity { const map StandardSources = map{ - {"coin", R"(import "CoinReg";import "Config";import "configUser";contract coin is configUser{function coin(string3 name, uint denom) {CoinReg(Config(configAddr()).lookup(3)).register(name, denom);}})"}, + {"coin", R"(import "CoinReg";import "Config";import "configUser";contract coin is configUser{function coin(bytes3 name, uint denom) {CoinReg(Config(configAddr()).lookup(3)).register(name, denom);}})"}, {"Coin", R"(contract Coin{function isApprovedFor(address _target,address _proxy)constant returns(bool _r){}function isApproved(address _proxy)constant returns(bool _r){}function sendCoinFrom(address _from,uint256 _val,address _to){}function coinBalanceOf(address _a)constant returns(uint256 _r){}function sendCoin(uint256 _val,address _to){}function coinBalance()constant returns(uint256 _r){}function approve(address _a){}})"}, - {"CoinReg", R"(contract CoinReg{function count()constant returns(uint256 r){}function info(uint256 i)constant returns(address addr,string3 name,uint256 denom){}function register(string3 name,uint256 denom){}function unregister(){}})"}, + {"CoinReg", R"(contract CoinReg{function count()constant returns(uint256 r){}function info(uint256 i)constant returns(address addr,bytes3 name,uint256 denom){}function register(bytes3 name,uint256 denom){}function unregister(){}})"}, {"configUser", R"(contract configUser{function configAddr()constant returns(address a){ return 0xc6d9d2cd449a754c494264e1809c50e34d64562b;}})"}, {"Config", R"(contract Config{function lookup(uint256 service)constant returns(address a){}function kill(){}function unregister(uint256 id){}function register(uint256 id,address service){}})"}, {"mortal", R"(import "owned";contract mortal is owned {function kill() { if (msg.sender == owner) suicide(owner); }})"}, - {"named", R"(import "Config";import "NameReg";import "configUser";contract named is configUser {function named(string32 name) {NameReg(Config(configAddr()).lookup(1)).register(name);}})"}, - {"NameReg", R"(contract NameReg{function register(string32 name){}function addressOf(string32 name)constant returns(address addr){}function unregister(){}function nameOf(address addr)constant returns(string32 name){}})"}, + {"named", R"(import "Config";import "NameReg";import "configUser";contract named is configUser {function named(bytes32 name) {NameReg(Config(configAddr()).lookup(1)).register(name);}})"}, + {"NameReg", R"(contract NameReg{function register(bytes32 name){}function addressOf(bytes32 name)constant returns(address addr){}function unregister(){}function nameOf(address addr)constant returns(bytes32 name){}})"}, {"owned", R"(contract owned{function owned(){owner = msg.sender;}modifier onlyowner(){if(msg.sender==owner)_}address owner;})"}, {"service", R"(import "Config";import "configUser";contract service is configUser{function service(uint _n){Config(configAddr()).register(_n, this);}})"}, {"std", R"(import "owned";import "mortal";import "Config";import "configUser";import "NameReg";import "named";)"} diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index 51e1fd0a9..089ebc32d 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -131,7 +131,6 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con // conversion from string to bytes. no need to clean the high bit // only to shift right because of opposite alignment IntegerType const& targetIntegerType = dynamic_cast(_targetType); - solAssert(targetIntegerType.isAddress(), "Only conversion between Address and FixedBytes is allowed."); solAssert(targetIntegerType.getNumBits() == typeOnStack.getNumBytes() * 8, "The size should be the same."); m_context << (u256(1) << (256 - typeOnStack.getNumBytes() * 8)) << eth::Instruction::SWAP1 << eth::Instruction::DIV; } @@ -164,7 +163,6 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con // only to shift left because of opposite alignment FixedBytesType const& targetBytesType = dynamic_cast(_targetType); IntegerType const& typeOnStack = dynamic_cast(_typeOnStack); - solAssert(typeOnStack.isAddress(), "Only conversion between Address and Bytes is allowed."); solAssert(typeOnStack.getNumBits() == targetBytesType.getNumBytes() * 8, "The size should be the same."); m_context << (u256(1) << (256 - typeOnStack.getNumBits())) << eth::Instruction::MUL; } diff --git a/GlobalContext.cpp b/GlobalContext.cpp index 411e99abb..7bd9c8dfa 100644 --- a/GlobalContext.cpp +++ b/GlobalContext.cpp @@ -41,23 +41,23 @@ m_magicVariables(vector>{make_shared< make_shared("suicide", make_shared(strings{"address"}, strings{}, FunctionType::Location::Suicide)), make_shared("sha3", - make_shared(strings(), strings{"hash"}, FunctionType::Location::SHA3, true)), + make_shared(strings(), strings{"bytes32"}, FunctionType::Location::SHA3, true)), make_shared("log0", - make_shared(strings{"hash"},strings{}, FunctionType::Location::Log0)), + make_shared(strings{"bytes32"}, strings{}, FunctionType::Location::Log0)), make_shared("log1", - make_shared(strings{"hash", "hash"},strings{}, FunctionType::Location::Log1)), + make_shared(strings{"bytes32", "bytes32"}, strings{}, FunctionType::Location::Log1)), make_shared("log2", - make_shared(strings{"hash", "hash", "hash"},strings{}, FunctionType::Location::Log2)), + make_shared(strings{"bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Location::Log2)), make_shared("log3", - make_shared(strings{"hash", "hash", "hash", "hash"},strings{}, FunctionType::Location::Log3)), + make_shared(strings{"bytes32", "bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Location::Log3)), make_shared("log4", - make_shared(strings{"hash", "hash", "hash", "hash", "hash"},strings{}, FunctionType::Location::Log4)), + make_shared(strings{"bytes32", "bytes32", "bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Location::Log4)), make_shared("sha256", - make_shared(strings(), strings{"hash"}, FunctionType::Location::SHA256, true)), + make_shared(strings(), strings{"bytes32"}, FunctionType::Location::SHA256, true)), make_shared("ecrecover", - make_shared(strings{"hash", "hash8", "hash", "hash"}, strings{"address"}, FunctionType::Location::ECRecover)), + make_shared(strings{"bytes32", "bytes1", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Location::ECRecover)), make_shared("ripemd160", - make_shared(strings(), strings{"hash160"}, FunctionType::Location::RIPEMD160, true))}) + make_shared(strings(), strings{"bytes20"}, FunctionType::Location::RIPEMD160, true))}) { } diff --git a/Token.h b/Token.h index 2a4d22495..c439c79ee 100644 --- a/Token.h +++ b/Token.h @@ -252,7 +252,7 @@ namespace solidity K(UInt240, "uint240", 0) \ K(UInt248, "uint248", 0) \ K(UInt256, "uint256", 0) \ - K(Bytes, "bytes", 0) \ + K(Bytes0, "bytes0", 0) \ K(Bytes1, "bytes1", 0) \ K(Bytes2, "bytes2", 0) \ K(Bytes3, "bytes3", 0) \ @@ -285,6 +285,7 @@ namespace solidity K(Bytes30, "bytes30", 0) \ K(Bytes31, "bytes31", 0) \ K(Bytes32, "bytes32", 0) \ + K(Bytes, "bytes", 0) \ K(Address, "address", 0) \ K(Bool, "bool", 0) \ K(StringType, "string", 0) \ diff --git a/Types.cpp b/Types.cpp index 9f307cbc6..6039895af 100644 --- a/Types.cpp +++ b/Types.cpp @@ -37,13 +37,15 @@ namespace solidity TypePointer Type::fromElementaryTypeName(Token::Value _typeToken) { - solAssert(Token::isElementaryTypeName(_typeToken), "Elementary type name expected."); + char const* tokenCstr = Token::toString(_typeToken); + solAssert(Token::isElementaryTypeName(_typeToken), + "Expected an elementary type name but got " + ((tokenCstr) ? std::string(Token::toString(_typeToken)) : "")); if (Token::Int <= _typeToken && _typeToken <= Token::Bytes32) { int offset = _typeToken - Token::Int; int bytes = offset % 33; - if (bytes == 0) + if (bytes == 0 && _typeToken != Token::Bytes0) bytes = 32; int modifier = offset / 33; switch(modifier) @@ -173,6 +175,11 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const { + if (_convertTo.getCategory() == Category::FixedBytes) + { + FixedBytesType const& convertTo = dynamic_cast(_convertTo); + return (m_bits == convertTo.getNumBytes() * 8); + } return _convertTo.getCategory() == getCategory() || _convertTo.getCategory() == Category::Contract || _convertTo.getCategory() == Category::Enum; @@ -436,7 +443,7 @@ shared_ptr FixedBytesType::smallestTypeForLiteral(string const& FixedBytesType::FixedBytesType(int _bytes): m_bytes(_bytes) { solAssert(m_bytes >= 0 && m_bytes <= 32, - "Invalid byte number for static string type: " + dev::toString(m_bytes)); + "Invalid byte number for fixed bytes type: " + dev::toString(m_bytes)); } bool FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) const From b2fadf6b933e9034d5edb49608329fb0f7d015dd Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Tue, 10 Mar 2015 18:22:19 +0100 Subject: [PATCH 07/14] Conversion changes after renaming Hash/String to Bytes. - Almost all end to end tests pass. Still needs a little bit of work --- ExpressionCompiler.cpp | 49 +++++++++++++++++++++++++----------------- GlobalContext.cpp | 2 +- LValue.cpp | 8 ++++--- Types.cpp | 20 ++++++++++------- 4 files changed, 47 insertions(+), 32 deletions(-) diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index 089ebc32d..f00b2d40c 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -123,16 +123,20 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con Type::Category stackTypeCategory = _typeOnStack.getCategory(); Type::Category targetTypeCategory = _targetType.getCategory(); - if (stackTypeCategory == Type::Category::FixedBytes) + switch (stackTypeCategory) + { + case Type::Category::FixedBytes: { FixedBytesType const& typeOnStack = dynamic_cast(_typeOnStack); if (targetTypeCategory == Type::Category::Integer) { - // conversion from string to bytes. no need to clean the high bit + // conversion from bytes to integer. no need to clean the high bit // only to shift right because of opposite alignment IntegerType const& targetIntegerType = dynamic_cast(_targetType); - solAssert(targetIntegerType.getNumBits() == typeOnStack.getNumBytes() * 8, "The size should be the same."); + // solAssert(targetIntegerType.getNumBits() == typeOnStack.getNumBytes() * 8, "The size should be the same."); m_context << (u256(1) << (256 - typeOnStack.getNumBytes() * 8)) << eth::Instruction::SWAP1 << eth::Instruction::DIV; + if (targetIntegerType.getNumBits() < typeOnStack.getNumBytes() * 8) + appendTypeConversion(IntegerType(typeOnStack.getNumBytes() * 8), _targetType, _cleanupNeeded); } else { @@ -150,21 +154,24 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con } } } - else if (stackTypeCategory == Type::Category::Enum) - solAssert(targetTypeCategory == Type::Category::Integer || - targetTypeCategory == Type::Category::Enum, ""); - else if (stackTypeCategory == Type::Category::Integer || - stackTypeCategory == Type::Category::Contract || - stackTypeCategory == Type::Category::IntegerConstant) - { - if (targetTypeCategory == Type::Category::FixedBytes && stackTypeCategory == Type::Category::Integer) + break; + case Type::Category::Enum: + solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Enum, ""); + break; + case Type::Category::Integer: + case Type::Category::Contract: + case Type::Category::IntegerConstant: + if (targetTypeCategory == Type::Category::FixedBytes) { + solAssert(stackTypeCategory == Type::Category::Integer || stackTypeCategory == Type::Category::IntegerConstant, + "Invalid conversion to FixedBytesType requested."); // conversion from bytes to string. no need to clean the high bit // only to shift left because of opposite alignment FixedBytesType const& targetBytesType = dynamic_cast(_targetType); - IntegerType const& typeOnStack = dynamic_cast(_typeOnStack); - solAssert(typeOnStack.getNumBits() == targetBytesType.getNumBytes() * 8, "The size should be the same."); - m_context << (u256(1) << (256 - typeOnStack.getNumBits())) << eth::Instruction::MUL; + if (auto typeOnStack = dynamic_cast(&_typeOnStack)) + if (targetBytesType.getNumBytes() * 8 > typeOnStack->getNumBits()) + appendHighBitsCleanup(*typeOnStack); + m_context << (u256(1) << (256 - targetBytesType.getNumBytes() * 8)) << eth::Instruction::MUL; } else if (targetTypeCategory == Type::Category::Enum) // just clean @@ -174,7 +181,7 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Contract, ""); IntegerType addressType(0, IntegerType::Modifier::Address); IntegerType const& targetType = targetTypeCategory == Type::Category::Integer - ? dynamic_cast(_targetType) : addressType; + ? dynamic_cast(_targetType) : addressType; if (stackTypeCategory == Type::Category::IntegerConstant) { IntegerConstantType const& constType = dynamic_cast(_typeOnStack); @@ -186,7 +193,7 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con else { IntegerType const& typeOnStack = stackTypeCategory == Type::Category::Integer - ? dynamic_cast(_typeOnStack) : addressType; + ? dynamic_cast(_typeOnStack) : addressType; // Widening: clean up according to source type width // Non-widening and force: clean up according to target type bits if (targetType.getNumBits() > typeOnStack.getNumBits()) @@ -195,10 +202,12 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con appendHighBitsCleanup(targetType); } } - } - else if (_typeOnStack != _targetType) + break; + default: // All other types should not be convertible to non-equal types. - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid type conversion requested.")); + solAssert(_typeOnStack == _targetType, "Invalid type conversion requested."); + break; + } } bool ExpressionCompiler::visit(Assignment const& _assignment) @@ -773,7 +782,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) // no lvalue, just retrieve the value m_context << eth::Instruction::ADD << eth::Instruction::CALLDATALOAD - << u256(0) << eth::Instruction::BYTE; + << ((u256(1) << (256 - 8)) - 1) << eth::Instruction::AND; break; case ArrayType::Location::Memory: solAssert(false, "Memory lvalues not yet implemented."); diff --git a/GlobalContext.cpp b/GlobalContext.cpp index 7bd9c8dfa..80cebd760 100644 --- a/GlobalContext.cpp +++ b/GlobalContext.cpp @@ -55,7 +55,7 @@ m_magicVariables(vector>{make_shared< make_shared("sha256", make_shared(strings(), strings{"bytes32"}, FunctionType::Location::SHA256, true)), make_shared("ecrecover", - make_shared(strings{"bytes32", "bytes1", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Location::ECRecover)), + make_shared(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Location::ECRecover)), make_shared("ripemd160", make_shared(strings(), strings{"bytes20"}, FunctionType::Location::RIPEMD160, true))}) { diff --git a/LValue.cpp b/LValue.cpp index db3cd56be..7b8374b81 100644 --- a/LValue.cpp +++ b/LValue.cpp @@ -246,10 +246,11 @@ void StorageByteArrayElement::retrieveValue(SourceLocation const&, bool _remove) // stack: ref byte_number if (_remove) m_context << eth::Instruction::SWAP1 << eth::Instruction::SLOAD - << eth::Instruction::SWAP1 << eth::Instruction::BYTE; + << eth::Instruction::SWAP1 << eth::Instruction::BYTE ; else m_context << eth::Instruction::DUP2 << eth::Instruction::SLOAD << eth::Instruction::DUP2 << eth::Instruction::BYTE; + m_context << (u256(1) << (256 - 8)) << eth::Instruction::MUL; } void StorageByteArrayElement::storeValue(Type const&, SourceLocation const&, bool _move) const @@ -265,8 +266,9 @@ void StorageByteArrayElement::storeValue(Type const&, SourceLocation const&, boo m_context << eth::Instruction::DUP2 << u256(0xff) << eth::Instruction::MUL << eth::Instruction::NOT << eth::Instruction::AND; // stack: value ref (1<<(32-byte_number)) old_full_value_with_cleared_byte - m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP4 << eth::Instruction::MUL - << eth::Instruction::OR; + m_context << eth::Instruction::SWAP1; + m_context << (u256(1) << (256 - 8)) << eth::Instruction::DUP5 << eth::Instruction::DIV + << eth::Instruction::MUL << eth::Instruction::OR; // stack: value ref new_full_value m_context << eth::Instruction::SWAP1 << eth::Instruction::SSTORE; if (_move) diff --git a/Types.cpp b/Types.cpp index 6039895af..8524cb8b0 100644 --- a/Types.cpp +++ b/Types.cpp @@ -175,14 +175,10 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - if (_convertTo.getCategory() == Category::FixedBytes) - { - FixedBytesType const& convertTo = dynamic_cast(_convertTo); - return (m_bits == convertTo.getNumBytes() * 8); - } return _convertTo.getCategory() == getCategory() || _convertTo.getCategory() == Category::Contract || - _convertTo.getCategory() == Category::Enum; + _convertTo.getCategory() == Category::Enum || + _convertTo.getCategory() == Category::FixedBytes; } TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const @@ -284,7 +280,15 @@ IntegerConstantType::IntegerConstantType(Literal const& _literal) bool IntegerConstantType::isImplicitlyConvertibleTo(Type const& _convertTo) const { - TypePointer integerType = getIntegerType(); + auto integerType = getIntegerType(); + if (_convertTo.getCategory() == Category::FixedBytes) + { + FixedBytesType const& convertTo = dynamic_cast(_convertTo); + if (convertTo.getNumBytes() * 8 >= integerType->getNumBits()) + return true; + return false; + } + return integerType && integerType->isImplicitlyConvertibleTo(_convertTo); } @@ -461,7 +465,7 @@ bool FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const if (_convertTo.getCategory() == Category::Integer) { IntegerType const& convertTo = dynamic_cast(_convertTo); - if (m_bytes * 8 == convertTo.getNumBits()) + if (m_bytes * 8 <= convertTo.getNumBits()) return true; } From cd3e8c175645e9a6a81b5b719868b1f74a528c65 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Wed, 11 Mar 2015 16:58:25 +0100 Subject: [PATCH 08/14] Fixing byte array index access code generation --- ExpressionCompiler.cpp | 3 +-- LValue.cpp | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index f00b2d40c..3cee40df1 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -133,7 +133,6 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con // conversion from bytes to integer. no need to clean the high bit // only to shift right because of opposite alignment IntegerType const& targetIntegerType = dynamic_cast(_targetType); - // solAssert(targetIntegerType.getNumBits() == typeOnStack.getNumBytes() * 8, "The size should be the same."); m_context << (u256(1) << (256 - typeOnStack.getNumBytes() * 8)) << eth::Instruction::SWAP1 << eth::Instruction::DIV; if (targetIntegerType.getNumBits() < typeOnStack.getNumBytes() * 8) appendTypeConversion(IntegerType(typeOnStack.getNumBytes() * 8), _targetType, _cleanupNeeded); @@ -782,7 +781,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) // no lvalue, just retrieve the value m_context << eth::Instruction::ADD << eth::Instruction::CALLDATALOAD - << ((u256(1) << (256 - 8)) - 1) << eth::Instruction::AND; + << ((u256(0xff) << (256 - 8))) << eth::Instruction::AND; break; case ArrayType::Location::Memory: solAssert(false, "Memory lvalues not yet implemented."); diff --git a/LValue.cpp b/LValue.cpp index 7b8374b81..dc07ec311 100644 --- a/LValue.cpp +++ b/LValue.cpp @@ -246,7 +246,7 @@ void StorageByteArrayElement::retrieveValue(SourceLocation const&, bool _remove) // stack: ref byte_number if (_remove) m_context << eth::Instruction::SWAP1 << eth::Instruction::SLOAD - << eth::Instruction::SWAP1 << eth::Instruction::BYTE ; + << eth::Instruction::SWAP1 << eth::Instruction::BYTE; else m_context << eth::Instruction::DUP2 << eth::Instruction::SLOAD << eth::Instruction::DUP2 << eth::Instruction::BYTE; From b8cede371dc5b7d5c40188dbb65eff293d7dd6ff Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Wed, 11 Mar 2015 17:41:12 +0100 Subject: [PATCH 09/14] byte is now an alias for byte1 --- Token.h | 7 ++++--- Types.cpp | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Token.h b/Token.h index c439c79ee..7b3467908 100644 --- a/Token.h +++ b/Token.h @@ -252,8 +252,8 @@ namespace solidity K(UInt240, "uint240", 0) \ K(UInt248, "uint248", 0) \ K(UInt256, "uint256", 0) \ - K(Bytes0, "bytes0", 0) \ - K(Bytes1, "bytes1", 0) \ + K(Bytes0, "bytes0", 0) \ + K(Bytes1, "bytes1", 0) \ K(Bytes2, "bytes2", 0) \ K(Bytes3, "bytes3", 0) \ K(Bytes4, "bytes4", 0) \ @@ -285,7 +285,8 @@ namespace solidity K(Bytes30, "bytes30", 0) \ K(Bytes31, "bytes31", 0) \ K(Bytes32, "bytes32", 0) \ - K(Bytes, "bytes", 0) \ + K(Bytes, "bytes", 0) \ + K(Byte, "byte", 0) \ K(Address, "address", 0) \ K(Bool, "bool", 0) \ K(StringType, "string", 0) \ diff --git a/Types.cpp b/Types.cpp index 8524cb8b0..aacf56fa3 100644 --- a/Types.cpp +++ b/Types.cpp @@ -61,6 +61,8 @@ TypePointer Type::fromElementaryTypeName(Token::Value _typeToken) return TypePointer(); } } + else if (_typeToken == Token::Byte) + return make_shared(1); else if (_typeToken == Token::Address) return make_shared(0, IntegerType::Modifier::Address); else if (_typeToken == Token::Bool) From c81b4989535cde327b95a65c53fd271dc1e52f82 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Wed, 11 Mar 2015 17:52:18 +0100 Subject: [PATCH 10/14] Style fixes in Types[cpp/h] --- Types.cpp | 5 ++--- Types.h | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Types.cpp b/Types.cpp index aacf56fa3..6f8d4b6b1 100644 --- a/Types.cpp +++ b/Types.cpp @@ -178,8 +178,8 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const { return _convertTo.getCategory() == getCategory() || - _convertTo.getCategory() == Category::Contract || - _convertTo.getCategory() == Category::Enum || + _convertTo.getCategory() == Category::Contract || + _convertTo.getCategory() == Category::Enum || _convertTo.getCategory() == Category::FixedBytes; } @@ -488,7 +488,6 @@ TypePointer FixedBytesType::unaryOperatorResult(Token::Value _operator) const TypePointer FixedBytesType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const { auto commonType = dynamic_pointer_cast(Type::commonType(shared_from_this(), _other)); - if (!commonType) return TypePointer(); diff --git a/Types.h b/Types.h index 6517cf070..fd59a37ad 100644 --- a/Types.h +++ b/Types.h @@ -158,7 +158,7 @@ protected: }; /** - * Any kind of integer type including address. + * Any kind of integer type (signed, unsigned, address). */ class IntegerType: public Type { @@ -231,7 +231,7 @@ private: }; /** - * Bytes type with fixed length of up to 32 bytes + * Bytes type with fixed length of up to 32 bytes. */ class FixedBytesType: public Type { From 3b54583d380886de57b0668bab4fff8cfa298b8c Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 12 Mar 2015 12:25:07 +0100 Subject: [PATCH 11/14] Style fixes and some additional hash to bytes32 renaming --- LValue.cpp | 2 +- Token.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/LValue.cpp b/LValue.cpp index dc07ec311..a56ed54c7 100644 --- a/LValue.cpp +++ b/LValue.cpp @@ -268,7 +268,7 @@ void StorageByteArrayElement::storeValue(Type const&, SourceLocation const&, boo // stack: value ref (1<<(32-byte_number)) old_full_value_with_cleared_byte m_context << eth::Instruction::SWAP1; m_context << (u256(1) << (256 - 8)) << eth::Instruction::DUP5 << eth::Instruction::DIV - << eth::Instruction::MUL << eth::Instruction::OR; + << eth::Instruction::MUL << eth::Instruction::OR; // stack: value ref new_full_value m_context << eth::Instruction::SWAP1 << eth::Instruction::SSTORE; if (_move) diff --git a/Token.h b/Token.h index 7b3467908..2d8a49fcd 100644 --- a/Token.h +++ b/Token.h @@ -262,9 +262,9 @@ namespace solidity K(Bytes7, "bytes7", 0) \ K(Bytes8, "bytes8", 0) \ K(Bytes9, "bytes9", 0) \ - K(Bytes10, "bytes10", 0) \ - K(Bytes11, "bytes11", 0) \ - K(Bytes12, "bytes12", 0) \ + K(Bytes10, "bytes10", 0) \ + K(Bytes11, "bytes11", 0) \ + K(Bytes12, "bytes12", 0) \ K(Bytes13, "bytes13", 0) \ K(Bytes14, "bytes14", 0) \ K(Bytes15, "bytes15", 0) \ From a62d3fa24060016f7c1faaa2ab211d692a2b6359 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 12 Mar 2015 13:39:12 +0100 Subject: [PATCH 12/14] Some fixes on Types.cpp for FixedBytesType --- Types.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Types.cpp b/Types.cpp index 6f8d4b6b1..22e9dfb81 100644 --- a/Types.cpp +++ b/Types.cpp @@ -231,7 +231,7 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe // All integer types can be compared if (Token::isCompareOp(_operator)) return commonType; - // Nothing else can be done with addresses, but hashes can receive bit operators + // Nothing else can be done with addresses if (commonType->isAddress()) return TypePointer(); @@ -282,16 +282,17 @@ IntegerConstantType::IntegerConstantType(Literal const& _literal) bool IntegerConstantType::isImplicitlyConvertibleTo(Type const& _convertTo) const { - auto integerType = getIntegerType(); + shared_ptr integerType = getIntegerType(); + if (!integerType) + return false; + if (_convertTo.getCategory() == Category::FixedBytes) { FixedBytesType const& convertTo = dynamic_cast(_convertTo); - if (convertTo.getNumBytes() * 8 >= integerType->getNumBits()) - return true; - return false; + return convertTo.getNumBytes() * 8 >= integerType->getNumBits(); } - - return integerType && integerType->isImplicitlyConvertibleTo(_convertTo); + + return integerType->isImplicitlyConvertibleTo(_convertTo); } bool IntegerConstantType::isExplicitlyConvertibleTo(Type const& _convertTo) const @@ -462,8 +463,6 @@ bool FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - if (_convertTo.getCategory() == getCategory()) - return true; if (_convertTo.getCategory() == Category::Integer) { IntegerType const& convertTo = dynamic_cast(_convertTo); @@ -471,7 +470,8 @@ bool FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const return true; } - return false; + return _convertTo.getCategory() == Category::Contract || + _convertTo.getCategory() == getCategory(); } TypePointer FixedBytesType::unaryOperatorResult(Token::Value _operator) const From 039b133c180b15863ee3104637c97822d815d932 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Thu, 12 Mar 2015 17:31:39 +0100 Subject: [PATCH 13/14] Small FixedBytes type fixes - Integer Constant is explicitly convertible to FixedBytes, so using that in the tests --- Types.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Types.cpp b/Types.cpp index 22e9dfb81..bd55e2a8b 100644 --- a/Types.cpp +++ b/Types.cpp @@ -191,13 +191,10 @@ TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const // no further unary operators for addresses else if (isAddress()) return TypePointer(); - // "~" is ok for all other types - else if (_operator == Token::BitNot) - return shared_from_this(); // for non-address integers, we allow +, -, ++ and -- else if (_operator == Token::Add || _operator == Token::Sub || _operator == Token::Inc || _operator == Token::Dec || - _operator == Token::After) + _operator == Token::After || _operator == Token::BitNot) return shared_from_this(); else return TypePointer(); @@ -1154,7 +1151,7 @@ MagicType::MagicType(MagicType::Kind _kind): case Kind::Block: m_members = MemberList({{"coinbase", make_shared(0, IntegerType::Modifier::Address)}, {"timestamp", make_shared(256)}, - {"blockhash", make_shared(strings{"uint"}, strings{"bytes"}, FunctionType::Location::BlockHash)}, + {"blockhash", make_shared(strings{"uint"}, strings{"bytes32"}, FunctionType::Location::BlockHash)}, {"difficulty", make_shared(256)}, {"number", make_shared(256)}, {"gaslimit", make_shared(256)}}); From a16677dcfbd7fd7d42fbd6166e234b1b7001ec59 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 13 Mar 2015 13:14:51 +0100 Subject: [PATCH 14/14] Fix gas for builtin. Fixes #1300 --- ArrayUtils.cpp | 1 + Compiler.cpp | 2 ++ CompilerUtils.cpp | 4 ++-- ExpressionCompiler.cpp | 6 ++++++ LValue.cpp | 1 + 5 files changed, 12 insertions(+), 2 deletions(-) diff --git a/ArrayUtils.cpp b/ArrayUtils.cpp index f0d7d6a81..a064b2f57 100644 --- a/ArrayUtils.cpp +++ b/ArrayUtils.cpp @@ -125,6 +125,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons CompilerUtils(m_context).loadFromMemoryDynamic(*sourceBaseType, true, true, false); else solAssert(false, "Copying of unknown type requested: " + sourceBaseType->toString()); + solAssert(2 + sourceBaseType->getSizeOnStack() <= 16, "Stack too deep."); m_context << eth::dupInstruction(2 + sourceBaseType->getSizeOnStack()); StorageItem(m_context, *targetBaseType).storeValue(*sourceBaseType, SourceLocation(), true); } diff --git a/Compiler.cpp b/Compiler.cpp index dc6e2c5a8..b8ca03d3e 100644 --- a/Compiler.cpp +++ b/Compiler.cpp @@ -228,6 +228,7 @@ void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool { // Retrieve data start offset by adding length to start offset of previous dynamic type unsigned stackDepth = m_context.getStackHeight() - stackHeightOfPreviousDynamicArgument; + solAssert(stackDepth <= 16, "Stack too deep."); m_context << eth::dupInstruction(stackDepth) << eth::dupInstruction(stackDepth); ArrayUtils(m_context).convertLengthToSize(*previousDynamicType, true); m_context << eth::Instruction::ADD; @@ -359,6 +360,7 @@ bool Compiler::visit(FunctionDefinition const& _function) stackLayout.push_back(i); stackLayout += vector(c_localVariablesSize, -1); + solAssert(stackLayout.size() <= 17, "Stack too deep."); while (stackLayout.back() != int(stackLayout.size() - 1)) if (stackLayout.back() < 0) { diff --git a/CompilerUtils.cpp b/CompilerUtils.cpp index 7b078e03e..e517e384d 100644 --- a/CompilerUtils.cpp +++ b/CompilerUtils.cpp @@ -138,6 +138,7 @@ void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable) { unsigned const stackPosition = m_context.baseToCurrentStackOffset(m_context.getBaseStackOffsetOfVariable(_variable)); unsigned const size = _variable.getType()->getSizeOnStack(); + solAssert(stackPosition >= size, "Variable size and position mismatch."); // move variable starting from its top end in the stack if (stackPosition - size + 1 > 16) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_variable.getLocation()) @@ -148,8 +149,7 @@ void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable) void CompilerUtils::copyToStackTop(unsigned _stackDepth, unsigned _itemSize) { - if (_stackDepth > 16) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Stack too deep.")); + solAssert(_stackDepth <= 16, "Stack too deep."); for (unsigned i = 0; i < _itemSize; ++i) m_context << eth::dupInstruction(_stackDepth); } diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index 3cee40df1..331979c7d 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -233,9 +233,12 @@ bool ExpressionCompiler::visit(Assignment const& _assignment) m_currentLValue->retrieveValue(_assignment.getLocation(), true); appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.getType()); if (lvalueSize > 0) + { + solAssert(itemSize + lvalueSize <= 16, "Stack too deep."); // value [lvalue_ref] updated_value for (unsigned i = 0; i < itemSize; ++i) m_context << eth::swapInstruction(itemSize + lvalueSize) << eth::Instruction::POP; + } } m_currentLValue->storeValue(*_assignment.getRightHandSide().getType(), _assignment.getLocation()); m_currentLValue.reset(); @@ -557,10 +560,13 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) case Location::SHA256: case Location::RIPEMD160: { + _functionCall.getExpression().accept(*this); static const map contractAddresses{{Location::ECRecover, 1}, {Location::SHA256, 2}, {Location::RIPEMD160, 3}}; m_context << contractAddresses.find(function.getLocation())->second; + for (unsigned i = function.getSizeOnStack(); i > 0; --i) + m_context << eth::swapInstruction(i); appendExternalFunctionCall(function, arguments, true); break; } diff --git a/LValue.cpp b/LValue.cpp index a56ed54c7..68d6797eb 100644 --- a/LValue.cpp +++ b/LValue.cpp @@ -167,6 +167,7 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc // stack: source_ref target_ref member_offset source_member_ref StorageItem(m_context, *memberType).retrieveValue(_location, true); // stack: source_ref target_ref member_offset source_value... + solAssert(2 + memberType->getSizeOnStack() <= 16, "Stack too deep."); m_context << eth::dupInstruction(2 + memberType->getSizeOnStack()) << eth::dupInstruction(2 + memberType->getSizeOnStack()) << eth::Instruction::ADD; // stack: source_ref target_ref member_offset source_value... target_member_ref