117 changed files with 5914 additions and 2827 deletions
@ -0,0 +1,36 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file OurWebThreeStubServer.h
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#include "OurWebThreeStubServer.h" |
|||
using namespace std; |
|||
using namespace dev; |
|||
using namespace dev::eth; |
|||
|
|||
OurWebThreeStubServer::OurWebThreeStubServer(jsonrpc::AbstractServerConnector* _conn, dev::WebThreeDirect& _web3, std::vector<dev::KeyPair> const& _accounts): |
|||
WebThreeStubServer(_conn, _web3, _accounts) |
|||
{} |
|||
|
|||
std::string OurWebThreeStubServer::newIdentity() |
|||
{ |
|||
dev::KeyPair kp = dev::KeyPair::create(); |
|||
emit onNewId(QString::fromStdString(toJS(kp.sec()))); |
|||
return toJS(kp.pub()); |
|||
} |
@ -0,0 +1,38 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file OurWebThreeStubServer.cpp
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#include <QtCore/QObject> |
|||
#include <libdevcore/CommonJS.h> |
|||
#include <libdevcrypto/Common.h> |
|||
#include <libweb3jsonrpc/WebThreeStubServer.h> |
|||
|
|||
class OurWebThreeStubServer: public QObject, public WebThreeStubServer |
|||
{ |
|||
Q_OBJECT |
|||
|
|||
public: |
|||
OurWebThreeStubServer(jsonrpc::AbstractServerConnector* _conn, dev::WebThreeDirect& _web3, std::vector<dev::KeyPair> const& _accounts); |
|||
|
|||
virtual std::string newIdentity() override; |
|||
|
|||
signals: |
|||
void onNewId(QString _s); |
|||
}; |
@ -0,0 +1,36 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file Exceptions.h
|
|||
* @author Christian <c@ethdev.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <libdevcore/Exceptions.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
|
|||
struct AssemblyException: virtual Exception {}; |
|||
struct InvalidDeposit: virtual AssemblyException {}; |
|||
struct InvalidOpcode: virtual AssemblyException {}; |
|||
|
|||
} |
|||
} |
File diff suppressed because it is too large
@ -0,0 +1,61 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/**
|
|||
* @author Christian <c@ethdev.com> |
|||
* @date 2014 |
|||
* Utilities for the solidity compiler. |
|||
*/ |
|||
|
|||
#include <utility> |
|||
#include <numeric> |
|||
#include <libsolidity/AST.h> |
|||
#include <libsolidity/Compiler.h> |
|||
|
|||
using namespace std; |
|||
|
|||
namespace dev { |
|||
namespace solidity { |
|||
|
|||
void CompilerContext::initializeLocalVariables(unsigned _numVariables) |
|||
{ |
|||
if (_numVariables > 0) |
|||
{ |
|||
*this << u256(0); |
|||
for (unsigned i = 1; i < _numVariables; ++i) |
|||
*this << eth::Instruction::DUP1; |
|||
m_asm.adjustDeposit(-_numVariables); |
|||
} |
|||
} |
|||
|
|||
int CompilerContext::getStackPositionOfVariable(Declaration const& _declaration) |
|||
{ |
|||
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(); |
|||
} |
|||
|
|||
eth::AssemblyItem CompilerContext::getFunctionEntryLabel(FunctionDefinition const& _function) const |
|||
{ |
|||
auto res = m_functionEntryLabels.find(&_function); |
|||
if (asserts(res != m_functionEntryLabels.end())) |
|||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Function entry label not found.")); |
|||
return res->second.tag(); |
|||
} |
|||
|
|||
} |
|||
} |
@ -0,0 +1,89 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/**
|
|||
* @author Christian <c@ethdev.com> |
|||
* @date 2014 |
|||
* Utilities for the solidity compiler. |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <ostream> |
|||
#include <libevmcore/Instruction.h> |
|||
#include <libevmcore/Assembly.h> |
|||
#include <libsolidity/Types.h> |
|||
|
|||
namespace dev { |
|||
namespace solidity { |
|||
|
|||
|
|||
/**
|
|||
* Context to be shared by all units that compile the same contract. |
|||
* It stores the generated bytecode and the position of identifiers in memory and on the stack. |
|||
*/ |
|||
class CompilerContext |
|||
{ |
|||
public: |
|||
CompilerContext() {} |
|||
|
|||
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); } |
|||
|
|||
/// 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
|
|||
CompilerContext& appendConditionalJumpTo(eth::AssemblyItem const& _tag) { m_asm.appendJumpI(_tag); return *this; } |
|||
/// Appends a JUMP to a new tag and @returns the tag
|
|||
eth::AssemblyItem appendJump() { return m_asm.appendJump().tag(); } |
|||
/// Appends a JUMP to a specific tag
|
|||
CompilerContext& appendJumpTo(eth::AssemblyItem const& _tag) { m_asm.appendJump(_tag); return *this; } |
|||
/// Appends pushing of a new tag and @returns the new tag.
|
|||
eth::AssemblyItem pushNewTag() { return m_asm.append(m_asm.newPushTag()).tag(); } |
|||
/// @returns a new tag without pushing any opcodes or data
|
|||
eth::AssemblyItem newTag() { return m_asm.newTag(); } |
|||
/// Adds a subroutine to the code (in the data section) and pushes its size (via a tag)
|
|||
/// on the stack. @returns the assembly item corresponding to the pushed subroutine, i.e. its offset.
|
|||
eth::AssemblyItem addSubroutine(eth::Assembly const& _assembly) { return m_asm.appendSubSize(_assembly); } |
|||
|
|||
/// Append elements to the current instruction list and adjust @a m_stackOffset.
|
|||
CompilerContext& operator<<(eth::AssemblyItem const& _item) { m_asm.append(_item); return *this; } |
|||
CompilerContext& operator<<(eth::Instruction _instruction) { m_asm.append(_instruction); return *this; } |
|||
CompilerContext& operator<<(u256 const& _value) { m_asm.append(_value); return *this; } |
|||
CompilerContext& operator<<(bytes const& _data) { m_asm.append(_data); return *this; } |
|||
|
|||
eth::Assembly const& getAssembly() const { return m_asm; } |
|||
void streamAssembly(std::ostream& _stream) const { _stream << m_asm; } |
|||
bytes getAssembledBytecode() const { return m_asm.assemble(); } |
|||
private: |
|||
eth::Assembly m_asm; |
|||
|
|||
/// 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; |
|||
}; |
|||
|
|||
} |
|||
} |
@ -0,0 +1,49 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/**
|
|||
* @author Christian <c@ethdev.com> |
|||
* @date 2014 |
|||
* Full-stack compiler that converts a source code string to bytecode. |
|||
*/ |
|||
|
|||
#include <libsolidity/AST.h> |
|||
#include <libsolidity/Scanner.h> |
|||
#include <libsolidity/Parser.h> |
|||
#include <libsolidity/NameAndTypeResolver.h> |
|||
#include <libsolidity/Compiler.h> |
|||
#include <libsolidity/CompilerStack.h> |
|||
|
|||
using namespace std; |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace solidity |
|||
{ |
|||
|
|||
bytes CompilerStack::compile(std::string const& _sourceCode, shared_ptr<Scanner> _scanner) |
|||
{ |
|||
if (!_scanner) |
|||
_scanner = make_shared<Scanner>(); |
|||
_scanner->reset(CharStream(_sourceCode)); |
|||
|
|||
ASTPointer<ContractDefinition> contract = Parser().parse(_scanner); |
|||
NameAndTypeResolver().resolveNamesAndTypes(*contract); |
|||
return Compiler::compile(*contract); |
|||
} |
|||
|
|||
} |
|||
} |
@ -0,0 +1,43 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/**
|
|||
* @author Christian <c@ethdev.com> |
|||
* @date 2014 |
|||
* Full-stack compiler that converts a source code string to bytecode. |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <string> |
|||
#include <memory> |
|||
#include <libdevcore/Common.h> |
|||
|
|||
namespace dev { |
|||
namespace solidity { |
|||
|
|||
class Scanner; // forward
|
|||
|
|||
class CompilerStack |
|||
{ |
|||
public: |
|||
/// Compile the given @a _sourceCode to bytecode. If a scanner is provided, it is used for
|
|||
/// scanning the source code - this is useful for printing exception information.
|
|||
static bytes compile(std::string const& _sourceCode, std::shared_ptr<Scanner> _scanner = std::shared_ptr<Scanner>()); |
|||
}; |
|||
|
|||
} |
|||
} |
@ -0,0 +1,419 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/**
|
|||
* @author Christian <c@ethdev.com> |
|||
* @date 2014 |
|||
* Solidity AST to EVM bytecode compiler for expressions. |
|||
*/ |
|||
|
|||
#include <utility> |
|||
#include <numeric> |
|||
#include <libsolidity/AST.h> |
|||
#include <libsolidity/ExpressionCompiler.h> |
|||
#include <libsolidity/CompilerContext.h> |
|||
|
|||
using namespace std; |
|||
|
|||
namespace dev { |
|||
namespace solidity { |
|||
|
|||
void ExpressionCompiler::compileExpression(CompilerContext& _context, Expression& _expression) |
|||
{ |
|||
ExpressionCompiler compiler(_context); |
|||
_expression.accept(compiler); |
|||
} |
|||
|
|||
void ExpressionCompiler::appendTypeConversion(CompilerContext& _context, |
|||
Type const& _typeOnStack, Type const& _targetType) |
|||
{ |
|||
ExpressionCompiler compiler(_context); |
|||
compiler.appendTypeConversion(_typeOnStack, _targetType); |
|||
} |
|||
|
|||
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.getLeftHandSide().accept(*this); |
|||
|
|||
Token::Value op = _assignment.getAssignmentOperator(); |
|||
if (op != Token::ASSIGN) |
|||
{ |
|||
// compound assignment
|
|||
m_context << eth::Instruction::SWAP1; |
|||
appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), resultType); |
|||
} |
|||
else |
|||
m_context << eth::Instruction::POP; //@todo do not retrieve the value in the first place
|
|||
|
|||
storeInLValue(_assignment); |
|||
return false; |
|||
} |
|||
|
|||
void ExpressionCompiler::endVisit(UnaryOperation& _unaryOperation) |
|||
{ |
|||
//@todo type checking and creating code for an operator should be in the same place:
|
|||
// the operator should know how to convert itself and to which types it applies, so
|
|||
// put this code together with "Type::acceptsBinary/UnaryOperator" into a class that
|
|||
// represents the operator
|
|||
switch (_unaryOperation.getOperator()) |
|||
{ |
|||
case Token::NOT: // !
|
|||
m_context << eth::Instruction::ISZERO; |
|||
break; |
|||
case Token::BIT_NOT: // ~
|
|||
m_context << eth::Instruction::NOT; |
|||
break; |
|||
case Token::DELETE: // delete
|
|||
{ |
|||
// a -> a xor a (= 0).
|
|||
// @todo semantics change for complex types
|
|||
m_context << eth::Instruction::DUP1 << eth::Instruction::XOR; |
|||
storeInLValue(_unaryOperation); |
|||
break; |
|||
} |
|||
case Token::INC: // ++ (pre- or postfix)
|
|||
case Token::DEC: // -- (pre- or postfix)
|
|||
if (!_unaryOperation.isPrefixOperation()) |
|||
m_context << eth::Instruction::DUP1; |
|||
m_context << u256(1); |
|||
if (_unaryOperation.getOperator() == Token::INC) |
|||
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); |
|||
break; |
|||
case Token::ADD: // +
|
|||
// unary add, so basically no-op
|
|||
break; |
|||
case Token::SUB: // -
|
|||
m_context << u256(0) << eth::Instruction::SUB; |
|||
break; |
|||
default: |
|||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid unary operator: " + |
|||
string(Token::toString(_unaryOperation.getOperator())))); |
|||
} |
|||
} |
|||
|
|||
bool ExpressionCompiler::visit(BinaryOperation& _binaryOperation) |
|||
{ |
|||
Expression& leftExpression = _binaryOperation.getLeftExpression(); |
|||
Expression& rightExpression = _binaryOperation.getRightExpression(); |
|||
Type const& commonType = _binaryOperation.getCommonType(); |
|||
Token::Value const op = _binaryOperation.getOperator(); |
|||
|
|||
if (op == Token::AND || op == Token::OR) |
|||
{ |
|||
// special case: short-circuiting
|
|||
appendAndOrOperatorCode(_binaryOperation); |
|||
} |
|||
else |
|||
{ |
|||
bool cleanupNeeded = false; |
|||
if (commonType.getCategory() == Type::Category::INTEGER) |
|||
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); |
|||
if (Token::isCompareOp(op)) |
|||
appendCompareOperatorCode(op, commonType); |
|||
else |
|||
appendOrdinaryBinaryOperatorCode(op, commonType); |
|||
} |
|||
|
|||
// do not visit the child nodes, we already did that explicitly
|
|||
return false; |
|||
} |
|||
|
|||
bool ExpressionCompiler::visit(FunctionCall& _functionCall) |
|||
{ |
|||
if (_functionCall.isTypeConversion()) |
|||
{ |
|||
//@todo we only have integers and bools for now which cannot be explicitly converted
|
|||
if (asserts(_functionCall.getArguments().size() == 1)) |
|||
BOOST_THROW_EXCEPTION(InternalCompilerError()); |
|||
Expression& firstArgument = *_functionCall.getArguments().front(); |
|||
firstArgument.accept(*this); |
|||
appendTypeConversion(*firstArgument.getType(), *_functionCall.getType()); |
|||
} |
|||
else |
|||
{ |
|||
// Calling convention: Caller pushes return address and arguments
|
|||
// Callee removes them and pushes return values
|
|||
m_currentLValue = nullptr; |
|||
_functionCall.getExpression().accept(*this); |
|||
FunctionDefinition const& function = dynamic_cast<FunctionDefinition&>(*m_currentLValue); |
|||
|
|||
eth::AssemblyItem returnLabel = m_context.pushNewTag(); |
|||
std::vector<ASTPointer<Expression>> const& arguments = _functionCall.getArguments(); |
|||
if (asserts(arguments.size() == function.getParameters().size())) |
|||
BOOST_THROW_EXCEPTION(InternalCompilerError()); |
|||
for (unsigned i = 0; i < arguments.size(); ++i) |
|||
{ |
|||
arguments[i]->accept(*this); |
|||
appendTypeConversion(*arguments[i]->getType(), |
|||
*function.getParameters()[i]->getType()); |
|||
} |
|||
|
|||
m_context.appendJumpTo(m_context.getFunctionEntryLabel(function)); |
|||
m_context << returnLabel; |
|||
|
|||
// callee adds return parameters, but removes arguments and return label
|
|||
m_context.adjustStackOffset(function.getReturnParameters().size() - arguments.size() - 1); |
|||
|
|||
// @todo for now, the return value of a function is its first return value, so remove
|
|||
// all others
|
|||
for (unsigned i = 1; i < function.getReturnParameters().size(); ++i) |
|||
m_context << eth::Instruction::POP; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
void ExpressionCompiler::endVisit(MemberAccess&) |
|||
{ |
|||
|
|||
} |
|||
|
|||
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; |
|||
} |
|||
} |
|||
|
|||
void ExpressionCompiler::endVisit(Literal& _literal) |
|||
{ |
|||
switch (_literal.getType()->getCategory()) |
|||
{ |
|||
case Type::Category::INTEGER: |
|||
case Type::Category::BOOL: |
|||
m_context << _literal.getType()->literalValue(_literal); |
|||
break; |
|||
default: |
|||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Only integer and boolean literals implemented for now.")); |
|||
} |
|||
} |
|||
|
|||
void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation& _binaryOperation) |
|||
{ |
|||
Token::Value const op = _binaryOperation.getOperator(); |
|||
if (asserts(op == Token::OR || op == Token::AND)) |
|||
BOOST_THROW_EXCEPTION(InternalCompilerError()); |
|||
|
|||
_binaryOperation.getLeftExpression().accept(*this); |
|||
m_context << eth::Instruction::DUP1; |
|||
if (op == Token::AND) |
|||
m_context << eth::Instruction::ISZERO; |
|||
eth::AssemblyItem endLabel = m_context.appendConditionalJump(); |
|||
m_context << eth::Instruction::POP; |
|||
_binaryOperation.getRightExpression().accept(*this); |
|||
m_context << endLabel; |
|||
} |
|||
|
|||
void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type const& _type) |
|||
{ |
|||
if (_operator == Token::EQ || _operator == Token::NE) |
|||
{ |
|||
m_context << eth::Instruction::EQ; |
|||
if (_operator == Token::NE) |
|||
m_context << eth::Instruction::ISZERO; |
|||
} |
|||
else |
|||
{ |
|||
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) |
|||
<< eth::Instruction::ISZERO; |
|||
break; |
|||
case Token::LTE: |
|||
m_context << (isSigned ? eth::Instruction::SLT : eth::Instruction::LT) |
|||
<< eth::Instruction::ISZERO; |
|||
break; |
|||
case Token::GT: |
|||
m_context << (isSigned ? eth::Instruction::SLT : eth::Instruction::LT); |
|||
break; |
|||
case Token::LT: |
|||
m_context << (isSigned ? eth::Instruction::SGT : eth::Instruction::GT); |
|||
break; |
|||
default: |
|||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown comparison operator.")); |
|||
} |
|||
} |
|||
} |
|||
|
|||
void ExpressionCompiler::appendOrdinaryBinaryOperatorCode(Token::Value _operator, Type const& _type) |
|||
{ |
|||
if (Token::isArithmeticOp(_operator)) |
|||
appendArithmeticOperatorCode(_operator, _type); |
|||
else if (Token::isBitOp(_operator)) |
|||
appendBitOperatorCode(_operator); |
|||
else if (Token::isShiftOp(_operator)) |
|||
appendShiftOperatorCode(_operator); |
|||
else |
|||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown binary operator.")); |
|||
} |
|||
|
|||
void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Type const& _type) |
|||
{ |
|||
IntegerType const& type = dynamic_cast<IntegerType const&>(_type); |
|||
bool const isSigned = type.isSigned(); |
|||
|
|||
switch (_operator) |
|||
{ |
|||
case Token::ADD: |
|||
m_context << eth::Instruction::ADD; |
|||
break; |
|||
case Token::SUB: |
|||
m_context << eth::Instruction::SWAP1 << 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); |
|||
break; |
|||
case Token::MOD: |
|||
m_context << eth::Instruction::SWAP1 << (isSigned ? eth::Instruction::SMOD : eth::Instruction::MOD); |
|||
break; |
|||
default: |
|||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown arithmetic operator.")); |
|||
} |
|||
} |
|||
|
|||
void ExpressionCompiler::appendBitOperatorCode(Token::Value _operator) |
|||
{ |
|||
switch (_operator) |
|||
{ |
|||
case Token::BIT_OR: |
|||
m_context << eth::Instruction::OR; |
|||
break; |
|||
case Token::BIT_AND: |
|||
m_context << eth::Instruction::AND; |
|||
break; |
|||
case Token::BIT_XOR: |
|||
m_context << eth::Instruction::XOR; |
|||
break; |
|||
default: |
|||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown bit operator.")); |
|||
} |
|||
} |
|||
|
|||
void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator) |
|||
{ |
|||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Shift operators not yet implemented.")); |
|||
switch (_operator) |
|||
{ |
|||
case Token::SHL: |
|||
break; |
|||
case Token::SAR: |
|||
break; |
|||
default: |
|||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown shift 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
|
|||
|
|||
if (_typeOnStack == _targetType && !_cleanupNeeded) |
|||
return; |
|||
if (_typeOnStack.getCategory() == Type::Category::INTEGER) |
|||
appendHighBitsCleanup(dynamic_cast<IntegerType const&>(_typeOnStack)); |
|||
else if (_typeOnStack != _targetType) |
|||
// All other types should not be convertible to non-equal types.
|
|||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid type conversion requested.")); |
|||
} |
|||
|
|||
void ExpressionCompiler::appendHighBitsCleanup(IntegerType const& _typeOnStack) |
|||
{ |
|||
if (_typeOnStack.getNumBits() == 256) |
|||
return; |
|||
else if (_typeOnStack.isSigned()) |
|||
m_context << u256(_typeOnStack.getNumBits() / 8 - 1) << eth::Instruction::SIGNEXTEND; |
|||
else |
|||
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) |
|||
{ |
|||
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; |
|||
} |
|||
|
|||
unsigned ExpressionCompiler::stackPositionOfLValue() const |
|||
{ |
|||
if (asserts(m_currentLValue)) |
|||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("LValue not available on request.")); |
|||
return m_context.getStackPositionOfVariable(*m_currentLValue); |
|||
} |
|||
|
|||
} |
|||
} |
@ -0,0 +1,89 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/**
|
|||
* @author Christian <c@ethdev.com> |
|||
* @date 2014 |
|||
* Solidity AST to EVM bytecode compiler for expressions. |
|||
*/ |
|||
|
|||
#include <libsolidity/ASTVisitor.h> |
|||
|
|||
namespace dev { |
|||
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.
|
|||
class ExpressionCompiler: private ASTVisitor |
|||
{ |
|||
public: |
|||
/// Compile the given @a _expression into the @a _context.
|
|||
static void compileExpression(CompilerContext& _context, Expression& _expression); |
|||
|
|||
/// 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); |
|||
|
|||
private: |
|||
ExpressionCompiler(CompilerContext& _compilerContext): m_currentLValue(nullptr), m_context(_compilerContext) {} |
|||
|
|||
virtual bool visit(Assignment& _assignment) override; |
|||
virtual void endVisit(UnaryOperation& _unaryOperation) override; |
|||
virtual bool visit(BinaryOperation& _binaryOperation) override; |
|||
virtual bool visit(FunctionCall& _functionCall) override; |
|||
virtual void endVisit(MemberAccess& _memberAccess) override; |
|||
virtual void endVisit(IndexAccess& _indexAccess) override; |
|||
virtual void endVisit(Identifier& _identifier) override; |
|||
virtual void endVisit(Literal& _literal) override; |
|||
|
|||
///@{
|
|||
///@name Append code for various operator types
|
|||
void appendAndOrOperatorCode(BinaryOperation& _binaryOperation); |
|||
void appendCompareOperatorCode(Token::Value _operator, Type const& _type); |
|||
void appendOrdinaryBinaryOperatorCode(Token::Value _operator, Type const& _type); |
|||
|
|||
void appendArithmeticOperatorCode(Token::Value _operator, Type const& _type); |
|||
void appendBitOperatorCode(Token::Value _operator); |
|||
void appendShiftOperatorCode(Token::Value _operator); |
|||
/// @}
|
|||
|
|||
/// Appends an implicit or explicit type conversion. For now this comprises only erasing
|
|||
/// higher-order bits (@see appendHighBitCleanup) when widening integer types.
|
|||
/// If @a _cleanupNeeded, high order bits cleanup is also done if no type conversion would be
|
|||
/// necessary.
|
|||
void appendTypeConversion(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded = false); |
|||
//// 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; |
|||
CompilerContext& m_context; |
|||
}; |
|||
|
|||
|
|||
} |
|||
} |
@ -0,0 +1,331 @@ |
|||
|
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/**
|
|||
* @author Christian <c@ethdev.com> |
|||
* @date 2014 |
|||
* Unit tests for the solidity expression compiler, testing the behaviour of the code. |
|||
*/ |
|||
|
|||
#include <string> |
|||
#include <boost/test/unit_test.hpp> |
|||
#include <libethereum/State.h> |
|||
#include <libethereum/Executive.h> |
|||
#include <libsolidity/CompilerStack.h> |
|||
|
|||
using namespace std; |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace solidity |
|||
{ |
|||
namespace test |
|||
{ |
|||
|
|||
class ExecutionFramework |
|||
{ |
|||
public: |
|||
ExecutionFramework() { g_logVerbosity = 0; } |
|||
|
|||
bytes const& compileAndRun(std::string const& _sourceCode) |
|||
{ |
|||
bytes code = dev::solidity::CompilerStack::compile(_sourceCode); |
|||
sendMessage(code, true); |
|||
BOOST_REQUIRE(!m_output.empty()); |
|||
return m_output; |
|||
} |
|||
|
|||
bytes const& callFunction(byte _index, bytes const& _data) |
|||
{ |
|||
sendMessage(bytes(1, _index) + _data, false); |
|||
return m_output; |
|||
} |
|||
|
|||
bytes const& callFunction(byte _index, u256 const& _argument1) |
|||
{ |
|||
return callFunction(_index, toBigEndian(_argument1)); |
|||
} |
|||
|
|||
private: |
|||
void sendMessage(bytes const& _data, bool _isCreation) |
|||
{ |
|||
eth::Executive executive(m_state); |
|||
eth::Transaction t = _isCreation ? eth::Transaction(0, m_gasPrice, m_gas, _data, 0, KeyPair::create().sec()) |
|||
: eth::Transaction(0, m_gasPrice, m_gas, m_contractAddress, _data, 0, KeyPair::create().sec()); |
|||
bytes transactionRLP = t.rlp(); |
|||
try |
|||
{ |
|||
// this will throw since the transaction is invalid, but it should nevertheless store the transaction
|
|||
executive.setup(&transactionRLP); |
|||
} |
|||
catch (...) {} |
|||
if (_isCreation) |
|||
{ |
|||
BOOST_REQUIRE(!executive.create(Address(), 0, m_gasPrice, m_gas, &_data, Address())); |
|||
m_contractAddress = executive.newAddress(); |
|||
BOOST_REQUIRE(m_contractAddress); |
|||
BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress)); |
|||
} |
|||
else |
|||
BOOST_REQUIRE(!executive.call(m_contractAddress, Address(), 0, m_gasPrice, &_data, m_gas, Address())); |
|||
BOOST_REQUIRE(executive.go()); |
|||
executive.finalize(); |
|||
m_output = executive.out().toVector(); |
|||
} |
|||
|
|||
Address m_contractAddress; |
|||
eth::State m_state; |
|||
u256 const m_gasPrice = 100 * eth::szabo; |
|||
u256 const m_gas = 1000000; |
|||
bytes m_output; |
|||
}; |
|||
|
|||
BOOST_FIXTURE_TEST_SUITE(SolidityCompilerEndToEndTest, ExecutionFramework) |
|||
|
|||
BOOST_AUTO_TEST_CASE(smoke_test) |
|||
{ |
|||
char const* sourceCode = "contract test {\n" |
|||
" function f(uint a) returns(uint d) { return a * 7; }\n" |
|||
"}\n"; |
|||
compileAndRun(sourceCode); |
|||
u256 a = 0x200030004; |
|||
BOOST_CHECK(callFunction(0, a) == toBigEndian(a * 7)); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(empty_contract) |
|||
{ |
|||
char const* sourceCode = "contract test {\n" |
|||
"}\n"; |
|||
compileAndRun(sourceCode); |
|||
BOOST_CHECK(callFunction(0, bytes()).empty()); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(recursive_calls) |
|||
{ |
|||
char const* sourceCode = "contract test {\n" |
|||
" function f(uint n) returns(uint nfac) {\n" |
|||
" if (n <= 1) return 1;\n" |
|||
" else return n * f(n - 1);\n" |
|||
" }\n" |
|||
"}\n"; |
|||
compileAndRun(sourceCode); |
|||
BOOST_CHECK(callFunction(0, u256(0)) == toBigEndian(u256(1))); |
|||
BOOST_CHECK(callFunction(0, u256(1)) == toBigEndian(u256(1))); |
|||
BOOST_CHECK(callFunction(0, u256(2)) == toBigEndian(u256(2))); |
|||
BOOST_CHECK(callFunction(0, u256(3)) == toBigEndian(u256(6))); |
|||
BOOST_CHECK(callFunction(0, u256(4)) == toBigEndian(u256(24))); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(while_loop) |
|||
{ |
|||
char const* sourceCode = "contract test {\n" |
|||
" function f(uint n) returns(uint nfac) {\n" |
|||
" nfac = 1;\n" |
|||
" var i = 2;\n" |
|||
" while (i <= n) nfac *= i++;\n" |
|||
" }\n" |
|||
"}\n"; |
|||
compileAndRun(sourceCode); |
|||
BOOST_CHECK(callFunction(0, u256(0)) == toBigEndian(u256(1))); |
|||
BOOST_CHECK(callFunction(0, u256(1)) == toBigEndian(u256(1))); |
|||
BOOST_CHECK(callFunction(0, u256(2)) == toBigEndian(u256(2))); |
|||
BOOST_CHECK(callFunction(0, u256(3)) == toBigEndian(u256(6))); |
|||
BOOST_CHECK(callFunction(0, u256(4)) == toBigEndian(u256(24))); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(break_outside_loop) |
|||
{ |
|||
// break and continue outside loops should be simply ignored
|
|||
char const* sourceCode = "contract test {\n" |
|||
" function f(uint x) returns(uint y) {\n" |
|||
" break; continue; return 2;\n" |
|||
" }\n" |
|||
"}\n"; |
|||
ExecutionFramework framework; |
|||
framework.compileAndRun(sourceCode); |
|||
BOOST_CHECK(framework.callFunction(0, u256(0)) == toBigEndian(u256(2))); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(nested_loops) |
|||
{ |
|||
// tests that break and continue statements in nested loops jump to the correct place
|
|||
char const* sourceCode = "contract test {\n" |
|||
" function f(uint x) returns(uint y) {\n" |
|||
" while (x > 1) {\n" |
|||
" if (x == 10) break;\n" |
|||
" while (x > 5) {\n" |
|||
" if (x == 8) break;\n" |
|||
" x--;\n" |
|||
" if (x == 6) continue;\n" |
|||
" return x;\n" |
|||
" }\n" |
|||
" x--;\n" |
|||
" if (x == 3) continue;\n" |
|||
" break;\n" |
|||
" }\n" |
|||
" return x;\n" |
|||
" }\n" |
|||
"}\n"; |
|||
ExecutionFramework framework; |
|||
framework.compileAndRun(sourceCode); |
|||
BOOST_CHECK(framework.callFunction(0, u256(0)) == toBigEndian(u256(0))); |
|||
BOOST_CHECK(framework.callFunction(0, u256(1)) == toBigEndian(u256(1))); |
|||
BOOST_CHECK(framework.callFunction(0, u256(2)) == toBigEndian(u256(1))); |
|||
BOOST_CHECK(framework.callFunction(0, u256(3)) == toBigEndian(u256(2))); |
|||
BOOST_CHECK(framework.callFunction(0, u256(4)) == toBigEndian(u256(2))); |
|||
BOOST_CHECK(framework.callFunction(0, u256(5)) == toBigEndian(u256(4))); |
|||
BOOST_CHECK(framework.callFunction(0, u256(6)) == toBigEndian(u256(5))); |
|||
BOOST_CHECK(framework.callFunction(0, u256(7)) == toBigEndian(u256(5))); |
|||
BOOST_CHECK(framework.callFunction(0, u256(8)) == toBigEndian(u256(7))); |
|||
BOOST_CHECK(framework.callFunction(0, u256(9)) == toBigEndian(u256(8))); |
|||
BOOST_CHECK(framework.callFunction(0, u256(10)) == toBigEndian(u256(10))); |
|||
BOOST_CHECK(framework.callFunction(0, u256(11)) == toBigEndian(u256(10))); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(calling_other_functions) |
|||
{ |
|||
// note that the index of a function is its index in the sorted sequence of functions
|
|||
char const* sourceCode = "contract collatz {\n" |
|||
" function run(uint x) returns(uint y) {\n" |
|||
" while ((y = x) > 1) {\n" |
|||
" if (x % 2 == 0) x = evenStep(x);\n" |
|||
" else x = oddStep(x);\n" |
|||
" }\n" |
|||
" }\n" |
|||
" function evenStep(uint x) returns(uint y) {\n" |
|||
" return x / 2;\n" |
|||
" }\n" |
|||
" function oddStep(uint x) returns(uint y) {\n" |
|||
" return 3 * x + 1;\n" |
|||
" }\n" |
|||
"}\n"; |
|||
compileAndRun(sourceCode); |
|||
BOOST_CHECK(callFunction(2, u256(0)) == toBigEndian(u256(0))); |
|||
BOOST_CHECK(callFunction(2, u256(1)) == toBigEndian(u256(1))); |
|||
BOOST_CHECK(callFunction(2, u256(2)) == toBigEndian(u256(1))); |
|||
BOOST_CHECK(callFunction(2, u256(8)) == toBigEndian(u256(1))); |
|||
BOOST_CHECK(callFunction(2, u256(127)) == toBigEndian(u256(1))); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(many_local_variables) |
|||
{ |
|||
char const* sourceCode = "contract test {\n" |
|||
" function run(uint x1, uint x2, uint x3) returns(uint y) {\n" |
|||
" var a = 0x1; var b = 0x10; var c = 0x100;\n" |
|||
" y = a + b + c + x1 + x2 + x3;\n" |
|||
" y += b + x2;\n" |
|||
" }\n" |
|||
"}\n"; |
|||
compileAndRun(sourceCode); |
|||
BOOST_CHECK(callFunction(0, toBigEndian(u256(0x1000)) + toBigEndian(u256(0x10000)) + toBigEndian(u256(0x100000))) |
|||
== toBigEndian(u256(0x121121))); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(packing_unpacking_types) |
|||
{ |
|||
char const* sourceCode = "contract test {\n" |
|||
" function run(bool a, uint32 b, uint64 c) returns(uint256 y) {\n" |
|||
" if (a) y = 1;\n" |
|||
" y = y * 0x100000000 | ~b;\n" |
|||
" y = y * 0x10000000000000000 | ~c;\n" |
|||
" }\n" |
|||
"}\n"; |
|||
compileAndRun(sourceCode); |
|||
BOOST_CHECK(callFunction(0, fromHex("01""0f0f0f0f""f0f0f0f0f0f0f0f0")) |
|||
== fromHex("00000000000000000000000000000000000000""01""f0f0f0f0""0f0f0f0f0f0f0f0f")); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(multiple_return_values) |
|||
{ |
|||
char const* sourceCode = "contract test {\n" |
|||
" function run(bool x1, uint x2) returns(uint y1, bool y2, uint y3) {\n" |
|||
" y1 = x2; y2 = x1;\n" |
|||
" }\n" |
|||
"}\n"; |
|||
compileAndRun(sourceCode); |
|||
BOOST_CHECK(callFunction(0, bytes(1, 1) + toBigEndian(u256(0xcd))) |
|||
== toBigEndian(u256(0xcd)) + bytes(1, 1) + toBigEndian(u256(0))); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(short_circuiting) |
|||
{ |
|||
char const* sourceCode = "contract test {\n" |
|||
" function run(uint x) returns(uint y) {\n" |
|||
" x == 0 || ((x = 8) > 0);\n" |
|||
" return x;" |
|||
" }\n" |
|||
"}\n"; |
|||
compileAndRun(sourceCode); |
|||
BOOST_CHECK(callFunction(0, u256(0)) == toBigEndian(u256(0))); |
|||
BOOST_CHECK(callFunction(0, u256(1)) == toBigEndian(u256(8))); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(high_bits_cleaning) |
|||
{ |
|||
char const* sourceCode = "contract test {\n" |
|||
" function run() returns(uint256 y) {\n" |
|||
" uint32 x = uint32(0xffffffff) + 10;\n" |
|||
" if (x >= 0xffffffff) return 0;\n" |
|||
" return x;" |
|||
" }\n" |
|||
"}\n"; |
|||
compileAndRun(sourceCode); |
|||
BOOST_CHECK(callFunction(0, bytes()) == toBigEndian(u256(9))); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(sign_extension) |
|||
{ |
|||
char const* sourceCode = "contract test {\n" |
|||
" function run() returns(uint256 y) {\n" |
|||
" int64 x = -int32(0xff);\n" |
|||
" if (x >= 0xff) return 0;\n" |
|||
" return -uint256(x);" |
|||
" }\n" |
|||
"}\n"; |
|||
compileAndRun(sourceCode); |
|||
BOOST_CHECK(callFunction(0, bytes()) == toBigEndian(u256(0xff))); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(small_unsigned_types) |
|||
{ |
|||
char const* sourceCode = "contract test {\n" |
|||
" function run() returns(uint256 y) {\n" |
|||
" uint32 x = uint32(0xffffff) * 0xffffff;\n" |
|||
" return x / 0x100;" |
|||
" }\n" |
|||
"}\n"; |
|||
compileAndRun(sourceCode); |
|||
BOOST_CHECK(callFunction(0, bytes()) == toBigEndian(u256(0xfe0000))); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(small_signed_types) |
|||
{ |
|||
char const* sourceCode = "contract test {\n" |
|||
" function run() returns(int256 y) {\n" |
|||
" return -int32(10) * -int64(20);\n" |
|||
" }\n" |
|||
"}\n"; |
|||
compileAndRun(sourceCode); |
|||
BOOST_CHECK(callFunction(0, bytes()) == toBigEndian(u256(200))); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_SUITE_END() |
|||
|
|||
} |
|||
} |
|||
} // end namespaces
|
|||
|
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue