aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ExpressionCompiler.cpp118
-rw-r--r--ExpressionCompiler.h5
-rw-r--r--Types.cpp18
-rw-r--r--Types.h11
4 files changed, 127 insertions, 25 deletions
diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp
index beda0132..8c9028e4 100644
--- a/ExpressionCompiler.cpp
+++ b/ExpressionCompiler.cpp
@@ -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.");
+ }
+}
+
}
}
diff --git a/ExpressionCompiler.h b/ExpressionCompiler.h
index 3c94d74c..bd45e067 100644
--- a/ExpressionCompiler.h
+++ b/ExpressionCompiler.h
@@ -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;
diff --git a/Types.cpp b/Types.cpp
index 9faa3f3c..16674742 100644
--- a/Types.cpp
+++ b/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)},
diff --git a/Types.h b/Types.h
index ddcfad9e..a23790fa 100644
--- a/Types.h
+++ b/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;