Browse Source

Some tests and bugfixes for the compiler.

cl-refactor
Christian 10 years ago
parent
commit
30e4cda0e9
  1. 2
      libethereum/Account.h
  2. 32
      libsolidity/Compiler.cpp
  3. 1
      libsolidity/Compiler.h
  4. 1
      libsolidity/ExpressionCompiler.cpp
  5. 210
      test/solidityEndToEndTest.cpp
  6. 3
      test/solidityExpressionCompiler.cpp

2
libethereum/Account.h

@ -82,7 +82,7 @@ public:
/// Construct an alive Account, with given endowment, for either a normal (non-contract) account or for a /// Construct an alive Account, with given endowment, for either a normal (non-contract) account or for a
/// contract account in the /// contract account in the
/// conception phase, where the code is not yet known. /// conception phase, where the code is not yet known.
Account(u256 _balance, NewAccountType _t): m_isAlive(true), m_balance(_balance), m_codeHash(_t == NormalCreation ? c_contractConceptionCodeHash : EmptySHA3) {} Account(u256 _balance, NewAccountType _t): m_isAlive(true), m_balance(_balance), m_codeHash(_t == NormalCreation ? EmptySHA3 : c_contractConceptionCodeHash) {}
/// Explicit constructor for wierd cases of construction of a normal account. /// Explicit constructor for wierd cases of construction of a normal account.
Account(u256 _nonce, u256 _balance): m_isAlive(true), m_nonce(_nonce), m_balance(_balance) {} Account(u256 _nonce, u256 _balance): m_isAlive(true), m_nonce(_nonce), m_balance(_balance) {}

32
libsolidity/Compiler.cpp

@ -94,25 +94,20 @@ void Compiler::appendFunctionSelector(vector<ASTPointer<FunctionDefinition>> con
for (pair<string, pair<FunctionDefinition const*, eth::AssemblyItem>> const& f: publicFunctions) for (pair<string, pair<FunctionDefinition const*, eth::AssemblyItem>> const& f: publicFunctions)
m_context.appendJumpTo(f.second.second) << eth::Instruction::JUMPDEST; m_context.appendJumpTo(f.second.second) << eth::Instruction::JUMPDEST;
m_context << returnTag << eth::Instruction::RETURN; m_context << returnTag << eth::Instruction::STOP;
for (pair<string, pair<FunctionDefinition const*, eth::AssemblyItem>> const& f: publicFunctions) for (pair<string, pair<FunctionDefinition const*, eth::AssemblyItem>> const& f: publicFunctions)
{ {
FunctionDefinition const& function = *f.second.first;
m_context << f.second.second; m_context << f.second.second;
appendFunctionCallSection(*f.second.first);
}
}
void Compiler::appendFunctionCallSection(FunctionDefinition const& _function)
{
eth::AssemblyItem returnTag = m_context.pushNewTag();
appendCalldataUnpacker(_function); eth::AssemblyItem returnTag = m_context.pushNewTag();
appendCalldataUnpacker(function);
m_context.appendJumpTo(m_context.getFunctionEntryLabel(function));
m_context << returnTag;
m_context.appendJumpTo(m_context.getFunctionEntryLabel(_function)); appendReturnValuePacker(function);
m_context << returnTag; }
appendReturnValuePacker(_function);
} }
void Compiler::appendCalldataUnpacker(FunctionDefinition const& _function) void Compiler::appendCalldataUnpacker(FunctionDefinition const& _function)
@ -142,7 +137,7 @@ void Compiler::appendReturnValuePacker(FunctionDefinition const& _function)
//@todo this can be also done more efficiently //@todo this can be also done more efficiently
unsigned dataOffset = 0; unsigned dataOffset = 0;
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(); unsigned numBytes = parameters[i]->getType()->getCalldataEncodedSize();
if (numBytes == 0) if (numBytes == 0)
@ -150,11 +145,9 @@ void Compiler::appendReturnValuePacker(FunctionDefinition const& _function)
<< errinfo_sourceLocation(parameters[i]->getLocation()) << errinfo_sourceLocation(parameters[i]->getLocation())
<< errinfo_comment("Type not yet supported.")); << errinfo_comment("Type 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(dataOffset) << eth::Instruction::MSTORE; m_context << (u256(1) << ((32 - numBytes) * 8)) << eth::Instruction::MUL;
else m_context << u256(dataOffset) << eth::Instruction::MSTORE;
m_context << u256(dataOffset) << (u256(1) << ((32 - numBytes) * 8))
<< eth::Instruction::MUL << eth::Instruction::MSTORE;
dataOffset += numBytes; dataOffset += numBytes;
} }
// note that the stack is not cleaned up here // note that the stack is not cleaned up here
@ -215,6 +208,7 @@ bool Compiler::visit(FunctionDefinition& _function)
m_context << eth::swapInstruction(stackLayout.size() - stackLayout.back() - 1); m_context << eth::swapInstruction(stackLayout.size() - stackLayout.back() - 1);
swap(stackLayout[stackLayout.back()], stackLayout.back()); swap(stackLayout[stackLayout.back()], stackLayout.back());
} }
//@todo assert that everything is in place now
m_context << eth::Instruction::JUMP; m_context << eth::Instruction::JUMP;

