Simple copy of bytes to storage.

This commit is contained in:
Christian
2015-02-12 11:33:09 +01:00
parent 20b4c69009
commit 79aec95228
4 changed files with 127 additions and 25 deletions
+98 -20
View File
@@ -59,13 +59,15 @@ void ExpressionCompiler::appendStateVariableAccessor(CompilerContext& _context,
bool ExpressionCompiler::visit(Assignment const& _assignment)
{
_assignment.getRightHandSide().accept(*this);
appendTypeConversion(*_assignment.getRightHandSide().getType(), *_assignment.getType());
if (_assignment.getType()->isValueType())
appendTypeConversion(*_assignment.getRightHandSide().getType(), *_assignment.getType());
_assignment.getLeftHandSide().accept(*this);
solAssert(m_currentLValue.isValid(), "LValue not retrieved.");
Token::Value op = _assignment.getAssignmentOperator();
if (op != Token::Assign) // compound 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);
@@ -73,7 +75,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
if (m_currentLValue.storesReferenceOnStack())
m_context << eth::Instruction::SWAP1;
}
m_currentLValue.storeValue(_assignment);
m_currentLValue.storeValue(_assignment, *_assignment.getRightHandSide().getType());
m_currentLValue.reset();
return false;
@@ -126,7 +128,7 @@ 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.isPrefixOperation());
m_currentLValue.storeValue(_unaryOperation, *_unaryOperation.getType(), !_unaryOperation.isPrefixOperation());
m_currentLValue.reset();
break;
case Token::Add: // +
@@ -472,6 +474,10 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
m_context << eth::Instruction::GAS;
else if (member == "gasprice")
m_context << eth::Instruction::GASPRICE;
else if (member == "data")
{
// nothing to store on the stack
}
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown magic member."));
break;
@@ -1014,7 +1020,7 @@ void ExpressionCompiler::LValue::retrieveValueFromStorage(TypePointer const& _ty
}
}
void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool _move) const
void ExpressionCompiler::LValue::storeValue(Expression const& _expression, Type const& _sourceType, bool _move) const
{
switch (m_type)
{
@@ -1032,28 +1038,45 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
break;
}
case LValueType::Storage:
if (!_expression.getType()->isValueType())
break; // no distinction between value and reference for non-value types
// stack layout: value value ... value ref
if (!_move) // copy values
if (_expression.getType()->isValueType())
{
if (m_size + 1 > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Stack too deep."));
if (!_move) // copy values
{
if (m_size + 1 > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Stack too deep."));
for (unsigned i = 0; i < m_size; ++i)
*m_context << eth::dupInstruction(m_size + 1) << eth::Instruction::SWAP1;
}
if (m_size > 0) // store high index value first
*m_context << u256(m_size - 1) << eth::Instruction::ADD;
for (unsigned i = 0; i < m_size; ++i)
*m_context << eth::dupInstruction(m_size + 1) << eth::Instruction::SWAP1;
{
if (i + 1 >= m_size)
*m_context << eth::Instruction::SSTORE;
else
// v v ... v v r+x
*m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2
<< eth::Instruction::SSTORE
<< u256(1) << eth::Instruction::SWAP1 << eth::Instruction::SUB;
}
}
if (m_size > 0) // store high index value first
*m_context << u256(m_size - 1) << eth::Instruction::ADD;
for (unsigned i = 0; i < m_size; ++i)
else
{
if (i + 1 >= m_size)
*m_context << eth::Instruction::SSTORE;
solAssert(!_move, "Move assign for non-value types not implemented.");
solAssert(_sourceType.getCategory() == _expression.getType()->getCategory(), "");
if (_expression.getType()->getCategory() == Type::Category::ByteArray)
copyByteArrayToStorage(dynamic_cast<ByteArrayType const&>(*_expression.getType()),
dynamic_cast<ByteArrayType const&>(_sourceType));
else if (_expression.getType()->getCategory() == Type::Category::Struct)
{
//@todo
solAssert(false, "Struct copy not yet implemented.");
}
else
// v v ... v v r+x
*m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2
<< eth::Instruction::SSTORE
<< u256(1) << eth::Instruction::SWAP1 << eth::Instruction::SUB;
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Invalid non-value type for assignment."));
}
break;
case LValueType::Memory:
@@ -1145,5 +1168,60 @@ void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, D
<< errinfo_comment("Identifier type not supported or identifier not found."));
}
void ExpressionCompiler::LValue::copyByteArrayToStorage(ByteArrayType const& _targetType,
ByteArrayType const& _sourceType) const
{
// stack layout: [source_ref] target_ref (head)
// need to leave target_ref on the stack at the end
solAssert(m_type == LValueType::Storage, "");
solAssert(_targetType.getLocation() == ByteArrayType::Location::Storage, "");
switch (_sourceType.getLocation())
{
case ByteArrayType::Location::CallData:
{
// @todo this does not take length into account. It also assumes that after "CALLDATALENGTH" we only have zeros.
// add some useful constants
*m_context << u256(32) << u256(1);
// stack here: target_ref 32 1
// store length (in bytes)
if (_sourceType.getOffset() == 0)
*m_context << eth::Instruction::CALLDATASIZE;
else
*m_context << _sourceType.getOffset() << eth::Instruction::CALLDATASIZE << eth::Instruction::SUB;
*m_context << eth::Instruction::DUP1 << eth::Instruction::DUP5 << eth::Instruction::SSTORE;
// jump to end if length is zero
*m_context << eth::Instruction::ISZERO;
eth::AssemblyItem loopEnd = m_context->newTag();
m_context->appendConditionalJumpTo(loopEnd);
// actual array data is stored at SHA3(storage_offset)
*m_context << eth::Instruction::DUP3;
CompilerUtils(*m_context).storeInMemory(0);
*m_context << u256(32) << u256(0) << eth::Instruction::SHA3;
*m_context << _sourceType.getOffset();
// stack now: target_ref 32 1 target_data_ref calldata_offset
eth::AssemblyItem loopStart = m_context->newTag();
*m_context << loopStart
// copy from calldata and store
<< eth::Instruction::DUP1 << eth::Instruction::CALLDATALOAD
<< eth::Instruction::DUP3 << eth::Instruction::SSTORE
// increment target_data_ref by 1
<< eth::Instruction::SWAP1 << eth::Instruction::DUP3 << eth::Instruction::ADD
// increment calldata_offset by 32
<< eth::Instruction::SWAP1 << eth::Instruction::DUP4 << eth::Instruction::ADD
// check for loop condition
<< eth::Instruction::DUP1 << eth::Instruction::CALLDATASIZE << eth::Instruction::GT;
m_context->appendConditionalJumpTo(loopStart);
*m_context << eth::Instruction::POP << eth::Instruction::POP;
*m_context << loopEnd << eth::Instruction::POP << eth::Instruction::POP;
break;
}
case ByteArrayType::Location::Storage:
break;
default:
solAssert(false, "Byte array location not implemented.");
}
}
}
}