diff options
-rw-r--r-- | AST.cpp | 13 | ||||
-rw-r--r-- | AST.h | 9 | ||||
-rw-r--r-- | Compiler.cpp | 113 | ||||
-rw-r--r-- | Compiler.h | 16 | ||||
-rw-r--r-- | Exceptions.h | 1 |
5 files changed, 123 insertions, 29 deletions
@@ -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()? @@ -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 +} + } @@ -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; |