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/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/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/CompilerUtils.cpp b/CompilerUtils.cpp index 8a26b5d17..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); } @@ -178,7 +178,7 @@ 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; + bool leftAligned = _type.getCategory() == Type::Category::FixedBytes; if (numBytes == 0) m_context << eth::Instruction::POP << u256(0); else @@ -202,7 +202,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 129261120..331979c7d 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -123,23 +123,25 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con Type::Category stackTypeCategory = _typeOnStack.getCategory(); Type::Category targetTypeCategory = _targetType.getCategory(); - if (stackTypeCategory == Type::Category::String) + switch (stackTypeCategory) { - StaticStringType const& typeOnStack = dynamic_cast(_typeOnStack); + case Type::Category::FixedBytes: + { + 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 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.isHash(), "Only conversion between String and Hash 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; + if (targetIntegerType.getNumBits() < typeOnStack.getNumBytes() * 8) + appendTypeConversion(IntegerType(typeOnStack.getNumBytes() * 8), _targetType, _cleanupNeeded); } 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) @@ -151,22 +153,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::String && 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) { - // conversion from hash to string. no need to clean the high bit + 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 - 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.getNumBits() == targetStringType.getNumBytes() * 8, "The size should be the same."); - m_context << (u256(1) << (256 - typeOnStack.getNumBits())) << eth::Instruction::MUL; + FixedBytesType const& targetBytesType = dynamic_cast(_targetType); + 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 @@ -176,7 +180,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); @@ -188,7 +192,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()) @@ -197,10 +201,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) @@ -227,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(); @@ -551,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; } @@ -775,7 +787,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(0xff) << (256 - 8))) << eth::Instruction::AND; break; case ArrayType::Location::Memory: solAssert(false, "Memory lvalues not yet implemented."); @@ -870,7 +882,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/GlobalContext.cpp b/GlobalContext.cpp index 411e99abb..80cebd760 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", "uint8", "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/LValue.cpp b/LValue.cpp index a036be80b..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 @@ -234,7 +235,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) @@ -250,6 +251,7 @@ void StorageByteArrayElement::retrieveValue(SourceLocation const&, bool _remove) 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 +267,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/Token.h b/Token.h index 85979b566..2d8a49fcd 100644 --- a/Token.h +++ b/Token.h @@ -252,77 +252,44 @@ namespace solidity K(UInt240, "uint240", 0) \ 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(Bytes0, "bytes0", 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(Bytes, "bytes", 0) \ + K(Byte, "byte", 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) \ T(TypesEnd, NULL, 0) /* used as type enum end marker */ \ diff --git a/Types.cpp b/Types.cpp index 454d79d9b..bd55e2a8b 100644 --- a/Types.cpp +++ b/Types.cpp @@ -37,26 +37,36 @@ 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::Hash256) + 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; - return make_shared(bytes * 8, - modifier == 0 ? IntegerType::Modifier::Signed : - modifier == 1 ? IntegerType::Modifier::Unsigned : - IntegerType::Modifier::Hash); + 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::Byte) + return make_shared(1); 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 @@ -123,7 +133,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(); } @@ -159,8 +169,6 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const return false; if (isAddress()) return convertTo.isAddress(); - else if (isHash()) - return convertTo.isHash(); else if (isSigned()) return convertTo.isSigned(); else @@ -169,14 +177,10 @@ 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 || + _convertTo.getCategory() == Category::FixedBytes; } TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const @@ -187,16 +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(); - // nothing else for hashes - else if (isHash()) - 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) + _operator == Token::After || _operator == Token::BitNot) return shared_from_this(); else return TypePointer(); @@ -214,7 +212,7 @@ string IntegerType::toString() const { if (isAddress()) return "address"; - string prefix = isHash() ? "hash" : (isSigned() ? "int" : "uint"); + string prefix = isSigned() ? "int" : "uint"; return prefix + dev::toString(m_bits); } @@ -230,14 +228,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 + // Nothing else can be done with addresses if (commonType->isAddress()) return TypePointer(); - else if (commonType->isHash() && !Token::isBitOp(_operator)) - return TypePointer(); - else - return commonType; + + return commonType; } const MemberList IntegerType::AddressMemberList = @@ -284,8 +279,17 @@ IntegerConstantType::IntegerConstantType(Literal const& _literal) bool IntegerConstantType::isImplicitlyConvertibleTo(Type const& _convertTo) const { - TypePointer integerType = getIntegerType(); - return integerType && integerType->isImplicitlyConvertibleTo(_convertTo); + shared_ptr integerType = getIntegerType(); + if (!integerType) + return false; + + if (_convertTo.getCategory() == Category::FixedBytes) + { + FixedBytesType const& convertTo = dynamic_cast(_convertTo); + return convertTo.getNumBytes() * 8 >= integerType->getNumBits(); + } + + return integerType->isImplicitlyConvertibleTo(_convertTo); } bool IntegerConstantType::isExplicitlyConvertibleTo(Type const& _convertTo) const @@ -433,50 +437,73 @@ 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)); + "Invalid byte number for fixed bytes 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.isHash() && (m_bytes * 8 == convertTo.getNumBits())) + if (m_bytes * 8 <= convertTo.getNumBits()) return true; } - return false; + return _convertTo.getCategory() == Category::Contract || + _convertTo.getCategory() == getCategory(); } -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; @@ -1124,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{"hash"}, 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)}}); diff --git a/Types.h b/Types.h index 6cef8d64a..fd59a37ad 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 (signed, unsigned, address). */ class IntegerType: public Type { public: enum class Modifier { - Unsigned, Signed, Hash, 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 isHash() const { return m_modifier == Modifier::Hash || 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; }