mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Compiler for assignments.
This commit is contained in:
parent
01224287f5
commit
b5a4d12fa3
13
AST.cpp
13
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()?
|
||||
|
9
AST.h
9
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;
|
||||
|
113
Compiler.cpp
113
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
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
16
Compiler.h
16
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;
|
||||
};
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user