Browse Source

Copying structs.

cl-refactor
Christian 10 years ago
parent
commit
02f2070ddb
  1. 6
      libsolidity/AST.cpp
  2. 167
      libsolidity/ExpressionCompiler.cpp
  3. 33
      libsolidity/ExpressionCompiler.h
  4. 2
      libsolidity/Types.cpp
  5. 60
      test/SolidityEndToEndTest.cpp
  6. 2
      test/SolidityNameAndTypeResolution.cpp

6
libsolidity/AST.cpp

@ -402,10 +402,8 @@ void Assignment::checkTypeRequirements()
{ {
m_leftHandSide->checkTypeRequirements(); m_leftHandSide->checkTypeRequirements();
m_leftHandSide->requireLValue(); m_leftHandSide->requireLValue();
//@todo later, assignments to structs might be possible, but not to mappings if (m_leftHandSide->getType()->getCategory() == Type::Category::Mapping)
if (m_leftHandSide->getType()->getCategory() != Type::Category::ByteArray && BOOST_THROW_EXCEPTION(createTypeError("Mappings cannot be assigned to."));
!m_leftHandSide->getType()->isValueType() && !m_leftHandSide->isLocalLValue())
BOOST_THROW_EXCEPTION(createTypeError("Assignment to non-local non-value lvalue."));
m_type = m_leftHandSide->getType(); m_type = m_leftHandSide->getType();
if (m_assigmentOperator == Token::Assign) if (m_assigmentOperator == Token::Assign)
m_rightHandSide->expectType(*m_type); m_rightHandSide->expectType(*m_type);

167
libsolidity/ExpressionCompiler.cpp

@ -70,12 +70,12 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
solAssert(_assignment.getType()->isValueType(), "Compound operators not implemented for non-value types."); solAssert(_assignment.getType()->isValueType(), "Compound operators not implemented for non-value types.");
if (m_currentLValue.storesReferenceOnStack()) if (m_currentLValue.storesReferenceOnStack())
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2; m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2;
m_currentLValue.retrieveValue(_assignment.getType(), _assignment.getLocation(), true); m_currentLValue.retrieveValue(_assignment.getLocation(), true);
appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.getType()); appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.getType());
if (m_currentLValue.storesReferenceOnStack()) if (m_currentLValue.storesReferenceOnStack())
m_context << eth::Instruction::SWAP1; m_context << eth::Instruction::SWAP1;
} }
m_currentLValue.storeValue(_assignment, *_assignment.getRightHandSide().getType()); m_currentLValue.storeValue(*_assignment.getRightHandSide().getType(), _assignment.getLocation());
m_currentLValue.reset(); m_currentLValue.reset();
return false; return false;
@ -105,13 +105,13 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
break; break;
case Token::Delete: // delete case Token::Delete: // delete
solAssert(m_currentLValue.isValid(), "LValue not retrieved."); solAssert(m_currentLValue.isValid(), "LValue not retrieved.");
m_currentLValue.setToZero(_unaryOperation, *_unaryOperation.getSubExpression().getType()); m_currentLValue.setToZero(_unaryOperation.getLocation());
m_currentLValue.reset(); m_currentLValue.reset();
break; break;
case Token::Inc: // ++ (pre- or postfix) case Token::Inc: // ++ (pre- or postfix)
case Token::Dec: // -- (pre- or postfix) case Token::Dec: // -- (pre- or postfix)
solAssert(m_currentLValue.isValid(), "LValue not retrieved."); solAssert(m_currentLValue.isValid(), "LValue not retrieved.");
m_currentLValue.retrieveValue(_unaryOperation.getType(), _unaryOperation.getLocation()); m_currentLValue.retrieveValue(_unaryOperation.getLocation());
if (!_unaryOperation.isPrefixOperation()) if (!_unaryOperation.isPrefixOperation())
{ {
if (m_currentLValue.storesReferenceOnStack()) if (m_currentLValue.storesReferenceOnStack())
@ -128,7 +128,8 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
// Stack for postfix: *ref [ref] (*ref)+-1 // Stack for postfix: *ref [ref] (*ref)+-1
if (m_currentLValue.storesReferenceOnStack()) if (m_currentLValue.storesReferenceOnStack())
m_context << eth::Instruction::SWAP1; m_context << eth::Instruction::SWAP1;
m_currentLValue.storeValue(_unaryOperation, *_unaryOperation.getType(), !_unaryOperation.isPrefixOperation()); m_currentLValue.storeValue(*_unaryOperation.getType(), _unaryOperation.getLocation(),
!_unaryOperation.isPrefixOperation());
m_currentLValue.reset(); m_currentLValue.reset();
break; break;
case Token::Add: // + case Token::Add: // +
@ -484,7 +485,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
{ {
StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.getExpression().getType()); StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.getExpression().getType());
m_context << type.getStorageOffsetOfMember(member) << eth::Instruction::ADD; m_context << type.getStorageOffsetOfMember(member) << eth::Instruction::ADD;
m_currentLValue = LValue(m_context, LValue::LValueType::Storage, *_memberAccess.getType()); m_currentLValue = LValue(m_context, LValue::LValueType::Storage, _memberAccess.getType());
m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess); m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess);
break; break;
} }
@ -530,7 +531,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
appendTypeMoveToMemory(IntegerType(256)); appendTypeMoveToMemory(IntegerType(256));
m_context << u256(0) << eth::Instruction::SHA3; 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);
return false; return false;
@ -939,8 +940,8 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
m_context << eth::Instruction::DUP1 m_context << eth::Instruction::DUP1
<< structType->getStorageOffsetOfMember(names[i]) << structType->getStorageOffsetOfMember(names[i])
<< eth::Instruction::ADD; << eth::Instruction::ADD;
m_currentLValue = LValue(m_context, LValue::LValueType::Storage, *types[i]); m_currentLValue = LValue(m_context, LValue::LValueType::Storage, types[i]);
m_currentLValue.retrieveValue(types[i], Location(), true); m_currentLValue.retrieveValue(Location(), true);
solAssert(types[i]->getSizeOnStack() == 1, "Returning struct elements with stack size != 1 not yet implemented."); solAssert(types[i]->getSizeOnStack() == 1, "Returning struct elements with stack size != 1 not yet implemented.");
m_context << eth::Instruction::SWAP1; m_context << eth::Instruction::SWAP1;
retSizeOnStack += types[i]->getSizeOnStack(); retSizeOnStack += types[i]->getSizeOnStack();
@ -951,27 +952,51 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
{ {
// simple value // simple value
solAssert(accessorType.getReturnParameterTypes().size() == 1, ""); solAssert(accessorType.getReturnParameterTypes().size() == 1, "");
m_currentLValue = LValue(m_context, LValue::LValueType::Storage, *returnType); m_currentLValue = LValue(m_context, LValue::LValueType::Storage, returnType);
m_currentLValue.retrieveValue(returnType, Location(), true); m_currentLValue.retrieveValue(Location(), true);
retSizeOnStack = returnType->getSizeOnStack(); retSizeOnStack = returnType->getSizeOnStack();
} }
solAssert(retSizeOnStack <= 15, "Stack too deep."); solAssert(retSizeOnStack <= 15, "Stack too deep.");
m_context << eth::dupInstruction(retSizeOnStack + 1) << eth::Instruction::JUMP; m_context << eth::dupInstruction(retSizeOnStack + 1) << eth::Instruction::JUMP;
} }
ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType, ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type,
unsigned _baseStackOffset): TypePointer const& _dataType, unsigned _baseStackOffset):
m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset) m_context(&_compilerContext), m_type(_type), m_dataType(_dataType),
m_baseStackOffset(_baseStackOffset)
{ {
//@todo change the type cast for arrays //@todo change the type cast for arrays
solAssert(_dataType.getStorageSize() <= numeric_limits<unsigned>::max(), "The storage size of " +_dataType.toString() + " should fit in unsigned"); solAssert(m_dataType->getStorageSize() <= numeric_limits<unsigned>::max(),
"The storage size of " + m_dataType->toString() + " should fit in unsigned");
if (m_type == LValueType::Storage) if (m_type == LValueType::Storage)
m_size = unsigned(_dataType.getStorageSize()); m_size = unsigned(m_dataType->getStorageSize());
else else
m_size = unsigned(_dataType.getSizeOnStack()); m_size = unsigned(m_dataType->getSizeOnStack());
} }
void ExpressionCompiler::LValue::retrieveValue(TypePointer const& _type, Location const& _location, bool _remove) const void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, Declaration const& _declaration)
{
if (m_context->isLocalVariable(&_declaration))
{
m_type = LValueType::Stack;
m_dataType = _identifier.getType();
m_size = m_dataType->getSizeOnStack();
m_baseStackOffset = m_context->getBaseStackOffsetOfVariable(_declaration);
}
else if (m_context->isStateVariable(&_declaration))
{
*m_context << m_context->getStorageLocationOfVariable(_declaration);
m_type = LValueType::Storage;
m_dataType = _identifier.getType();
solAssert(m_dataType->getStorageSize() <= numeric_limits<unsigned>::max(),
"The storage size of " + m_dataType->toString() + " should fit in an unsigned");
m_size = unsigned(m_dataType->getStorageSize()); }
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_identifier.getLocation())
<< errinfo_comment("Identifier type not supported or identifier not found."));
}
void ExpressionCompiler::LValue::retrieveValue(Location const& _location, bool _remove) const
{ {
switch (m_type) switch (m_type)
{ {
@ -986,10 +1011,10 @@ void ExpressionCompiler::LValue::retrieveValue(TypePointer const& _type, Locatio
break; break;
} }
case LValueType::Storage: case LValueType::Storage:
retrieveValueFromStorage(_type, _remove); retrieveValueFromStorage(_remove);
break; break;
case LValueType::Memory: case LValueType::Memory:
if (!_type->isValueType()) if (!m_dataType->isValueType())
break; // no distinction between value and reference for non-value types break; // no distinction between value and reference for non-value types
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_location) BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_location)
<< errinfo_comment("Location type not yet implemented.")); << errinfo_comment("Location type not yet implemented."));
@ -1001,9 +1026,9 @@ void ExpressionCompiler::LValue::retrieveValue(TypePointer const& _type, Locatio
} }
} }
void ExpressionCompiler::LValue::retrieveValueFromStorage(TypePointer const& _type, bool _remove) const void ExpressionCompiler::LValue::retrieveValueFromStorage(bool _remove) const
{ {
if (!_type->isValueType()) if (!m_dataType->isValueType())
return; // no distinction between value and reference for non-value types return; // no distinction between value and reference for non-value types
if (!_remove) if (!_remove)
*m_context << eth::Instruction::DUP1; *m_context << eth::Instruction::DUP1;
@ -1020,7 +1045,7 @@ void ExpressionCompiler::LValue::retrieveValueFromStorage(TypePointer const& _ty
} }
} }
void ExpressionCompiler::LValue::storeValue(Expression const& _expression, Type const& _sourceType, bool _move) const void ExpressionCompiler::LValue::storeValue(Type const& _sourceType, Location const& _location, bool _move) const
{ {
switch (m_type) switch (m_type)
{ {
@ -1028,23 +1053,23 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, Type
{ {
unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)) - m_size + 1; unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)) - m_size + 1;
if (stackDiff > 16) if (stackDiff > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation()) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_location)
<< errinfo_comment("Stack too deep.")); << errinfo_comment("Stack too deep."));
else if (stackDiff > 0) else if (stackDiff > 0)
for (unsigned i = 0; i < m_size; ++i) for (unsigned i = 0; i < m_size; ++i)
*m_context << eth::swapInstruction(stackDiff) << eth::Instruction::POP; *m_context << eth::swapInstruction(stackDiff) << eth::Instruction::POP;
if (!_move) if (!_move)
retrieveValue(_expression.getType(), _expression.getLocation()); retrieveValue(_location);
break; break;
} }
case LValueType::Storage: case LValueType::Storage:
// stack layout: value value ... value target_ref // stack layout: value value ... value target_ref
if (_expression.getType()->isValueType()) if (m_dataType->isValueType())
{ {
if (!_move) // copy values if (!_move) // copy values
{ {
if (m_size + 1 > 16) if (m_size + 1 > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation()) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_location)
<< errinfo_comment("Stack too deep.")); << errinfo_comment("Stack too deep."));
for (unsigned i = 0; i < m_size; ++i) for (unsigned i = 0; i < m_size; ++i)
*m_context << eth::dupInstruction(m_size + 1) << eth::Instruction::SWAP1; *m_context << eth::dupInstruction(m_size + 1) << eth::Instruction::SWAP1;
@ -1064,36 +1089,60 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, Type
} }
else else
{ {
solAssert(!_move, "Move assign for non-value types not implemented."); solAssert(_sourceType.getCategory() == m_dataType->getCategory(), "");
solAssert(_sourceType.getCategory() == _expression.getType()->getCategory(), ""); if (m_dataType->getCategory() == Type::Category::ByteArray)
if (_expression.getType()->getCategory() == Type::Category::ByteArray)
CompilerUtils(*m_context).copyByteArrayToStorage( CompilerUtils(*m_context).copyByteArrayToStorage(
dynamic_cast<ByteArrayType const&>(*_expression.getType()), dynamic_cast<ByteArrayType const&>(*m_dataType),
dynamic_cast<ByteArrayType const&>(_sourceType)); dynamic_cast<ByteArrayType const&>(_sourceType));
else if (_expression.getType()->getCategory() == Type::Category::Struct) else if (m_dataType->getCategory() == Type::Category::Struct)
{ {
//@todo // stack layout: source_ref target_ref
solAssert(false, "Struct copy not yet implemented."); auto const& structType = dynamic_cast<StructType const&>(*m_dataType);
solAssert(structType == _sourceType, "Struct assignment with conversion.");
for (auto const& member: structType.getMembers())
{
// assign each member that is not a mapping
TypePointer const& memberType = member.second;
if (memberType->getCategory() == Type::Category::Mapping)
continue;
*m_context << structType.getStorageOffsetOfMember(member.first)
<< eth::Instruction::DUP3 << eth::Instruction::DUP2
<< eth::Instruction::ADD;
LValue rightHandSide(*m_context, LValueType::Storage, memberType);
rightHandSide.retrieveValue(_location, true);
// stack: source_ref target_ref offset source_value...
*m_context << eth::dupInstruction(2 + memberType->getSizeOnStack())
<< eth::dupInstruction(2 + memberType->getSizeOnStack())
<< eth::Instruction::ADD;
LValue memberLValue(*m_context, LValueType::Storage, memberType);
memberLValue.storeValue(*memberType, _location, true);
*m_context << eth::Instruction::POP;
}
if (_move)
*m_context << eth::Instruction::POP;
else
*m_context << eth::Instruction::SWAP1;
*m_context << eth::Instruction::POP;
} }
else else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation()) BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_location)
<< errinfo_comment("Invalid non-value type for assignment.")); << errinfo_comment("Invalid non-value type for assignment."));
} }
break; break;
case LValueType::Memory: case LValueType::Memory:
if (!_expression.getType()->isValueType()) if (!m_dataType->isValueType())
break; // no distinction between value and reference for non-value types break; // no distinction between value and reference for non-value types
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation()) BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_location)
<< errinfo_comment("Location type not yet implemented.")); << errinfo_comment("Location type not yet implemented."));
break; break;
default: default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation()) BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_location)
<< errinfo_comment("Unsupported location type.")); << errinfo_comment("Unsupported location type."));
break; break;
} }
} }
void ExpressionCompiler::LValue::setToZero(Expression const& _expression, Type const& _type) const void ExpressionCompiler::LValue::setToZero(Location const& _location) const
{ {
switch (m_type) switch (m_type)
{ {
@ -1101,7 +1150,7 @@ void ExpressionCompiler::LValue::setToZero(Expression const& _expression, Type c
{ {
unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)); unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset));
if (stackDiff > 16) if (stackDiff > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation()) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_location)
<< errinfo_comment("Stack too deep.")); << errinfo_comment("Stack too deep."));
solAssert(stackDiff >= m_size - 1, ""); solAssert(stackDiff >= m_size - 1, "");
for (unsigned i = 0; i < m_size; ++i) for (unsigned i = 0; i < m_size; ++i)
@ -1110,8 +1159,8 @@ void ExpressionCompiler::LValue::setToZero(Expression const& _expression, Type c
break; break;
} }
case LValueType::Storage: case LValueType::Storage:
if (_type.getCategory() == Type::Category::ByteArray) if (m_dataType->getCategory() == Type::Category::ByteArray)
CompilerUtils(*m_context).clearByteArray(dynamic_cast<ByteArrayType const&>(_type)); CompilerUtils(*m_context).clearByteArray(dynamic_cast<ByteArrayType const&>(*m_dataType));
else else
{ {
if (m_size == 0) if (m_size == 0)
@ -1125,11 +1174,11 @@ void ExpressionCompiler::LValue::setToZero(Expression const& _expression, Type c
} }
break; break;
case LValueType::Memory: case LValueType::Memory:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation()) BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_location)
<< errinfo_comment("Location type not yet implemented.")); << errinfo_comment("Location type not yet implemented."));
break; break;
default: default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation()) BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_location)
<< errinfo_comment("Unsupported location type.")); << errinfo_comment("Unsupported location type."));
break; break;
} }
@ -1139,36 +1188,10 @@ void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression co
{ {
if (!_expression.lvalueRequested()) if (!_expression.lvalueRequested())
{ {
retrieveValue(_expression.getType(), _expression.getLocation(), true); retrieveValue(_expression.getLocation(), true);
reset(); reset();
} }
} }
void ExpressionCompiler::LValue::fromStateVariable(Declaration const& _varDecl, TypePointer const& _type)
{
m_type = LValueType::Storage;
solAssert(_type->getStorageSize() <= numeric_limits<unsigned>::max(), "The storage size of " + _type->toString() + " should fit in an unsigned");
*m_context << m_context->getStorageLocationOfVariable(_varDecl);
m_size = unsigned(_type->getStorageSize());
}
void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, Declaration const& _declaration)
{
if (m_context->isLocalVariable(&_declaration))
{
m_type = LValueType::Stack;
m_size = _identifier.getType()->getSizeOnStack();
m_baseStackOffset = m_context->getBaseStackOffsetOfVariable(_declaration);
}
else if (m_context->isStateVariable(&_declaration))
{
fromStateVariable(_declaration, _identifier.getType());
}
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_identifier.getLocation())
<< errinfo_comment("Identifier type not supported or identifier not found."));
}
} }
} }

