Browse Source

Dynamic copy to memory.

cl-refactor
Christian 10 years ago
parent
commit
9be8307f22
  1. 7
      libsolidity/Compiler.cpp
  2. 41
      libsolidity/CompilerUtils.cpp
  3. 12
      libsolidity/CompilerUtils.h
  4. 146
      libsolidity/ExpressionCompiler.cpp
  5. 27
      libsolidity/ExpressionCompiler.h
  6. 31
      libsolidity/Types.cpp
  7. 1
      libsolidity/Types.h
  8. 86
      test/SolidityEndToEndTest.cpp

7
libsolidity/Compiler.cpp

@ -207,15 +207,10 @@ void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters)
for (TypePointer const& type: _typeParameters) for (TypePointer const& type: _typeParameters)
{ {
unsigned numBytes = type->getCalldataEncodedSize();
if (numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_comment("Type " + type->toString() + " not yet supported."));
CompilerUtils(m_context).copyToStackTop(stackDepth, *type); CompilerUtils(m_context).copyToStackTop(stackDepth, *type);
ExpressionCompiler::appendTypeConversion(m_context, *type, *type, true); ExpressionCompiler::appendTypeConversion(m_context, *type, *type, true);
bool const c_leftAligned = type->getCategory() == Type::Category::String;
bool const c_padToWords = true; bool const c_padToWords = true;
dataOffset += CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, c_leftAligned, c_padToWords); dataOffset += CompilerUtils(m_context).storeInMemory(dataOffset, *type, c_padToWords);
stackDepth -= type->getSizeOnStack(); stackDepth -= type->getSizeOnStack();
} }
// note that the stack is not cleaned up here // note that the stack is not cleaned up here

41
libsolidity/CompilerUtils.cpp

@ -62,20 +62,22 @@ unsigned CompilerUtils::loadFromMemory(unsigned _offset, unsigned _bytes, bool _
} }
} }
unsigned CompilerUtils::storeInMemory(unsigned _offset, unsigned _bytes, bool _leftAligned, unsigned CompilerUtils::storeInMemory(unsigned _offset, Type const& _type, bool _padToWordBoundaries)
bool _padToWordBoundaries)
{ {
if (_bytes == 0) unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries);
if (numBytes > 0)
m_context << u256(_offset) << eth::Instruction::MSTORE;
return numBytes;
}
void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries)
{
unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries);
if (numBytes > 0)
{ {
m_context << eth::Instruction::POP; m_context << eth::Instruction::DUP2 << eth::Instruction::MSTORE;
return 0; m_context << u256(numBytes) << eth::Instruction::ADD;
} }
solAssert(_bytes <= 32, "Memory store of more than 32 bytes requested.");
if (_bytes != 32 && !_leftAligned && !_padToWordBoundaries)
// shift the value accordingly before storing
m_context << (u256(1) << ((32 - _bytes) * 8)) << eth::Instruction::MUL;
m_context << u256(_offset) << eth::Instruction::MSTORE;
return _padToWordBoundaries ? 32 : _bytes;
} }
void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable) void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
@ -114,5 +116,22 @@ unsigned CompilerUtils::getSizeOnStack(vector<shared_ptr<Type const>> const& _va
return size; return size;
} }
unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBoundaries)
{
unsigned _encodedSize = _type.getCalldataEncodedSize();
unsigned numBytes = _padToWordBoundaries ? getPaddedSize(_encodedSize) : _encodedSize;
bool leftAligned = _type.getCategory() == Type::Category::String;
if (numBytes == 0)
m_context << eth::Instruction::POP;
else
{
solAssert(numBytes <= 32, "Memory store of more than 32 bytes requested.");
if (numBytes != 32 && !leftAligned && !_padToWordBoundaries)
// shift the value accordingly before storing
m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL;
}
return numBytes;
}
} }
} }

12
libsolidity/CompilerUtils.h

