diff options
author | Christian <c@ethdev.com> | 2014-11-10 20:30:59 +0800 |
---|---|---|
committer | Christian <c@ethdev.com> | 2014-11-10 20:30:59 +0800 |
commit | a0c72065fee89c4558eeb4a98e5273633635bc39 (patch) | |
tree | d9629fd7a81f46b76d82086a211de831c19657b3 | |
parent | dc8fb45e1f1d098442458f14d80c343e4a445619 (diff) | |
parent | d9822190c6fb3ac5025296c0f47977cca9547b91 (diff) | |
download | dexon-solidity-a0c72065fee89c4558eeb4a98e5273633635bc39.tar dexon-solidity-a0c72065fee89c4558eeb4a98e5273633635bc39.tar.gz dexon-solidity-a0c72065fee89c4558eeb4a98e5273633635bc39.tar.bz2 dexon-solidity-a0c72065fee89c4558eeb4a98e5273633635bc39.tar.lz dexon-solidity-a0c72065fee89c4558eeb4a98e5273633635bc39.tar.xz dexon-solidity-a0c72065fee89c4558eeb4a98e5273633635bc39.tar.zst dexon-solidity-a0c72065fee89c4558eeb4a98e5273633635bc39.zip |
Merge remote-tracking branch 'ethereum/develop' into sol_optimizer
Conflicts:
libevmcore/Instruction.cpp
-rw-r--r-- | AST.h | 8 | ||||
-rw-r--r-- | CMakeLists.txt | 7 | ||||
-rw-r--r-- | Compiler.cpp | 19 | ||||
-rw-r--r-- | CompilerContext.cpp | 36 | ||||
-rw-r--r-- | CompilerContext.h | 25 | ||||
-rw-r--r-- | ExpressionCompiler.cpp | 177 | ||||
-rw-r--r-- | ExpressionCompiler.h | 54 | ||||
-rw-r--r-- | Types.cpp | 22 | ||||
-rw-r--r-- | Types.h | 16 |
9 files changed, 237 insertions, 127 deletions
@@ -116,9 +116,9 @@ public: virtual void accept(ASTVisitor& _visitor) override; - std::vector<ASTPointer<StructDefinition>> const& getDefinedStructs() { return m_definedStructs; } - std::vector<ASTPointer<VariableDeclaration>> const& getStateVariables() { return m_stateVariables; } - std::vector<ASTPointer<FunctionDefinition>> const& getDefinedFunctions() { return m_definedFunctions; } + std::vector<ASTPointer<StructDefinition>> const& getDefinedStructs() const { return m_definedStructs; } + std::vector<ASTPointer<VariableDeclaration>> const& getStateVariables() const { return m_stateVariables; } + std::vector<ASTPointer<FunctionDefinition>> const& getDefinedFunctions() const { return m_definedFunctions; } private: std::vector<ASTPointer<StructDefinition>> m_definedStructs; @@ -135,6 +135,8 @@ public: Declaration(_location, _name), m_members(_members) {} virtual void accept(ASTVisitor& _visitor) override; + std::vector<ASTPointer<VariableDeclaration>> const& getMembers() const { return m_members; } + private: std::vector<ASTPointer<VariableDeclaration>> m_members; }; diff --git a/CMakeLists.txt b/CMakeLists.txt index f335dd75..ea2ef4b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,14 +6,13 @@ aux_source_directory(. SRC_LIST) set(EXECUTABLE solidity) +file(GLOB HEADERS "*.h") if(ETH_STATIC) - add_library(${EXECUTABLE} STATIC ${SRC_LIST}) + add_library(${EXECUTABLE} STATIC ${SRC_LIST} ${HEADERS}) else() - add_library(${EXECUTABLE} SHARED ${SRC_LIST}) + add_library(${EXECUTABLE} SHARED ${SRC_LIST} ${HEADERS}) endif() -file(GLOB HEADERS "*.h") - include_directories(..) target_link_libraries(${EXECUTABLE} evmcore devcore) diff --git a/Compiler.cpp b/Compiler.cpp index 9ae8d7c6..ed2b1f45 100644 --- a/Compiler.cpp +++ b/Compiler.cpp @@ -21,6 +21,8 @@ */ #include <algorithm> +#include <libevmcore/Instruction.h> +#include <libevmcore/Assembly.h> #include <libsolidity/AST.h> #include <libsolidity/Compiler.h> #include <libsolidity/ExpressionCompiler.h> @@ -42,10 +44,12 @@ void Compiler::compileContract(ContractDefinition& _contract) m_context = CompilerContext(); // clear it just in case //@todo constructor - //@todo register state variables for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions()) m_context.addFunction(*function); + //@todo sort them? + for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables()) + m_context.addStateVariable(*variable); appendFunctionSelector(_contract.getDefinedFunctions()); for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions()) @@ -123,7 +127,7 @@ void Compiler::appendCalldataUnpacker(FunctionDefinition const& _function) if (numBytes == 0) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(var->getLocation()) - << errinfo_comment("Type not yet supported.")); + << errinfo_comment("Type " + var->getType()->toString() + " not yet supported.")); if (numBytes == 32) m_context << u256(dataOffset) << eth::Instruction::CALLDATALOAD; else @@ -140,11 +144,12 @@ void Compiler::appendReturnValuePacker(FunctionDefinition const& _function) vector<ASTPointer<VariableDeclaration>> const& parameters = _function.getReturnParameters(); for (unsigned i = 0; i < parameters.size(); ++i) { - unsigned numBytes = parameters[i]->getType()->getCalldataEncodedSize(); + Type const& paramType = *parameters[i]->getType(); + unsigned numBytes = paramType.getCalldataEncodedSize(); if (numBytes == 0) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(parameters[i]->getLocation()) - << errinfo_comment("Type not yet supported.")); + << errinfo_comment("Type " + paramType.toString() + " not yet supported.")); m_context << eth::dupInstruction(parameters.size() - i); if (numBytes != 32) m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL; @@ -273,7 +278,8 @@ bool Compiler::visit(Return& _return) ExpressionCompiler::compileExpression(m_context, *expression); VariableDeclaration const& firstVariable = *_return.getFunctionReturnParameters().getParameters().front(); ExpressionCompiler::appendTypeConversion(m_context, *expression->getType(), *firstVariable.getType()); - int stackPosition = m_context.getStackPositionOfVariable(firstVariable); + + unsigned stackPosition = m_context.baseToCurrentStackOffset(m_context.getBaseStackOffsetOfVariable(firstVariable)); m_context << eth::swapInstruction(stackPosition) << eth::Instruction::POP; } m_context.appendJumpTo(m_returnTag); @@ -288,7 +294,8 @@ bool Compiler::visit(VariableDefinition& _variableDefinition) ExpressionCompiler::appendTypeConversion(m_context, *expression->getType(), *_variableDefinition.getDeclaration().getType()); - int stackPosition = m_context.getStackPositionOfVariable(_variableDefinition.getDeclaration()); + unsigned baseStackOffset = m_context.getBaseStackOffsetOfVariable(_variableDefinition.getDeclaration()); + unsigned stackPosition = m_context.baseToCurrentStackOffset(baseStackOffset); m_context << eth::swapInstruction(stackPosition) << eth::Instruction::POP; } return false; diff --git a/CompilerContext.cpp b/CompilerContext.cpp index 99cf090e..3c1acdfa 100644 --- a/CompilerContext.cpp +++ b/CompilerContext.cpp @@ -30,6 +30,12 @@ using namespace std; namespace dev { namespace solidity { +void CompilerContext::addStateVariable(VariableDeclaration const& _declaration) +{ + m_stateVariables[&_declaration] = m_stateVariablesSize; + m_stateVariablesSize += _declaration.getType()->getStorageSize(); +} + void CompilerContext::initializeLocalVariables(unsigned _numVariables) { if (_numVariables > 0) @@ -41,12 +47,9 @@ void CompilerContext::initializeLocalVariables(unsigned _numVariables) } } -int CompilerContext::getStackPositionOfVariable(Declaration const& _declaration) +bool CompilerContext::isLocalVariable(Declaration const* _declaration) const { - auto res = find(begin(m_localVariables), end(m_localVariables), &_declaration); - if (asserts(res != m_localVariables.end())) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Variable not found on stack.")); - return end(m_localVariables) - res - 1 + m_asm.deposit(); + return std::find(m_localVariables.begin(), m_localVariables.end(), _declaration) != m_localVariables.end(); } eth::AssemblyItem CompilerContext::getFunctionEntryLabel(FunctionDefinition const& _function) const @@ -57,5 +60,28 @@ eth::AssemblyItem CompilerContext::getFunctionEntryLabel(FunctionDefinition cons return res->second.tag(); } +unsigned CompilerContext::getBaseStackOffsetOfVariable(Declaration const& _declaration) const +{ + auto res = find(begin(m_localVariables), end(m_localVariables), &_declaration); + if (asserts(res != m_localVariables.end())) + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Variable not found on stack.")); + return unsigned(end(m_localVariables) - res - 1); +} + +unsigned CompilerContext::baseToCurrentStackOffset(unsigned _baseOffset) const +{ + return _baseOffset + m_asm.deposit(); +} + +u256 CompilerContext::getStorageLocationOfVariable(const Declaration& _declaration) const +{ + auto it = m_stateVariables.find(&_declaration); + if (it == m_stateVariables.end()) + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Variable not found in storage.")); + return it->second; +} + + + } } diff --git a/CompilerContext.h b/CompilerContext.h index 866c621d..562c2932 100644 --- a/CompilerContext.h +++ b/CompilerContext.h @@ -38,19 +38,28 @@ namespace solidity { class CompilerContext { public: - CompilerContext() {} + CompilerContext(): m_stateVariablesSize(0) {} + void addStateVariable(VariableDeclaration const& _declaration); void startNewFunction() { m_localVariables.clear(); m_asm.setDeposit(0); } void initializeLocalVariables(unsigned _numVariables); void addVariable(VariableDeclaration const& _declaration) { m_localVariables.push_back(&_declaration); } - /// Returns the distance of the given local variable from the top of the stack. - int getStackPositionOfVariable(Declaration const& _declaration); - void addFunction(FunctionDefinition const& _function) { m_functionEntryLabels.insert(std::make_pair(&_function, m_asm.newTag())); } - eth::AssemblyItem getFunctionEntryLabel(FunctionDefinition const& _function) const; void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); } + bool isFunctionDefinition(Declaration const* _declaration) const { return m_functionEntryLabels.count(_declaration); } + bool isLocalVariable(Declaration const* _declaration) const; + bool isStateVariable(Declaration const* _declaration) const { return m_stateVariables.count(_declaration); } + + eth::AssemblyItem getFunctionEntryLabel(FunctionDefinition const& _function) const; + /// Returns the distance of the given local variable from the top of the local variable stack. + unsigned getBaseStackOffsetOfVariable(Declaration const& _declaration) const; + /// If supplied by a value returned by @ref getBaseStackOffsetOfVariable(variable), returns + /// the distance of that variable from the current top of the stack. + unsigned baseToCurrentStackOffset(unsigned _baseOffset) const; + u256 getStorageLocationOfVariable(Declaration const& _declaration) const; + /// Appends a JUMPI instruction to a new tag and @returns the tag eth::AssemblyItem appendConditionalJump() { return m_asm.appendJumpI().tag(); } /// Appends a JUMPI instruction to @a _tag @@ -79,10 +88,14 @@ public: private: eth::Assembly m_asm; + /// Size of the state variables, offset of next variable to be added. + u256 m_stateVariablesSize; + /// Storage offsets of state variables + std::map<Declaration const*, u256> m_stateVariables; /// Offsets of local variables on the stack. std::vector<Declaration const*> m_localVariables; /// Labels pointing to the entry points of funcitons. - std::map<FunctionDefinition const*, eth::AssemblyItem> m_functionEntryLabels; + std::map<Declaration const*, eth::AssemblyItem> m_functionEntryLabels; }; } diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index 6efb8b20..05bbb091 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -46,23 +46,16 @@ void ExpressionCompiler::appendTypeConversion(CompilerContext& _context, bool ExpressionCompiler::visit(Assignment& _assignment) { - m_currentLValue = nullptr; - - Expression& rightHandSide = _assignment.getRightHandSide(); - rightHandSide.accept(*this); - Type const& resultType = *_assignment.getType(); - appendTypeConversion(*rightHandSide.getType(), resultType); + _assignment.getRightHandSide().accept(*this); + appendTypeConversion(*_assignment.getRightHandSide().getType(), *_assignment.getType()); + m_currentLValue.reset(); _assignment.getLeftHandSide().accept(*this); Token::Value op = _assignment.getAssignmentOperator(); - if (op != Token::ASSIGN) - { - // compound assignment - m_context << eth::Instruction::SWAP1; - appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), resultType); - } + if (op != Token::ASSIGN) // compound assignment + appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.getType()); else - m_context << eth::Instruction::POP; //@todo do not retrieve the value in the first place + m_context << eth::Instruction::POP; storeInLValue(_assignment); return false; @@ -99,10 +92,7 @@ void ExpressionCompiler::endVisit(UnaryOperation& _unaryOperation) m_context << eth::Instruction::ADD; else m_context << eth::Instruction::SWAP1 << eth::Instruction::SUB; // @todo avoid the swap - if (_unaryOperation.isPrefixOperation()) - storeInLValue(_unaryOperation); - else - moveToLValue(_unaryOperation); + storeInLValue(_unaryOperation, !_unaryOperation.isPrefixOperation()); break; case Token::ADD: // + // unary add, so basically no-op @@ -123,11 +113,8 @@ bool ExpressionCompiler::visit(BinaryOperation& _binaryOperation) Type const& commonType = _binaryOperation.getCommonType(); Token::Value const op = _binaryOperation.getOperator(); - if (op == Token::AND || op == Token::OR) - { - // special case: short-circuiting + if (op == Token::AND || op == Token::OR) // special case: short-circuiting appendAndOrOperatorCode(_binaryOperation); - } else { bool cleanupNeeded = false; @@ -135,10 +122,10 @@ bool ExpressionCompiler::visit(BinaryOperation& _binaryOperation) if (Token::isCompareOp(op) || op == Token::DIV || op == Token::MOD) cleanupNeeded = true; - leftExpression.accept(*this); - appendTypeConversion(*leftExpression.getType(), commonType, cleanupNeeded); rightExpression.accept(*this); appendTypeConversion(*rightExpression.getType(), commonType, cleanupNeeded); + leftExpression.accept(*this); + appendTypeConversion(*leftExpression.getType(), commonType, cleanupNeeded); if (Token::isCompareOp(op)) appendCompareOperatorCode(op, commonType); else @@ -164,9 +151,13 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall) { // Calling convention: Caller pushes return address and arguments // Callee removes them and pushes return values - m_currentLValue = nullptr; + m_currentLValue.reset(); _functionCall.getExpression().accept(*this); - FunctionDefinition const& function = dynamic_cast<FunctionDefinition&>(*m_currentLValue); + if (asserts(m_currentLValue.isInCode())) + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Code reference expected.")); + eth::AssemblyItem functionTag(eth::PushTag, m_currentLValue.location); + + FunctionDefinition const& function = dynamic_cast<FunctionType const&>(*_functionCall.getExpression().getType()).getFunction(); eth::AssemblyItem returnLabel = m_context.pushNewTag(); std::vector<ASTPointer<Expression>> const& arguments = _functionCall.getArguments(); @@ -175,11 +166,10 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall) for (unsigned i = 0; i < arguments.size(); ++i) { arguments[i]->accept(*this); - appendTypeConversion(*arguments[i]->getType(), - *function.getParameters()[i]->getType()); + appendTypeConversion(*arguments[i]->getType(), *function.getParameters()[i]->getType()); } - m_context.appendJumpTo(m_context.getFunctionEntryLabel(function)); + m_context.appendJumpTo(functionTag); m_context << returnLabel; // callee adds return parameters, but removes arguments and return label @@ -205,24 +195,20 @@ void ExpressionCompiler::endVisit(IndexAccess&) void ExpressionCompiler::endVisit(Identifier& _identifier) { - m_currentLValue = _identifier.getReferencedDeclaration(); - switch (_identifier.getType()->getCategory()) - { - case Type::Category::BOOL: - case Type::Category::INTEGER: - case Type::Category::REAL: - { - //@todo we also have to check where to retrieve them from once we add storage variables - 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.")); - m_context << eth::dupInstruction(stackPos + 1); - break; - } - default: - break; - } + Declaration const* declaration = _identifier.getReferencedDeclaration(); + if (m_context.isLocalVariable(declaration)) + m_currentLValue = LValueLocation(LValueLocation::STACK, + m_context.getBaseStackOffsetOfVariable(*declaration)); + else if (m_context.isStateVariable(declaration)) + m_currentLValue = LValueLocation(LValueLocation::STORAGE, + m_context.getStorageLocationOfVariable(*declaration)); + else if (m_context.isFunctionDefinition(declaration)) + m_currentLValue = LValueLocation(LValueLocation::CODE, + m_context.getFunctionEntryLabel(dynamic_cast<FunctionDefinition const&>(*declaration)).data()); + else + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not supported or identifier not found.")); + + retrieveLValueValue(_identifier); } void ExpressionCompiler::endVisit(Literal& _literal) @@ -267,23 +253,21 @@ void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type IntegerType const& type = dynamic_cast<IntegerType const&>(_type); bool const isSigned = type.isSigned(); - // note that EVM opcodes compare like "stack[0] < stack[1]", - // but our left value is at stack[1], so everyhing is reversed. switch (_operator) { case Token::GTE: - m_context << (isSigned ? eth::Instruction::SGT : eth::Instruction::GT) + m_context << (isSigned ? eth::Instruction::SLT : eth::Instruction::LT) << eth::Instruction::ISZERO; break; case Token::LTE: - m_context << (isSigned ? eth::Instruction::SLT : eth::Instruction::LT) + m_context << (isSigned ? eth::Instruction::SGT : eth::Instruction::GT) << eth::Instruction::ISZERO; break; case Token::GT: - m_context << (isSigned ? eth::Instruction::SLT : eth::Instruction::LT); + m_context << (isSigned ? eth::Instruction::SGT : eth::Instruction::GT); break; case Token::LT: - m_context << (isSigned ? eth::Instruction::SGT : eth::Instruction::GT); + m_context << (isSigned ? eth::Instruction::SLT : eth::Instruction::LT); break; default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown comparison operator.")); @@ -314,16 +298,16 @@ void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Ty m_context << eth::Instruction::ADD; break; case Token::SUB: - m_context << eth::Instruction::SWAP1 << eth::Instruction::SUB; + m_context << eth::Instruction::SUB; break; case Token::MUL: m_context << eth::Instruction::MUL; break; case Token::DIV: - m_context << eth::Instruction::SWAP1 << (isSigned ? eth::Instruction::SDIV : eth::Instruction::DIV); + m_context << (isSigned ? eth::Instruction::SDIV : eth::Instruction::DIV); break; case Token::MOD: - m_context << eth::Instruction::SWAP1 << (isSigned ? eth::Instruction::SMOD : eth::Instruction::MOD); + m_context << (isSigned ? eth::Instruction::SMOD : eth::Instruction::MOD); break; default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown arithmetic operator.")); @@ -364,10 +348,9 @@ void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator) void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded) { - // If the type of one of the operands is extended, we need to remove all - // higher-order bits that we might have ignored in previous operations. - // @todo: store in the AST whether the operand might have "dirty" higher - // order bits + // For a type extension, we need to remove all higher-order bits that we might have ignored in + // previous operations. + // @todo: store in the AST whether the operand might have "dirty" higher order bits if (_typeOnStack == _targetType && !_cleanupNeeded) return; @@ -388,31 +371,65 @@ void ExpressionCompiler::appendHighBitsCleanup(IntegerType const& _typeOnStack) m_context << ((u256(1) << _typeOnStack.getNumBits()) - 1) << eth::Instruction::AND; } -void ExpressionCompiler::storeInLValue(Expression const& _expression) -{ - moveToLValue(_expression); - unsigned stackPos = stackPositionOfLValue(); - if (stackPos > 16) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation()) - << errinfo_comment("Stack too deep.")); - m_context << eth::dupInstruction(stackPos + 1); -} - -void ExpressionCompiler::moveToLValue(Expression const& _expression) +void ExpressionCompiler::retrieveLValueValue(Expression const& _expression) { - unsigned stackPos = stackPositionOfLValue(); - if (stackPos > 16) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation()) - << errinfo_comment("Stack too deep.")); - else if (stackPos > 0) - m_context << eth::swapInstruction(stackPos) << eth::Instruction::POP; + switch (m_currentLValue.locationType) + { + case LValueLocation::CODE: + // not stored on the stack + break; + case LValueLocation::STACK: + { + unsigned stackPos = m_context.baseToCurrentStackOffset(unsigned(m_currentLValue.location)); + if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation()) + << errinfo_comment("Stack too deep.")); + m_context << eth::dupInstruction(stackPos + 1); + break; + } + case LValueLocation::STORAGE: + m_context << m_currentLValue.location << eth::Instruction::SLOAD; + break; + case LValueLocation::MEMORY: + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Location type not yet implemented.")); + break; + default: + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unsupported location type.")); + break; + } } -unsigned ExpressionCompiler::stackPositionOfLValue() const +void ExpressionCompiler::storeInLValue(Expression const& _expression, bool _move) { - if (asserts(m_currentLValue)) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("LValue not available on request.")); - return m_context.getStackPositionOfVariable(*m_currentLValue); + switch (m_currentLValue.locationType) + { + case LValueLocation::STACK: + { + unsigned stackPos = m_context.baseToCurrentStackOffset(unsigned(m_currentLValue.location)); + if (stackPos > 16) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation()) + << errinfo_comment("Stack too deep.")); + else if (stackPos > 0) + m_context << eth::swapInstruction(stackPos) << eth::Instruction::POP; + if (!_move) + retrieveLValueValue(_expression); + break; + } + case LValueLocation::STORAGE: + if (!_move) + m_context << eth::Instruction::DUP1; + m_context << m_currentLValue.location << eth::Instruction::SSTORE; + break; + case LValueLocation::CODE: + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Location type does not support assignment.")); + break; + case LValueLocation::MEMORY: + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Location type not yet implemented.")); + break; + default: + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unsupported location type.")); + break; + } } } diff --git a/ExpressionCompiler.h b/ExpressionCompiler.h index d67814be..bd5a9f86 100644 --- a/ExpressionCompiler.h +++ b/ExpressionCompiler.h @@ -20,18 +20,25 @@ * Solidity AST to EVM bytecode compiler for expressions. */ +#include <libdevcore/Common.h> #include <libsolidity/ASTVisitor.h> namespace dev { +namespace eth +{ +class AssemblyItem; // forward +} namespace solidity { class CompilerContext; // forward class Type; // forward class IntegerType; // forward -/// Compiler for expressions, i.e. converts an AST tree whose root is an Expression into a stream -/// of EVM instructions. It needs a compiler context that is the same for the whole compilation -/// unit. +/** + * Compiler for expressions, i.e. converts an AST tree whose root is an Expression into a stream + * of EVM instructions. It needs a compiler context that is the same for the whole compilation + * unit. + */ class ExpressionCompiler: private ASTVisitor { public: @@ -42,7 +49,7 @@ public: static void appendTypeConversion(CompilerContext& _context, Type const& _typeOnStack, Type const& _targetType); private: - ExpressionCompiler(CompilerContext& _compilerContext): m_currentLValue(nullptr), m_context(_compilerContext) {} + ExpressionCompiler(CompilerContext& _compilerContext): m_context(_compilerContext) {} virtual bool visit(Assignment& _assignment) override; virtual void endVisit(UnaryOperation& _unaryOperation) override; @@ -72,15 +79,36 @@ private: //// Appends code that cleans higher-order bits for integer types. void appendHighBitsCleanup(IntegerType const& _typeOnStack); - /// 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; + /// Copies the value of the current lvalue to the top of the stack. + void retrieveLValueValue(Expression const& _expression); + /// Stores the value on top of the stack in the current lvalue. Removes it from the stack if + /// @a _move is true. + void storeInLValue(Expression const& _expression, bool _move = false); + + /** + * Location of an lvalue, either in code (for a function) on the stack, in the storage or memory. + */ + struct LValueLocation + { + enum LocationType { INVALID, CODE, STACK, MEMORY, STORAGE }; + + LValueLocation() { reset(); } + LValueLocation(LocationType _type, u256 const& _location): locationType(_type), location(_location) {} + void reset() { locationType = INVALID; location = 0; } + bool isValid() const { return locationType != INVALID; } + bool isInCode() const { return locationType == CODE; } + bool isInOnStack() const { return locationType == STACK; } + bool isInMemory() const { return locationType == MEMORY; } + bool isInStorage() const { return locationType == STORAGE; } + + LocationType locationType; + /// Depending on the type, this is the id of a tag (code), the base offset of a stack + /// variable (@see CompilerContext::getBaseStackOffsetOfVariable) or the offset in + /// storage or memory. + u256 location; + }; + + LValueLocation m_currentLValue; CompilerContext& m_context; }; @@ -56,7 +56,6 @@ shared_ptr<Type> Type::fromElementaryTypeName(Token::Value _typeToken) else BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " + std::string(Token::toString(_typeToken)) + " to type.")); - return shared_ptr<Type>(); } shared_ptr<Type> Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName) @@ -67,7 +66,6 @@ shared_ptr<Type> Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeN shared_ptr<Type> Type::fromMapping(Mapping const&) { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Mapping types not yet implemented.")); - return shared_ptr<Type>(); } shared_ptr<Type> Type::forLiteral(Literal const& _literal) @@ -103,8 +101,8 @@ IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier): m_bits(_bits), m_modifier(_modifier) { if (isAddress()) - _bits = 160; - if (asserts(_bits > 0 && _bits <= 256 && _bits % 8 == 0)) + m_bits = 160; + if (asserts(m_bits > 0 && m_bits <= 256 && m_bits % 8 == 0)) BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid bit number for integer type: " + dev::toString(_bits))); } @@ -207,6 +205,14 @@ bool ContractType::operator==(Type const& _other) const return other.m_contract == m_contract; } +u256 ContractType::getStorageSize() const +{ + u256 size = 0; + for (ASTPointer<VariableDeclaration> const& variable: m_contract.getStateVariables()) + size += variable->getType()->getStorageSize(); + return max<u256>(1, size); +} + bool StructType::operator==(Type const& _other) const { if (_other.getCategory() != getCategory()) @@ -215,6 +221,14 @@ bool StructType::operator==(Type const& _other) const return other.m_struct == m_struct; } +u256 StructType::getStorageSize() const +{ + u256 size = 0; + for (ASTPointer<VariableDeclaration> const& variable: m_struct.getMembers()) + size += variable->getType()->getStorageSize(); + return max<u256>(1, size); +} + bool FunctionType::operator==(Type const& _other) const { if (_other.getCategory() != getCategory()) @@ -75,6 +75,9 @@ public: /// @returns number of bytes used by this type when encoded for CALL, or 0 if the encoding /// is not a simple big-endian encoding or the type cannot be stored on the stack. virtual unsigned getCalldataEncodedSize() const { return 0; } + /// @returns number of bytes required to hold this value in storage. + /// For dynamically "allocated" types, it returns the size of the statically allocated head, + virtual u256 getStorageSize() const { return 1; } virtual std::string toString() const = 0; virtual u256 literalValue(Literal const&) const @@ -157,7 +160,7 @@ public: ContractType(ContractDefinition const& _contract): m_contract(_contract) {} virtual bool operator==(Type const& _other) const override; - + virtual u256 getStorageSize() const; virtual std::string toString() const override { return "contract{...}"; } private: @@ -178,7 +181,7 @@ public: } virtual bool operator==(Type const& _other) const override; - + virtual u256 getStorageSize() const; virtual std::string toString() const override { return "struct{...}"; } private: @@ -196,9 +199,9 @@ public: FunctionDefinition const& getFunction() const { return m_function; } - virtual std::string toString() const override { return "function(...)returns(...)"; } - virtual bool operator==(Type const& _other) const override; + virtual std::string toString() const override { return "function(...)returns(...)"; } + virtual u256 getStorageSize() const { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable function type requested.")); } private: FunctionDefinition const& m_function; @@ -212,9 +215,9 @@ class MappingType: public Type public: virtual Category getCategory() const override { return Category::MAPPING; } MappingType() {} - virtual std::string toString() const override { return "mapping(...=>...)"; } virtual bool operator==(Type const& _other) const override; + virtual std::string toString() const override { return "mapping(...=>...)"; } private: std::shared_ptr<Type const> m_keyType; @@ -232,6 +235,7 @@ public: VoidType() {} virtual std::string toString() const override { return "void"; } + virtual u256 getStorageSize() const { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable void type requested.")); } }; /** @@ -247,7 +251,7 @@ public: std::shared_ptr<Type const> const& getActualType() const { return m_actualType; } virtual bool operator==(Type const& _other) const override; - + virtual u256 getStorageSize() const { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable type type requested.")); } virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; } private: |