aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AST.cpp2
-rw-r--r--ArrayUtils.cpp1
-rw-r--r--Compiler.cpp2
-rw-r--r--CompilerStack.cpp8
-rw-r--r--CompilerUtils.cpp8
-rw-r--r--ExpressionCompiler.cpp70
-rw-r--r--GlobalContext.cpp18
-rw-r--r--LValue.cpp9
-rw-r--r--Token.h103
-rw-r--r--Types.cpp131
-rw-r--r--Types.h27
11 files changed, 196 insertions, 183 deletions
diff --git a/AST.cpp b/AST.cpp
index 79b755e9..46c44995 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<IntegerType>(8, IntegerType::Modifier::Hash);
+ m_type = make_shared<FixedBytesType>(1);
else
m_type = type.getBaseType();
m_isLValue = type.getLocation() != ArrayType::Location::CallData;
diff --git a/ArrayUtils.cpp b/ArrayUtils.cpp
index f0d7d6a8..a064b2f5 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 dc6e2c5a..b8ca03d3 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<int>(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 a878bb61..55ec0cb5 100644
--- a/CompilerStack.cpp
+++ b/CompilerStack.cpp
@@ -41,14 +41,14 @@ namespace solidity
{
const map<string, string> StandardSources = map<string, string>{
- {"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 8a26b5d1..e517e384 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 12926112..331979c7 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<StaticStringType const&>(_typeOnStack);
+ case Type::Category::FixedBytes:
+ {
+ FixedBytesType const& typeOnStack = dynamic_cast<FixedBytesType const&>(_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<IntegerType const&>(_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<StaticStringType const&>(_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<FixedBytesType const&>(_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<StaticStringType const&>(_targetType);
- IntegerType const& typeOnStack = dynamic_cast<IntegerType const&>(_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<FixedBytesType const&>(_targetType);
+ if (auto typeOnStack = dynamic_cast<IntegerType const*>(&_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<IntegerType const&>(_targetType) : addressType;
+ ? dynamic_cast<IntegerType const&>(_targetType) : addressType;
if (stackTypeCategory == Type::Category::IntegerConstant)
{
IntegerConstantType const& constType = dynamic_cast<IntegerConstantType const&>(_typeOnStack);
@@ -188,7 +192,7 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con
else
{
IntegerType const& typeOnStack = stackTypeCategory == Type::Category::Integer
- ? dynamic_cast<IntegerType const&>(_typeOnStack) : addressType;
+ ? dynamic_cast<IntegerType const&>(_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<Location, u256> 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 411e99ab..80cebd76 100644
--- a/GlobalContext.cpp
+++ b/GlobalContext.cpp
@@ -41,23 +41,23 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{make_shared<
make_shared<MagicVariableDeclaration>("suicide",
make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Location::Suicide)),
make_shared<MagicVariableDeclaration>("sha3",
- make_shared<FunctionType>(strings(), strings{"hash"}, FunctionType::Location::SHA3, true)),
+ make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Location::SHA3, true)),
make_shared<MagicVariableDeclaration>("log0",
- make_shared<FunctionType>(strings{"hash"},strings{}, FunctionType::Location::Log0)),
+ make_shared<FunctionType>(strings{"bytes32"}, strings{}, FunctionType::Location::Log0)),
make_shared<MagicVariableDeclaration>("log1",
- make_shared<FunctionType>(strings{"hash", "hash"},strings{}, FunctionType::Location::Log1)),
+ make_shared<FunctionType>(strings{"bytes32", "bytes32"}, strings{}, FunctionType::Location::Log1)),
make_shared<MagicVariableDeclaration>("log2",
- make_shared<FunctionType>(strings{"hash", "hash", "hash"},strings{}, FunctionType::Location::Log2)),
+ make_shared<FunctionType>(strings{"bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Location::Log2)),
make_shared<MagicVariableDeclaration>("log3",
- make_shared<FunctionType>(strings{"hash", "hash", "hash", "hash"},strings{}, FunctionType::Location::Log3)),
+ make_shared<FunctionType>(strings{"bytes32", "bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Location::Log3)),
make_shared<MagicVariableDeclaration>("log4",
- make_shared<FunctionType>(strings{"hash", "hash", "hash", "hash", "hash"},strings{}, FunctionType::Location::Log4)),
+ make_shared<FunctionType>(strings{"bytes32", "bytes32", "bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Location::Log4)),
make_shared<MagicVariableDeclaration>("sha256",
- make_shared<FunctionType>(strings(), strings{"hash"}, FunctionType::Location::SHA256, true)),
+ make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Location::SHA256, true)),
make_shared<MagicVariableDeclaration>("ecrecover",
- make_shared<FunctionType>(strings{"hash", "hash8", "hash", "hash"}, strings{"address"}, FunctionType::Location::ECRecover)),
+ make_shared<FunctionType>(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Location::ECRecover)),
make_shared<MagicVariableDeclaration>("ripemd160",
- make_shared<FunctionType>(strings(), strings{"hash160"}, FunctionType::Location::RIPEMD160, true))})
+ make_shared<FunctionType>(strings(), strings{"bytes20"}, FunctionType::Location::RIPEMD160, true))})
{
}
diff --git a/LValue.cpp b/LValue.cpp
index a036be80..68d6797e 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 85979b56..2d8a49fc 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 454d79d9..bd55e2a8 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<IntegerType>(bytes * 8,
- modifier == 0 ? IntegerType::Modifier::Signed :
- modifier == 1 ? IntegerType::Modifier::Unsigned :
- IntegerType::Modifier::Hash);
+ switch(modifier)
+ {
+ case 0:
+ return make_shared<IntegerType>(bytes * 8, IntegerType::Modifier::Signed);
+ case 1:
+ return make_shared<IntegerType>(bytes * 8, IntegerType::Modifier::Unsigned);
+ case 2:
+ return make_shared<FixedBytesType>(bytes);
+ default:
+ solAssert(false, "Unexpected modifier value. Should never happen");
+ return TypePointer();
+ }
}
+ else if (_typeToken == Token::Byte)
+ return make_shared<FixedBytesType>(1);
else if (_typeToken == Token::Address)
return make_shared<IntegerType>(0, IntegerType::Modifier::Address);
else if (_typeToken == Token::Bool)
return make_shared<BoolType>();
- else if (Token::String0 <= _typeToken && _typeToken <= Token::String32)
- return make_shared<StaticStringType>(int(_typeToken) - int(Token::String0));
else if (_typeToken == Token::Bytes)
return make_shared<ArrayType>(ArrayType::Location::Storage);
else
@@ -123,7 +133,7 @@ TypePointer Type::forLiteral(Literal const& _literal)
return make_shared<IntegerConstantType>(_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<Type>();
}
@@ -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<StaticStringType const&>(_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 const> integerType = getIntegerType();
+ if (!integerType)
+ return false;
+
+ if (_convertTo.getCategory() == Category::FixedBytes)
+ {
+ FixedBytesType const& convertTo = dynamic_cast<FixedBytesType const&>(_convertTo);
+ return convertTo.getNumBytes() * 8 >= integerType->getNumBits();
+ }
+
+ return integerType->isImplicitlyConvertibleTo(_convertTo);
}
bool IntegerConstantType::isExplicitlyConvertibleTo(Type const& _convertTo) const
@@ -433,50 +437,73 @@ shared_ptr<IntegerType const> IntegerConstantType::getIntegerType() const
: IntegerType::Modifier::Unsigned);
}
-shared_ptr<StaticStringType> StaticStringType::smallestTypeForLiteral(string const& _literal)
+shared_ptr<FixedBytesType> FixedBytesType::smallestTypeForLiteral(string const& _literal)
{
if (_literal.length() <= 32)
- return make_shared<StaticStringType>(_literal.length());
- return shared_ptr<StaticStringType>();
+ return make_shared<FixedBytesType>(_literal.length());
+ return shared_ptr<FixedBytesType>();
}
-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<StaticStringType const&>(_convertTo);
+ FixedBytesType const& convertTo = dynamic_cast<FixedBytesType const&>(_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<IntegerType const&>(_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();
+}
+
+TypePointer FixedBytesType::unaryOperatorResult(Token::Value _operator) const
+{
+ // "delete" and "~" is okay for FixedBytesType
+ if (_operator == Token::Delete)
+ return make_shared<VoidType>();
+ 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<FixedBytesType const>(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 StaticStringType::operator==(Type const& _other) const
+bool FixedBytesType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
return false;
- StaticStringType const& other = dynamic_cast<StaticStringType const&>(_other);
+ FixedBytesType const& other = dynamic_cast<FixedBytesType const&>(_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<IntegerType>(0, IntegerType::Modifier::Address)},
{"timestamp", make_shared<IntegerType>(256)},
- {"blockhash", make_shared<FunctionType>(strings{"uint"}, strings{"hash"}, FunctionType::Location::BlockHash)},
+ {"blockhash", make_shared<FunctionType>(strings{"uint"}, strings{"bytes32"}, FunctionType::Location::BlockHash)},
{"difficulty", make_shared<IntegerType>(256)},
{"number", make_shared<IntegerType>(256)},
{"gaslimit", make_shared<IntegerType>(256)}});
diff --git a/Types.h b/Types.h
index 6cef8d64..fd59a37a 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<StaticStringType> smallestTypeForLiteral(std::string const& _literal);
+ static std::shared_ptr<FixedBytesType> 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; }