String types.

This commit is contained in:
Christian 2014-12-09 18:46:18 +01:00
parent e8b7d26664
commit 130ff85e85
7 changed files with 130 additions and 9 deletions

View File

@ -582,7 +582,7 @@ void Literal::checkTypeRequirements()
{ {
m_type = Type::forLiteral(*this); m_type = Type::forLiteral(*this);
if (!m_type) if (!m_type)
BOOST_THROW_EXCEPTION(createTypeError("Literal value too large.")); BOOST_THROW_EXCEPTION(createTypeError("Literal value too large or too small."));
} }
} }

View File

@ -142,6 +142,9 @@ unsigned Compiler::appendCalldataUnpacker(FunctionDefinition const& _function, b
<< errinfo_comment("Type " + var->getType()->toString() + " not yet supported.")); << errinfo_comment("Type " + var->getType()->toString() + " not yet supported."));
if (numBytes == 32) if (numBytes == 32)
m_context << u256(dataOffset) << load; m_context << u256(dataOffset) << load;
else if (var->getType()->getCategory() == Type::Category::STRING)
m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::DUP1
<< u256(dataOffset) << load << eth::Instruction::DIV << eth::Instruction::MUL;
else else
m_context << (u256(1) << ((32 - numBytes) * 8)) << u256(dataOffset) m_context << (u256(1) << ((32 - numBytes) * 8)) << u256(dataOffset)
<< load << eth::Instruction::DIV; << load << eth::Instruction::DIV;
@ -166,7 +169,8 @@ void Compiler::appendReturnValuePacker(FunctionDefinition const& _function)
<< errinfo_comment("Type " + paramType.toString() + " not yet supported.")); << errinfo_comment("Type " + paramType.toString() + " not yet supported."));
CompilerUtils(m_context).copyToStackTop(stackDepth, paramType); CompilerUtils(m_context).copyToStackTop(stackDepth, paramType);
if (numBytes != 32) if (numBytes != 32)
m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL; if (paramType.getCategory() != Type::Category::STRING)
m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL;
m_context << u256(dataOffset) << eth::Instruction::MSTORE; m_context << u256(dataOffset) << eth::Instruction::MSTORE;
stackDepth -= paramType.getSizeOnStack(); stackDepth -= paramType.getSizeOnStack();
dataOffset += numBytes; dataOffset += numBytes;

View File

@ -226,7 +226,8 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
<< errinfo_sourceLocation(arguments[i]->getLocation()) << errinfo_sourceLocation(arguments[i]->getLocation())
<< errinfo_comment("Type " + type.toString() + " not yet supported.")); << errinfo_comment("Type " + type.toString() + " not yet supported."));
if (numBytes != 32) if (numBytes != 32)
m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL; if (type.getCategory() != Type::Category::STRING)
m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL;
m_context << u256(dataOffset) << eth::Instruction::MSTORE; m_context << u256(dataOffset) << eth::Instruction::MSTORE;
dataOffset += numBytes; dataOffset += numBytes;
} }
@ -243,8 +244,15 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
if (retSize == 32) if (retSize == 32)
m_context << u256(0) << eth::Instruction::MLOAD; m_context << u256(0) << eth::Instruction::MLOAD;
else if (retSize > 0) else if (retSize > 0)
m_context << (u256(1) << ((32 - retSize) * 8)) {
<< u256(0) << eth::Instruction::MLOAD << eth::Instruction::DIV; if (function.getReturnParameterTypes().front()->getCategory() == Type::Category::STRING)
m_context << (u256(1) << ((32 - retSize) * 8)) << eth::Instruction::DUP1
<< u256(0) << eth::Instruction::MLOAD
<< eth::Instruction::DIV << eth::Instruction::MUL;
else
m_context << (u256(1) << ((32 - retSize) * 8))
<< u256(0) << eth::Instruction::MLOAD << eth::Instruction::DIV;
}
break; break;
} }
case Location::SEND: case Location::SEND:
@ -411,10 +419,11 @@ void ExpressionCompiler::endVisit(Literal& _literal)
{ {
case Type::Category::INTEGER: case Type::Category::INTEGER:
case Type::Category::BOOL: case Type::Category::BOOL:
case Type::Category::STRING:
m_context << _literal.getType()->literalValue(_literal); m_context << _literal.getType()->literalValue(_literal);
break; break;
default: default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Only integer and boolean literals implemented for now.")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Only integer, boolean and string literals implemented for now."));
} }
} }
@ -550,6 +559,11 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con
return; return;
if (_typeOnStack.getCategory() == Type::Category::INTEGER) if (_typeOnStack.getCategory() == Type::Category::INTEGER)
appendHighBitsCleanup(dynamic_cast<IntegerType const&>(_typeOnStack)); appendHighBitsCleanup(dynamic_cast<IntegerType const&>(_typeOnStack));
else if (_typeOnStack.getCategory() == Type::Category::STRING)
{
// nothing to do, strings are high-order-bit-aligned
//@todo clear lower-order bytes if we allow explicit conversion to shorter strings
}
else if (_typeOnStack != _targetType) else if (_typeOnStack != _targetType)
// All other types should not be convertible to non-equal types. // All other types should not be convertible to non-equal types.
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid type conversion requested.")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid type conversion requested."));

View File