1
libsolidity/Compiler.h

@ -43,7 +43,6 @@ private:
/// Creates a new compiler context / assembly and packs the current code into the data part. /// Creates a new compiler context / assembly and packs the current code into the data part.
void packIntoContractCreator(); void packIntoContractCreator();
void appendFunctionSelector(std::vector<ASTPointer<FunctionDefinition> > const& _functions); void appendFunctionSelector(std::vector<ASTPointer<FunctionDefinition> > const& _functions);
void appendFunctionCallSection(FunctionDefinition const& _function);
void appendCalldataUnpacker(FunctionDefinition const& _function); void appendCalldataUnpacker(FunctionDefinition const& _function);
void appendReturnValuePacker(FunctionDefinition const& _function); void appendReturnValuePacker(FunctionDefinition const& _function);

1
libsolidity/ExpressionCompiler.cpp

@ -264,6 +264,7 @@ void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation& _binaryOperati
if (op == Token::AND) if (op == Token::AND)
m_context << eth::Instruction::ISZERO; m_context << eth::Instruction::ISZERO;
eth::AssemblyItem endLabel = m_context.appendConditionalJump(); eth::AssemblyItem endLabel = m_context.appendConditionalJump();
m_context << eth::Instruction::POP;
_binaryOperation.getRightExpression().accept(*this); _binaryOperation.getRightExpression().accept(*this);
m_context << endLabel; m_context << endLabel;
} }

210
test/solidityEndToEndTest.cpp