@ -47,13 +47,14 @@ public:
bool _fromCalldata = false, bool _padToWordBoundaries = false); bool _fromCalldata = false, bool _padToWordBoundaries = false);
/// Stores data from stack in memory. /// Stores data from stack in memory.
/// @param _offset offset in memory /// @param _offset offset in memory
/// @param _bytes number of bytes to store /// @param _type type of the data on the stack
/// @param _leftAligned if true, data is left aligned on stack (otherwise right aligned)
/// @param _padToWordBoundaries if true, pad the data to word (32 byte) boundaries /// @param _padToWordBoundaries if true, pad the data to word (32 byte) boundaries
/// @returns the number of bytes written to memory (can be different from _bytes if /// @returns the number of bytes written to memory (can be different from _bytes if
/// _padToWordBoundaries is true) /// _padToWordBoundaries is true)
unsigned storeInMemory(unsigned _offset, unsigned _bytes = 32, bool _leftAligned = false, unsigned storeInMemory(unsigned _offset, Type const& _type = IntegerType(256), bool _padToWordBoundaries = false);
bool _padToWordBoundaries = false); /// Dynamic version of @see storeInMemory, expects the memory offset below the value on the stack
/// and also updates that.
void storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries = true);
/// @returns _size rounded up to the next multiple of 32 (the number of bytes occupied in the /// @returns _size rounded up to the next multiple of 32 (the number of bytes occupied in the
/// padded calldata) /// padded calldata)
static unsigned getPaddedSize(unsigned _size) { return ((_size + 31) / 32) * 32; } static unsigned getPaddedSize(unsigned _size) { return ((_size + 31) / 32) * 32; }
@ -72,7 +73,10 @@ public:
/// Bytes we need to the start of call data. /// Bytes we need to the start of call data.
/// - The size in bytes of the function (hash) identifier. /// - The size in bytes of the function (hash) identifier.
static const unsigned int dataStartOffset; static const unsigned int dataStartOffset;
private: private:
unsigned prepareMemoryStore(Type const& _type, bool _padToWordBoundaries);
CompilerContext& m_context; CompilerContext& m_context;
}; };

146
libsolidity/ExpressionCompiler.cpp

