aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian <c@ethdev.com>2014-11-10 20:30:59 +0800
committerChristian <c@ethdev.com>2014-11-10 20:30:59 +0800
commita0c72065fee89c4558eeb4a98e5273633635bc39 (patch)
treed9629fd7a81f46b76d82086a211de831c19657b3
parentdc8fb45e1f1d098442458f14d80c343e4a445619 (diff)
parentd9822190c6fb3ac5025296c0f47977cca9547b91 (diff)
downloaddexon-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.h8
-rw-r--r--CMakeLists.txt7
-rw-r--r--Compiler.cpp19
-rw-r--r--CompilerContext.cpp36
-rw-r--r--CompilerContext.h25
-rw-r--r--ExpressionCompiler.cpp177
-rw-r--r--ExpressionCompiler.h54
-rw-r--r--Types.cpp22
-rw-r--r--Types.h16
9 files changed, 237 insertions, 127 deletions
diff --git a/AST.h b/AST.h
index ce9190ea..19328e5f 100644
--- a/AST.h
+++ b/AST.h
@@ -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;
};
diff --git a/Types.cpp b/Types.cpp
index 7354255e..3a4112c4 100644
--- a/Types.cpp
+++ b/Types.cpp
@@ -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())
diff --git a/Types.h b/Types.h
index 8c88ca79..607ee3a6 100644
--- a/Types.h
+++ b/Types.h
@@ -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: