aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian <c@ethdev.com>2014-10-25 22:52:22 +0800
committerChristian <c@ethdev.com>2014-10-29 21:33:25 +0800
commitb5a4d12fa328f69d4dfba9bf57ac289935877649 (patch)
treedfa92c635af822197b7bb25a7b23870d1d26a179
parent01224287f5e24f7074f398ac9b064b27b2c3877e (diff)
downloaddexon-solidity-b5a4d12fa328f69d4dfba9bf57ac289935877649.tar.gz
dexon-solidity-b5a4d12fa328f69d4dfba9bf57ac289935877649.tar.zst
dexon-solidity-b5a4d12fa328f69d4dfba9bf57ac289935877649.zip
Compiler for assignments.
-rw-r--r--AST.cpp13
-rw-r--r--AST.h9
-rw-r--r--Compiler.cpp113
-rw-r--r--Compiler.h16
-rw-r--r--Exceptions.h1
5 files changed, 123 insertions, 29 deletions
diff --git a/AST.cpp b/AST.cpp
index 0b635339..7b5b6a73 100644
--- a/AST.cpp
+++ b/AST.cpp
@@ -326,6 +326,8 @@ void Assignment::checkTypeRequirements()
//@todo lefthandside actually has to be assignable
// add a feature to the type system to check that
m_leftHandSide->checkTypeRequirements();
+ if (!m_leftHandSide->isLvalue())
+ BOOST_THROW_EXCEPTION(createTypeError("Expression has to be an lvalue."));
expectType(*m_rightHandSide, *m_leftHandSide->getType());
m_type = m_leftHandSide->getType();
if (m_assigmentOperator != Token::ASSIGN)
@@ -338,8 +340,13 @@ void Assignment::checkTypeRequirements()
void UnaryOperation::checkTypeRequirements()
{
- // INC, DEC, NOT, BIT_NOT, DELETE
+ // INC, DEC, ADD, SUB, NOT, BIT_NOT, DELETE
m_subExpression->checkTypeRequirements();
+ if (m_operator == Token::Value::INC || m_operator == Token::Value::DEC || m_operator == Token::Value::DELETE)
+ {
+ if (!m_subExpression->isLvalue())
+ BOOST_THROW_EXCEPTION(createTypeError("Expression has to be an lvalue."));
+ }
m_type = m_subExpression->getType();
if (!m_type->acceptsUnaryOperator(m_operator))
BOOST_THROW_EXCEPTION(createTypeError("Unary operator not compatible with type."));
@@ -441,9 +448,9 @@ void Identifier::checkTypeRequirements()
if (variable)
{
if (!variable->getType())
- BOOST_THROW_EXCEPTION(createTypeError("Variable referenced before type "
- "could be determined."));
+ BOOST_THROW_EXCEPTION(createTypeError("Variable referenced before type could be determined."));
m_type = variable->getType();
+ m_isLvalue = true;
return;
}
//@todo can we unify these with TypeName::toType()?
diff --git a/AST.h b/AST.h
index db6637ae..856c222e 100644
--- a/AST.h
+++ b/AST.h
@@ -402,13 +402,17 @@ private:
class Expression: public Statement
{
public:
- Expression(Location const& _location): Statement(_location) {}
+ Expression(Location const& _location): Statement(_location), m_isLvalue(false) {}
std::shared_ptr<Type const> const& getType() const { return m_type; }
+ bool isLvalue() const { return m_isLvalue; }
protected:
//! Inferred type of the expression, only filled after a call to checkTypeRequirements().
std::shared_ptr<Type const> m_type;
+ //! Whether or not this expression is an lvalue, i.e. something that can be assigned to.
+ //! This is set during calls to @a checkTypeRequirements()
+ bool m_isLvalue;
};
/// @}
@@ -492,6 +496,9 @@ public:
virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override;
+ Expression& getExpression() const { return *m_expression; }
+ std::vector<ASTPointer<Expression>> const& getArguments() const { return m_arguments; }
+
/// Returns true if this is not an actual function call, but an explicit type conversion
/// or constructor call.
bool isTypeConversion() const;
diff --git a/Compiler.cpp b/Compiler.cpp
index 4244a3e0..dbb38324 100644
--- a/Compiler.cpp
+++ b/Compiler.cpp
@@ -81,22 +81,29 @@ AssemblyItems ExpressionCompiler::compileExpression(CompilerContext& _context,
return compiler.getAssemblyItems();
}
-void ExpressionCompiler::endVisit(Assignment& _assignment)
+bool ExpressionCompiler::visit(Assignment& _assignment)
{
+ m_currentLValue = nullptr;
+ _assignment.getLeftHandSide().accept(*this);
+
Expression& rightHandSide = _assignment.getRightHandSide();
Token::Value op = _assignment.getAssignmentOperator();
if (op != Token::ASSIGN)
{
// compound assignment
- // @todo retrieve lvalue value
rightHandSide.accept(*this);
Type const& resultType = *_assignment.getType();
cleanHigherOrderBitsIfNeeded(*rightHandSide.getType(), resultType);
appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), resultType);
}
else
+ {
+ append(eth::Instruction::POP); //@todo do not retrieve the value in the first place
rightHandSide.accept(*this);
- // @todo store value
+ }
+
+ storeInLValue(_assignment);
+ return false;
}
void ExpressionCompiler::endVisit(UnaryOperation& _unaryOperation)
@@ -114,30 +121,31 @@ void ExpressionCompiler::endVisit(UnaryOperation& _unaryOperation)
append(eth::Instruction::BNOT);
break;
case Token::DELETE: // delete
+ {
// a -> a xor a (= 0).
- // @todo this should also be an assignment
// @todo semantics change for complex types
append(eth::Instruction::DUP1);
append(eth::Instruction::XOR);
+ storeInLValue(_unaryOperation);
break;
+ }
case Token::INC: // ++ (pre- or postfix)
- // @todo this should also be an assignment
- if (_unaryOperation.isPrefixOperation())
- {
- append(eth::Instruction::PUSH1);
- append(1);
- append(eth::Instruction::ADD);
- }
- break;
case Token::DEC: // -- (pre- or postfix)
- // @todo this should also be an assignment
- if (_unaryOperation.isPrefixOperation())
+ if (!_unaryOperation.isPrefixOperation())
+ append(eth::Instruction::DUP1);
+ append(eth::Instruction::PUSH1);
+ append(1);
+ if (_unaryOperation.getOperator() == Token::INC)
+ append(eth::Instruction::ADD);
+ else
{
- append(eth::Instruction::PUSH1);
- append(1);
append(eth::Instruction::SWAP1); //@todo avoid this
append(eth::Instruction::SUB);
}
+ if (_unaryOperation.isPrefixOperation())
+ storeInLValue(_unaryOperation);
+ else
+ moveToLValue(_unaryOperation);
break;
case Token::ADD: // +
// unary add, so basically no-op
@@ -190,28 +198,38 @@ void ExpressionCompiler::endVisit(FunctionCall& _functionCall)
{
if (_functionCall.isTypeConversion())
{
- //@todo binary representation for all supported types (bool and int) is the same, so no-op
- // here for now.
+ //@todo we only have integers and bools for now which cannot be explicitly converted
+ assert(_functionCall.getArguments().size() == 1);
+ cleanHigherOrderBitsIfNeeded(*_functionCall.getArguments().front()->getType(),
+ *_functionCall.getType());
}
else
{
- //@todo
+ //@todo: arguments are already on the stack
+ // push return label (below arguments?)
+ // jump to function label
+ // discard all but the first function return argument
}
}
-void ExpressionCompiler::endVisit(MemberAccess& _memberAccess)
+void ExpressionCompiler::endVisit(MemberAccess&)
{
}
-void ExpressionCompiler::endVisit(IndexAccess& _indexAccess)
+void ExpressionCompiler::endVisit(IndexAccess&)
{
}
void ExpressionCompiler::endVisit(Identifier& _identifier)
{
-
+ m_currentLValue = _identifier.getReferencedDeclaration();
+ unsigned stackPos = stackPositionOfLValue();
+ if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_identifier.getLocation())
+ << errinfo_comment("Stack too deep."));
+ appendDup(stackPos + 1);
}
void ExpressionCompiler::endVisit(Literal& _literal)
@@ -224,7 +242,7 @@ void ExpressionCompiler::endVisit(Literal& _literal)
bytes value = _literal.getType()->literalToBigEndian(_literal);
assert(value.size() <= 32);
assert(!value.empty());
- append(static_cast<byte>(eth::Instruction::PUSH1) + static_cast<byte>(value.size() - 1));
+ appendPush(value.size());
append(value);
break;
}
@@ -391,6 +409,24 @@ uint32_t ExpressionCompiler::appendConditionalJump()
return label;
}
+void ExpressionCompiler::appendPush(unsigned _number)
+{
+ assert(1 <= _number && _number <= 32);
+ append(eth::Instruction(unsigned(eth::Instruction::PUSH1) + _number - 1));
+}
+
+void ExpressionCompiler::appendDup(unsigned _number)
+{
+ assert(1 <= _number && _number <= 16);
+ append(eth::Instruction(unsigned(eth::Instruction::DUP1) + _number - 1));
+}
+
+void ExpressionCompiler::appendSwap(unsigned _number)
+{
+ assert(1 <= _number && _number <= 16);
+ append(eth::Instruction(unsigned(eth::Instruction::SWAP1) + _number - 1));
+}
+
void ExpressionCompiler::append(bytes const& _data)
{
m_assemblyItems.reserve(m_assemblyItems.size() + _data.size());
@@ -398,6 +434,37 @@ void ExpressionCompiler::append(bytes const& _data)
append(b);
}
+void ExpressionCompiler::storeInLValue(Expression const& _expression)
+{
+ assert(m_currentLValue);
+ moveToLValue(_expression);
+ unsigned stackPos = stackPositionOfLValue();
+ if (stackPos > 16)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
+ << errinfo_comment("Stack too deep."));
+ if (stackPos >= 1)
+ appendDup(stackPos);
+}
+
+void ExpressionCompiler::moveToLValue(Expression const& _expression)
+{
+ assert(m_currentLValue);
+ unsigned stackPos = stackPositionOfLValue();
+ if (stackPos > 16)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
+ << errinfo_comment("Stack too deep."));
+ else if (stackPos > 0)
+ {
+ appendSwap(stackPos);
+ append(eth::Instruction::POP);
+ }
+}
+
+unsigned ExpressionCompiler::stackPositionOfLValue() const
+{
+ return 8; // @todo ask the context and track stack changes due to m_assemblyItems
+}
+
}
diff --git a/Compiler.h b/Compiler.h
index ac5e10ec..1c5523e0 100644
--- a/Compiler.h
+++ b/Compiler.h
@@ -87,7 +87,7 @@ private:
class ExpressionCompiler: public ASTVisitor
{
public:
- ExpressionCompiler(CompilerContext& _compilerContext): m_context(_compilerContext) {}
+ ExpressionCompiler(CompilerContext& _compilerContext): m_currentLValue(nullptr), m_context(_compilerContext) {}
/// Compile the given expression and (re-)populate the assembly item list.
void compile(Expression& _expression);
@@ -98,7 +98,7 @@ public:
static AssemblyItems compileExpression(CompilerContext& _context, Expression& _expression);
private:
- virtual void endVisit(Assignment& _assignment) override;
+ virtual bool visit(Assignment& _assignment) override;
virtual void endVisit(UnaryOperation& _unaryOperation) override;
virtual bool visit(BinaryOperation& _binaryOperation) override;
virtual void endVisit(FunctionCall& _functionCall) override;
@@ -123,6 +123,9 @@ private:
/// Appends a JUMPI instruction to a new label and returns the label
uint32_t appendConditionalJump();
+ void appendPush(unsigned _number);
+ void appendDup(unsigned _number);
+ void appendSwap(unsigned _number);
/// Append elements to the current instruction list.
void append(eth::Instruction const& _instruction) { m_assemblyItems.push_back(AssemblyItem(_instruction)); }
@@ -131,6 +134,15 @@ private:
void appendLabelref(byte _label) { m_assemblyItems.push_back(AssemblyItem::labelRef(_label)); }
void appendLabel(byte _label) { m_assemblyItems.push_back(AssemblyItem::label(_label)); }
+ /// Stores the value on top of the stack in the current lvalue and copies that value to the
+ /// top of the stack again
+ void storeInLValue(Expression const& _expression);
+ /// The same as storeInLValue but do not again retrieve the value to the top of the stack.
+ void moveToLValue(Expression const& _expression);
+ /// Returns the position of @a m_currentLValue in the stack, where 0 is the top of the stack.
+ unsigned stackPositionOfLValue() const;
+
+ Declaration* m_currentLValue;
AssemblyItems m_assemblyItems;
CompilerContext& m_context;
};
diff --git a/Exceptions.h b/Exceptions.h
index 5a48c47d..1d981e7c 100644
--- a/Exceptions.h
+++ b/Exceptions.h
@@ -34,6 +34,7 @@ namespace solidity
struct ParserError: virtual Exception {};
struct TypeError: virtual Exception {};
struct DeclarationError: virtual Exception {};
+struct CompilerError: virtual Exception {};
typedef boost::error_info<struct tag_sourcePosition, int> errinfo_sourcePosition;
typedef boost::error_info<struct tag_sourceLocation, Location> errinfo_sourceLocation;