33
libsolidity/ExpressionCompiler.h

@ -22,8 +22,10 @@
*/ */
#include <functional> #include <functional>
#include <memory>
#include <boost/noncopyable.hpp> #include <boost/noncopyable.hpp>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libsolidity/BaseTypes.h>
#include <libsolidity/ASTVisitor.h> #include <libsolidity/ASTVisitor.h>
namespace dev { namespace dev {
@ -37,6 +39,7 @@ namespace solidity {
class CompilerContext; class CompilerContext;
class Type; class Type;
class IntegerType; class IntegerType;
class ByteArrayType;
class StaticStringType; class StaticStringType;
/** /**
@ -119,14 +122,13 @@ private:
enum class LValueType { None, Stack, Memory, Storage }; enum class LValueType { None, Stack, Memory, Storage };
explicit LValue(CompilerContext& _compilerContext): m_context(&_compilerContext) { reset(); } explicit LValue(CompilerContext& _compilerContext): m_context(&_compilerContext) { reset(); }
LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType, unsigned _baseStackOffset = 0); LValue(CompilerContext& _compilerContext, LValueType _type,
std::shared_ptr<Type const> const& _dataType, unsigned _baseStackOffset = 0);
/// Set type according to the declaration and retrieve the reference. /// Set type according to the declaration and retrieve the reference.
/// @a _expression is the current expression /// @a _expression is the current expression
void fromIdentifier(Identifier const& _identifier, Declaration const& _declaration); void fromIdentifier(Identifier const& _identifier, Declaration const& _declaration);
/// Convenience function to set type for a state variable and retrieve the reference void reset() { m_type = LValueType::None; m_dataType.reset(); m_baseStackOffset = 0; m_size = 0; }
void fromStateVariable(Declaration const& _varDecl, TypePointer const& _type);
void reset() { m_type = LValueType::None; m_baseStackOffset = 0; m_size = 0; }
bool isValid() const { return m_type != LValueType::None; } bool isValid() const { return m_type != LValueType::None; }
bool isInOnStack() const { return m_type == LValueType::Stack; } bool isInOnStack() const { return m_type == LValueType::Stack; }
@ -138,30 +140,29 @@ private:
/// Copies the value of the current lvalue to the top of the stack and, if @a _remove is true, /// Copies the value of the current lvalue to the top of the stack and, if @a _remove is true,
/// also removes the reference from the stack (note that is does not reset the type to @a NONE). /// also removes the reference from the stack (note that is does not reset the type to @a NONE).
/// @a _type is the type of the current expression and @ _location its location, used for error reporting. /// @a _location source location of the current expression, used for error reporting.
/// @a _location can be a nullptr for expressions that don't have an actual ASTNode equivalent void retrieveValue(Location const& _location, bool _remove = false) const;
void retrieveValue(TypePointer const& _type, Location const& _location, bool _remove = false) const; /// Moves a value from the stack to the lvalue. Removes the value if @a _move is true.
/// Stores a value (from the stack directly beneath the reference, which is assumed to /// @a _location is the source location of the expression that caused this operation.
/// be on the top of the stack, if any) in the lvalue and removes the reference. /// Stack pre: [lvalue_ref] value
/// Also removes the stored value from the stack if @a _move is /// Stack post if !_move: value_of(lvalue_ref)
/// true. @a _expression is the current expression, used for error reporting. void storeValue(Type const& _sourceType, Location const& _location = Location(), bool _move = false) const;
/// @a _sourceType is the type of the expression that is assigned.
void storeValue(Expression const& _expression, Type const& _sourceType, bool _move = false) const;
/// Stores zero in the lvalue. /// Stores zero in the lvalue.
/// @a _expression is the current expression, used for error reporting. /// @a _location is the source location of the requested operation
void setToZero(Expression const& _expression, Type const& _type) const; void setToZero(Location const& _location = Location()) const;
/// Convenience function to convert the stored reference to a value and reset type to NONE if /// Convenience function to convert the stored reference to a value and reset type to NONE if
/// the reference was not requested by @a _expression. /// the reference was not requested by @a _expression.
void retrieveValueIfLValueNotRequested(Expression const& _expression); void retrieveValueIfLValueNotRequested(Expression const& _expression);
private: private:
/// Convenience function to retrieve Value from Storage. Specific version of @ref retrieveValue /// Convenience function to retrieve Value from Storage. Specific version of @ref retrieveValue
void retrieveValueFromStorage(TypePointer const& _type, bool _remove = false) const; void retrieveValueFromStorage(bool _remove = false) const;
/// Copies from a byte array to a byte array in storage, both references on the stack. /// Copies from a byte array to a byte array in storage, both references on the stack.
void copyByteArrayToStorage(ByteArrayType const& _targetType, ByteArrayType const& _sourceType) const; void copyByteArrayToStorage(ByteArrayType const& _targetType, ByteArrayType const& _sourceType) const;
CompilerContext* m_context; CompilerContext* m_context;
LValueType m_type = LValueType::None; LValueType m_type = LValueType::None;
std::shared_ptr<Type const> m_dataType;
/// If m_type is STACK, this is base stack offset (@see /// If m_type is STACK, this is base stack offset (@see
/// CompilerContext::getBaseStackOffsetOfVariable) of a local variable. /// CompilerContext::getBaseStackOffsetOfVariable) of a local variable.
unsigned m_baseStackOffset = 0; unsigned m_baseStackOffset = 0;

2
libsolidity/Types.cpp

@ -653,7 +653,7 @@ u256 StructType::getStorageOffsetOfMember(string const& _name) const
{ {
//@todo cache member offset? //@todo cache member offset?
u256 offset; u256 offset;
for (ASTPointer<VariableDeclaration> variable: m_struct.getMembers()) for (ASTPointer<VariableDeclaration> const& variable: m_struct.getMembers())
{ {
if (variable->getName() == _name) if (variable->getName() == _name)
return offset; return offset;

60
test/SolidityEndToEndTest.cpp

@ -2424,6 +2424,66 @@ BOOST_AUTO_TEST_CASE(bytes_length_member)
BOOST_CHECK(callContractFunction("getLength()") == encodeArgs(4+32+32)); BOOST_CHECK(callContractFunction("getLength()") == encodeArgs(4+32+32));
} }
BOOST_AUTO_TEST_CASE(struct_copy)
{
char const* sourceCode = R"(
contract c {
struct Nested { uint x; uint y; }
struct Struct { uint a; mapping(uint => Struct) b; Nested nested; uint c; }
mapping(uint => Struct) public data;
function set(uint k) returns (bool) {
data[k].a = 1;
data[k].nested.x = 3;
data[k].nested.y = 4;
data[k].c = 2;
return true;
}
function copy(uint from, uint to) returns (bool) {
data[to] = data[from];
return true;
}
function retrieve(uint k) returns (uint a, uint x, uint y, uint c)
{
a = data[k].a;
x = data[k].nested.x;
y = data[k].nested.y;
c = data[k].c;
}
}
)";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction("set(uint256)", 7) == encodeArgs(true));
BOOST_CHECK(callContractFunction("retrieve(uint256)", 7) == encodeArgs(1, 3, 4, 2));
BOOST_CHECK(callContractFunction("copy(uint256,uint256)", 7, 8) == encodeArgs(true));
BOOST_CHECK(callContractFunction("retrieve(uint256)", 7) == encodeArgs(1, 3, 4, 2));
BOOST_CHECK(callContractFunction("retrieve(uint256)", 8) == encodeArgs(1, 3, 4, 2));
BOOST_CHECK(callContractFunction("copy(uint256,uint256)", 0, 7) == encodeArgs(true));
BOOST_CHECK(callContractFunction("retrieve(uint256)", 7) == encodeArgs(0, 0, 0, 0));
BOOST_CHECK(callContractFunction("retrieve(uint256)", 8) == encodeArgs(1, 3, 4, 2));
BOOST_CHECK(callContractFunction("copy(uint256,uint256)", 7, 8) == encodeArgs(true));
BOOST_CHECK(callContractFunction("retrieve(uint256)", 8) == encodeArgs(0, 0, 0, 0));
}
BOOST_AUTO_TEST_CASE(struct_copy_via_local)
{
char const* sourceCode = R"(
contract c {
struct Struct { uint a; uint b; }
Struct data1;
Struct data2;
function test() returns (bool) {
data1.a = 1;
data1.b = 2;
var x = data1;
data2 = x;
return data2.a == data1.a && data2.b == data1.b;
}
}
)";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction("test()") == encodeArgs(true));
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

2
test/SolidityNameAndTypeResolution.cpp

@ -332,7 +332,7 @@ BOOST_AUTO_TEST_CASE(assignment_to_struct)
" data = a;\n" " data = a;\n"
" }\n" " }\n"
"}\n"; "}\n";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
} }
BOOST_AUTO_TEST_CASE(returns_in_constructor) BOOST_AUTO_TEST_CASE(returns_in_constructor)

Loading…
Cancel
Save