diff options
-rw-r--r-- | CompilerUtils.cpp | 138 | ||||
-rw-r--r-- | CompilerUtils.h | 7 | ||||
-rw-r--r-- | ExpressionCompiler.cpp | 13 | ||||
-rw-r--r-- | LValue.cpp | 2 | ||||
-rw-r--r-- | Types.cpp | 41 | ||||
-rw-r--r-- | Types.h | 36 |
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.")); } @@ -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 && @@ -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 @@ -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; |