mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge remote-tracking branch 'upstream/develop' into evmjit
This commit is contained in:
commit
0be8fa49f0
2
AST.cpp
2
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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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";)"}
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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))})
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
103
Token.h
103
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 */ \
|
||||
|
131
Types.cpp
131
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();
|
||||
}
|
||||
|
||||
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<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 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)}});
|
||||
|
27
Types.h
27
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; }
|
||||
|
Loading…
Reference in New Issue
Block a user