@ -35,6 +35,7 @@ namespace solidity {
class CompilerContext; class CompilerContext;
class Type; class Type;
class IntegerType; class IntegerType;
class StaticStringType;
/** /**
* Compiler for expressions, i.e. converts an AST tree whose root is an Expression into a stream * Compiler for expressions, i.e. converts an AST tree whose root is an Expression into a stream
@ -75,7 +76,7 @@ private:
/// @} /// @}
/// Appends an implicit or explicit type conversion. For now this comprises only erasing /// Appends an implicit or explicit type conversion. For now this comprises only erasing
/// higher-order bits (@see appendHighBitCleanup) when widening integer types. /// higher-order bits (@see appendHighBitCleanup) when widening integer.
/// If @a _cleanupNeeded, high order bits cleanup is also done if no type conversion would be /// If @a _cleanupNeeded, high order bits cleanup is also done if no type conversion would be
/// necessary. /// necessary.
void appendTypeConversion(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded = false); void appendTypeConversion(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded = false);

32
Token.h
View File

@ -269,6 +269,38 @@ namespace solidity
K(ADDRESS, "address", 0) \ K(ADDRESS, "address", 0) \
K(BOOL, "bool", 0) \ K(BOOL, "bool", 0) \
K(STRING_TYPE, "string", 0) \ K(STRING_TYPE, "string", 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(TEXT, "text", 0) \
K(REAL, "real", 0) \ K(REAL, "real", 0) \
K(UREAL, "ureal", 0) \ K(UREAL, "ureal", 0) \

View File

@ -53,6 +53,8 @@ shared_ptr<Type> Type::fromElementaryTypeName(Token::Value _typeToken)
return make_shared<IntegerType>(0, IntegerType::Modifier::ADDRESS); return make_shared<IntegerType>(0, IntegerType::Modifier::ADDRESS);
else if (_typeToken == Token::BOOL) else if (_typeToken == Token::BOOL)
return make_shared<BoolType>(); return make_shared<BoolType>();
else if (Token::STRING1 <= _typeToken && _typeToken <= Token::STRING32)
return make_shared<StaticStringType>(int(_typeToken) - int(Token::STRING1) + 1);
else else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " +
std::string(Token::toString(_typeToken)) + " to type.")); std::string(Token::toString(_typeToken)) + " to type."));
@ -91,7 +93,8 @@ shared_ptr<Type> Type::forLiteral(Literal const& _literal)
case Token::NUMBER: case Token::NUMBER:
return IntegerType::smallestTypeForLiteral(_literal.getValue()); return IntegerType::smallestTypeForLiteral(_literal.getValue());
case Token::STRING_LITERAL: case Token::STRING_LITERAL:
return shared_ptr<Type>(); // @todo add string literals //@todo put larger strings into dynamic strings
return StaticStringType::smallestTypeForLiteral(_literal.getValue());
default: default:
return shared_ptr<Type>(); return shared_ptr<Type>();
} }
@ -194,6 +197,44 @@ const MemberList IntegerType::AddressMemberList =
{"send", make_shared<FunctionType const>(TypePointers({make_shared<IntegerType const>(256)}), {"send", make_shared<FunctionType const>(TypePointers({make_shared<IntegerType const>(256)}),
TypePointers(), FunctionType::Location::SEND)}}); TypePointers(), FunctionType::Location::SEND)}});
shared_ptr<StaticStringType> StaticStringType::smallestTypeForLiteral(string const& _literal)
{
if (0 < _literal.length() && _literal.length() <= 32)
return make_shared<StaticStringType>(_literal.length());
return shared_ptr<StaticStringType>();
}
StaticStringType::StaticStringType(int _bytes): m_bytes(_bytes)
{
if (asserts(m_bytes > 0 && m_bytes <= 32))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid byte number for static string type: " +
dev::toString(m_bytes)));
}
bool StaticStringType::isImplicitlyConvertibleTo(Type const& _convertTo) const
{
if (_convertTo.getCategory() != getCategory())
return false;
StaticStringType const& convertTo = dynamic_cast<StaticStringType const&>(_convertTo);
return convertTo.m_bytes >= m_bytes;
}
bool StaticStringType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
return false;
StaticStringType const& other = dynamic_cast<StaticStringType const&>(_other);
return other.m_bytes == m_bytes;
}
u256 StaticStringType::literalValue(const Literal& _literal) const
{
u256 value = 0;
for (char c: _literal.getValue())
value = (value << 8) | byte(c);
return value << ((32 - _literal.getValue().length()) * 8);
}
bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{ {
// conversion to integer is fine, but not to address // conversion to integer is fine, but not to address

31
Types.h
View File

@ -36,7 +36,7 @@ namespace dev
namespace solidity namespace solidity
{ {
// @todo realMxN, string<N> // @todo realMxN, dynamic strings, text, arrays
class Type; // forward class Type; // forward
using TypePointer = std::shared_ptr<Type const>; using TypePointer = std::shared_ptr<Type const>;
@ -178,6 +178,35 @@ private:
static const MemberList AddressMemberList; static const MemberList AddressMemberList;
}; };
/**
* String type with fixed length, up to 32 bytes.
*/
class StaticStringType: public Type
{
public:
virtual Category getCategory() const override { return Category::STRING; }
/// @returns the smallest string type for the given literal or an empty pointer
/// if no type fits.
static std::shared_ptr<StaticStringType> smallestTypeForLiteral(std::string const& _literal);
StaticStringType(int _bytes);
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool operator==(Type const& _other) const override;
virtual unsigned getCalldataEncodedSize() const override { return m_bytes; }
virtual bool isValueType() const override { return true; }
virtual std::string toString() const override { return "string" + dev::toString(m_bytes); }
virtual u256 literalValue(Literal const& _literal) const override;
int getNumBytes() const { return m_bytes; }
private:
int m_bytes;
};
/** /**
* The boolean type. * The boolean type.
*/ */