@ -0,0 +1,210 @@
/*
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 compileAndRun(std::string const& _sourceCode)
{
bytes code = dev::solidity::CompilerStack::compile(_sourceCode);
eth::Executive ex(m_state);
BOOST_REQUIRE(!ex.create(Address(), 0, m_gasPrice, m_gas, &code, Address()));
BOOST_REQUIRE(ex.go());
ex.finalize();
m_contractAddress = ex.newAddress();
return ex.out().toBytes();
}
bytes callFunction(byte _index, bytes const& _data)
{
bytes data = bytes(1, _index) + _data;
eth::Executive ex(m_state);
BOOST_REQUIRE(!ex.call(m_contractAddress, Address(), 0, m_gasPrice, &data, m_gas, Address()));
BOOST_REQUIRE(ex.go());
ex.finalize();
return ex.out().toBytes();
}
bytes callFunction(byte _index, u256 const& _argument)
{
return callFunction(_index, toBigEndian(_argument));
}
private:
Address m_contractAddress;
eth::State m_state;
u256 const m_gasPrice = 100 * eth::szabo;
u256 const m_gas = 1000000;
};
BOOST_AUTO_TEST_SUITE(SolidityCompilerEndToEndTest)
BOOST_AUTO_TEST_CASE(smoke_test)
{
char const* sourceCode = "contract test {\n"
" function f(uint a) returns(uint d) { return a * 7; }\n"
"}\n";
ExecutionFramework framework;
framework.compileAndRun(sourceCode);
u256 a = 0x200030004;
bytes result = framework.callFunction(0, a);
BOOST_CHECK(result == toBigEndian(a * 7));
}
BOOST_AUTO_TEST_CASE(empty_contract)
{
char const* sourceCode = "contract test {\n"
"}\n";
ExecutionFramework framework;
framework.compileAndRun(sourceCode);
BOOST_CHECK(framework.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";
ExecutionFramework framework;
framework.compileAndRun(sourceCode);
BOOST_CHECK(framework.callFunction(0, u256(0)) == toBigEndian(u256(1)));
BOOST_CHECK(framework.callFunction(0, u256(1)) == toBigEndian(u256(1)));
BOOST_CHECK(framework.callFunction(0, u256(2)) == toBigEndian(u256(2)));
BOOST_CHECK(framework.callFunction(0, u256(3)) == toBigEndian(u256(6)));
BOOST_CHECK(framework.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";
ExecutionFramework framework;
framework.compileAndRun(sourceCode);
BOOST_CHECK(framework.callFunction(0, u256(0)) == toBigEndian(u256(1)));
BOOST_CHECK(framework.callFunction(0, u256(1)) == toBigEndian(u256(1)));
BOOST_CHECK(framework.callFunction(0, u256(2)) == toBigEndian(u256(2)));
BOOST_CHECK(framework.callFunction(0, u256(3)) == toBigEndian(u256(6)));
BOOST_CHECK(framework.callFunction(0, u256(4)) == toBigEndian(u256(24)));
}
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";
ExecutionFramework framework;
framework.compileAndRun(sourceCode);
BOOST_CHECK(framework.callFunction(2, u256(0)) == toBigEndian(u256(0)));
BOOST_CHECK(framework.callFunction(2, u256(1)) == toBigEndian(u256(1)));
BOOST_CHECK(framework.callFunction(2, u256(2)) == toBigEndian(u256(1)));
BOOST_CHECK(framework.callFunction(2, u256(8)) == toBigEndian(u256(1)));
BOOST_CHECK(framework.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";
ExecutionFramework framework;
framework.compileAndRun(sourceCode);
BOOST_CHECK(framework.callFunction(0, toBigEndian(u256(0x1000)) + toBigEndian(u256(0x10000)) + toBigEndian(u256(0x100000)))
== toBigEndian(u256(0x121121)));
}
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";
ExecutionFramework framework;
framework.compileAndRun(sourceCode);
BOOST_CHECK(framework.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";
ExecutionFramework framework;
framework.compileAndRun(sourceCode);
BOOST_CHECK(framework.callFunction(0, u256(0)) == toBigEndian(u256(0)));
BOOST_CHECK(framework.callFunction(0, u256(1)) == toBigEndian(u256(8)));
}
//@todo test smaller types
BOOST_AUTO_TEST_SUITE_END()
}
}
} // end namespaces

3
test/solidityExpressionCompiler.cpp

@ -177,8 +177,9 @@ BOOST_AUTO_TEST_CASE(short_circuiting)
byte(eth::Instruction::GT), byte(eth::Instruction::GT),
byte(eth::Instruction::ISZERO), // after this we have 10 + 8 >= 4 byte(eth::Instruction::ISZERO), // after this we have 10 + 8 >= 4
byte(eth::Instruction::DUP1), byte(eth::Instruction::DUP1),
byte(eth::Instruction::PUSH1), 0x14, byte(eth::Instruction::PUSH1), 0x15,
byte(eth::Instruction::JUMPI), // short-circuit if it is true byte(eth::Instruction::JUMPI), // short-circuit if it is true
byte(eth::Instruction::POP),
byte(eth::Instruction::PUSH1), 0x2, byte(eth::Instruction::PUSH1), 0x2,
byte(eth::Instruction::PUSH1), 0x9, byte(eth::Instruction::PUSH1), 0x9,
byte(eth::Instruction::EQ), byte(eth::Instruction::EQ),

Loading…
Cancel
Save