mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge branch 'develop' of https://github.com/ethereum/cpp-ethereum into build_enhancement
Conflicts: windows/LibEthereum.vcxproj windows/LibEthereum.vcxproj.filters Solved by removing files (not necessary anymore)
This commit is contained in:
commit
000e46cb24
36
Compiler.cpp
36
Compiler.cpp
@ -109,8 +109,8 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
|
|||||||
callDataUnpackerEntryPoints.push_back(m_context.newTag());
|
callDataUnpackerEntryPoints.push_back(m_context.newTag());
|
||||||
m_context << eth::dupInstruction(2) << eth::dupInstruction(2) << eth::Instruction::EQ;
|
m_context << eth::dupInstruction(2) << eth::dupInstruction(2) << eth::Instruction::EQ;
|
||||||
m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.back());
|
m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.back());
|
||||||
m_context << eth::dupInstruction(4) << eth::Instruction::ADD;
|
if (funid < interfaceFunctions.size() - 1)
|
||||||
//@todo avoid the last ADD (or remove it in the optimizer)
|
m_context << eth::dupInstruction(4) << eth::Instruction::ADD;
|
||||||
}
|
}
|
||||||
m_context << eth::Instruction::STOP; // function not found
|
m_context << eth::Instruction::STOP; // function not found
|
||||||
|
|
||||||
@ -130,21 +130,17 @@ unsigned Compiler::appendCalldataUnpacker(FunctionDefinition const& _function, b
|
|||||||
{
|
{
|
||||||
// We do not check the calldata size, everything is zero-padded.
|
// We do not check the calldata size, everything is zero-padded.
|
||||||
unsigned dataOffset = 1;
|
unsigned dataOffset = 1;
|
||||||
eth::Instruction load = _fromMemory ? eth::Instruction::MLOAD : eth::Instruction::CALLDATALOAD;
|
|
||||||
|
|
||||||
//@todo this can be done more efficiently, saving some CALLDATALOAD calls
|
//@todo this can be done more efficiently, saving some CALLDATALOAD calls
|
||||||
for (ASTPointer<VariableDeclaration> const& var: _function.getParameters())
|
for (ASTPointer<VariableDeclaration> const& var: _function.getParameters())
|
||||||
{
|
{
|
||||||
unsigned const numBytes = var->getType()->getCalldataEncodedSize();
|
unsigned const numBytes = var->getType()->getCalldataEncodedSize();
|
||||||
if (numBytes == 0 || numBytes > 32)
|
if (numBytes > 32)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError()
|
BOOST_THROW_EXCEPTION(CompilerError()
|
||||||
<< errinfo_sourceLocation(var->getLocation())
|
<< errinfo_sourceLocation(var->getLocation())
|
||||||
<< errinfo_comment("Type " + var->getType()->toString() + " not yet supported."));
|
<< errinfo_comment("Type " + var->getType()->toString() + " not yet supported."));
|
||||||
if (numBytes == 32)
|
bool leftAligned = var->getType()->getCategory() == Type::Category::STRING;
|
||||||
m_context << u256(dataOffset) << load;
|
CompilerUtils(m_context).loadFromMemory(dataOffset, numBytes, leftAligned, !_fromMemory);
|
||||||
else
|
|
||||||
m_context << (u256(1) << ((32 - numBytes) * 8)) << u256(dataOffset)
|
|
||||||
<< load << eth::Instruction::DIV;
|
|
||||||
dataOffset += numBytes;
|
dataOffset += numBytes;
|
||||||
}
|
}
|
||||||
return dataOffset;
|
return dataOffset;
|
||||||
@ -160,14 +156,13 @@ void Compiler::appendReturnValuePacker(FunctionDefinition const& _function)
|
|||||||
{
|
{
|
||||||
Type const& paramType = *parameters[i]->getType();
|
Type const& paramType = *parameters[i]->getType();
|
||||||
unsigned numBytes = paramType.getCalldataEncodedSize();
|
unsigned numBytes = paramType.getCalldataEncodedSize();
|
||||||
if (numBytes == 0 || numBytes > 32)
|
if (numBytes > 32)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError()
|
BOOST_THROW_EXCEPTION(CompilerError()
|
||||||
<< errinfo_sourceLocation(parameters[i]->getLocation())
|
<< errinfo_sourceLocation(parameters[i]->getLocation())
|
||||||
<< errinfo_comment("Type " + paramType.toString() + " not yet supported."));
|
<< errinfo_comment("Type " + paramType.toString() + " not yet supported."));
|
||||||
CompilerUtils(m_context).copyToStackTop(stackDepth, paramType);
|
CompilerUtils(m_context).copyToStackTop(stackDepth, paramType);
|
||||||
if (numBytes != 32)
|
bool const leftAligned = paramType.getCategory() == Type::Category::STRING;
|
||||||
m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL;
|
CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, leftAligned);
|
||||||
m_context << u256(dataOffset) << eth::Instruction::MSTORE;
|
|
||||||
stackDepth -= paramType.getSizeOnStack();
|
stackDepth -= paramType.getSizeOnStack();
|
||||||
dataOffset += numBytes;
|
dataOffset += numBytes;
|
||||||
}
|
}
|
||||||
@ -246,7 +241,7 @@ bool Compiler::visit(FunctionDefinition const& _function)
|
|||||||
|
|
||||||
bool Compiler::visit(IfStatement const& _ifStatement)
|
bool Compiler::visit(IfStatement const& _ifStatement)
|
||||||
{
|
{
|
||||||
ExpressionCompiler::compileExpression(m_context, _ifStatement.getCondition());
|
compileExpression(_ifStatement.getCondition());
|
||||||
eth::AssemblyItem trueTag = m_context.appendConditionalJump();
|
eth::AssemblyItem trueTag = m_context.appendConditionalJump();
|
||||||
if (_ifStatement.getFalseStatement())
|
if (_ifStatement.getFalseStatement())
|
||||||
_ifStatement.getFalseStatement()->accept(*this);
|
_ifStatement.getFalseStatement()->accept(*this);
|
||||||
@ -265,7 +260,7 @@ bool Compiler::visit(WhileStatement const& _whileStatement)
|
|||||||
m_breakTags.push_back(loopEnd);
|
m_breakTags.push_back(loopEnd);
|
||||||
|
|
||||||
m_context << loopStart;
|
m_context << loopStart;
|
||||||
ExpressionCompiler::compileExpression(m_context, _whileStatement.getCondition());
|
compileExpression(_whileStatement.getCondition());
|
||||||
m_context << eth::Instruction::ISZERO;
|
m_context << eth::Instruction::ISZERO;
|
||||||
m_context.appendConditionalJumpTo(loopEnd);
|
m_context.appendConditionalJumpTo(loopEnd);
|
||||||
|
|
||||||
@ -298,7 +293,7 @@ bool Compiler::visit(Return const& _return)
|
|||||||
//@todo modifications are needed to make this work with functions returning multiple values
|
//@todo modifications are needed to make this work with functions returning multiple values
|
||||||
if (Expression const* expression = _return.getExpression())
|
if (Expression const* expression = _return.getExpression())
|
||||||
{
|
{
|
||||||
ExpressionCompiler::compileExpression(m_context, *expression);
|
compileExpression(*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());
|
||||||
|
|
||||||
@ -312,7 +307,7 @@ bool Compiler::visit(VariableDefinition const& _variableDefinition)
|
|||||||
{
|
{
|
||||||
if (Expression const* expression = _variableDefinition.getExpression())
|
if (Expression const* expression = _variableDefinition.getExpression())
|
||||||
{
|
{
|
||||||
ExpressionCompiler::compileExpression(m_context, *expression);
|
compileExpression(*expression);
|
||||||
ExpressionCompiler::appendTypeConversion(m_context,
|
ExpressionCompiler::appendTypeConversion(m_context,
|
||||||
*expression->getType(),
|
*expression->getType(),
|
||||||
*_variableDefinition.getDeclaration().getType());
|
*_variableDefinition.getDeclaration().getType());
|
||||||
@ -324,10 +319,15 @@ bool Compiler::visit(VariableDefinition const& _variableDefinition)
|
|||||||
bool Compiler::visit(ExpressionStatement const& _expressionStatement)
|
bool Compiler::visit(ExpressionStatement const& _expressionStatement)
|
||||||
{
|
{
|
||||||
Expression const& expression = _expressionStatement.getExpression();
|
Expression const& expression = _expressionStatement.getExpression();
|
||||||
ExpressionCompiler::compileExpression(m_context, expression);
|
compileExpression(expression);
|
||||||
CompilerUtils(m_context).popStackElement(*expression.getType());
|
CompilerUtils(m_context).popStackElement(*expression.getType());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Compiler::compileExpression(Expression const& _expression)
|
||||||
|
{
|
||||||
|
ExpressionCompiler::compileExpression(m_context, _expression, m_optimize);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,10 +30,10 @@ namespace solidity {
|
|||||||
class Compiler: private ASTConstVisitor
|
class Compiler: private ASTConstVisitor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Compiler(): m_returnTag(m_context.newTag()) {}
|
explicit Compiler(bool _optimize = false): m_optimize(_optimize), m_returnTag(m_context.newTag()) {}
|
||||||
|
|
||||||
void compileContract(ContractDefinition const& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals);
|
void compileContract(ContractDefinition const& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals);
|
||||||
bytes getAssembledBytecode(bool _optimize = false) { return m_context.getAssembledBytecode(_optimize); }
|
bytes getAssembledBytecode() { return m_context.getAssembledBytecode(m_optimize); }
|
||||||
void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); }
|
void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -57,7 +57,9 @@ private:
|
|||||||
virtual bool visit(VariableDefinition const& _variableDefinition) override;
|
virtual bool visit(VariableDefinition const& _variableDefinition) override;
|
||||||
virtual bool visit(ExpressionStatement const& _expressionStatement) override;
|
virtual bool visit(ExpressionStatement const& _expressionStatement) override;
|
||||||
|
|
||||||
|
void compileExpression(Expression const& _expression);
|
||||||
|
|
||||||
|
bool const m_optimize;
|
||||||
CompilerContext m_context;
|
CompilerContext m_context;
|
||||||
std::vector<eth::AssemblyItem> m_breakTags; ///< tag to jump to for a "break" statement
|
std::vector<eth::AssemblyItem> m_breakTags; ///< tag to jump to for a "break" statement
|
||||||
std::vector<eth::AssemblyItem> m_continueTags; ///< tag to jump to for a "continue" statement
|
std::vector<eth::AssemblyItem> m_continueTags; ///< tag to jump to for a "continue" statement
|
||||||
|
@ -101,10 +101,10 @@ void CompilerStack::compile(bool _optimize)
|
|||||||
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
||||||
{
|
{
|
||||||
m_globalContext->setCurrentContract(*contract);
|
m_globalContext->setCurrentContract(*contract);
|
||||||
shared_ptr<Compiler> compiler = make_shared<Compiler>();
|
shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize);
|
||||||
compiler->compileContract(*contract, m_globalContext->getMagicVariables());
|
compiler->compileContract(*contract, m_globalContext->getMagicVariables());
|
||||||
Contract& compiledContract = m_contracts[contract->getName()];
|
Contract& compiledContract = m_contracts[contract->getName()];
|
||||||
compiledContract.bytecode = compiler->getAssembledBytecode(_optimize);
|
compiledContract.bytecode = compiler->getAssembledBytecode();
|
||||||
compiledContract.compiler = move(compiler);
|
compiledContract.compiler = move(compiler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,46 @@ namespace dev
|
|||||||
namespace solidity
|
namespace solidity
|
||||||
{
|
{
|
||||||
|
|
||||||
|
void CompilerUtils::loadFromMemory(unsigned _offset, unsigned _bytes, bool _leftAligned, bool _fromCalldata)
|
||||||
|
{
|
||||||
|
if (_bytes == 0)
|
||||||
|
{
|
||||||
|
m_context << u256(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
eth::Instruction load = _fromCalldata ? eth::Instruction::CALLDATALOAD : eth::Instruction::MLOAD;
|
||||||
|
if (asserts(_bytes <= 32))
|
||||||
|
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Memory load of more than 32 bytes requested."));
|
||||||
|
if (_bytes == 32)
|
||||||
|
m_context << u256(_offset) << load;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// load data and add leading or trailing zeros by dividing/multiplying depending on alignment
|
||||||
|
u256 shiftFactor = u256(1) << ((32 - _bytes) * 8);
|
||||||
|
m_context << shiftFactor;
|
||||||
|
if (_leftAligned)
|
||||||
|
m_context << eth::Instruction::DUP1;
|
||||||
|
m_context << u256(_offset) << load << eth::Instruction::DIV;
|
||||||
|
if (_leftAligned)
|
||||||
|
m_context << eth::Instruction::MUL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompilerUtils::storeInMemory(unsigned _offset, unsigned _bytes, bool _leftAligned)
|
||||||
|
{
|
||||||
|
if (_bytes == 0)
|
||||||
|
{
|
||||||
|
m_context << eth::Instruction::POP;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (asserts(_bytes <= 32))
|
||||||
|
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Memory store of more than 32 bytes requested."));
|
||||||
|
if (_bytes != 32 && !_leftAligned)
|
||||||
|
// shift the value accordingly before storing
|
||||||
|
m_context << (u256(1) << ((32 - _bytes) * 8)) << eth::Instruction::MUL;
|
||||||
|
m_context << u256(_offset) << eth::Instruction::MSTORE;
|
||||||
|
}
|
||||||
|
|
||||||
void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
|
void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
|
||||||
{
|
{
|
||||||
unsigned const stackPosition = m_context.baseToCurrentStackOffset(m_context.getBaseStackOffsetOfVariable(_variable));
|
unsigned const stackPosition = m_context.baseToCurrentStackOffset(m_context.getBaseStackOffsetOfVariable(_variable));
|
||||||
|
@ -35,6 +35,18 @@ class CompilerUtils
|
|||||||
public:
|
public:
|
||||||
CompilerUtils(CompilerContext& _context): m_context(_context) {}
|
CompilerUtils(CompilerContext& _context): m_context(_context) {}
|
||||||
|
|
||||||
|
/// Loads data from memory to the stack.
|
||||||
|
/// @param _offset offset in memory (or calldata)
|
||||||
|
/// @param _bytes number of bytes to load
|
||||||
|
/// @param _leftAligned if true, store left aligned on stack (otherwise right aligned)
|
||||||
|
/// @param _fromCalldata if true, load from calldata, not from memory
|
||||||
|
void loadFromMemory(unsigned _offset, unsigned _bytes = 32, bool _leftAligned = false, bool _fromCalldata = false);
|
||||||
|
/// Stores data from stack in memory.
|
||||||
|
/// @param _offset offset in memory
|
||||||
|
/// @param _bytes number of bytes to store
|
||||||
|
/// @param _leftAligned if true, data is left aligned on stack (otherwise right aligned)
|
||||||
|
void storeInMemory(unsigned _offset, unsigned _bytes = 32, bool _leftAligned = false);
|
||||||
|
|
||||||
/// Moves the value that is at the top of the stack to a stack variable.
|
/// Moves the value that is at the top of the stack to a stack variable.
|
||||||
void moveToStackVariable(VariableDeclaration const& _variable);
|
void moveToStackVariable(VariableDeclaration const& _variable);
|
||||||
/// Copies a variable of type @a _type from a stack depth of @a _stackDepth to the top of the stack.
|
/// Copies a variable of type @a _type from a stack depth of @a _stackDepth to the top of the stack.
|
||||||
|
@ -33,9 +33,9 @@ using namespace std;
|
|||||||
namespace dev {
|
namespace dev {
|
||||||
namespace solidity {
|
namespace solidity {
|
||||||
|
|
||||||
void ExpressionCompiler::compileExpression(CompilerContext& _context, Expression const& _expression)
|
void ExpressionCompiler::compileExpression(CompilerContext& _context, Expression const& _expression, bool _optimize)
|
||||||
{
|
{
|
||||||
ExpressionCompiler compiler(_context);
|
ExpressionCompiler compiler(_context, _optimize);
|
||||||
_expression.accept(compiler);
|
_expression.accept(compiler);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,10 +145,24 @@ bool ExpressionCompiler::visit(BinaryOperation const& _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;
|
||||||
|
|
||||||
rightExpression.accept(*this);
|
// for commutative operators, push the literal as late as possible to allow improved optimization
|
||||||
appendTypeConversion(*rightExpression.getType(), commonType, cleanupNeeded);
|
//@todo this has to be extended for literal expressions
|
||||||
leftExpression.accept(*this);
|
bool swap = (m_optimize && Token::isCommutativeOp(op) && dynamic_cast<Literal const*>(&rightExpression)
|
||||||
appendTypeConversion(*leftExpression.getType(), commonType, cleanupNeeded);
|
&& !dynamic_cast<Literal const*>(&leftExpression));
|
||||||
|
if (swap)
|
||||||
|
{
|
||||||
|
leftExpression.accept(*this);
|
||||||
|
appendTypeConversion(*leftExpression.getType(), commonType, cleanupNeeded);
|
||||||
|
rightExpression.accept(*this);
|
||||||
|
appendTypeConversion(*rightExpression.getType(), commonType, cleanupNeeded);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rightExpression.accept(*this);
|
||||||
|
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
|
||||||
@ -225,14 +239,14 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
BOOST_THROW_EXCEPTION(CompilerError()
|
BOOST_THROW_EXCEPTION(CompilerError()
|
||||||
<< errinfo_sourceLocation(arguments[i]->getLocation())
|
<< errinfo_sourceLocation(arguments[i]->getLocation())
|
||||||
<< errinfo_comment("Type " + type.toString() + " not yet supported."));
|
<< errinfo_comment("Type " + type.toString() + " not yet supported."));
|
||||||
if (numBytes != 32)
|
bool const leftAligned = type.getCategory() == Type::Category::STRING;
|
||||||
m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL;
|
CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, leftAligned);
|
||||||
m_context << u256(dataOffset) << eth::Instruction::MSTORE;
|
|
||||||
dataOffset += numBytes;
|
dataOffset += numBytes;
|
||||||
}
|
}
|
||||||
//@todo only return the first return value for now
|
//@todo only return the first return value for now
|
||||||
unsigned retSize = function.getReturnParameterTypes().empty() ? 0
|
Type const* firstType = function.getReturnParameterTypes().empty() ? nullptr :
|
||||||
: function.getReturnParameterTypes().front()->getCalldataEncodedSize();
|
function.getReturnParameterTypes().front().get();
|
||||||
|
unsigned retSize = firstType ? firstType->getCalldataEncodedSize() : 0;
|
||||||
// CALL arguments: outSize, outOff, inSize, inOff, value, addr, gas (stack top)
|
// CALL arguments: outSize, outOff, inSize, inOff, value, addr, gas (stack top)
|
||||||
m_context << u256(retSize) << u256(0) << u256(dataOffset) << u256(0) << u256(0);
|
m_context << u256(retSize) << u256(0) << u256(dataOffset) << u256(0) << u256(0);
|
||||||
_functionCall.getExpression().accept(*this); // pushes addr and function index
|
_functionCall.getExpression().accept(*this); // pushes addr and function index
|
||||||
@ -240,11 +254,11 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
<< u256(25) << eth::Instruction::GAS << eth::Instruction::SUB
|
<< u256(25) << eth::Instruction::GAS << eth::Instruction::SUB
|
||||||
<< eth::Instruction::CALL
|
<< eth::Instruction::CALL
|
||||||
<< eth::Instruction::POP; // @todo do not ignore failure indicator
|
<< eth::Instruction::POP; // @todo do not ignore failure indicator
|
||||||
if (retSize == 32)
|
if (retSize > 0)
|
||||||
m_context << u256(0) << eth::Instruction::MLOAD;
|
{
|
||||||
else if (retSize > 0)
|
bool const leftAligned = firstType->getCategory() == Type::Category::STRING;
|
||||||
m_context << (u256(1) << ((32 - retSize) * 8))
|
CompilerUtils(m_context).loadFromMemory(0, retSize, leftAligned);
|
||||||
<< u256(0) << eth::Instruction::MLOAD << eth::Instruction::DIV;
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Location::SEND:
|
case Location::SEND:
|
||||||
@ -267,7 +281,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
arguments.front()->accept(*this);
|
arguments.front()->accept(*this);
|
||||||
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
|
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
|
||||||
// @todo move this once we actually use memory
|
// @todo move this once we actually use memory
|
||||||
m_context << u256(0) << eth::Instruction::MSTORE << u256(32) << u256(0) << eth::Instruction::SHA3;
|
CompilerUtils(m_context).storeInMemory(0);
|
||||||
|
m_context << u256(32) << u256(0) << eth::Instruction::SHA3;
|
||||||
break;
|
break;
|
||||||
case Location::ECRECOVER:
|
case Location::ECRECOVER:
|
||||||
case Location::SHA256:
|
case Location::SHA256:
|
||||||
@ -283,13 +298,13 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
arguments[i]->accept(*this);
|
arguments[i]->accept(*this);
|
||||||
appendTypeConversion(*arguments[i]->getType(), *function.getParameterTypes()[i], true);
|
appendTypeConversion(*arguments[i]->getType(), *function.getParameterTypes()[i], true);
|
||||||
// @todo move this once we actually use memory
|
// @todo move this once we actually use memory
|
||||||
m_context << u256(i * 32) << eth::Instruction::MSTORE;
|
CompilerUtils(m_context).storeInMemory(i * 32);
|
||||||
}
|
}
|
||||||
m_context << u256(32) << u256(0) << u256(arguments.size() * 32) << u256(0) << u256(0)
|
m_context << u256(32) << u256(0) << u256(arguments.size() * 32) << u256(0) << u256(0)
|
||||||
<< contractAddress << u256(500) //@todo determine actual gas requirement
|
<< contractAddress << u256(500) //@todo determine actual gas requirement
|
||||||
<< eth::Instruction::CALL
|
<< eth::Instruction::CALL
|
||||||
<< eth::Instruction::POP
|
<< eth::Instruction::POP;
|
||||||
<< u256(0) << eth::Instruction::MLOAD;
|
CompilerUtils(m_context).loadFromMemory(0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -373,7 +388,8 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
|
|||||||
*dynamic_cast<MappingType const&>(*_indexAccess.getBaseExpression().getType()).getKeyType(),
|
*dynamic_cast<MappingType const&>(*_indexAccess.getBaseExpression().getType()).getKeyType(),
|
||||||
true);
|
true);
|
||||||
// @todo move this once we actually use memory
|
// @todo move this once we actually use memory
|
||||||
m_context << u256(32) << eth::Instruction::MSTORE << u256(0) << eth::Instruction::MSTORE;
|
CompilerUtils(m_context).storeInMemory(0);
|
||||||
|
CompilerUtils(m_context).storeInMemory(32);
|
||||||
m_context << u256(64) << u256(0) << eth::Instruction::SHA3;
|
m_context << u256(64) << u256(0) << eth::Instruction::SHA3;
|
||||||
|
|
||||||
m_currentLValue = LValue(m_context, LValue::STORAGE, *_indexAccess.getType());
|
m_currentLValue = LValue(m_context, LValue::STORAGE, *_indexAccess.getType());
|
||||||
@ -411,10 +427,11 @@ void ExpressionCompiler::endVisit(Literal const& _literal)
|
|||||||
{
|
{
|
||||||
case Type::Category::INTEGER:
|
case Type::Category::INTEGER:
|
||||||
case Type::Category::BOOL:
|
case Type::Category::BOOL:
|
||||||
|
case Type::Category::STRING:
|
||||||
m_context << _literal.getType()->literalValue(_literal);
|
m_context << _literal.getType()->literalValue(_literal);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Only integer and boolean literals implemented for now."));
|
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Only integer, boolean and string literals implemented for now."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -550,6 +567,11 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con
|
|||||||
return;
|
return;
|
||||||
if (_typeOnStack.getCategory() == Type::Category::INTEGER)
|
if (_typeOnStack.getCategory() == Type::Category::INTEGER)
|
||||||
appendHighBitsCleanup(dynamic_cast<IntegerType const&>(_typeOnStack));
|
appendHighBitsCleanup(dynamic_cast<IntegerType const&>(_typeOnStack));
|
||||||
|
else if (_typeOnStack.getCategory() == Type::Category::STRING)
|
||||||
|
{
|
||||||
|
// nothing to do, strings are high-order-bit-aligned
|
||||||
|
//@todo clear lower-order bytes if we allow explicit conversion to shorter strings
|
||||||
|
}
|
||||||
else if (_typeOnStack != _targetType)
|
else if (_typeOnStack != _targetType)
|
||||||
// All other types should not be convertible to non-equal types.
|
// All other types should not be convertible to non-equal types.
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid type conversion requested."));
|
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid type conversion requested."));
|
||||||
|
@ -35,6 +35,7 @@ namespace solidity {
|
|||||||
class CompilerContext;
|
class CompilerContext;
|
||||||
class Type;
|
class Type;
|
||||||
class IntegerType;
|
class IntegerType;
|
||||||
|
class StaticStringType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compiler for expressions, i.e. converts an AST tree whose root is an Expression into a stream
|
* Compiler for expressions, i.e. converts an AST tree whose root is an Expression into a stream
|
||||||
@ -45,14 +46,14 @@ class ExpressionCompiler: private ASTConstVisitor
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Compile the given @a _expression into the @a _context.
|
/// Compile the given @a _expression into the @a _context.
|
||||||
static void compileExpression(CompilerContext& _context, Expression const& _expression);
|
static void compileExpression(CompilerContext& _context, Expression const& _expression, bool _optimize = false);
|
||||||
|
|
||||||
/// Appends code to remove dirty higher order bits in case of an implicit promotion to a wider type.
|
/// Appends code to remove dirty higher order bits in case of an implicit promotion to a wider type.
|
||||||
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):
|
explicit ExpressionCompiler(CompilerContext& _compilerContext, bool _optimize = false):
|
||||||
m_context(_compilerContext), m_currentLValue(m_context) {}
|
m_optimize(_optimize), m_context(_compilerContext), m_currentLValue(m_context) {}
|
||||||
|
|
||||||
virtual bool visit(Assignment const& _assignment) override;
|
virtual bool visit(Assignment const& _assignment) override;
|
||||||
virtual void endVisit(UnaryOperation const& _unaryOperation) override;
|
virtual void endVisit(UnaryOperation const& _unaryOperation) override;
|
||||||
@ -75,7 +76,7 @@ private:
|
|||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
/// Appends an implicit or explicit type conversion. For now this comprises only erasing
|
/// Appends an implicit or explicit type conversion. For now this comprises only erasing
|
||||||
/// higher-order bits (@see appendHighBitCleanup) when widening integer types.
|
/// higher-order bits (@see appendHighBitCleanup) when widening integer.
|
||||||
/// If @a _cleanupNeeded, high order bits cleanup is also done if no type conversion would be
|
/// If @a _cleanupNeeded, high order bits cleanup is also done if no type conversion would be
|
||||||
/// necessary.
|
/// necessary.
|
||||||
void appendTypeConversion(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded = false);
|
void appendTypeConversion(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded = false);
|
||||||
@ -132,6 +133,7 @@ private:
|
|||||||
unsigned m_stackSize;
|
unsigned m_stackSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool m_optimize;
|
||||||
CompilerContext& m_context;
|
CompilerContext& m_context;
|
||||||
LValue m_currentLValue;
|
LValue m_currentLValue;
|
||||||
};
|
};
|
||||||
|
35
Token.h
35
Token.h
@ -269,6 +269,39 @@ namespace solidity
|
|||||||
K(ADDRESS, "address", 0) \
|
K(ADDRESS, "address", 0) \
|
||||||
K(BOOL, "bool", 0) \
|
K(BOOL, "bool", 0) \
|
||||||
K(STRING_TYPE, "string", 0) \
|
K(STRING_TYPE, "string", 0) \
|
||||||
|
K(STRING0, "string0", 0) \
|
||||||
|
K(STRING1, "string1", 0) \
|
||||||
|
K(STRING2, "string2", 0) \
|
||||||
|
K(STRING3, "string3", 0) \
|
||||||
|
K(STRING4, "string4", 0) \
|
||||||
|
K(STRING5, "string5", 0) \
|
||||||
|
K(STRING6, "string6", 0) \
|
||||||
|
K(STRING7, "string7", 0) \
|
||||||
|
K(STRING8, "string8", 0) \
|
||||||
|
K(STRING9, "string9", 0) \
|
||||||
|
K(STRING10, "string10", 0) \
|
||||||
|
K(STRING11, "string11", 0) \
|
||||||
|
K(STRING12, "string12", 0) \
|
||||||
|
K(STRING13, "string13", 0) \
|
||||||
|
K(STRING14, "string14", 0) \
|
||||||
|
K(STRING15, "string15", 0) \
|
||||||
|
K(STRING16, "string16", 0) \
|
||||||
|
K(STRING17, "string17", 0) \
|
||||||
|
K(STRING18, "string18", 0) \
|
||||||
|
K(STRING19, "string19", 0) \
|
||||||
|
K(STRING20, "string20", 0) \
|
||||||
|
K(STRING21, "string21", 0) \
|
||||||
|
K(STRING22, "string22", 0) \
|
||||||
|
K(STRING23, "string23", 0) \
|
||||||
|
K(STRING24, "string24", 0) \
|
||||||
|
K(STRING25, "string25", 0) \
|
||||||
|
K(STRING26, "string26", 0) \
|
||||||
|
K(STRING27, "string27", 0) \
|
||||||
|
K(STRING28, "string28", 0) \
|
||||||
|
K(STRING29, "string29", 0) \
|
||||||
|
K(STRING30, "string30", 0) \
|
||||||
|
K(STRING31, "string31", 0) \
|
||||||
|
K(STRING32, "string32", 0) \
|
||||||
K(TEXT, "text", 0) \
|
K(TEXT, "text", 0) \
|
||||||
K(REAL, "real", 0) \
|
K(REAL, "real", 0) \
|
||||||
K(UREAL, "ureal", 0) \
|
K(UREAL, "ureal", 0) \
|
||||||
@ -320,6 +353,8 @@ public:
|
|||||||
static bool isElementaryTypeName(Value tok) { return INT <= tok && tok < TYPES_END; }
|
static bool isElementaryTypeName(Value tok) { return INT <= tok && tok < TYPES_END; }
|
||||||
static bool isAssignmentOp(Value tok) { return ASSIGN <= tok && tok <= ASSIGN_MOD; }
|
static bool isAssignmentOp(Value tok) { return ASSIGN <= tok && tok <= ASSIGN_MOD; }
|
||||||
static bool isBinaryOp(Value op) { return COMMA <= op && op <= MOD; }
|
static bool isBinaryOp(Value op) { return COMMA <= op && op <= MOD; }
|
||||||
|
static bool isCommutativeOp(Value op) { return op == BIT_OR || op == BIT_XOR || op == BIT_AND ||
|
||||||
|
op == ADD || op == MUL || op == EQ || op == NE; }
|
||||||
static bool isArithmeticOp(Value op) { return ADD <= op && op <= MOD; }
|
static bool isArithmeticOp(Value op) { return ADD <= op && op <= MOD; }
|
||||||
static bool isCompareOp(Value op) { return EQ <= op && op <= IN; }
|
static bool isCompareOp(Value op) { return EQ <= op && op <= IN; }
|
||||||
|
|
||||||
|
43
Types.cpp
43
Types.cpp
@ -53,6 +53,8 @@ shared_ptr<Type const> Type::fromElementaryTypeName(Token::Value _typeToken)
|
|||||||
return make_shared<IntegerType const>(0, IntegerType::Modifier::ADDRESS);
|
return make_shared<IntegerType const>(0, IntegerType::Modifier::ADDRESS);
|
||||||
else if (_typeToken == Token::BOOL)
|
else if (_typeToken == Token::BOOL)
|
||||||
return make_shared<BoolType const>();
|
return make_shared<BoolType const>();
|
||||||
|
else if (Token::STRING0 <= _typeToken && _typeToken <= Token::STRING32)
|
||||||
|
return make_shared<StaticStringType const>(int(_typeToken) - int(Token::STRING0));
|
||||||
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."));
|
||||||
@ -91,7 +93,8 @@ shared_ptr<Type const> Type::forLiteral(Literal const& _literal)
|
|||||||
case Token::NUMBER:
|
case Token::NUMBER:
|
||||||
return IntegerType::smallestTypeForLiteral(_literal.getValue());
|
return IntegerType::smallestTypeForLiteral(_literal.getValue());
|
||||||
case Token::STRING_LITERAL:
|
case Token::STRING_LITERAL:
|
||||||
return shared_ptr<Type const>(); // @todo add string literals
|
//@todo put larger strings into dynamic strings
|
||||||
|
return StaticStringType::smallestTypeForLiteral(_literal.getValue());
|
||||||
default:
|
default:
|
||||||
return shared_ptr<Type const>();
|
return shared_ptr<Type const>();
|
||||||
}
|
}
|
||||||
@ -194,6 +197,44 @@ const MemberList IntegerType::AddressMemberList =
|
|||||||
{"send", make_shared<FunctionType const>(TypePointers({make_shared<IntegerType const>(256)}),
|
{"send", make_shared<FunctionType const>(TypePointers({make_shared<IntegerType const>(256)}),
|
||||||
TypePointers(), FunctionType::Location::SEND)}});
|
TypePointers(), FunctionType::Location::SEND)}});
|
||||||
|
|
||||||
|
shared_ptr<StaticStringType> StaticStringType::smallestTypeForLiteral(string const& _literal)
|
||||||
|
{
|
||||||
|
if (_literal.length() <= 32)
|
||||||
|
return make_shared<StaticStringType>(_literal.length());
|
||||||
|
return shared_ptr<StaticStringType>();
|
||||||
|
}
|
||||||
|
|
||||||
|
StaticStringType::StaticStringType(int _bytes): m_bytes(_bytes)
|
||||||
|
{
|
||||||
|
if (asserts(m_bytes >= 0 && m_bytes <= 32))
|
||||||
|
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid byte number for static string type: " +
|
||||||
|
dev::toString(m_bytes)));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StaticStringType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
||||||
|
{
|
||||||
|
if (_convertTo.getCategory() != getCategory())
|
||||||
|
return false;
|
||||||
|
StaticStringType const& convertTo = dynamic_cast<StaticStringType const&>(_convertTo);
|
||||||
|
return convertTo.m_bytes >= m_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StaticStringType::operator==(Type const& _other) const
|
||||||
|
{
|
||||||
|
if (_other.getCategory() != getCategory())
|
||||||
|
return false;
|
||||||
|
StaticStringType const& other = dynamic_cast<StaticStringType const&>(_other);
|
||||||
|
return other.m_bytes == m_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
u256 StaticStringType::literalValue(const Literal& _literal) const
|
||||||
|
{
|
||||||
|
u256 value = 0;
|
||||||
|
for (char c: _literal.getValue())
|
||||||
|
value = (value << 8) | byte(c);
|
||||||
|
return value << ((32 - _literal.getValue().length()) * 8);
|
||||||
|
}
|
||||||
|
|
||||||
bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||||
{
|
{
|
||||||
// conversion to integer is fine, but not to address
|
// conversion to integer is fine, but not to address
|
||||||
|
31
Types.h
31
Types.h
@ -36,7 +36,7 @@ namespace dev
|
|||||||
namespace solidity
|
namespace solidity
|
||||||
{
|
{
|
||||||
|
|
||||||
// @todo realMxN, string<N>
|
// @todo realMxN, dynamic strings, text, arrays
|
||||||
|
|
||||||
class Type; // forward
|
class Type; // forward
|
||||||
using TypePointer = std::shared_ptr<Type const>;
|
using TypePointer = std::shared_ptr<Type const>;
|
||||||
@ -178,6 +178,35 @@ private:
|
|||||||
static const MemberList AddressMemberList;
|
static const MemberList AddressMemberList;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* String type with fixed length, up to 32 bytes.
|
||||||
|
*/
|
||||||
|
class StaticStringType: public Type
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual Category getCategory() const override { return Category::STRING; }
|
||||||
|
|
||||||
|
/// @returns the smallest string type for the given literal or an empty pointer
|
||||||
|
/// if no type fits.
|
||||||
|
static std::shared_ptr<StaticStringType> smallestTypeForLiteral(std::string const& _literal);
|
||||||
|
|
||||||
|
StaticStringType(int _bytes);
|
||||||
|
|
||||||
|
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
|
virtual bool operator==(Type const& _other) const override;
|
||||||
|
|
||||||
|
virtual unsigned getCalldataEncodedSize() const override { return m_bytes; }
|
||||||
|
virtual bool isValueType() const override { return true; }
|
||||||
|
|
||||||
|
virtual std::string toString() const override { return "string" + dev::toString(m_bytes); }
|
||||||
|
virtual u256 literalValue(Literal const& _literal) const override;
|
||||||
|
|
||||||
|
int getNumBytes() const { return m_bytes; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_bytes;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The boolean type.
|
* The boolean type.
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user