@ -276,10 +276,10 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
//@todo copy to memory position 0, shift as soon as we use memory //@todo copy to memory position 0, shift as soon as we use memory
m_context << u256(0) << eth::Instruction::CODECOPY; m_context << u256(0) << eth::Instruction::CODECOPY;
unsigned length = bytecode.size(); m_context << u256(bytecode.size());
length += appendArgumentsCopyToMemory(arguments, function.getParameterTypes(), length); appendArgumentsCopyToMemory(arguments, function.getParameterTypes());
// size, offset, endowment // size, offset, endowment
m_context << u256(length) << u256(0); m_context << u256(0);
if (function.valueSet()) if (function.valueSet())
m_context << eth::dupInstruction(3); m_context << eth::dupInstruction(3);
else else
@ -329,8 +329,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
break; break;
case Location::SHA3: case Location::SHA3:
{ {
unsigned length = appendArgumentsCopyToMemory(arguments, TypePointers(), 0, function.padArguments()); m_context << u256(0);
m_context << u256(length) << u256(0) << eth::Instruction::SHA3; appendArgumentsCopyToMemory(arguments, TypePointers(), function.padArguments());
m_context << u256(0) << eth::Instruction::SHA3;
break; break;
} }
case Location::Log0: case Location::Log0:
@ -345,23 +346,16 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
arguments[arg]->accept(*this); arguments[arg]->accept(*this);
appendTypeConversion(*arguments[arg]->getType(), *function.getParameterTypes()[arg], true); appendTypeConversion(*arguments[arg]->getType(), *function.getParameterTypes()[arg], true);
} }
unsigned length = appendExpressionCopyToMemory(*function.getParameterTypes().front(), m_context << u256(0);
*arguments.front()); appendExpressionCopyToMemory(*function.getParameterTypes().front(), *arguments.front());
solAssert(length == 32, "Log data should be 32 bytes long (for now)."); m_context << u256(0) << eth::logInstruction(logNumber);
m_context << u256(length) << u256(0) << eth::logInstruction(logNumber);
break; break;
} }
case Location::Event: case Location::Event:
{ {
_functionCall.getExpression().accept(*this); _functionCall.getExpression().accept(*this);
auto const& event = dynamic_cast<EventDefinition const&>(function.getDeclaration()); auto const& event = dynamic_cast<EventDefinition const&>(function.getDeclaration());
// Copy all non-indexed arguments to memory (data)
unsigned numIndexed = 0; unsigned numIndexed = 0;
unsigned memLength = 0;
for (unsigned arg = 0; arg < arguments.size(); ++arg)
if (!event.getParameters()[arg]->isIndexed())
memLength += appendExpressionCopyToMemory(*function.getParameterTypes()[arg],
*arguments[arg], memLength);
// All indexed arguments go to the stack // All indexed arguments go to the stack
for (unsigned arg = arguments.size(); arg > 0; --arg) for (unsigned arg = arguments.size(); arg > 0; --arg)
if (event.getParameters()[arg - 1]->isIndexed()) if (event.getParameters()[arg - 1]->isIndexed())
@ -374,7 +368,12 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << u256(h256::Arith(dev::sha3(function.getCanonicalSignature(event.getName())))); m_context << u256(h256::Arith(dev::sha3(function.getCanonicalSignature(event.getName()))));
++numIndexed; ++numIndexed;
solAssert(numIndexed <= 4, "Too many indexed arguments."); solAssert(numIndexed <= 4, "Too many indexed arguments.");
m_context << u256(memLength) << u256(0) << eth::logInstruction(numIndexed); // Copy all non-indexed arguments to memory (data)
m_context << u256(0);
for (unsigned arg = 0; arg < arguments.size(); ++arg)
if (!event.getParameters()[arg]->isIndexed())
appendExpressionCopyToMemory(*function.getParameterTypes()[arg], *arguments[arg]);
m_context << u256(0) << eth::logInstruction(numIndexed);
break; break;
} }
case Location::BlockHash: case Location::BlockHash:
@ -514,12 +513,16 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
{ {
_indexAccess.getBaseExpression().accept(*this); _indexAccess.getBaseExpression().accept(*this);
TypePointer const& keyType = dynamic_cast<MappingType const&>(*_indexAccess.getBaseExpression().getType()).getKeyType(); Type const& baseType = *_indexAccess.getBaseExpression().getType();
unsigned length = appendExpressionCopyToMemory(*keyType, _indexAccess.getIndexExpression()); solAssert(baseType.getCategory() == Type::Category::Mapping, "");
solAssert(length == 32, "Mapping key has to take 32 bytes in memory (for now)."); Type const& keyType = *dynamic_cast<MappingType const&>(baseType).getKeyType();
// @todo move this once we actually use memory m_context << u256(0);
length += CompilerUtils(m_context).storeInMemory(length); appendExpressionCopyToMemory(keyType, _indexAccess.getIndexExpression());
m_context << u256(length) << u256(0) << eth::Instruction::SHA3; solAssert(baseType.getSizeOnStack() == 1,
"Unexpected: Not exactly one stack slot taken by subscriptable expression.");
m_context << eth::Instruction::SWAP1;
appendTypeMoveToMemory(IntegerType(256));
m_context << u256(0) << eth::Instruction::SHA3;
m_currentLValue = LValue(m_context, LValue::LValueType::Storage, *_indexAccess.getType()); m_currentLValue = LValue(m_context, LValue::LValueType::Storage, *_indexAccess.getType());
m_currentLValue.retrieveValueIfLValueNotRequested(_indexAccess); m_currentLValue.retrieveValueIfLValueNotRequested(_indexAccess);
@ -810,26 +813,30 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio
unsigned gasStackPos = m_context.currentToBaseStackOffset(gasValueSize); unsigned gasStackPos = m_context.currentToBaseStackOffset(gasValueSize);
unsigned valueStackPos = m_context.currentToBaseStackOffset(1); unsigned valueStackPos = m_context.currentToBaseStackOffset(1);
if (!bare) //@todo only return the first return value for now
Type const* firstType = _functionType.getReturnParameterTypes().empty() ? nullptr :
_functionType.getReturnParameterTypes().front().get();
unsigned retSize = firstType ? CompilerUtils::getPaddedSize(firstType->getCalldataEncodedSize()) : 0;
m_context << u256(retSize) << u256(0);
if (bare)
m_context << u256(0);
else
{ {
// copy function identifier // copy function identifier
m_context << eth::dupInstruction(gasValueSize + 1); m_context << eth::dupInstruction(gasValueSize + 3);
CompilerUtils(m_context).storeInMemory(0, CompilerUtils::dataStartOffset); CompilerUtils(m_context).storeInMemory(0, IntegerType(CompilerUtils::dataStartOffset * 8));
m_context << u256(CompilerUtils::dataStartOffset);
} }
// reserve space for the function identifier
unsigned dataOffset = bare ? 0 : CompilerUtils::dataStartOffset;
// For bare call, activate "4 byte pad exception": If the first argument has exactly 4 bytes, // For bare call, activate "4 byte pad exception": If the first argument has exactly 4 bytes,
// do not pad it to 32 bytes. // do not pad it to 32 bytes.
dataOffset += appendArgumentsCopyToMemory(_arguments, _functionType.getParameterTypes(), dataOffset, appendArgumentsCopyToMemory(_arguments, _functionType.getParameterTypes(),
_functionType.padArguments(), bare); _functionType.padArguments(), bare);
//@todo only return the first return value for now // CALL arguments: outSize, outOff, inSize, (already present up to here)
Type const* firstType = _functionType.getReturnParameterTypes().empty() ? nullptr : // inOff, value, addr, gas (stack top)
_functionType.getReturnParameterTypes().front().get(); m_context << u256(0);
unsigned retSize = firstType ? CompilerUtils::getPaddedSize(firstType->getCalldataEncodedSize()) : 0;
// CALL arguments: outSize, outOff, inSize, inOff, value, addr, gas (stack top)
m_context << u256(retSize) << u256(0) << u256(dataOffset) << u256(0);
if (_functionType.valueSet()) if (_functionType.valueSet())
m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(valueStackPos)); m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(valueStackPos));
else else
@ -858,14 +865,12 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio
} }
} }
unsigned ExpressionCompiler::appendArgumentsCopyToMemory(vector<ASTPointer<Expression const>> const& _arguments, void ExpressionCompiler::appendArgumentsCopyToMemory(vector<ASTPointer<Expression const>> const& _arguments,
TypePointers const& _types, TypePointers const& _types,
unsigned _memoryOffset, bool _padToWordBoundaries,
bool _padToWordBoundaries, bool _padExceptionIfFourBytes)
bool _padExceptionIfFourBytes)
{ {
solAssert(_types.empty() || _types.size() == _arguments.size(), ""); solAssert(_types.empty() || _types.size() == _arguments.size(), "");
unsigned length = 0;
for (size_t i = 0; i < _arguments.size(); ++i) for (size_t i = 0; i < _arguments.size(); ++i)
{ {
_arguments[i]->accept(*this); _arguments[i]->accept(*this);
@ -875,31 +880,55 @@ unsigned ExpressionCompiler::appendArgumentsCopyToMemory(vector<ASTPointer<Expre
// Do not pad if the first argument has exactly four bytes // Do not pad if the first argument has exactly four bytes
if (i == 0 && pad && _padExceptionIfFourBytes && expectedType->getCalldataEncodedSize() == 4) if (i == 0 && pad && _padExceptionIfFourBytes && expectedType->getCalldataEncodedSize() == 4)
pad = false; pad = false;
length += appendTypeMoveToMemory(*expectedType, _arguments[i]->getLocation(), appendTypeMoveToMemory(*expectedType, pad);
_memoryOffset + length, pad);
} }
return length;
} }
unsigned ExpressionCompiler::appendTypeMoveToMemory(Type const& _type, Location const& _location, unsigned _memoryOffset, bool _padToWordBoundaries) void ExpressionCompiler::appendTypeMoveToMemory(Type const& _type, bool _padToWordBoundaries)
{ {
unsigned const c_encodedSize = _type.getCalldataEncodedSize(); if (_type.getCategory() == Type::Category::ByteArray)
unsigned const c_numBytes = _padToWordBoundaries ? CompilerUtils::getPaddedSize(c_encodedSize) : c_encodedSize; {
if (c_numBytes == 0 || c_numBytes > 32) auto const& type = dynamic_cast<ByteArrayType const&>(_type);
BOOST_THROW_EXCEPTION(CompilerError() solAssert(type.getLocation() == ByteArrayType::Location::Storage, "Non-storage byte arrays not yet implemented.");
<< errinfo_sourceLocation(_location)
<< errinfo_comment("Type " + _type.toString() + " not yet supported.")); m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD;
bool const c_leftAligned = _type.getCategory() == Type::Category::String; // stack here: memory_offset storage_offset length_bytes
return CompilerUtils(m_context).storeInMemory(_memoryOffset, c_numBytes, c_leftAligned, _padToWordBoundaries); // jump to end if length is zero
m_context << eth::Instruction::DUP1 << eth::Instruction::ISZERO;
eth::AssemblyItem loopEnd = m_context.newTag();
m_context.appendConditionalJumpTo(loopEnd);
// compute memory end offset
m_context << eth::Instruction::DUP3 << eth::Instruction::ADD << eth::Instruction::SWAP2;
// actual array data is stored at SHA3(storage_offset)
m_context << eth::Instruction::SWAP1;
CompilerUtils(m_context).storeInMemory(0);
m_context << u256(32) << u256(0) << eth::Instruction::SHA3
<< eth::Instruction::SWAP1;
// stack here: memory_end_offset storage_data_offset memory_offset
eth::AssemblyItem loopStart = m_context.newTag();
m_context << loopStart
// load and store
<< eth::Instruction::DUP2 << eth::Instruction::SLOAD
<< eth::Instruction::DUP2 << eth::Instruction::MSTORE
// increment storage_data_offset by 1
<< eth::Instruction::SWAP1 << u256(1) << eth::Instruction::ADD
// increment memory offset by 32
<< eth::Instruction::SWAP1 << u256(32) << eth::Instruction::ADD
// check for loop condition
<< eth::Instruction::DUP1 << eth::Instruction::DUP4 << eth::Instruction::GT;
m_context.appendConditionalJumpTo(loopStart);
m_context << loopEnd << eth::Instruction::POP << eth::Instruction::POP;
}
else
CompilerUtils(m_context).storeInMemoryDynamic(_type, _padToWordBoundaries);
} }
unsigned ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType, void ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression)
Expression const& _expression,
unsigned _memoryOffset)
{ {
_expression.accept(*this); _expression.accept(*this);
appendTypeConversion(*_expression.getType(), _expectedType, true); appendTypeConversion(*_expression.getType(), _expectedType, true);
return appendTypeMoveToMemory(_expectedType, _expression.getLocation(), _memoryOffset); appendTypeMoveToMemory(_expectedType);
} }
void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl) void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl)
@ -910,7 +939,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
TypePointers const& paramTypes = accessorType.getParameterTypes(); TypePointers const& paramTypes = accessorType.getParameterTypes();
// move arguments to memory // move arguments to memory
for (TypePointer const& paramType: boost::adaptors::reverse(paramTypes)) for (TypePointer const& paramType: boost::adaptors::reverse(paramTypes))
length += appendTypeMoveToMemory(*paramType, Location(), length); length += CompilerUtils(m_context).storeInMemory(length, *paramType, true);
// retrieve the position of the variable // retrieve the position of the variable
m_context << m_context.getStorageLocationOfVariable(_varDecl); m_context << m_context.getStorageLocationOfVariable(_varDecl);
@ -1217,6 +1246,7 @@ void ExpressionCompiler::LValue::copyByteArrayToStorage(ByteArrayType const& _ta
break; break;
} }
case ByteArrayType::Location::Storage: case ByteArrayType::Location::Storage:
solAssert(false, "Not Yet implemented.");
break; break;
default: default:
solAssert(false, "Byte array location not implemented."); solAssert(false, "Byte array location not implemented.");

