Copying structs.

This commit is contained in:
Christian 2015-02-12 18:38:07 +01:00
parent 0e40b21c56
commit 1c3c1f1e5d
4 changed files with 114 additions and 92 deletions

View File

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

View File

@ -70,12 +70,12 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
solAssert(_assignment.getType()->isValueType(), "Compound operators not implemented for non-value types.");
if (m_currentLValue.storesReferenceOnStack())
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());
if (m_currentLValue.storesReferenceOnStack())
m_context << eth::Instruction::SWAP1;
}
m_currentLValue.storeValue(_assignment, *_assignment.getRightHandSide().getType());
m_currentLValue.storeValue(*_assignment.getRightHandSide().getType(), _assignment.getLocation());
m_currentLValue.reset();
return false;
@ -105,13 +105,13 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
break;
case Token::Delete: // delete
solAssert(m_currentLValue.isValid(), "LValue not retrieved.");
m_currentLValue.setToZero(_unaryOperation, *_unaryOperation.getSubExpression().getType());
m_currentLValue.setToZero(_unaryOperation.getLocation());
m_currentLValue.reset();
break;
case Token::Inc: // ++ (pre- or postfix)
case Token::Dec: // -- (pre- or postfix)
solAssert(m_currentLValue.isValid(), "LValue not retrieved.");
m_currentLValue.retrieveValue(_unaryOperation.getType(), _unaryOperation.getLocation());
m_currentLValue.retrieveValue(_unaryOperation.getLocation());
if (!_unaryOperation.isPrefixOperation())
{
if (m_currentLValue.storesReferenceOnStack())
@ -128,7 +128,8 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
// Stack for postfix: *ref [ref] (*ref)+-1
if (m_currentLValue.storesReferenceOnStack())
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();
break;
case Token::Add: // +
@ -484,7 +485,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
{
StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.getExpression().getType());
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);
break;
}
@ -530,7 +531,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
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);
return false;
@ -939,8 +940,8 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
m_context << eth::Instruction::DUP1
<< structType->getStorageOffsetOfMember(names[i])
<< eth::Instruction::ADD;
m_currentLValue = LValue(m_context, LValue::LValueType::Storage, *types[i]);
m_currentLValue.retrieveValue(types[i], Location(), true);
m_currentLValue = LValue(m_context, LValue::LValueType::Storage, types[i]);
m_currentLValue.retrieveValue(Location(), true);
solAssert(types[i]->getSizeOnStack() == 1, "Returning struct elements with stack size != 1 not yet implemented.");
m_context << eth::Instruction::SWAP1;
retSizeOnStack += types[i]->getSizeOnStack();
@ -951,27 +952,51 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
{
// simple value
solAssert(accessorType.getReturnParameterTypes().size() == 1, "");
m_currentLValue = LValue(m_context, LValue::LValueType::Storage, *returnType);
m_currentLValue.retrieveValue(returnType, Location(), true);
m_currentLValue = LValue(m_context, LValue::LValueType::Storage, returnType);
m_currentLValue.retrieveValue(Location(), true);
retSizeOnStack = returnType->getSizeOnStack();
}
solAssert(retSizeOnStack <= 15, "Stack too deep.");
m_context << eth::dupInstruction(retSizeOnStack + 1) << eth::Instruction::JUMP;
}
ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType,
unsigned _baseStackOffset):
m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset)
ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type,
TypePointer const& _dataType, unsigned _baseStackOffset):
m_context(&_compilerContext), m_type(_type), m_dataType(_dataType),
m_baseStackOffset(_baseStackOffset)
{
//@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)
m_size = unsigned(_dataType.getStorageSize());
m_size = unsigned(m_dataType->getStorageSize());
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)
{
@ -986,10 +1011,10 @@ void ExpressionCompiler::LValue::retrieveValue(TypePointer const& _type, Locatio
break;
}
case LValueType::Storage:
retrieveValueFromStorage(_type, _remove);
retrieveValueFromStorage(_remove);
break;
case LValueType::Memory:
if (!_type->isValueType())
if (!m_dataType->isValueType())
break; // no distinction between value and reference for non-value types
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_location)
<< 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
if (!_remove)
*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)
{
@ -1028,23 +1053,23 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, Type
{
unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)) - m_size + 1;
if (stackDiff > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_location)
<< errinfo_comment("Stack too deep."));
else if (stackDiff > 0)
for (unsigned i = 0; i < m_size; ++i)
*m_context << eth::swapInstruction(stackDiff) << eth::Instruction::POP;
if (!_move)
retrieveValue(_expression.getType(), _expression.getLocation());
retrieveValue(_location);
break;
}
case LValueType::Storage:
// stack layout: value value ... value target_ref
if (_expression.getType()->isValueType())
if (m_dataType->isValueType())
{
if (!_move) // copy values
{
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."));
for (unsigned i = 0; i < m_size; ++i)
*m_context << eth::dupInstruction(m_size + 1) << eth::Instruction::SWAP1;
@ -1064,36 +1089,60 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, Type
}
else
{
solAssert(!_move, "Move assign for non-value types not implemented.");
solAssert(_sourceType.getCategory() == _expression.getType()->getCategory(), "");
if (_expression.getType()->getCategory() == Type::Category::ByteArray)
solAssert(_sourceType.getCategory() == m_dataType->getCategory(), "");
if (m_dataType->getCategory() == Type::Category::ByteArray)
CompilerUtils(*m_context).copyByteArrayToStorage(
dynamic_cast<ByteArrayType const&>(*_expression.getType()),
dynamic_cast<ByteArrayType const&>(*m_dataType),
dynamic_cast<ByteArrayType const&>(_sourceType));
else if (_expression.getType()->getCategory() == Type::Category::Struct)
else if (m_dataType->getCategory() == Type::Category::Struct)
{
//@todo
solAssert(false, "Struct copy not yet implemented.");
// stack layout: source_ref target_ref
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
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_location)
<< errinfo_comment("Invalid non-value type for assignment."));
}
break;
case LValueType::Memory:
if (!_expression.getType()->isValueType())
if (!m_dataType->isValueType())
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."));
break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_location)
<< errinfo_comment("Unsupported location type."));
break;
}
}
void ExpressionCompiler::LValue::setToZero(Expression const& _expression, Type const& _type) const
void ExpressionCompiler::LValue::setToZero(Location const& _location) const
{
switch (m_type)
{
@ -1101,7 +1150,7 @@ void ExpressionCompiler::LValue::setToZero(Expression const& _expression, Type c
{
unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset));
if (stackDiff > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_location)
<< errinfo_comment("Stack too deep."));
solAssert(stackDiff >= m_size - 1, "");
for (unsigned i = 0; i < m_size; ++i)
@ -1110,8 +1159,8 @@ void ExpressionCompiler::LValue::setToZero(Expression const& _expression, Type c
break;
}
case LValueType::Storage:
if (_type.getCategory() == Type::Category::ByteArray)
CompilerUtils(*m_context).clearByteArray(dynamic_cast<ByteArrayType const&>(_type));
if (m_dataType->getCategory() == Type::Category::ByteArray)
CompilerUtils(*m_context).clearByteArray(dynamic_cast<ByteArrayType const&>(*m_dataType));
else
{
if (m_size == 0)
@ -1125,11 +1174,11 @@ void ExpressionCompiler::LValue::setToZero(Expression const& _expression, Type c
}
break;
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."));
break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_location)
<< errinfo_comment("Unsupported location type."));
break;
}
@ -1139,36 +1188,10 @@ void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression co
{
if (!_expression.lvalueRequested())
{
retrieveValue(_expression.getType(), _expression.getLocation(), true);
retrieveValue(_expression.getLocation(), true);
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."));
}
}
}

View File

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

View File

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