mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Simple copy of bytes to storage.
This commit is contained in:
parent
20b4c69009
commit
79aec95228
@ -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.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -148,7 +148,8 @@ private:
|
||||
/// 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.
|
||||
void storeValue(Expression const& _expression, 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.
|
||||
/// @a _expression is the current expression, used for error reporting.
|
||||
void setToZero(Expression const& _expression) const;
|
||||
@ -159,6 +160,8 @@ private:
|
||||
private:
|
||||
/// Convenience function to retrieve Value from Storage. Specific version of @ref retrieveValue
|
||||
void retrieveValueFromStorage(TypePointer const& _type, 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;
|
||||
|
18
Types.cpp
18
Types.cpp
@ -518,6 +518,13 @@ bool ByteArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const
|
||||
return (m_dynamicLength == other.m_dynamicLength || m_length == other.m_length);
|
||||
}
|
||||
|
||||
TypePointer ByteArrayType::unaryOperatorResult(Token::Value _operator) const
|
||||
{
|
||||
if (_operator == Token::Delete)
|
||||
return make_shared<VoidType>();
|
||||
return TypePointer();
|
||||
}
|
||||
|
||||
bool ByteArrayType::operator==(Type const& _other) const
|
||||
{
|
||||
if (_other.getCategory() != getCategory())
|
||||
@ -527,6 +534,14 @@ bool ByteArrayType::operator==(Type const& _other) const
|
||||
&& other.m_length == m_length && other.m_offset == m_offset;
|
||||
}
|
||||
|
||||
unsigned ByteArrayType::getSizeOnStack() const
|
||||
{
|
||||
if (m_location == Location::CallData)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool ContractType::operator==(Type const& _other) const
|
||||
{
|
||||
if (_other.getCategory() != getCategory())
|
||||
@ -963,8 +978,7 @@ MagicType::MagicType(MagicType::Kind _kind):
|
||||
m_members = MemberList({{"sender", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
|
||||
{"gas", make_shared<IntegerType>(256)},
|
||||
{"value", make_shared<IntegerType>(256)},
|
||||
{"data", make_shared<ByteArrayType>(ByteArrayType::Location::CallData,
|
||||
0, 0, true)}});
|
||||
{"data", make_shared<ByteArrayType>(ByteArrayType::Location::CallData)}});
|
||||
break;
|
||||
case Kind::Transaction:
|
||||
m_members = MemberList({{"origin", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
|
||||
|
11
Types.h
11
Types.h
@ -285,13 +285,20 @@ public:
|
||||
enum class Location { Storage, CallData, Memory };
|
||||
|
||||
virtual Category getCategory() const override { return Category::ByteArray; }
|
||||
ByteArrayType(Location _location, u256 const& _offset, u256 const& _length, bool _dynamicLength):
|
||||
explicit ByteArrayType(Location _location, u256 const& _offset = 0, u256 const& _length = 0,
|
||||
bool _dynamicLength = false):
|
||||
m_location(_location), m_offset(_offset), m_length(_length), m_dynamicLength(_dynamicLength) {}
|
||||
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
||||
virtual bool operator==(const Type& _other) const override;
|
||||
virtual unsigned getSizeOnStack() const override { return 1; /* TODO */ }
|
||||
virtual unsigned getSizeOnStack() const override;
|
||||
virtual std::string toString() const override { return "bytes"; }
|
||||
|
||||
Location getLocation() const { return m_location; }
|
||||
u256 const& getOffset() const { return m_offset; }
|
||||
u256 const& getLength() const { return m_length; }
|
||||
bool hasDynamicLength() const { return m_dynamicLength; }
|
||||
|
||||
private:
|
||||
Location m_location;
|
||||
u256 m_offset;
|
||||
|
Loading…
Reference in New Issue
Block a user