aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CompilerUtils.cpp138
-rw-r--r--CompilerUtils.h7
-rw-r--r--ExpressionCompiler.cpp13
-rw-r--r--LValue.cpp2
-rw-r--r--Types.cpp41
-rw-r--r--Types.h36
6 files changed, 186 insertions, 51 deletions
diff --git a/CompilerUtils.cpp b/CompilerUtils.cpp
index ed6a6cf7..297e6aa0 100644
--- a/CompilerUtils.cpp
+++ b/CompilerUtils.cpp
@@ -113,6 +113,16 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
solAssert(ref->location() == DataLocation::Memory, "");
storeInMemoryDynamic(IntegerType(256), _padToWordBoundaries);
}
+ else if (auto str = dynamic_cast<StringLiteralType const*>(&_type))
+ {
+ m_context << eth::Instruction::DUP1;
+ storeStringData(bytesConstRef(str->value()));
+ if (_padToWordBoundaries)
+ m_context << u256(((str->value().size() + 31) / 32) * 32);
+ else
+ m_context << u256(str->value().size());
+ m_context << eth::Instruction::ADD;
+ }
else
{
unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries);
@@ -169,7 +179,8 @@ void CompilerUtils::encodeToMemory(
TypePointer type = targetType;
if (
_givenTypes[i]->dataStoredIn(DataLocation::Storage) ||
- _givenTypes[i]->dataStoredIn(DataLocation::CallData)
+ _givenTypes[i]->dataStoredIn(DataLocation::CallData) ||
+ _givenTypes[i]->getCategory() == Type::Category::StringLiteral
)
type = _givenTypes[i]; // delay conversion
else
@@ -192,36 +203,48 @@ void CompilerUtils::encodeToMemory(
solAssert(!!targetType, "Externalable type expected.");
if (targetType->isDynamicallySized() && !_copyDynamicDataInPlace)
{
- solAssert(_givenTypes[i]->getCategory() == Type::Category::Array, "Unknown dynamic type.");
- auto const& arrayType = dynamic_cast<ArrayType const&>(*_givenTypes[i]);
// copy tail pointer (=mem_end - mem_start) to memory
m_context << eth::dupInstruction(2 + dynPointers) << eth::Instruction::DUP2;
m_context << eth::Instruction::SUB;
m_context << eth::dupInstruction(2 + dynPointers - thisDynPointer);
m_context << eth::Instruction::MSTORE;
- // now copy the array
- copyToStackTop(argSize - stackPos + dynPointers + 2, arrayType.getSizeOnStack());
- // stack: ... <end_of_mem> <value...>
- // copy length to memory
- m_context << eth::dupInstruction(1 + arrayType.getSizeOnStack());
- if (arrayType.location() == DataLocation::CallData)
- m_context << eth::Instruction::DUP2; // length is on stack
- else if (arrayType.location() == DataLocation::Storage)
- m_context << eth::Instruction::DUP3 << eth::Instruction::SLOAD;
+ // stack: ... <end_of_mem>
+ if (_givenTypes[i]->getCategory() == Type::Category::StringLiteral)
+ {
+ auto const& strType = dynamic_cast<StringLiteralType const&>(*_givenTypes[i]);
+ m_context << u256(strType.value().size());
+ storeInMemoryDynamic(IntegerType(256), true);
+ // stack: ... <end_of_mem'>
+ storeInMemoryDynamic(strType, _padToWordBoundaries);
+ }
else
{
- solAssert(arrayType.location() == DataLocation::Memory, "");
- m_context << eth::Instruction::DUP2 << eth::Instruction::MLOAD;
+ solAssert(_givenTypes[i]->getCategory() == Type::Category::Array, "Unknown dynamic type.");
+ auto const& arrayType = dynamic_cast<ArrayType const&>(*_givenTypes[i]);
+ // now copy the array
+ copyToStackTop(argSize - stackPos + dynPointers + 2, arrayType.getSizeOnStack());
+ // stack: ... <end_of_mem> <value...>
+ // copy length to memory
+ m_context << eth::dupInstruction(1 + arrayType.getSizeOnStack());
+ if (arrayType.location() == DataLocation::CallData)
+ m_context << eth::Instruction::DUP2; // length is on stack
+ else if (arrayType.location() == DataLocation::Storage)
+ m_context << eth::Instruction::DUP3 << eth::Instruction::SLOAD;
+ else
+ {
+ solAssert(arrayType.location() == DataLocation::Memory, "");
+ m_context << eth::Instruction::DUP2 << eth::Instruction::MLOAD;
+ }
+ // stack: ... <end_of_mem> <value...> <end_of_mem'> <length>
+ storeInMemoryDynamic(IntegerType(256), true);
+ // stack: ... <end_of_mem> <value...> <end_of_mem''>
+ // copy the new memory pointer
+ m_context << eth::swapInstruction(arrayType.getSizeOnStack() + 1) << eth::Instruction::POP;
+ // stack: ... <end_of_mem''> <value...>
+ // copy data part
+ ArrayUtils(m_context).copyArrayToMemory(arrayType, _padToWordBoundaries);
+ // stack: ... <end_of_mem'''>
}
- // stack: ... <end_of_mem> <value...> <end_of_mem'> <length>
- storeInMemoryDynamic(IntegerType(256), true);
- // stack: ... <end_of_mem> <value...> <end_of_mem''>
- // copy the new memory pointer
- m_context << eth::swapInstruction(arrayType.getSizeOnStack() + 1) << eth::Instruction::POP;
- // stack: ... <end_of_mem''> <value...>
- // copy data part
- ArrayUtils(m_context).copyArrayToMemory(arrayType, _padToWordBoundaries);
- // stack: ... <end_of_mem'''>
thisDynPointer++;
}
@@ -269,22 +292,22 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
// 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);
- m_context << (u256(1) << (256 - typeOnStack.getNumBytes() * 8)) << eth::Instruction::SWAP1 << eth::Instruction::DIV;
- if (targetIntegerType.getNumBits() < typeOnStack.getNumBytes() * 8)
- convertType(IntegerType(typeOnStack.getNumBytes() * 8), _targetType, _cleanupNeeded);
+ m_context << (u256(1) << (256 - typeOnStack.numBytes() * 8)) << eth::Instruction::SWAP1 << eth::Instruction::DIV;
+ if (targetIntegerType.getNumBits() < typeOnStack.numBytes() * 8)
+ convertType(IntegerType(typeOnStack.numBytes() * 8), _targetType, _cleanupNeeded);
}
else
{
// 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.numBytes() < typeOnStack.numBytes())
{
- if (targetType.getNumBytes() == 0)
+ if (targetType.numBytes() == 0)
m_context << eth::Instruction::DUP1 << eth::Instruction::XOR;
else
{
- m_context << (u256(1) << (256 - targetType.getNumBytes() * 8));
+ m_context << (u256(1) << (256 - targetType.numBytes() * 8));
m_context << eth::Instruction::DUP1 << eth::Instruction::SWAP2;
m_context << eth::Instruction::DIV << eth::Instruction::MUL;
}
@@ -306,9 +329,9 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
// only to shift left because of opposite alignment
FixedBytesType const& targetBytesType = dynamic_cast<FixedBytesType const&>(_targetType);
if (auto typeOnStack = dynamic_cast<IntegerType const*>(&_typeOnStack))
- if (targetBytesType.getNumBytes() * 8 > typeOnStack->getNumBits())
+ if (targetBytesType.numBytes() * 8 > typeOnStack->getNumBits())
cleanHigherOrderBits(*typeOnStack);
- m_context << (u256(1) << (256 - targetBytesType.getNumBytes() * 8)) << eth::Instruction::MUL;
+ m_context << (u256(1) << (256 - targetBytesType.numBytes() * 8)) << eth::Instruction::MUL;
}
else if (targetTypeCategory == Type::Category::Enum)
// just clean
@@ -340,6 +363,37 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
}
}
break;
+ case Type::Category::StringLiteral:
+ {
+ auto const& literalType = dynamic_cast<StringLiteralType const&>(_typeOnStack);
+ string const& value = literalType.value();
+ bytesConstRef data(value);
+ if (targetTypeCategory == Type::Category::FixedBytes)
+ {
+ solAssert(data.size() <= 32, "");
+ m_context << h256::Arith(h256(data, h256::AlignLeft));
+ }
+ else if (targetTypeCategory == Type::Category::Array)
+ {
+ auto const& arrayType = dynamic_cast<ArrayType const&>(_targetType);
+ solAssert(arrayType.isByteArray(), "");
+ u256 storageSize(32 + ((data.size() + 31) / 32) * 32);
+ m_context << storageSize;
+ allocateMemory();
+ // stack: mempos
+ m_context << eth::Instruction::DUP1 << u256(data.size());
+ storeInMemoryDynamic(IntegerType(256));
+ // stack: mempos datapos
+ storeStringData(data);
+ break;
+ }
+ else
+ solAssert(
+ false,
+ "Invalid conversion from string literal to " + _targetType.toString(false) + " requested."
+ );
+ break;
+ }
case Type::Category::Array:
{
solAssert(targetTypeCategory == stackTypeCategory, "");
@@ -606,6 +660,28 @@ void CompilerUtils::computeHashStatic()
m_context << u256(32) << u256(0) << eth::Instruction::SHA3;
}
+void CompilerUtils::storeStringData(bytesConstRef _data)
+{
+ //@todo provide both alternatives to the optimiser
+ // stack: mempos
+ if (_data.size() <= 128)
+ {
+ for (unsigned i = 0; i < _data.size(); i += 32)
+ {
+ m_context << h256::Arith(h256(_data.cropped(i), h256::AlignLeft));
+ storeInMemoryDynamic(IntegerType(256));
+ }
+ m_context << eth::Instruction::POP;
+ }
+ else
+ {
+ // stack: mempos mempos_data
+ m_context.appendData(_data.toBytes());
+ m_context << u256(_data.size()) << eth::Instruction::SWAP2;
+ m_context << eth::Instruction::CODECOPY;
+ }
+}
+
unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries)
{
unsigned numBytes = _type.getCalldataEncodedSize(_padToWordBoundaries);
diff --git a/CompilerUtils.h b/CompilerUtils.h
index 7dd44da8..dbb00a1d 100644
--- a/CompilerUtils.h
+++ b/CompilerUtils.h
@@ -148,7 +148,12 @@ private:
/// Address of the precompiled identity contract.
static const unsigned identityContractAddress;
- //// Appends code that cleans higher-order bits for integer types.
+ /// Stores the given string in memory.
+ /// Stack pre: mempos
+ /// Stack post:
+ void storeStringData(bytesConstRef _data);
+
+ /// Appends code that cleans higher-order bits for integer types.
void cleanHigherOrderBits(IntegerType const& _typeOnStack);
/// Prepares the given type for storing in memory by shifting it if necessary.
diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp
index 13cd4032..05835818 100644
--- a/ExpressionCompiler.cpp
+++ b/ExpressionCompiler.cpp
@@ -158,6 +158,11 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
utils().convertType(*type, *_assignment.getType());
type = _assignment.getType();
}
+ else
+ {
+ utils().convertType(*type, *type->mobileType());
+ type = type->mobileType();
+ }
_assignment.getLeftHandSide().accept(*this);
solAssert(!!m_currentLValue, "LValue not retrieved.");
@@ -898,13 +903,15 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
void ExpressionCompiler::endVisit(Literal const& _literal)
{
CompilerContext::LocationSetter locationSetter(m_context, _literal);
- switch (_literal.getType()->getCategory())
+ TypePointer type = _literal.getType();
+ switch (type->getCategory())
{
case Type::Category::IntegerConstant:
case Type::Category::Bool:
- case Type::Category::FixedBytes:
- m_context << _literal.getType()->literalValue(&_literal);
+ m_context << type->literalValue(&_literal);
break;
+ case Type::Category::StringLiteral:
+ break; // will be done during conversion
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Only integer, boolean and string literals implemented for now."));
}
diff --git a/LValue.cpp b/LValue.cpp
index 7b5879e4..82701ea9 100644
--- a/LValue.cpp
+++ b/LValue.cpp
@@ -209,7 +209,7 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
// stack: value storage_ref cleared_value multiplier value
if (m_dataType.getCategory() == Type::Category::FixedBytes)
m_context
- << (u256(0x1) << (256 - 8 * dynamic_cast<FixedBytesType const&>(m_dataType).getNumBytes()))
+ << (u256(0x1) << (256 - 8 * dynamic_cast<FixedBytesType const&>(m_dataType).numBytes()))
<< eth::Instruction::SWAP1 << eth::Instruction::DIV;
else if (
m_dataType.getCategory() == Type::Category::Integer &&
diff --git a/Types.cpp b/Types.cpp
index 5a949299..3ea9caa7 100644
--- a/Types.cpp
+++ b/Types.cpp
@@ -212,8 +212,7 @@ TypePointer Type::forLiteral(Literal const& _literal)
case Token::Number:
return make_shared<IntegerConstantType>(_literal);
case Token::StringLiteral:
- //@todo put larger strings into dynamic strings
- return FixedBytesType::smallestTypeForLiteral(_literal.getValue());
+ return make_shared<StringLiteralType>(_literal);
default:
return shared_ptr<Type>();
}
@@ -378,7 +377,7 @@ bool IntegerConstantType::isImplicitlyConvertibleTo(Type const& _convertTo) cons
else if (_convertTo.getCategory() == Category::FixedBytes)
{
FixedBytesType const& fixedBytes = dynamic_cast<FixedBytesType const&>(_convertTo);
- return fixedBytes.getNumBytes() * 8 >= getIntegerType()->getNumBits();
+ return fixedBytes.numBytes() * 8 >= getIntegerType()->getNumBits();
}
else
return false;
@@ -530,6 +529,33 @@ shared_ptr<IntegerType const> IntegerConstantType::getIntegerType() const
);
}
+StringLiteralType::StringLiteralType(Literal const& _literal):
+ m_value(_literal.getValue())
+{
+}
+
+bool StringLiteralType::isImplicitlyConvertibleTo(Type const& _convertTo) const
+{
+ if (auto fixedBytes = dynamic_cast<FixedBytesType const*>(&_convertTo))
+ return size_t(fixedBytes->numBytes()) >= m_value.size();
+ else if (auto arrayType = dynamic_cast<ArrayType const*>(&_convertTo))
+ return arrayType->isByteArray();
+ else
+ return false;
+}
+
+bool StringLiteralType::operator==(const Type& _other) const
+{
+ if (_other.getCategory() != getCategory())
+ return false;
+ return m_value == dynamic_cast<StringLiteralType const&>(_other).m_value;
+}
+
+TypePointer StringLiteralType::mobileType() const
+{
+ return make_shared<ArrayType>(DataLocation::Memory, true);
+}
+
shared_ptr<FixedBytesType> FixedBytesType::smallestTypeForLiteral(string const& _literal)
{
if (_literal.length() <= 32)
@@ -590,15 +616,6 @@ bool FixedBytesType::operator==(Type const& _other) const
return other.m_bytes == m_bytes;
}
-u256 FixedBytesType::literalValue(const Literal* _literal) const
-{
- solAssert(_literal, "");
- 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
{
// conversion to integer is fine, but not to address
diff --git a/Types.h b/Types.h
index ba53a913..e17a262c 100644
--- a/Types.h
+++ b/Types.h
@@ -131,7 +131,7 @@ class Type: private boost::noncopyable, public std::enable_shared_from_this<Type
public:
enum class Category
{
- Integer, IntegerConstant, Bool, Real, Array,
+ Integer, IntegerConstant, StringLiteral, Bool, Real, Array,
FixedBytes, Contract, Struct, Function, Enum,
Mapping, Void, TypeType, Modifier, Magic
};
@@ -312,6 +312,37 @@ private:
};
/**
+ * Literal string, can be converted to bytes, bytesX or string.
+ */
+class StringLiteralType: public Type
+{
+public:
+ virtual Category getCategory() const override { return Category::StringLiteral; }
+
+ explicit StringLiteralType(Literal const& _literal);
+
+ virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
+ virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override
+ {
+ return TypePointer();
+ }
+
+ virtual bool operator==(Type const& _other) const override;
+
+ virtual bool canBeStored() const override { return false; }
+ virtual bool canLiveOutsideStorage() const override { return false; }
+ virtual unsigned getSizeOnStack() const override { return 0; }
+
+ virtual std::string toString(bool) const override { return "literal_string \"" + m_value + "\""; }
+ virtual TypePointer mobileType() const override;
+
+ std::string const& value() const { return m_value; }
+
+private:
+ std::string m_value;
+};
+
+/**
* Bytes type with fixed length of up to 32 bytes.
*/
class FixedBytesType: public Type
@@ -336,10 +367,9 @@ public:
virtual bool isValueType() const override { return true; }
virtual std::string toString(bool) const override { return "bytes" + dev::toString(m_bytes); }
- virtual u256 literalValue(Literal const* _literal) const override;
virtual TypePointer externalType() const override { return shared_from_this(); }
- int getNumBytes() const { return m_bytes; }
+ int numBytes() const { return m_bytes; }
private:
int m_bytes;