mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge remote-tracking branch 'ethereum/develop' into sol_optimizer
Conflicts: libevmcore/Instruction.cpp
This commit is contained in:
commit
a0c72065fe
8
AST.h
8
AST.h
@ -116,9 +116,9 @@ public:
|
|||||||
|
|
||||||
virtual void accept(ASTVisitor& _visitor) override;
|
virtual void accept(ASTVisitor& _visitor) override;
|
||||||
|
|
||||||
std::vector<ASTPointer<StructDefinition>> const& getDefinedStructs() { return m_definedStructs; }
|
std::vector<ASTPointer<StructDefinition>> const& getDefinedStructs() const { return m_definedStructs; }
|
||||||
std::vector<ASTPointer<VariableDeclaration>> const& getStateVariables() { return m_stateVariables; }
|
std::vector<ASTPointer<VariableDeclaration>> const& getStateVariables() const { return m_stateVariables; }
|
||||||
std::vector<ASTPointer<FunctionDefinition>> const& getDefinedFunctions() { return m_definedFunctions; }
|
std::vector<ASTPointer<FunctionDefinition>> const& getDefinedFunctions() const { return m_definedFunctions; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<ASTPointer<StructDefinition>> m_definedStructs;
|
std::vector<ASTPointer<StructDefinition>> m_definedStructs;
|
||||||
@ -135,6 +135,8 @@ public:
|
|||||||
Declaration(_location, _name), m_members(_members) {}
|
Declaration(_location, _name), m_members(_members) {}
|
||||||
virtual void accept(ASTVisitor& _visitor) override;
|
virtual void accept(ASTVisitor& _visitor) override;
|
||||||
|
|
||||||
|
std::vector<ASTPointer<VariableDeclaration>> const& getMembers() const { return m_members; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<ASTPointer<VariableDeclaration>> m_members;
|
std::vector<ASTPointer<VariableDeclaration>> m_members;
|
||||||
};
|
};
|
||||||
|
@ -6,13 +6,12 @@ aux_source_directory(. SRC_LIST)
|
|||||||
|
|
||||||
set(EXECUTABLE solidity)
|
set(EXECUTABLE solidity)
|
||||||
|
|
||||||
if(ETH_STATIC)
|
|
||||||
add_library(${EXECUTABLE} STATIC ${SRC_LIST})
|
|
||||||
else()
|
|
||||||
add_library(${EXECUTABLE} SHARED ${SRC_LIST})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
file(GLOB HEADERS "*.h")
|
file(GLOB HEADERS "*.h")
|
||||||
|
if(ETH_STATIC)
|
||||||
|
add_library(${EXECUTABLE} STATIC ${SRC_LIST} ${HEADERS})
|
||||||
|
else()
|
||||||
|
add_library(${EXECUTABLE} SHARED ${SRC_LIST} ${HEADERS})
|
||||||
|
endif()
|
||||||
|
|
||||||
include_directories(..)
|
include_directories(..)
|
||||||
|
|
||||||
|
19
Compiler.cpp
19
Compiler.cpp
@ -21,6 +21,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <libevmcore/Instruction.h>
|
||||||
|
#include <libevmcore/Assembly.h>
|
||||||
#include <libsolidity/AST.h>
|
#include <libsolidity/AST.h>
|
||||||
#include <libsolidity/Compiler.h>
|
#include <libsolidity/Compiler.h>
|
||||||
#include <libsolidity/ExpressionCompiler.h>
|
#include <libsolidity/ExpressionCompiler.h>
|
||||||
@ -42,10 +44,12 @@ void Compiler::compileContract(ContractDefinition& _contract)
|
|||||||
m_context = CompilerContext(); // clear it just in case
|
m_context = CompilerContext(); // clear it just in case
|
||||||
|
|
||||||
//@todo constructor
|
//@todo constructor
|
||||||
//@todo register state variables
|
|
||||||
|
|
||||||
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
|
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
|
||||||
m_context.addFunction(*function);
|
m_context.addFunction(*function);
|
||||||
|
//@todo sort them?
|
||||||
|
for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables())
|
||||||
|
m_context.addStateVariable(*variable);
|
||||||
|
|
||||||
appendFunctionSelector(_contract.getDefinedFunctions());
|
appendFunctionSelector(_contract.getDefinedFunctions());
|
||||||
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
|
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
|
||||||
@ -123,7 +127,7 @@ void Compiler::appendCalldataUnpacker(FunctionDefinition const& _function)
|
|||||||
if (numBytes == 0)
|
if (numBytes == 0)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError()
|
BOOST_THROW_EXCEPTION(CompilerError()
|
||||||
<< errinfo_sourceLocation(var->getLocation())
|
<< errinfo_sourceLocation(var->getLocation())
|
||||||
<< errinfo_comment("Type not yet supported."));
|
<< errinfo_comment("Type " + var->getType()->toString() + " not yet supported."));
|
||||||
if (numBytes == 32)
|
if (numBytes == 32)
|
||||||
m_context << u256(dataOffset) << eth::Instruction::CALLDATALOAD;
|
m_context << u256(dataOffset) << eth::Instruction::CALLDATALOAD;
|
||||||
else
|
else
|
||||||
@ -140,11 +144,12 @@ void Compiler::appendReturnValuePacker(FunctionDefinition const& _function)
|
|||||||
vector<ASTPointer<VariableDeclaration>> const& parameters = _function.getReturnParameters();
|
vector<ASTPointer<VariableDeclaration>> const& parameters = _function.getReturnParameters();
|
||||||
for (unsigned i = 0; i < parameters.size(); ++i)
|
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)
|
if (numBytes == 0)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError()
|
BOOST_THROW_EXCEPTION(CompilerError()
|
||||||
<< errinfo_sourceLocation(parameters[i]->getLocation())
|
<< 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);
|
m_context << eth::dupInstruction(parameters.size() - i);
|
||||||
if (numBytes != 32)
|
if (numBytes != 32)
|
||||||
m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL;
|
m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL;
|
||||||
@ -273,7 +278,8 @@ bool Compiler::visit(Return& _return)
|
|||||||
ExpressionCompiler::compileExpression(m_context, *expression);
|
ExpressionCompiler::compileExpression(m_context, *expression);
|
||||||
VariableDeclaration const& firstVariable = *_return.getFunctionReturnParameters().getParameters().front();
|
VariableDeclaration const& firstVariable = *_return.getFunctionReturnParameters().getParameters().front();
|
||||||
ExpressionCompiler::appendTypeConversion(m_context, *expression->getType(), *firstVariable.getType());
|
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 << eth::swapInstruction(stackPosition) << eth::Instruction::POP;
|
||||||
}
|
}
|
||||||
m_context.appendJumpTo(m_returnTag);
|
m_context.appendJumpTo(m_returnTag);
|
||||||
@ -288,7 +294,8 @@ bool Compiler::visit(VariableDefinition& _variableDefinition)
|
|||||||
ExpressionCompiler::appendTypeConversion(m_context,
|
ExpressionCompiler::appendTypeConversion(m_context,
|
||||||
*expression->getType(),
|
*expression->getType(),
|
||||||
*_variableDefinition.getDeclaration().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;
|
m_context << eth::swapInstruction(stackPosition) << eth::Instruction::POP;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -30,6 +30,12 @@ using namespace std;
|
|||||||
namespace dev {
|
namespace dev {
|
||||||
namespace solidity {
|
namespace solidity {
|
||||||
|
|
||||||
|
void CompilerContext::addStateVariable(VariableDeclaration const& _declaration)
|
||||||
|
{
|
||||||
|
m_stateVariables[&_declaration] = m_stateVariablesSize;
|
||||||
|
m_stateVariablesSize += _declaration.getType()->getStorageSize();
|
||||||
|
}
|
||||||
|
|
||||||
void CompilerContext::initializeLocalVariables(unsigned _numVariables)
|
void CompilerContext::initializeLocalVariables(unsigned _numVariables)
|
||||||
{
|
{
|
||||||
if (_numVariables > 0)
|
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);
|
return std::find(m_localVariables.begin(), m_localVariables.end(), _declaration) != m_localVariables.end();
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
eth::AssemblyItem CompilerContext::getFunctionEntryLabel(FunctionDefinition const& _function) const
|
eth::AssemblyItem CompilerContext::getFunctionEntryLabel(FunctionDefinition const& _function) const
|
||||||
@ -57,5 +60,28 @@ eth::AssemblyItem CompilerContext::getFunctionEntryLabel(FunctionDefinition cons
|
|||||||
return res->second.tag();
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,19 +38,28 @@ namespace solidity {
|
|||||||
class CompilerContext
|
class CompilerContext
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CompilerContext() {}
|
CompilerContext(): m_stateVariablesSize(0) {}
|
||||||
|
|
||||||
|
void addStateVariable(VariableDeclaration const& _declaration);
|
||||||
void startNewFunction() { m_localVariables.clear(); m_asm.setDeposit(0); }
|
void startNewFunction() { m_localVariables.clear(); m_asm.setDeposit(0); }
|
||||||
void initializeLocalVariables(unsigned _numVariables);
|
void initializeLocalVariables(unsigned _numVariables);
|
||||||
void addVariable(VariableDeclaration const& _declaration) { m_localVariables.push_back(&_declaration); }
|
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())); }
|
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); }
|
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
|
/// Appends a JUMPI instruction to a new tag and @returns the tag
|
||||||
eth::AssemblyItem appendConditionalJump() { return m_asm.appendJumpI().tag(); }
|
eth::AssemblyItem appendConditionalJump() { return m_asm.appendJumpI().tag(); }
|
||||||
/// Appends a JUMPI instruction to @a _tag
|
/// Appends a JUMPI instruction to @a _tag
|
||||||
@ -79,10 +88,14 @@ public:
|
|||||||
private:
|
private:
|
||||||
eth::Assembly m_asm;
|
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.
|
/// Offsets of local variables on the stack.
|
||||||
std::vector<Declaration const*> m_localVariables;
|
std::vector<Declaration const*> m_localVariables;
|
||||||
/// Labels pointing to the entry points of funcitons.
|
/// Labels pointing to the entry points of funcitons.
|
||||||
std::map<FunctionDefinition const*, eth::AssemblyItem> m_functionEntryLabels;
|
std::map<Declaration const*, eth::AssemblyItem> m_functionEntryLabels;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -46,23 +46,16 @@ void ExpressionCompiler::appendTypeConversion(CompilerContext& _context,
|
|||||||
|
|
||||||
bool ExpressionCompiler::visit(Assignment& _assignment)
|
bool ExpressionCompiler::visit(Assignment& _assignment)
|
||||||
{
|
{
|
||||||
m_currentLValue = nullptr;
|
_assignment.getRightHandSide().accept(*this);
|
||||||
|
appendTypeConversion(*_assignment.getRightHandSide().getType(), *_assignment.getType());
|
||||||
Expression& rightHandSide = _assignment.getRightHandSide();
|
m_currentLValue.reset();
|
||||||
rightHandSide.accept(*this);
|
|
||||||
Type const& resultType = *_assignment.getType();
|
|
||||||
appendTypeConversion(*rightHandSide.getType(), resultType);
|
|
||||||
_assignment.getLeftHandSide().accept(*this);
|
_assignment.getLeftHandSide().accept(*this);
|
||||||
|
|
||||||
Token::Value op = _assignment.getAssignmentOperator();
|
Token::Value op = _assignment.getAssignmentOperator();
|
||||||
if (op != Token::ASSIGN)
|
if (op != Token::ASSIGN) // compound assignment
|
||||||
{
|
appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.getType());
|
||||||
// compound assignment
|
|
||||||
m_context << eth::Instruction::SWAP1;
|
|
||||||
appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), resultType);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
m_context << eth::Instruction::POP; //@todo do not retrieve the value in the first place
|
m_context << eth::Instruction::POP;
|
||||||
|
|
||||||
storeInLValue(_assignment);
|
storeInLValue(_assignment);
|
||||||
return false;
|
return false;
|
||||||
@ -99,10 +92,7 @@ void ExpressionCompiler::endVisit(UnaryOperation& _unaryOperation)
|
|||||||
m_context << eth::Instruction::ADD;
|
m_context << eth::Instruction::ADD;
|
||||||
else
|
else
|
||||||
m_context << eth::Instruction::SWAP1 << eth::Instruction::SUB; // @todo avoid the swap
|
m_context << eth::Instruction::SWAP1 << eth::Instruction::SUB; // @todo avoid the swap
|
||||||
if (_unaryOperation.isPrefixOperation())
|
storeInLValue(_unaryOperation, !_unaryOperation.isPrefixOperation());
|
||||||
storeInLValue(_unaryOperation);
|
|
||||||
else
|
|
||||||
moveToLValue(_unaryOperation);
|
|
||||||
break;
|
break;
|
||||||
case Token::ADD: // +
|
case Token::ADD: // +
|
||||||
// unary add, so basically no-op
|
// unary add, so basically no-op
|
||||||
@ -123,11 +113,8 @@ bool ExpressionCompiler::visit(BinaryOperation& _binaryOperation)
|
|||||||
Type const& commonType = _binaryOperation.getCommonType();
|
Type const& commonType = _binaryOperation.getCommonType();
|
||||||
Token::Value const op = _binaryOperation.getOperator();
|
Token::Value const op = _binaryOperation.getOperator();
|
||||||
|
|
||||||
if (op == Token::AND || op == Token::OR)
|
if (op == Token::AND || op == Token::OR) // special case: short-circuiting
|
||||||
{
|
|
||||||
// special case: short-circuiting
|
|
||||||
appendAndOrOperatorCode(_binaryOperation);
|
appendAndOrOperatorCode(_binaryOperation);
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bool cleanupNeeded = false;
|
bool cleanupNeeded = false;
|
||||||
@ -135,10 +122,10 @@ bool ExpressionCompiler::visit(BinaryOperation& _binaryOperation)
|
|||||||
if (Token::isCompareOp(op) || op == Token::DIV || op == Token::MOD)
|
if (Token::isCompareOp(op) || op == Token::DIV || op == Token::MOD)
|
||||||
cleanupNeeded = true;
|
cleanupNeeded = true;
|
||||||
|
|
||||||
leftExpression.accept(*this);
|
|
||||||
appendTypeConversion(*leftExpression.getType(), commonType, cleanupNeeded);
|
|
||||||
rightExpression.accept(*this);
|
rightExpression.accept(*this);
|
||||||
appendTypeConversion(*rightExpression.getType(), commonType, cleanupNeeded);
|
appendTypeConversion(*rightExpression.getType(), commonType, cleanupNeeded);
|
||||||
|
leftExpression.accept(*this);
|
||||||
|
appendTypeConversion(*leftExpression.getType(), commonType, cleanupNeeded);
|
||||||
if (Token::isCompareOp(op))
|
if (Token::isCompareOp(op))
|
||||||
appendCompareOperatorCode(op, commonType);
|
appendCompareOperatorCode(op, commonType);
|
||||||
else
|
else
|
||||||
@ -164,9 +151,13 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
|
|||||||
{
|
{
|
||||||
// Calling convention: Caller pushes return address and arguments
|
// Calling convention: Caller pushes return address and arguments
|
||||||
// Callee removes them and pushes return values
|
// Callee removes them and pushes return values
|
||||||
m_currentLValue = nullptr;
|
m_currentLValue.reset();
|
||||||
_functionCall.getExpression().accept(*this);
|
_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();
|
eth::AssemblyItem returnLabel = m_context.pushNewTag();
|
||||||
std::vector<ASTPointer<Expression>> const& arguments = _functionCall.getArguments();
|
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)
|
for (unsigned i = 0; i < arguments.size(); ++i)
|
||||||
{
|
{
|
||||||
arguments[i]->accept(*this);
|
arguments[i]->accept(*this);
|
||||||
appendTypeConversion(*arguments[i]->getType(),
|
appendTypeConversion(*arguments[i]->getType(), *function.getParameters()[i]->getType());
|
||||||
*function.getParameters()[i]->getType());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_context.appendJumpTo(m_context.getFunctionEntryLabel(function));
|
m_context.appendJumpTo(functionTag);
|
||||||
m_context << returnLabel;
|
m_context << returnLabel;
|
||||||
|
|
||||||
// callee adds return parameters, but removes arguments and return label
|
// callee adds return parameters, but removes arguments and return label
|
||||||
@ -205,24 +195,20 @@ void ExpressionCompiler::endVisit(IndexAccess&)
|
|||||||
|
|
||||||
void ExpressionCompiler::endVisit(Identifier& _identifier)
|
void ExpressionCompiler::endVisit(Identifier& _identifier)
|
||||||
{
|
{
|
||||||
m_currentLValue = _identifier.getReferencedDeclaration();
|
Declaration const* declaration = _identifier.getReferencedDeclaration();
|
||||||
switch (_identifier.getType()->getCategory())
|
if (m_context.isLocalVariable(declaration))
|
||||||
{
|
m_currentLValue = LValueLocation(LValueLocation::STACK,
|
||||||
case Type::Category::BOOL:
|
m_context.getBaseStackOffsetOfVariable(*declaration));
|
||||||
case Type::Category::INTEGER:
|
else if (m_context.isStateVariable(declaration))
|
||||||
case Type::Category::REAL:
|
m_currentLValue = LValueLocation(LValueLocation::STORAGE,
|
||||||
{
|
m_context.getStorageLocationOfVariable(*declaration));
|
||||||
//@todo we also have to check where to retrieve them from once we add storage variables
|
else if (m_context.isFunctionDefinition(declaration))
|
||||||
unsigned stackPos = stackPositionOfLValue();
|
m_currentLValue = LValueLocation(LValueLocation::CODE,
|
||||||
if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory
|
m_context.getFunctionEntryLabel(dynamic_cast<FunctionDefinition const&>(*declaration)).data());
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_identifier.getLocation())
|
else
|
||||||
<< errinfo_comment("Stack too deep."));
|
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not supported or identifier not found."));
|
||||||
m_context << eth::dupInstruction(stackPos + 1);
|
|
||||||
break;
|
retrieveLValueValue(_identifier);
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExpressionCompiler::endVisit(Literal& _literal)
|
void ExpressionCompiler::endVisit(Literal& _literal)
|
||||||
@ -267,23 +253,21 @@ void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type
|
|||||||
IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
|
IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
|
||||||
bool const isSigned = type.isSigned();
|
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)
|
switch (_operator)
|
||||||
{
|
{
|
||||||
case Token::GTE:
|
case Token::GTE:
|
||||||
m_context << (isSigned ? eth::Instruction::SGT : eth::Instruction::GT)
|
|
||||||
<< eth::Instruction::ISZERO;
|
|
||||||
break;
|
|
||||||
case Token::LTE:
|
|
||||||
m_context << (isSigned ? eth::Instruction::SLT : eth::Instruction::LT)
|
m_context << (isSigned ? eth::Instruction::SLT : eth::Instruction::LT)
|
||||||
<< eth::Instruction::ISZERO;
|
<< eth::Instruction::ISZERO;
|
||||||
break;
|
break;
|
||||||
|
case Token::LTE:
|
||||||
|
m_context << (isSigned ? eth::Instruction::SGT : eth::Instruction::GT)
|
||||||
|
<< eth::Instruction::ISZERO;
|
||||||
|
break;
|
||||||
case Token::GT:
|
case Token::GT:
|
||||||
m_context << (isSigned ? eth::Instruction::SLT : eth::Instruction::LT);
|
m_context << (isSigned ? eth::Instruction::SGT : eth::Instruction::GT);
|
||||||
break;
|
break;
|
||||||
case Token::LT:
|
case Token::LT:
|
||||||
m_context << (isSigned ? eth::Instruction::SGT : eth::Instruction::GT);
|
m_context << (isSigned ? eth::Instruction::SLT : eth::Instruction::LT);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown comparison operator."));
|
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;
|
m_context << eth::Instruction::ADD;
|
||||||
break;
|
break;
|
||||||
case Token::SUB:
|
case Token::SUB:
|
||||||
m_context << eth::Instruction::SWAP1 << eth::Instruction::SUB;
|
m_context << eth::Instruction::SUB;
|
||||||
break;
|
break;
|
||||||
case Token::MUL:
|
case Token::MUL:
|
||||||
m_context << eth::Instruction::MUL;
|
m_context << eth::Instruction::MUL;
|
||||||
break;
|
break;
|
||||||
case Token::DIV:
|
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;
|
break;
|
||||||
case Token::MOD:
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown arithmetic operator."));
|
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)
|
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
|
// For a type extension, we need to remove all higher-order bits that we might have ignored in
|
||||||
// higher-order bits that we might have ignored in previous operations.
|
// previous operations.
|
||||||
// @todo: store in the AST whether the operand might have "dirty" higher
|
// @todo: store in the AST whether the operand might have "dirty" higher order bits
|
||||||
// order bits
|
|
||||||
|
|
||||||
if (_typeOnStack == _targetType && !_cleanupNeeded)
|
if (_typeOnStack == _targetType && !_cleanupNeeded)
|
||||||
return;
|
return;
|
||||||
@ -388,31 +371,65 @@ void ExpressionCompiler::appendHighBitsCleanup(IntegerType const& _typeOnStack)
|
|||||||
m_context << ((u256(1) << _typeOnStack.getNumBits()) - 1) << eth::Instruction::AND;
|
m_context << ((u256(1) << _typeOnStack.getNumBits()) - 1) << eth::Instruction::AND;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExpressionCompiler::storeInLValue(Expression const& _expression)
|
void ExpressionCompiler::retrieveLValueValue(Expression const& _expression)
|
||||||
{
|
{
|
||||||
moveToLValue(_expression);
|
switch (m_currentLValue.locationType)
|
||||||
unsigned stackPos = stackPositionOfLValue();
|
{
|
||||||
if (stackPos > 16)
|
case LValueLocation::CODE:
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
|
// not stored on the stack
|
||||||
<< errinfo_comment("Stack too deep."));
|
break;
|
||||||
m_context << eth::dupInstruction(stackPos + 1);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExpressionCompiler::moveToLValue(Expression const& _expression)
|
void ExpressionCompiler::storeInLValue(Expression const& _expression, bool _move)
|
||||||
{
|
{
|
||||||
unsigned stackPos = stackPositionOfLValue();
|
switch (m_currentLValue.locationType)
|
||||||
if (stackPos > 16)
|
{
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
|
case LValueLocation::STACK:
|
||||||
<< errinfo_comment("Stack too deep."));
|
{
|
||||||
else if (stackPos > 0)
|
unsigned stackPos = m_context.baseToCurrentStackOffset(unsigned(m_currentLValue.location));
|
||||||
m_context << eth::swapInstruction(stackPos) << eth::Instruction::POP;
|
if (stackPos > 16)
|
||||||
}
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
|
||||||
|
<< errinfo_comment("Stack too deep."));
|
||||||
unsigned ExpressionCompiler::stackPositionOfLValue() const
|
else if (stackPos > 0)
|
||||||
{
|
m_context << eth::swapInstruction(stackPos) << eth::Instruction::POP;
|
||||||
if (asserts(m_currentLValue))
|
if (!_move)
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("LValue not available on request."));
|
retrieveLValueValue(_expression);
|
||||||
return m_context.getStackPositionOfVariable(*m_currentLValue);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -20,18 +20,25 @@
|
|||||||
* Solidity AST to EVM bytecode compiler for expressions.
|
* Solidity AST to EVM bytecode compiler for expressions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <libdevcore/Common.h>
|
||||||
#include <libsolidity/ASTVisitor.h>
|
#include <libsolidity/ASTVisitor.h>
|
||||||
|
|
||||||
namespace dev {
|
namespace dev {
|
||||||
|
namespace eth
|
||||||
|
{
|
||||||
|
class AssemblyItem; // forward
|
||||||
|
}
|
||||||
namespace solidity {
|
namespace solidity {
|
||||||
|
|
||||||
class CompilerContext; // forward
|
class CompilerContext; // forward
|
||||||
class Type; // forward
|
class Type; // forward
|
||||||
class IntegerType; // 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
|
* Compiler for expressions, i.e. converts an AST tree whose root is an Expression into a stream
|
||||||
/// unit.
|
* of EVM instructions. It needs a compiler context that is the same for the whole compilation
|
||||||
|
* unit.
|
||||||
|
*/
|
||||||
class ExpressionCompiler: private ASTVisitor
|
class ExpressionCompiler: private ASTVisitor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -42,7 +49,7 @@ public:
|
|||||||
static void appendTypeConversion(CompilerContext& _context, Type const& _typeOnStack, Type const& _targetType);
|
static void appendTypeConversion(CompilerContext& _context, Type const& _typeOnStack, Type const& _targetType);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ExpressionCompiler(CompilerContext& _compilerContext): m_currentLValue(nullptr), m_context(_compilerContext) {}
|
ExpressionCompiler(CompilerContext& _compilerContext): m_context(_compilerContext) {}
|
||||||
|
|
||||||
virtual bool visit(Assignment& _assignment) override;
|
virtual bool visit(Assignment& _assignment) override;
|
||||||
virtual void endVisit(UnaryOperation& _unaryOperation) override;
|
virtual void endVisit(UnaryOperation& _unaryOperation) override;
|
||||||
@ -72,15 +79,36 @@ private:
|
|||||||
//// Appends code that cleans higher-order bits for integer types.
|
//// Appends code that cleans higher-order bits for integer types.
|
||||||
void appendHighBitsCleanup(IntegerType const& _typeOnStack);
|
void appendHighBitsCleanup(IntegerType const& _typeOnStack);
|
||||||
|
|
||||||
/// Stores the value on top of the stack in the current lvalue and copies that value to the
|
/// Copies the value of the current lvalue to the top of the stack.
|
||||||
/// top of the stack again
|
void retrieveLValueValue(Expression const& _expression);
|
||||||
void storeInLValue(Expression const& _expression);
|
/// Stores the value on top of the stack in the current lvalue. Removes it from the stack if
|
||||||
/// The same as storeInLValue but do not again retrieve the value to the top of the stack.
|
/// @a _move is true.
|
||||||
void moveToLValue(Expression const& _expression);
|
void storeInLValue(Expression const& _expression, bool _move = false);
|
||||||
/// Returns the position of @a m_currentLValue in the stack, where 0 is the top of the stack.
|
|
||||||
unsigned stackPositionOfLValue() const;
|
|
||||||
|
|
||||||
Declaration* m_currentLValue;
|
/**
|
||||||
|
* 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;
|
CompilerContext& m_context;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
22
Types.cpp
22
Types.cpp
@ -56,7 +56,6 @@ shared_ptr<Type> Type::fromElementaryTypeName(Token::Value _typeToken)
|
|||||||
else
|
else
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " +
|
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " +
|
||||||
std::string(Token::toString(_typeToken)) + " to type."));
|
std::string(Token::toString(_typeToken)) + " to type."));
|
||||||
return shared_ptr<Type>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
shared_ptr<Type> Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName)
|
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&)
|
shared_ptr<Type> Type::fromMapping(Mapping const&)
|
||||||
{
|
{
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Mapping types not yet implemented."));
|
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Mapping types not yet implemented."));
|
||||||
return shared_ptr<Type>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
shared_ptr<Type> Type::forLiteral(Literal const& _literal)
|
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)
|
m_bits(_bits), m_modifier(_modifier)
|
||||||
{
|
{
|
||||||
if (isAddress())
|
if (isAddress())
|
||||||
_bits = 160;
|
m_bits = 160;
|
||||||
if (asserts(_bits > 0 && _bits <= 256 && _bits % 8 == 0))
|
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)));
|
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;
|
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
|
bool StructType::operator==(Type const& _other) const
|
||||||
{
|
{
|
||||||
if (_other.getCategory() != getCategory())
|
if (_other.getCategory() != getCategory())
|
||||||
@ -215,6 +221,14 @@ bool StructType::operator==(Type const& _other) const
|
|||||||
return other.m_struct == m_struct;
|
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
|
bool FunctionType::operator==(Type const& _other) const
|
||||||
{
|
{
|
||||||
if (_other.getCategory() != getCategory())
|
if (_other.getCategory() != getCategory())
|
||||||
|
16
Types.h
16
Types.h
@ -75,6 +75,9 @@ public:
|
|||||||
/// @returns number of bytes used by this type when encoded for CALL, or 0 if the encoding
|
/// @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.
|
/// is not a simple big-endian encoding or the type cannot be stored on the stack.
|
||||||
virtual unsigned getCalldataEncodedSize() const { return 0; }
|
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 std::string toString() const = 0;
|
||||||
virtual u256 literalValue(Literal const&) const
|
virtual u256 literalValue(Literal const&) const
|
||||||
@ -157,7 +160,7 @@ public:
|
|||||||
ContractType(ContractDefinition const& _contract): m_contract(_contract) {}
|
ContractType(ContractDefinition const& _contract): m_contract(_contract) {}
|
||||||
|
|
||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
|
virtual u256 getStorageSize() const;
|
||||||
virtual std::string toString() const override { return "contract{...}"; }
|
virtual std::string toString() const override { return "contract{...}"; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -178,7 +181,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
|
virtual u256 getStorageSize() const;
|
||||||
virtual std::string toString() const override { return "struct{...}"; }
|
virtual std::string toString() const override { return "struct{...}"; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -196,9 +199,9 @@ public:
|
|||||||
|
|
||||||
FunctionDefinition const& getFunction() const { return m_function; }
|
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 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:
|
private:
|
||||||
FunctionDefinition const& m_function;
|
FunctionDefinition const& m_function;
|
||||||
@ -212,9 +215,9 @@ class MappingType: public Type
|
|||||||
public:
|
public:
|
||||||
virtual Category getCategory() const override { return Category::MAPPING; }
|
virtual Category getCategory() const override { return Category::MAPPING; }
|
||||||
MappingType() {}
|
MappingType() {}
|
||||||
virtual std::string toString() const override { return "mapping(...=>...)"; }
|
|
||||||
|
|
||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
|
virtual std::string toString() const override { return "mapping(...=>...)"; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<Type const> m_keyType;
|
std::shared_ptr<Type const> m_keyType;
|
||||||
@ -232,6 +235,7 @@ public:
|
|||||||
VoidType() {}
|
VoidType() {}
|
||||||
|
|
||||||
virtual std::string toString() const override { return "void"; }
|
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; }
|
std::shared_ptr<Type const> const& getActualType() const { return m_actualType; }
|
||||||
|
|
||||||
virtual bool operator==(Type const& _other) const override;
|
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() + ")"; }
|
virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Loading…
Reference in New Issue
Block a user