27
libsolidity/ExpressionCompiler.h

@ -92,21 +92,18 @@ private:
/// Appends code to call a function of the given type with the given arguments. /// Appends code to call a function of the given type with the given arguments.
void appendExternalFunctionCall(FunctionType const& _functionType, std::vector<ASTPointer<Expression const>> const& _arguments, void appendExternalFunctionCall(FunctionType const& _functionType, std::vector<ASTPointer<Expression const>> const& _arguments,
bool bare = false); bool bare = false);
/// Appends code that evaluates the given arguments and moves the result to memory (with optional offset). /// Appends code that evaluates the given arguments and moves the result to memory. The memory offset is
/// @returns the number of bytes moved to memory /// expected to be on the stack and is updated by this call.
unsigned appendArgumentsCopyToMemory(std::vector<ASTPointer<Expression const>> const& _arguments, void appendArgumentsCopyToMemory(std::vector<ASTPointer<Expression const>> const& _arguments,
TypePointers const& _types = {}, TypePointers const& _types = {},
unsigned _memoryOffset = 0, bool _padToWordBoundaries = true,
bool _padToWordBoundaries = true, bool _padExceptionIfFourBytes = false);
bool _padExceptionIfFourBytes = false); /// Appends code that moves a stack element of the given type to memory. The memory offset is
/// Appends code that moves a stack element of the given type to memory /// expected below the stack element and is updated by this call.
/// @returns the number of bytes moved to memory void appendTypeMoveToMemory(Type const& _type, bool _padToWordBoundaries = true);
unsigned appendTypeMoveToMemory(Type const& _type, Location const& _location, unsigned _memoryOffset, /// Appends code that evaluates a single expression and moves the result to memory. The memory offset is
bool _padToWordBoundaries = true); /// expected to be on the stack and is updated by this call.
/// Appends code that evaluates a single expression and moves the result to memory (with optional offset). void appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression);
/// @returns the number of bytes moved to memory
unsigned appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression,
unsigned _memoryOffset = 0);
/// Appends code for a State Variable accessor function /// Appends code for a State Variable accessor function
void appendStateVariableAccessor(VariableDeclaration const& _varDecl); void appendStateVariableAccessor(VariableDeclaration const& _varDecl);

31
libsolidity/Types.cpp

@ -35,7 +35,7 @@ namespace dev
namespace solidity namespace solidity
{ {
shared_ptr<Type const> Type::fromElementaryTypeName(Token::Value _typeToken) TypePointer Type::fromElementaryTypeName(Token::Value _typeToken)
{ {
solAssert(Token::isElementaryTypeName(_typeToken), "Elementary type name expected."); solAssert(Token::isElementaryTypeName(_typeToken), "Elementary type name expected.");
@ -64,7 +64,12 @@ shared_ptr<Type const> Type::fromElementaryTypeName(Token::Value _typeToken)
std::string(Token::toString(_typeToken)) + " to type.")); std::string(Token::toString(_typeToken)) + " to type."));
} }
shared_ptr<Type const> Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName) TypePointer Type::fromElementaryTypeName(string const& _name)
{
return fromElementaryTypeName(Token::fromIdentifierOrKeyword(_name));
}
TypePointer Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName)
{ {
Declaration const* declaration = _typeName.getReferencedDeclaration(); Declaration const* declaration = _typeName.getReferencedDeclaration();
if (StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(declaration)) if (StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(declaration))
@ -73,21 +78,21 @@ shared_ptr<Type const> Type::fromUserDefinedTypeName(UserDefinedTypeName const&
return make_shared<FunctionType>(*function); return make_shared<FunctionType>(*function);
else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration)) else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration))
return make_shared<ContractType>(*contract); return make_shared<ContractType>(*contract);
return shared_ptr<Type const>(); return TypePointer();
} }
shared_ptr<Type const> Type::fromMapping(Mapping const& _typeName) TypePointer Type::fromMapping(Mapping const& _typeName)
{ {
shared_ptr<Type const> keyType = _typeName.getKeyType().toType(); TypePointer keyType = _typeName.getKeyType().toType();
if (!keyType) if (!keyType)
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Error resolving type name.")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Error resolving type name."));
shared_ptr<Type const> valueType = _typeName.getValueType().toType(); TypePointer valueType = _typeName.getValueType().toType();
if (!valueType) if (!valueType)
BOOST_THROW_EXCEPTION(_typeName.getValueType().createTypeError("Invalid type name")); BOOST_THROW_EXCEPTION(_typeName.getValueType().createTypeError("Invalid type name"));
return make_shared<MappingType>(keyType, valueType); return make_shared<MappingType>(keyType, valueType);
} }
shared_ptr<Type const> Type::forLiteral(Literal const& _literal) TypePointer Type::forLiteral(Literal const& _literal)
{ {
switch (_literal.getToken()) switch (_literal.getToken())
{ {
@ -561,8 +566,8 @@ MemberList const& ContractType::getMembers() const
if (!m_members) if (!m_members)
{ {
// All address members and all interface functions // All address members and all interface functions
map<string, shared_ptr<Type const>> members(IntegerType::AddressMemberList.begin(), map<string, TypePointer> members(IntegerType::AddressMemberList.begin(),
IntegerType::AddressMemberList.end()); IntegerType::AddressMemberList.end());
if (m_super) if (m_super)
{ {
for (ContractDefinition const* base: m_contract.getLinearizedBaseContracts()) for (ContractDefinition const* base: m_contract.getLinearizedBaseContracts())
@ -617,14 +622,14 @@ bool StructType::operator==(Type const& _other) const
u256 StructType::getStorageSize() const u256 StructType::getStorageSize() const
{ {
u256 size = 0; u256 size = 0;
for (pair<string, shared_ptr<Type const>> const& member: getMembers()) for (pair<string, TypePointer> const& member: getMembers())
size += member.second->getStorageSize(); size += member.second->getStorageSize();
return max<u256>(1, size); return max<u256>(1, size);
} }
bool StructType::canLiveOutsideStorage() const bool StructType::canLiveOutsideStorage() const
{ {
for (pair<string, shared_ptr<Type const>> const& member: getMembers()) for (pair<string, TypePointer> const& member: getMembers())
if (!member.second->canLiveOutsideStorage()) if (!member.second->canLiveOutsideStorage())
return false; return false;
return true; return true;
@ -640,7 +645,7 @@ MemberList const& StructType::getMembers() const
// We need to lazy-initialize it because of recursive references. // We need to lazy-initialize it because of recursive references.
if (!m_members) if (!m_members)
{ {
map<string, shared_ptr<Type const>> members; map<string, TypePointer> members;
for (ASTPointer<VariableDeclaration> const& variable: m_struct.getMembers()) for (ASTPointer<VariableDeclaration> const& variable: m_struct.getMembers())
members[variable->getName()] = variable->getType(); members[variable->getName()] = variable->getType();
m_members.reset(new MemberList(members)); m_members.reset(new MemberList(members));
@ -847,7 +852,7 @@ TypePointers FunctionType::parseElementaryTypeVector(strings const& _types)
TypePointers pointers; TypePointers pointers;
pointers.reserve(_types.size()); pointers.reserve(_types.size());
for (string const& type: _types) for (string const& type: _types)
pointers.push_back(Type::fromElementaryTypeName(Token::fromIdentifierOrKeyword(type))); pointers.push_back(Type::fromElementaryTypeName(type));
return pointers; return pointers;
} }

1
libsolidity/Types.h

@ -86,6 +86,7 @@ public:
///@name Factory functions ///@name Factory functions
/// Factory functions that convert an AST @ref TypeName to a Type. /// Factory functions that convert an AST @ref TypeName to a Type.
static TypePointer fromElementaryTypeName(Token::Value _typeToken); static TypePointer fromElementaryTypeName(Token::Value _typeToken);
static TypePointer fromElementaryTypeName(std::string const& _name);
static TypePointer fromUserDefinedTypeName(UserDefinedTypeName const& _typeName); static TypePointer fromUserDefinedTypeName(UserDefinedTypeName const& _typeName);
static TypePointer fromMapping(Mapping const& _typeName); static TypePointer fromMapping(Mapping const& _typeName);
static TypePointer fromFunction(FunctionDefinition const& _function); static TypePointer fromFunction(FunctionDefinition const& _function);

86
test/SolidityEndToEndTest.cpp

@ -668,7 +668,7 @@ BOOST_AUTO_TEST_CASE(mapping_state)
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(0)); testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(0));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(1)); testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(1));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(2)); testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(2));
// voting without vote right shourd be rejected // voting without vote right should be rejected
testSolidityAgainstCpp("vote(address,address)", vote, u160(0), u160(2)); testSolidityAgainstCpp("vote(address,address)", vote, u160(0), u160(2));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(0)); testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(0));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(1)); testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(1));
@ -2276,68 +2276,66 @@ BOOST_AUTO_TEST_CASE(store_bytes)
BOOST_AUTO_TEST_CASE(call_forward_bytes) BOOST_AUTO_TEST_CASE(call_forward_bytes)
{ {
char const* sourceCode = R"( char const* sourceCode = R"(
contract C { contract receiver {
function save() { uint public received;
savedData = msg.data; function receive(uint x) { received += x + 1; }
} function() { received = 0x80; }
function forward() { }
this.call(savedData); contract sender {
} function sender() { rec = new receiver(); }
function clear() { function() { savedData = msg.data; }
delete savedData; function forward() { rec.call(savedData); }
} function clear() { delete savedData; }
function doubleIt(uint a) { val += a * 2; } function val() returns (uint) { return rec.received(); }
receiver rec;
bytes savedData; bytes savedData;
uint public val;
} }
)"; )";
compileAndRun(sourceCode); compileAndRun(sourceCode, 0, "sender");
FixedHash<4> innerHash(dev::sha3("doubleIt(uint256)")); BOOST_CHECK(callContractFunction("receive(uint256)", 7) == bytes());
BOOST_CHECK(callContractFunction("save()", innerHash.asBytes(), 7) == bytes()); BOOST_CHECK(callContractFunction("val()") == encodeArgs(0));
BOOST_CHECK(callContractFunction("val()") == bytes(0));
BOOST_CHECK(callContractFunction("forward()") == bytes()); BOOST_CHECK(callContractFunction("forward()") == bytes());
BOOST_CHECK(callContractFunction("val()") == bytes(14)); BOOST_CHECK(callContractFunction("val()") == encodeArgs(8));
BOOST_CHECK(callContractFunction("clear()") == bytes()); BOOST_CHECK(callContractFunction("clear()") == bytes());
BOOST_CHECK(callContractFunction("val()") == bytes(14)); BOOST_CHECK(callContractFunction("val()") == encodeArgs(8));
BOOST_CHECK(callContractFunction("forward()") == bytes()); BOOST_CHECK(callContractFunction("forward()") == bytes());
BOOST_CHECK(callContractFunction("val()") == bytes(14)); BOOST_CHECK(callContractFunction("val()") == encodeArgs(0x80));
} }
BOOST_AUTO_TEST_CASE(copying_bytes_multiassign) BOOST_AUTO_TEST_CASE(copying_bytes_multiassign)
{ {
char const* sourceCode = R"( char const* sourceCode = R"(
contract C { contract receiver {
function save() { uint public received;
data1 = data2 = msg.data; function receive(uint x) { received += x + 1; }
} function() { received = 0x80; }
}
contract sender {
function sender() { rec = new receiver(); }
function() { savedData1 = savedData2 = msg.data; }
function forward(bool selector) { function forward(bool selector) {
if (selector) if (selector) { rec.call(savedData1); delete savedData1; }
{ else { rec.call(savedData2); delete savedData2; }
this.call(data1);
delete data1;
}
else
{
this.call(data2);
delete data2;
}
} }
function doubleIt(uint a) returns (uint b) { val += a * 2; } function val() returns (uint) { return rec.received(); }
bytes data1; receiver rec;
bytes data2; bytes savedData1;
uint public val; bytes savedData2;
} }
)"; )";
compileAndRun(sourceCode); compileAndRun(sourceCode, 0, "sender");
FixedHash<4> innerHash(dev::sha3("doubleIt(uint256)")); BOOST_CHECK(callContractFunction("receive(uint256)", 7) == bytes());
BOOST_CHECK(callContractFunction("save()", innerHash.asBytes(), 7) == bytes()); BOOST_CHECK(callContractFunction("val()") == encodeArgs(0));
BOOST_CHECK(callContractFunction("val()") == bytes(0));
BOOST_CHECK(callContractFunction("forward(bool)", true) == bytes()); BOOST_CHECK(callContractFunction("forward(bool)", true) == bytes());
BOOST_CHECK(callContractFunction("val()") == bytes(14)); BOOST_CHECK(callContractFunction("val()") == encodeArgs(8));
BOOST_CHECK(callContractFunction("forward(bool)", false) == bytes()); BOOST_CHECK(callContractFunction("forward(bool)", false) == bytes());
BOOST_CHECK(callContractFunction("val()") == bytes(28)); BOOST_CHECK(callContractFunction("val()") == encodeArgs(16));
BOOST_CHECK(callContractFunction("forward(bool)", true) == bytes());
BOOST_CHECK(callContractFunction("val()") == encodeArgs(0x80));
} }
// TODO test that "delete" also clears the values
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

Loading…
Cancel
Save