Browse Source

Merge branch 'develop' into ethereumjs

cl-refactor
Marek Kotewicz 10 years ago
parent
commit
30220d8dcc
  1. 2
      .gitignore
  2. 87
      alethzero/MainWin.cpp
  3. 2
      alethzero/MainWin.h
  4. 1
      evmjit/evmcc/evmcc.cpp
  5. 20
      evmjit/libevmjit-cpp/Env.cpp
  6. 1
      evmjit/libevmjit-cpp/JitVM.cpp
  7. 7
      evmjit/libevmjit/BasicBlock.h
  8. 2
      evmjit/libevmjit/Common.h
  9. 152
      evmjit/libevmjit/Compiler.cpp
  10. 18
      evmjit/libevmjit/Compiler.h
  11. 5
      evmjit/libevmjit/CompilerHelper.cpp
  12. 7
      evmjit/libevmjit/CompilerHelper.h
  13. 151
      evmjit/libevmjit/Ext.cpp
  14. 50
      evmjit/libevmjit/Ext.h
  15. 4
      evmjit/libevmjit/GasMeter.cpp
  16. 2
      evmjit/libevmjit/Instruction.h
  17. 8
      evmjit/libevmjit/Memory.cpp
  18. 23
      evmjit/libevmjit/Runtime.cpp
  19. 3
      evmjit/libevmjit/Runtime.h
  20. 1
      evmjit/libevmjit/RuntimeData.h
  21. 2
      evmjit/libevmjit/RuntimeManager.cpp
  22. 9
      evmjit/libevmjit/Utils.cpp
  23. 2
      evmjit/libevmjit/Utils.h
  24. 17
      libsolidity/AST.cpp
  25. 5
      libsolidity/AST.h
  26. 1
      libsolidity/CMakeLists.txt
  27. 44
      libsolidity/Compiler.cpp
  28. 13
      libsolidity/CompilerStack.cpp
  29. 12
      libsolidity/CompilerStack.h
  30. 2
      libsolidity/CompilerUtils.cpp
  31. 4
      libsolidity/CompilerUtils.h
  32. 72
      libsolidity/ExpressionCompiler.cpp
  33. 1
      libsolidity/ExpressionCompiler.h
  34. 56
      libsolidity/GlobalContext.cpp
  35. 48
      libsolidity/InterfaceHandler.cpp
  36. 1
      libsolidity/InterfaceHandler.h
  37. 18
      libsolidity/Types.cpp
  38. 4
      libsolidity/Types.h
  39. 10
      mix/QContractDefinition.cpp
  40. 41
      solc/CommandLineInterface.cpp
  41. 2
      solc/CommandLineInterface.h
  42. 30
      test/SolidityABIJSON.cpp
  43. 17
      test/SolidityCompiler.cpp
  44. 377
      test/SolidityEndToEndTest.cpp
  45. 4
      test/SolidityNatspecJSON.cpp
  46. 26
      test/SolidityOptimizer.cpp
  47. 20
      test/solidityExecutionFramework.h

2
.gitignore

@ -66,3 +66,5 @@ project.pbxproj
evmjit
doc/html
*.autosave

87
alethzero/MainWin.cpp

@ -105,9 +105,9 @@ static QString contentsOfQResource(std::string const& res)
return in.readAll();
}
Address c_config = Address("661005d2720d855f1d9976f88bb10c1a3398c77f");
Address c_newConfig = Address("d5f9d8d94886e70b06e474c3fb14fd43e2f23970");
Address c_nameReg = Address("ddd1cea741d548f90d86fb87a3ae6492e18c03a1");
//Address c_config = Address("661005d2720d855f1d9976f88bb10c1a3398c77f");
Address c_newConfig = Address("661005d2720d855f1d9976f88bb10c1a3398c77f");
//Address c_nameReg = Address("ddd1cea741d548f90d86fb87a3ae6492e18c03a1");
Main::Main(QWidget *parent) :
QMainWindow(parent),
@ -265,22 +265,32 @@ void Main::uninstallWatch(unsigned _w)
void Main::installWatches()
{
installWatch(dev::eth::LogFilter().address(c_config), [=]() { installNameRegWatch(); });
installWatch(dev::eth::LogFilter().address(c_config), [=]() { installCurrenciesWatch(); });
installWatch(dev::eth::LogFilter().address(c_newConfig), [=]() { installNameRegWatch(); });
installWatch(dev::eth::LogFilter().address(c_newConfig), [=]() { installCurrenciesWatch(); });
installWatch(dev::eth::PendingChangedFilter, [=](){ onNewPending(); });
installWatch(dev::eth::ChainChangedFilter, [=](){ onNewBlock(); });
}
Address Main::getNameReg() const
{
return abiOut<Address>(ethereum()->call(c_newConfig, abiIn(1, (u256)1)));
}
Address Main::getCurrencies() const
{
return abiOut<Address>(ethereum()->call(c_newConfig, abiIn(1, (u256)2)));
}
void Main::installNameRegWatch()
{
uninstallWatch(m_nameRegFilter);
m_nameRegFilter = installWatch(dev::eth::LogFilter().address((u160)ethereum()->stateAt(c_config, 0)), [=](){ onNameRegChange(); });
m_nameRegFilter = installWatch(dev::eth::LogFilter().address((u160)getNameReg()), [=](){ onNameRegChange(); });
}
void Main::installCurrenciesWatch()
{
uninstallWatch(m_currenciesFilter);
m_currenciesFilter = installWatch(dev::eth::LogFilter().address((u160)ethereum()->stateAt(c_config, 1)), [=](){ onCurrenciesChange(); });
m_currenciesFilter = installWatch(dev::eth::LogFilter().address((u160)getCurrencies()), [=](){ onCurrenciesChange(); });
}
void Main::installBalancesWatch()
@ -288,7 +298,9 @@ void Main::installBalancesWatch()
dev::eth::LogFilter tf;
vector<Address> altCoins;
Address coinsAddr = right160(ethereum()->stateAt(c_config, 1));
Address coinsAddr = getCurrencies();
// TODO: Update for new currencies reg.
for (unsigned i = 0; i < ethereum()->stateAt(coinsAddr, 0); ++i)
altCoins.push_back(right160(ethereum()->stateAt(coinsAddr, i + 1)));
for (auto i: m_myKeys)
@ -450,37 +462,37 @@ static Public stringToPublic(QString const& _a)
return Public();
}
static Address g_newNameReg;
//static Address g_newNameReg;
QString Main::pretty(dev::Address _a) const
{
static std::map<Address, QString> s_memos;
/* static std::map<Address, QString> s_memos;
if (!s_memos.count(_a))
{
if (!g_newNameReg)
g_newNameReg = abiOut<Address>(ethereum()->call(c_newConfig, abiIn(1, (u256)1)));
{*/
// if (!g_newNameReg)
auto g_newNameReg = getNameReg();
if (g_newNameReg)
{
QString s = QString::fromStdString(toString(abiOut<string32>(ethereum()->call(g_newNameReg, abiIn(2, _a)))));
s_memos[_a] = s;
// s_memos[_a] = s;
if (s.size())
return s;
}
}
/* }
else
if (s_memos[_a].size())
return s_memos[_a];
return s_memos[_a];*/
h256 n;
/*
if (h160 nameReg = (u160)ethereum()->stateAt(c_config, 0))
n = ethereum()->stateAt(nameReg, (u160)(_a));
if (!n)
n = ethereum()->stateAt(m_nameReg, (u160)(_a));
*/
return fromRaw(n);
}
@ -505,21 +517,21 @@ Address Main::fromString(QString const& _n) const
if (_n == "(Create Contract)")
return Address();
static std::map<QString, Address> s_memos;
/* static std::map<QString, Address> s_memos;
if (!s_memos.count(_n))
{
if (!g_newNameReg)
g_newNameReg = abiOut<Address>(ethereum()->call(c_newConfig, abiIn(1, (u256)1)));
{*/
// if (!g_newNameReg)
auto g_newNameReg = abiOut<Address>(ethereum()->call(c_newConfig, abiIn(1, (u256)1)));
if (g_newNameReg)
{
Address a = abiOut<Address>(ethereum()->call(g_newNameReg, abiIn(0, ::fromString(_n.toStdString()))));
s_memos[_n] = a;
// s_memos[_n] = a;
if (a)
return a;
}
}
/* }
else
if (s_memos[_n])
return s_memos[_n];
@ -532,14 +544,13 @@ Address Main::fromString(QString const& _n) const
memset(n.data() + sn.size(), 0, 32 - sn.size());
if (_n.size())
{
if (h160 nameReg = (u160)ethereum()->stateAt(c_config, 0))
if (h256 a = ethereum()->stateAt(nameReg, n))
return right160(a);
if (h256 a = ethereum()->stateAt(m_nameReg, n))
return right160(a);
}
}*/
if (_n.size() == 40)
return Address(fromHex(_n.toStdString()));
@ -566,8 +577,9 @@ QString Main::lookup(QString const& _a) const
*/
h256 ret;
if (h160 dnsReg = (u160)ethereum()->stateAt(c_config, 4, 0))
ret = ethereum()->stateAt(dnsReg, n);
// TODO: fix with the new DNSreg contract
// if (h160 dnsReg = (u160)ethereum()->stateAt(c_config, 4, 0))
// ret = ethereum()->stateAt(dnsReg, n);
/* if (!ret)
if (h160 nameReg = (u160)ethereum()->stateAt(c_config, 0, 0))
ret = ethereum()->stateAt(nameReg, n2);
@ -861,8 +873,8 @@ void Main::refreshBalances()
// update all the balance-dependent stuff.
ui->ourAccounts->clear();
u256 totalBalance = 0;
map<Address, tuple<QString, u256, u256>> altCoins;
Address coinsAddr = right160(ethereum()->stateAt(c_config, 1));
/* map<Address, tuple<QString, u256, u256>> altCoins;
Address coinsAddr = getCurrencies();
for (unsigned i = 0; i < ethereum()->stateAt(coinsAddr, 0); ++i)
{
auto n = ethereum()->stateAt(coinsAddr, i + 1);
@ -872,7 +884,7 @@ void Main::refreshBalances()
denom = 1;
// cdebug << n << addr << denom << sha3(h256(n).asBytes());
altCoins[addr] = make_tuple(fromRaw(n), 0, denom);
}
}*/
for (auto i: m_myKeys)
{
u256 b = ethereum()->balanceAt(i.address());
@ -880,18 +892,18 @@ void Main::refreshBalances()
->setData(Qt::UserRole, QByteArray((char const*)i.address().data(), Address::size));
totalBalance += b;
for (auto& c: altCoins)
get<1>(c.second) += (u256)ethereum()->stateAt(c.first, (u160)i.address());
// for (auto& c: altCoins)
// get<1>(c.second) += (u256)ethereum()->stateAt(c.first, (u160)i.address());
}
QString b;
for (auto const& c: altCoins)
/* for (auto const& c: altCoins)
if (get<1>(c.second))
{
stringstream s;
s << setw(toString(get<2>(c.second) - 1).size()) << setfill('0') << (get<1>(c.second) % get<2>(c.second));
b += QString::fromStdString(toString(get<1>(c.second) / get<2>(c.second)) + "." + s.str() + " ") + get<0>(c.second).toUpper() + " | ";
}
}*/
ui->balance->setText(b + QString::fromStdString(formatBalance(totalBalance)));
}
@ -1619,12 +1631,15 @@ void Main::on_data_textChanged()
{
m_data = fromHex(src);
}
else if (src.substr(0, 8) == "contract") // improve this heuristic
else if (src.substr(0, 8) == "contract" || src.substr(0, 2) == "/*") // improve this heuristic
{
dev::solidity::CompilerStack compiler;
try
{
m_data = compiler.compile(src, m_enableOptimizer);
solidity = "<h4>Solidity</h4>";
solidity += "<pre>" + QString::fromStdString(compiler.getInterface()).replace(QRegExp("\\s"), "").toHtmlEscaped() + "</pre>";
solidity += "<pre>" + QString::fromStdString(compiler.getSolidityInterface()).toHtmlEscaped() + "</pre>";
}
catch (dev::Exception const& exception)
{

2
alethzero/MainWin.h

@ -170,6 +170,8 @@ private:
QString prettyU256(dev::u256 _n) const;
QString lookup(QString const& _n) const;
dev::Address getNameReg() const;
dev::Address getCurrencies() const;
void populateDebugger(dev::bytesConstRef r);
void initDebugger();

1
evmjit/evmcc/evmcc.cpp

@ -190,7 +190,6 @@ int main(int argc, char** argv)
data.set(RuntimeData::CallValue, 0xabcd);
data.set(RuntimeData::CallDataSize, 3);
data.set(RuntimeData::GasPrice, 1003);
data.set(RuntimeData::PrevHash, 1003);
data.set(RuntimeData::CoinBase, (u160)Address(101010101010101015));
data.set(RuntimeData::TimeStamp, 1005);
data.set(RuntimeData::Number, 1006);

20
evmjit/libevmjit-cpp/Env.cpp

@ -42,16 +42,15 @@ extern "C"
*o_value = eth2llvm(u);
}
EXPORT void env_create(ExtVMFace* _env, i256* io_gas, i256* _endowment, byte* _initBeg, uint64_t _initSize, h256* o_address)
EXPORT void env_blockhash(ExtVMFace* _env, i256* _number, h256* o_hash)
{
if (_env->depth == 1024)
jit::terminate(jit::ReturnCode::OutOfGas);
assert(_env->depth < 1024);
*o_hash = _env->blockhash(llvm2eth(*_number));
}
EXPORT void env_create(ExtVMFace* _env, i256* io_gas, i256* _endowment, byte* _initBeg, uint64_t _initSize, h256* o_address)
{
auto endowment = llvm2eth(*_endowment);
if (_env->balance(_env->myAddress) >= endowment)
if (_env->balance(_env->myAddress) >= endowment && _env->depth < 1024)
{
_env->subBalance(endowment);
auto gas = llvm2eth(*io_gas);
@ -66,13 +65,8 @@ extern "C"
EXPORT bool env_call(ExtVMFace* _env, i256* io_gas, h256* _receiveAddress, i256* _value, byte* _inBeg, uint64_t _inSize, byte* _outBeg, uint64_t _outSize, h256* _codeAddress)
{
if (_env->depth == 1024)
jit::terminate(jit::ReturnCode::OutOfGas);
assert(_env->depth < 1024);
auto value = llvm2eth(*_value);
if (_env->balance(_env->myAddress) >= value)
if (_env->balance(_env->myAddress) >= value && _env->depth < 1024)
{
_env->subBalance(value);
auto receiveAddress = right160(*_receiveAddress);

1
evmjit/libevmjit-cpp/JitVM.cpp

@ -20,7 +20,6 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const&, uint64_t)
m_data.set(RuntimeData::CallValue, _ext.value);
m_data.set(RuntimeData::CallDataSize, _ext.data.size());
m_data.set(RuntimeData::GasPrice, _ext.gasPrice);
m_data.set(RuntimeData::PrevHash, _ext.previousBlock.hash);
m_data.set(RuntimeData::CoinBase, fromAddress(_ext.currentBlock.coinbaseAddress));
m_data.set(RuntimeData::TimeStamp, _ext.currentBlock.timestamp);
m_data.set(RuntimeData::Number, _ext.currentBlock.number);

7
evmjit/libevmjit/BasicBlock.h

@ -67,6 +67,9 @@ public:
ProgramCounter begin() { return m_beginInstIdx; }
ProgramCounter end() { return m_endInstIdx; }
bool isJumpDest() const { return m_isJumpDest; }
void markAsJumpDest() { m_isJumpDest = true; }
LocalStack& localStack() { return m_stack; }
/// Optimization: propagates values between local stacks in basic blocks
@ -109,6 +112,10 @@ private:
/// How many items higher is the current stack than the initial one.
/// May be negative.
int m_tosOffset = 0;
/// Is the basic block a valid jump destination.
/// JUMPDEST is the first instruction of the basic block.
bool m_isJumpDest = false;
};
}

2
evmjit/libevmjit/Common.h

@ -44,6 +44,8 @@ struct i256
};
static_assert(sizeof(i256) == 32, "Wrong i265 size");
#define UNTESTED assert(false)
}
}
}

152
evmjit/libevmjit/Compiler.cpp

@ -5,8 +5,6 @@
#include <fstream>
#include <chrono>
#include <boost/dynamic_bitset.hpp>
#include <llvm/ADT/PostOrderIterator.h>
#include <llvm/IR/CFG.h>
#include <llvm/IR/Module.h>
@ -42,19 +40,17 @@ Compiler::Compiler(Options const& _options):
void Compiler::createBasicBlocks(bytes const& _bytecode)
{
// FIXME: Simplify this algorithm. All can be done in one pass
std::set<ProgramCounter> splitPoints; // Sorted collections of instruction indices where basic blocks start/end
std::map<ProgramCounter, ProgramCounter> directJumpTargets;
std::vector<ProgramCounter> indirectJumpTargets;
boost::dynamic_bitset<> validJumpTargets(std::max(_bytecode.size(), size_t(1)));
splitPoints.insert(0); // First basic block
validJumpTargets[0] = true;
for (auto curr = _bytecode.begin(); curr != _bytecode.end(); ++curr)
{
ProgramCounter currentPC = curr - _bytecode.begin();
validJumpTargets[currentPC] = true;
auto inst = Instruction(*curr);
switch (inst)
@ -62,21 +58,7 @@ void Compiler::createBasicBlocks(bytes const& _bytecode)
case Instruction::ANY_PUSH:
{
auto val = readPushData(curr, _bytecode.end());
auto next = curr + 1;
if (next == _bytecode.end())
break;
auto nextInst = Instruction(*next);
if (nextInst == Instruction::JUMP || nextInst == Instruction::JUMPI)
{
// Create a block for the JUMP target.
ProgramCounter targetPC = val.ult(_bytecode.size()) ? val.getZExtValue() : _bytecode.size();
splitPoints.insert(targetPC);
ProgramCounter jumpPC = (next - _bytecode.begin());
directJumpTargets[jumpPC] = targetPC;
}
readPushData(curr, _bytecode.end());
break;
}
@ -105,15 +87,6 @@ void Compiler::createBasicBlocks(bytes const& _bytecode)
}
}
// Remove split points generated from jumps out of code or into data.
for (auto it = splitPoints.cbegin(); it != splitPoints.cend();)
{
if (*it > _bytecode.size() || !validJumpTargets[*it])
it = splitPoints.erase(it);
else
++it;
}
for (auto it = splitPoints.cbegin(); it != splitPoints.cend();)
{
auto beginInstIdx = *it;
@ -123,33 +96,39 @@ void Compiler::createBasicBlocks(bytes const& _bytecode)
}
m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc);
m_badJumpBlock = std::unique_ptr<BasicBlock>(new BasicBlock("BadJumpBlock", m_mainFunc, m_builder));
m_jumpTableBlock = std::unique_ptr<BasicBlock>(new BasicBlock("JumpTableBlock", m_mainFunc, m_builder));
for (auto it = directJumpTargets.cbegin(); it != directJumpTargets.cend(); ++it)
{
if (it->second >= _bytecode.size())
{
// Jumping out of code means STOP
m_directJumpTargets[it->first] = m_stopBB;
continue;
}
for (auto it = indirectJumpTargets.cbegin(); it != indirectJumpTargets.cend(); ++it)
basicBlocks.find(*it)->second.markAsJumpDest();
}
auto blockIter = basicBlocks.find(it->second);
if (blockIter != basicBlocks.end())
{
m_directJumpTargets[it->first] = blockIter->second.llvm();
}
else
llvm::BasicBlock* Compiler::getJumpTableBlock()
{
if (!m_jumpTableBlock)
{
m_jumpTableBlock.reset(new BasicBlock("JumpTable", m_mainFunc, m_builder));
InsertPointGuard g{m_builder};
m_builder.SetInsertPoint(m_jumpTableBlock->llvm());
auto dest = m_jumpTableBlock->localStack().pop();
auto switchInstr = m_builder.CreateSwitch(dest, getBadJumpBlock());
for (auto&& p : basicBlocks)
{
clog(JIT) << "Bad JUMP at PC " << it->first
<< ": " << it->second << " is not a valid PC";
m_directJumpTargets[it->first] = m_badJumpBlock->llvm();
if (p.second.isJumpDest())
switchInstr->addCase(Constant::get(p.first), p.second.llvm());
}
}
return m_jumpTableBlock->llvm();
}
for (auto it = indirectJumpTargets.cbegin(); it != indirectJumpTargets.cend(); ++it)
m_indirectJumpTargets.push_back(&basicBlocks.find(*it)->second);
llvm::BasicBlock* Compiler::getBadJumpBlock()
{
if (!m_badJumpBlock)
{
m_badJumpBlock.reset(new BasicBlock("BadJump", m_mainFunc, m_builder));
InsertPointGuard g{m_builder};
m_builder.SetInsertPoint(m_badJumpBlock->llvm());
m_builder.CreateRet(Constant::get(ReturnCode::BadJumpDestination));
}
return m_badJumpBlock->llvm();
}
std::unique_ptr<llvm::Module> Compiler::compile(bytes const& _bytecode, std::string const& _id)
@ -192,25 +171,6 @@ std::unique_ptr<llvm::Module> Compiler::compile(bytes const& _bytecode, std::str
m_builder.SetInsertPoint(m_stopBB);
m_builder.CreateRet(Constant::get(ReturnCode::Stop));
m_builder.SetInsertPoint(m_badJumpBlock->llvm());
m_builder.CreateRet(Constant::get(ReturnCode::BadJumpDestination));
m_builder.SetInsertPoint(m_jumpTableBlock->llvm());
if (m_indirectJumpTargets.size() > 0)
{
auto dest = m_jumpTableBlock->localStack().pop();
auto switchInstr = m_builder.CreateSwitch(dest, m_badJumpBlock->llvm(),
m_indirectJumpTargets.size());
for (auto it = m_indirectJumpTargets.cbegin(); it != m_indirectJumpTargets.cend(); ++it)
{
auto& bb = *it;
auto dest = Constant::get(bb->begin());
switchInstr->addCase(dest, bb->llvm());
}
}
else
m_builder.CreateBr(m_badJumpBlock->llvm());
removeDeadBlocks();
dumpCFGifRequired("blocks-init.dot");
@ -596,16 +556,21 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
case Instruction::JUMP:
case Instruction::JUMPI:
{
// Generate direct jump iff:
// 1. this is not the first instruction in the block
// 2. m_directJumpTargets[currentPC] is defined (meaning that the previous instruction is a PUSH)
// Otherwise generate a indirect jump (a switch).
llvm::BasicBlock* targetBlock = nullptr;
if (currentPC != _basicBlock.begin())
auto target = stack.pop();
if (auto constant = llvm::dyn_cast<llvm::ConstantInt>(target))
{
auto pairIter = m_directJumpTargets.find(currentPC);
if (pairIter != m_directJumpTargets.end())
targetBlock = pairIter->second;
auto&& c = constant->getValue();
if (c.ult(_bytecode.size()))
{
auto v = c.getZExtValue();
auto it = basicBlocks.find(v);
if (it != basicBlocks.end() && it->second.isJumpDest())
targetBlock = it->second.llvm();
}
if (!targetBlock)
targetBlock = getBadJumpBlock();
}
if (inst == Instruction::JUMP)
@ -614,28 +579,30 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
{
// The target address is computed at compile time,
// just pop it without looking...
stack.pop();
m_builder.CreateBr(targetBlock);
}
else
m_builder.CreateBr(m_jumpTableBlock->llvm());
{
stack.push(target);
m_builder.CreateBr(getJumpTableBlock());
}
}
else // JUMPI
{
stack.swap(1);
auto val = stack.pop();
auto zero = Constant::get(0);
auto cond = m_builder.CreateICmpNE(val, zero, "nonzero");
if (targetBlock)
{
stack.pop();
m_builder.CreateCondBr(cond, targetBlock, _nextBasicBlock);
}
else
m_builder.CreateCondBr(cond, m_jumpTableBlock->llvm(), _nextBasicBlock);
{
stack.push(target);
m_builder.CreateCondBr(cond, getJumpTableBlock(), _nextBasicBlock);
}
}
break;
}
@ -666,7 +633,6 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
case Instruction::CALLDATASIZE:
case Instruction::CODESIZE:
case Instruction::GASPRICE:
case Instruction::PREVHASH:
case Instruction::COINBASE:
case Instruction::TIMESTAMP:
case Instruction::NUMBER:
@ -678,6 +644,14 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
break;
}
case Instruction::BLOCKHASH:
{
auto number = stack.pop();
auto hash = _ext.blockhash(number);
stack.push(hash);
break;
}
case Instruction::BALANCE:
{
auto address = stack.pop();
@ -835,6 +809,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
default: // Invalid instruction - runtime exception
{
// TODO: Replace with return statement
_runtimeManager.raiseException(ReturnCode::BadInstruction);
}
@ -871,13 +846,6 @@ void Compiler::removeDeadBlocks()
}
}
while (sthErased);
// Remove jump table block if no predecessors
if (llvm::pred_begin(m_jumpTableBlock->llvm()) == llvm::pred_end(m_jumpTableBlock->llvm()))
{
m_jumpTableBlock->llvm()->eraseFromParent();
m_jumpTableBlock.reset();
}
}
void Compiler::dumpCFGifRequired(std::string const& _dotfilePath)

18
evmjit/libevmjit/Compiler.h

@ -47,6 +47,10 @@ private:
void compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode, class RuntimeManager& _runtimeManager, class Arith256& _arith, class Memory& _memory, class Ext& _ext, class GasMeter& _gasMeter, llvm::BasicBlock* _nextBasicBlock);
llvm::BasicBlock* getJumpTableBlock();
llvm::BasicBlock* getBadJumpBlock();
void removeDeadBlocks();
/// Dumps basic block graph in graphviz format to a file, if option dumpCFG is enabled.
@ -65,22 +69,16 @@ private:
llvm::IRBuilder<> m_builder;
/// Maps a program counter pc to a basic block that starts at pc (if any).
std::map<ProgramCounter, BasicBlock> basicBlocks = {};
/// Maps a pc at which there is a JUMP or JUMPI to the target block of the jump.
std::map<ProgramCounter, llvm::BasicBlock*> m_directJumpTargets = {};
/// A list of possible blocks to which there may be indirect jumps.
std::vector<BasicBlock*> m_indirectJumpTargets = {};
std::map<ProgramCounter, BasicBlock> basicBlocks;
/// Stop basic block - terminates execution with STOP code (0)
llvm::BasicBlock* m_stopBB = nullptr;
/// Block with a jump table.
std::unique_ptr<BasicBlock> m_jumpTableBlock = nullptr;
std::unique_ptr<BasicBlock> m_jumpTableBlock;
/// Default destination for indirect jumps.
std::unique_ptr<BasicBlock> m_badJumpBlock = nullptr;
/// Destination for invalid jumps
std::unique_ptr<BasicBlock> m_badJumpBlock;
/// Main program function
llvm::Function* m_mainFunc = nullptr;

5
evmjit/libevmjit/CompilerHelper.cpp

@ -35,6 +35,11 @@ llvm::Function* CompilerHelper::getMainFunction()
return nullptr;
}
llvm::CallInst* CompilerHelper::createCall(llvm::Function* _func, std::initializer_list<llvm::Value*> const& _args)
{
return getBuilder().CreateCall(_func, {_args.begin(), _args.size()});
}
RuntimeHelper::RuntimeHelper(RuntimeManager& _runtimeManager):
CompilerHelper(_runtimeManager.getBuilder()),

7
evmjit/libevmjit/CompilerHelper.h

@ -31,12 +31,7 @@ protected:
llvm::IRBuilder<>& m_builder;
llvm::IRBuilder<>& getBuilder() { return m_builder; }
template<typename ..._Args>
llvm::CallInst* createCall(llvm::Function* _func, _Args*... _args)
{
llvm::Value* args[] = {_args...};
return getBuilder().CreateCall(_func, args);
}
llvm::CallInst* createCall(llvm::Function* _func, std::initializer_list<llvm::Value*> const& _args);
friend class RuntimeHelper;
};

151
evmjit/libevmjit/Ext.cpp

@ -24,113 +24,132 @@ Ext::Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan):
RuntimeHelper(_runtimeManager),
m_memoryMan(_memoryMan)
{
auto module = getModule();
m_args[0] = m_builder.CreateAlloca(Type::Word, nullptr, "ext.index");
m_args[1] = m_builder.CreateAlloca(Type::Word, nullptr, "ext.value");
m_arg2 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg2");
m_arg3 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg3");
m_arg4 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg4");
m_arg5 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg5");
m_arg6 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg6");
m_arg7 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg7");
m_arg8 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg8");
m_size = m_builder.CreateAlloca(Type::Size, nullptr, "env.size");
}
using Linkage = llvm::GlobalValue::LinkageTypes;
llvm::Type* argsTypes[] = {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr};
m_sload = llvm::Function::Create(llvm::FunctionType::get(Type::Void, {argsTypes, 3}, false), Linkage::ExternalLinkage, "env_sload", module);
m_sstore = llvm::Function::Create(llvm::FunctionType::get(Type::Void, {argsTypes, 3}, false), Linkage::ExternalLinkage, "env_sstore", module);
llvm::Type* sha3ArgsTypes[] = {Type::BytePtr, Type::Size, Type::WordPtr};
m_sha3 = llvm::Function::Create(llvm::FunctionType::get(Type::Void, sha3ArgsTypes, false), Linkage::ExternalLinkage, "env_sha3", module);
llvm::Type* createArgsTypes[] = {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::WordPtr};
m_create = llvm::Function::Create(llvm::FunctionType::get(Type::Void, createArgsTypes, false), Linkage::ExternalLinkage, "env_create", module);
llvm::Type* callArgsTypes[] = {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::BytePtr, Type::Size, Type::WordPtr};
m_call = llvm::Function::Create(llvm::FunctionType::get(Type::Bool, callArgsTypes, false), Linkage::ExternalLinkage, "env_call", module);
using FuncDesc = std::tuple<char const*, llvm::FunctionType*>;
llvm::Type* logArgsTypes[] = {Type::EnvPtr, Type::BytePtr, Type::Size, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr};
m_log = llvm::Function::Create(llvm::FunctionType::get(Type::Void, logArgsTypes, false), Linkage::ExternalLinkage, "env_log", module);
llvm::FunctionType* getFunctionType(llvm::Type* _returnType, std::initializer_list<llvm::Type*> const& _argsTypes)
{
return llvm::FunctionType::get(_returnType, llvm::ArrayRef<llvm::Type*>{_argsTypes.begin(), _argsTypes.size()}, false);
}
llvm::Type* getExtCodeArgsTypes[] = {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()};
m_getExtCode = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, getExtCodeArgsTypes, false), Linkage::ExternalLinkage, "env_getExtCode", module);
std::array<FuncDesc, sizeOf<EnvFunc>::value> const& getEnvFuncDescs()
{
static std::array<FuncDesc, sizeOf<EnvFunc>::value> descs{{
FuncDesc{"env_sload", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
FuncDesc{"env_sstore", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
FuncDesc{"env_sha3", getFunctionType(Type::Void, {Type::BytePtr, Type::Size, Type::WordPtr})},
FuncDesc{"env_balance", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
FuncDesc{"env_create", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::WordPtr})},
FuncDesc{"env_call", getFunctionType(Type::Bool, {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::BytePtr, Type::Size, Type::WordPtr})},
FuncDesc{"env_log", getFunctionType(Type::Void, {Type::EnvPtr, Type::BytePtr, Type::Size, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr})},
FuncDesc{"env_blockhash", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
FuncDesc{"env_getExtCode", getFunctionType(Type::BytePtr, {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()})},
FuncDesc{"ext_calldataload", getFunctionType(Type::Void, {Type::RuntimeDataPtr, Type::WordPtr, Type::WordPtr})},
}};
return descs;
}
// Helper function, not client Env interface
llvm::Type* callDataLoadArgsTypes[] = {Type::RuntimeDataPtr, Type::WordPtr, Type::WordPtr};
m_calldataload = llvm::Function::Create(llvm::FunctionType::get(Type::Void, callDataLoadArgsTypes, false), Linkage::ExternalLinkage, "ext_calldataload", module);
llvm::Function* createFunc(EnvFunc _id, llvm::Module* _module)
{
auto&& desc = getEnvFuncDescs()[static_cast<size_t>(_id)];
return llvm::Function::Create(std::get<1>(desc), llvm::Function::ExternalLinkage, std::get<0>(desc), _module);
}
llvm::Function* Ext::getBalanceFunc()
llvm::Value* Ext::getArgAlloca()
{
if (!m_balance)
auto& a = m_argAllocas[m_argCounter++];
if (!a)
{
llvm::Type* argsTypes[] = {Type::EnvPtr, Type::WordPtr, Type::WordPtr};
m_balance = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argsTypes, false), llvm::Function::ExternalLinkage, "env_balance", getModule());
// FIXME: Improve order and names
InsertPointGuard g{getBuilder()};
getBuilder().SetInsertPoint(getMainFunction()->front().getFirstNonPHI());
a = getBuilder().CreateAlloca(Type::Word, nullptr, "arg");
}
return m_balance;
return a;
}
llvm::Value* Ext::byPtr(llvm::Value* _value)
{
auto a = getArgAlloca();
getBuilder().CreateStore(_value, a);
return a;
}
llvm::CallInst* Ext::createCall(EnvFunc _funcId, std::initializer_list<llvm::Value*> const& _args)
{
auto& func = m_funcs[static_cast<size_t>(_funcId)];
if (!func)
func = createFunc(_funcId, getModule());
m_argCounter = 0;
return getBuilder().CreateCall(func, {_args.begin(), _args.size()});
}
llvm::Value* Ext::sload(llvm::Value* _index)
{
m_builder.CreateStore(_index, m_args[0]);
m_builder.CreateCall3(m_sload, getRuntimeManager().getEnvPtr(), m_args[0], m_args[1]); // Uses native endianness
return m_builder.CreateLoad(m_args[1]);
auto ret = getArgAlloca();
createCall(EnvFunc::sload, {getRuntimeManager().getEnvPtr(), byPtr(_index), ret}); // Uses native endianness
return m_builder.CreateLoad(ret);
}
void Ext::sstore(llvm::Value* _index, llvm::Value* _value)
{
m_builder.CreateStore(_index, m_args[0]);
m_builder.CreateStore(_value, m_args[1]);
m_builder.CreateCall3(m_sstore, getRuntimeManager().getEnvPtr(), m_args[0], m_args[1]); // Uses native endianness
createCall(EnvFunc::sstore, {getRuntimeManager().getEnvPtr(), byPtr(_index), byPtr(_value)}); // Uses native endianness
}
llvm::Value* Ext::calldataload(llvm::Value* _index)
{
m_builder.CreateStore(_index, m_args[0]);
createCall(m_calldataload, getRuntimeManager().getDataPtr(), m_args[0], m_args[1]);
auto ret = m_builder.CreateLoad(m_args[1]);
auto ret = getArgAlloca();
createCall(EnvFunc::calldataload, {getRuntimeManager().getDataPtr(), byPtr(_index), ret});
ret = m_builder.CreateLoad(ret);
return Endianness::toNative(m_builder, ret);
}
llvm::Value* Ext::balance(llvm::Value* _address)
{
auto address = Endianness::toBE(m_builder, _address);
m_builder.CreateStore(address, m_args[0]);
createCall(getBalanceFunc(), getRuntimeManager().getEnvPtr(), m_args[0], m_args[1]);
return m_builder.CreateLoad(m_args[1]);
auto ret = getArgAlloca();
createCall(EnvFunc::balance, {getRuntimeManager().getEnvPtr(), byPtr(address), ret});
return m_builder.CreateLoad(ret);
}
llvm::Value* Ext::blockhash(llvm::Value* _number)
{
auto hash = getArgAlloca();
createCall(EnvFunc::blockhash, {getRuntimeManager().getEnvPtr(), byPtr(_number), hash});
hash = m_builder.CreateLoad(hash);
return Endianness::toNative(getBuilder(), hash);
}
llvm::Value* Ext::create(llvm::Value*& _gas, llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize)
{
m_builder.CreateStore(_gas, m_args[0]);
m_builder.CreateStore(_endowment, m_arg2);
auto gas = byPtr(_gas);
auto ret = getArgAlloca();
auto begin = m_memoryMan.getBytePtr(_initOff);
auto size = m_builder.CreateTrunc(_initSize, Type::Size, "size");
createCall(m_create, getRuntimeManager().getEnvPtr(), m_args[0], m_arg2, begin, size, m_args[1]);
_gas = m_builder.CreateLoad(m_args[0]); // Return gas
llvm::Value* address = m_builder.CreateLoad(m_args[1]);
createCall(EnvFunc::create, {getRuntimeManager().getEnvPtr(), gas, byPtr(_endowment), begin, size, ret});
_gas = m_builder.CreateLoad(gas); // Return gas
llvm::Value* address = m_builder.CreateLoad(ret);
address = Endianness::toNative(m_builder, address);
return address;
}
llvm::Value* Ext::call(llvm::Value*& _gas, llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize, llvm::Value* _codeAddress)
{
m_builder.CreateStore(_gas, m_args[0]);
auto gas = byPtr(_gas);
auto receiveAddress = Endianness::toBE(m_builder, _receiveAddress);
m_builder.CreateStore(receiveAddress, m_arg2);
m_builder.CreateStore(_value, m_arg3);
auto inBeg = m_memoryMan.getBytePtr(_inOff);
auto inSize = m_builder.CreateTrunc(_inSize, Type::Size, "in.size");
auto outBeg = m_memoryMan.getBytePtr(_outOff);
auto outSize = m_builder.CreateTrunc(_outSize, Type::Size, "out.size");
auto codeAddress = Endianness::toBE(m_builder, _codeAddress);
m_builder.CreateStore(codeAddress, m_arg8);
auto ret = createCall(m_call, getRuntimeManager().getEnvPtr(), m_args[0], m_arg2, m_arg3, inBeg, inSize, outBeg, outSize, m_arg8);
_gas = m_builder.CreateLoad(m_args[0]); // Return gas
auto ret = createCall(EnvFunc::call, {getRuntimeManager().getEnvPtr(), gas, byPtr(receiveAddress), byPtr(_value), inBeg, inSize, outBeg, outSize, byPtr(codeAddress)});
_gas = m_builder.CreateLoad(gas); // Return gas
return m_builder.CreateZExt(ret, Type::Word, "ret");
}
@ -138,8 +157,9 @@ llvm::Value* Ext::sha3(llvm::Value* _inOff, llvm::Value* _inSize)
{
auto begin = m_memoryMan.getBytePtr(_inOff);
auto size = m_builder.CreateTrunc(_inSize, Type::Size, "size");
createCall(m_sha3, begin, size, m_args[1]);
llvm::Value* hash = m_builder.CreateLoad(m_args[1]);
auto ret = getArgAlloca();
createCall(EnvFunc::sha3, {begin, size, ret});
llvm::Value* hash = m_builder.CreateLoad(ret);
hash = Endianness::toNative(m_builder, hash);
return hash;
}
@ -147,8 +167,7 @@ llvm::Value* Ext::sha3(llvm::Value* _inOff, llvm::Value* _inSize)
MemoryRef Ext::getExtCode(llvm::Value* _addr)
{
auto addr = Endianness::toBE(m_builder, _addr);
m_builder.CreateStore(addr, m_args[0]);
auto code = createCall(m_getExtCode, getRuntimeManager().getEnvPtr(), m_args[0], m_size);
auto code = createCall(EnvFunc::getExtCode, {getRuntimeManager().getEnvPtr(), byPtr(addr), m_size});
auto codeSize = m_builder.CreateLoad(m_size);
auto codeSize256 = m_builder.CreateZExt(codeSize, Type::Word);
return {code, codeSize256};
@ -158,7 +177,7 @@ void Ext::log(llvm::Value* _memIdx, llvm::Value* _numBytes, std::array<llvm::Val
{
auto begin = m_memoryMan.getBytePtr(_memIdx);
auto size = m_builder.CreateTrunc(_numBytes, Type::Size, "size");
llvm::Value* args[] = {getRuntimeManager().getEnvPtr(), begin, size, m_arg2, m_arg3, m_arg4, m_arg5};
llvm::Value* args[] = {getRuntimeManager().getEnvPtr(), begin, size, getArgAlloca(), getArgAlloca(), getArgAlloca(), getArgAlloca()};
auto topicArgPtr = &args[3];
for (auto&& topic : _topics)
@ -170,7 +189,7 @@ void Ext::log(llvm::Value* _memIdx, llvm::Value* _numBytes, std::array<llvm::Val
++topicArgPtr;
}
m_builder.CreateCall(m_log, args);
createCall(EnvFunc::log, {args[0], args[1], args[2], args[3], args[4], args[5], args[6]}); // TODO: use std::initializer_list<>
}
}

50
evmjit/libevmjit/Ext.h

@ -18,6 +18,28 @@ struct MemoryRef
llvm::Value* size;
};
template<typename _EnumT>
struct sizeOf
{
static const size_t value = static_cast<size_t>(_EnumT::_size);
};
enum class EnvFunc
{
sload,
sstore,
sha3,
balance,
create,
call,
log,
blockhash,
getExtCode,
calldataload, // Helper function, not client Env interface
_size
};
class Ext : public RuntimeHelper
{
public:
@ -30,6 +52,7 @@ public:
llvm::Value* calldataload(llvm::Value* _index);
llvm::Value* create(llvm::Value*& _gas, llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize);
llvm::Value* call(llvm::Value*& _gas, llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize, llvm::Value* _codeAddress);
llvm::Value* blockhash(llvm::Value* _number);
llvm::Value* sha3(llvm::Value* _inOff, llvm::Value* _inSize);
MemoryRef getExtCode(llvm::Value* _addr);
@ -39,27 +62,16 @@ public:
private:
Memory& m_memoryMan;
llvm::Value* m_args[2];
llvm::Value* m_arg2;
llvm::Value* m_arg3;
llvm::Value* m_arg4;
llvm::Value* m_arg5;
llvm::Value* m_arg6;
llvm::Value* m_arg7;
llvm::Value* m_arg8;
llvm::Value* m_size;
llvm::Value* m_data = nullptr;
llvm::Function* m_sload;
llvm::Function* m_sstore;
llvm::Function* m_calldataload;
llvm::Function* m_balance = nullptr;
llvm::Function* m_create;
llvm::Function* m_call;
llvm::Function* m_sha3;
llvm::Function* m_getExtCode;
llvm::Function* m_log;
llvm::Function* getBalanceFunc();
std::array<llvm::Function*, sizeOf<EnvFunc>::value> m_funcs = {};
std::array<llvm::Value*, 8> m_argAllocas = {};
size_t m_argCounter = 0;
llvm::CallInst* createCall(EnvFunc _funcId, std::initializer_list<llvm::Value*> const& _args);
llvm::Value* getArgAlloca();
llvm::Value* byPtr(llvm::Value* _value);
};

4
evmjit/libevmjit/GasMeter.cpp

@ -119,7 +119,7 @@ void GasMeter::count(Instruction _inst)
if (!m_checkCall)
{
// Create gas check call with mocked block cost at begining of current cost-block
m_checkCall = createCall(m_gasCheckFunc, m_runtimeManager.getRuntimePtr(), llvm::UndefValue::get(Type::Word));
m_checkCall = createCall(m_gasCheckFunc, {m_runtimeManager.getRuntimePtr(), llvm::UndefValue::get(Type::Word)});
}
m_blockCost += getStepCost(_inst);
@ -127,7 +127,7 @@ void GasMeter::count(Instruction _inst)
void GasMeter::count(llvm::Value* _cost)
{
createCall(m_gasCheckFunc, m_runtimeManager.getRuntimePtr(), _cost);
createCall(m_gasCheckFunc, {m_runtimeManager.getRuntimePtr(), _cost});
}
void GasMeter::countExp(llvm::Value* _exponent)

2
evmjit/libevmjit/Instruction.h

@ -58,7 +58,7 @@ enum class Instruction: uint8_t
EXTCODESIZE, ///< get external code size (from another contract)
EXTCODECOPY, ///< copy external code (from another contract)
PREVHASH = 0x40, ///< get hash of most recent complete block
BLOCKHASH = 0x40, ///< get hash of most recent complete block
COINBASE, ///< get the block's coinbase address
TIMESTAMP, ///< get the block's timestamp
NUMBER, ///< get the block's number

8
evmjit/libevmjit/Memory.cpp

@ -146,18 +146,18 @@ llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType, GasMet
llvm::Value* Memory::loadWord(llvm::Value* _addr)
{
return createCall(m_loadWord, getRuntimeManager().getRuntimePtr(), _addr);
return createCall(m_loadWord, {getRuntimeManager().getRuntimePtr(), _addr});
}
void Memory::storeWord(llvm::Value* _addr, llvm::Value* _word)
{
createCall(m_storeWord, getRuntimeManager().getRuntimePtr(), _addr, _word);
createCall(m_storeWord, {getRuntimeManager().getRuntimePtr(), _addr, _word});
}
void Memory::storeByte(llvm::Value* _addr, llvm::Value* _word)
{
auto byte = m_builder.CreateTrunc(_word, Type::Byte, "byte");
createCall(m_storeByte, getRuntimeManager().getRuntimePtr(), _addr, byte);
createCall(m_storeByte, {getRuntimeManager().getRuntimePtr(), _addr, byte});
}
llvm::Value* Memory::getData()
@ -181,7 +181,7 @@ llvm::Value* Memory::getBytePtr(llvm::Value* _index)
void Memory::require(llvm::Value* _offset, llvm::Value* _size)
{
createCall(m_require, getRuntimeManager().getRuntimePtr(), _offset, _size);
createCall(m_require, {getRuntimeManager().getRuntimePtr(), _offset, _size});
}
void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIdx,

23
evmjit/libevmjit/Runtime.cpp

@ -11,29 +11,12 @@ namespace eth
{
namespace jit
{
namespace
{
jmp_buf_ref g_currJmpBuf;
}
jmp_buf_ref Runtime::getCurrJmpBuf()
{
return g_currJmpBuf;
}
Runtime::Runtime(RuntimeData* _data, Env* _env):
Runtime::Runtime(RuntimeData* _data, Env* _env) :
m_data(*_data),
m_env(*_env),
m_currJmpBuf(m_jmpBuf),
m_prevJmpBuf(g_currJmpBuf)
{
g_currJmpBuf = m_jmpBuf;
}
Runtime::~Runtime()
{
g_currJmpBuf = m_prevJmpBuf;
}
m_currJmpBuf(m_jmpBuf)
{}
bytes Runtime::getReturnData() const // FIXME: Reconsider returning by copy
{

3
evmjit/libevmjit/Runtime.h

@ -32,7 +32,6 @@ class Runtime
{
public:
Runtime(RuntimeData* _data, Env* _env);
~Runtime();
Runtime(const Runtime&) = delete;
void operator=(const Runtime&) = delete;
@ -43,7 +42,6 @@ public:
bytes getReturnData() const;
jmp_buf_ref getJmpBuf() { return m_jmpBuf; }
static jmp_buf_ref getCurrJmpBuf();
private:
RuntimeData& m_data; ///< Pointer to data. Expected by compiled contract.
@ -51,7 +49,6 @@ private:
jmp_buf_ref m_currJmpBuf; ///< Pointer to jump buffer. Expected by compiled contract.
byte* m_memoryData = nullptr;
i256 m_memorySize = {};
jmp_buf_ref m_prevJmpBuf;
std::jmp_buf m_jmpBuf;
StackImpl m_stack;
MemoryImpl m_memory;

1
evmjit/libevmjit/RuntimeData.h

@ -21,7 +21,6 @@ struct RuntimeData
CallValue,
CallDataSize,
GasPrice,
PrevHash,
CoinBase,
TimeStamp,
Number,

2
evmjit/libevmjit/RuntimeManager.cpp

@ -63,7 +63,6 @@ llvm::Twine getName(RuntimeData::Index _index)
case RuntimeData::CallValue: return "callvalue";
case RuntimeData::CallDataSize: return "calldatasize";
case RuntimeData::GasPrice: return "gasprice";
case RuntimeData::PrevHash: return "prevhash";
case RuntimeData::CoinBase: return "coinbase";
case RuntimeData::TimeStamp: return "timestamp";
case RuntimeData::Number: return "number";
@ -154,7 +153,6 @@ llvm::Value* RuntimeManager::get(Instruction _inst)
case Instruction::CALLVALUE: return get(RuntimeData::CallValue);
case Instruction::CALLDATASIZE: return get(RuntimeData::CallDataSize);
case Instruction::GASPRICE: return get(RuntimeData::GasPrice);
case Instruction::PREVHASH: return get(RuntimeData::PrevHash);
case Instruction::COINBASE: return get(RuntimeData::CoinBase);
case Instruction::TIMESTAMP: return get(RuntimeData::TimeStamp);
case Instruction::NUMBER: return get(RuntimeData::Number);

9
evmjit/libevmjit/Utils.cpp

@ -1,8 +1,7 @@
#include <csetjmp>
#include <llvm/ADT/APInt.h>
#include "Utils.h"
#include "Instruction.h"
#include "Runtime.h"
namespace dev
{
@ -55,12 +54,6 @@ llvm::APInt readPushData(bytes::const_iterator& _curr, bytes::const_iterator _en
return value;
}
void terminate(ReturnCode _returnCode)
{
auto jmpBuf = Runtime::getCurrJmpBuf();
std::longjmp(jmpBuf, static_cast<int>(_returnCode));
}
}
}
}

2
evmjit/libevmjit/Utils.h

@ -17,8 +17,6 @@ struct JIT: public NoteChannel { static const char* name() { return "JIT"; } };
u256 llvm2eth(i256);
i256 eth2llvm(u256);
void terminate(ReturnCode _returnCode);
}
}
}

17
libsolidity/AST.cpp

@ -27,6 +27,8 @@
#include <libsolidity/Exceptions.h>
#include <libsolidity/AST_accept.h>
#include <libdevcrypto/SHA3.h>
using namespace std;
namespace dev
@ -50,18 +52,17 @@ void ContractDefinition::checkTypeRequirements()
function->checkTypeRequirements();
}
vector<FunctionDefinition const*> ContractDefinition::getInterfaceFunctions() const
map<FixedHash<4>, FunctionDefinition const*> ContractDefinition::getInterfaceFunctions() const
{
vector<FunctionDefinition const*> exportedFunctions;
map<FixedHash<4>, FunctionDefinition const*> exportedFunctions;
for (ASTPointer<FunctionDefinition> const& f: m_definedFunctions)
if (f->isPublic() && f->getName() != getName())
exportedFunctions.push_back(f.get());
auto compareNames = [](FunctionDefinition const* _a, FunctionDefinition const* _b)
{
return _a->getName().compare(_b->getName()) < 0;
};
{
FixedHash<4> hash(dev::sha3(f->getCanonicalSignature()));
auto res = exportedFunctions.insert(std::make_pair(hash,f.get()));
solAssert(res.second, "Hash collision at Function Definition Hash calculation");
}
sort(exportedFunctions.begin(), exportedFunctions.end(), compareNames);
return exportedFunctions;
}

5
libsolidity/AST.h

@ -183,8 +183,9 @@ public:
/// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; }
/// Returns the functions that make up the calling interface in the intended order.
std::vector<FunctionDefinition const*> getInterfaceFunctions() const;
/// @returns a map of canonical function signatures to FunctionDefinitions
/// as intended for use by the ABI.
std::map<FixedHash<4>, FunctionDefinition const*> getInterfaceFunctions() const;
/// Returns the constructor or nullptr if no constructor was specified
FunctionDefinition const* getConstructor() const;

1
libsolidity/CMakeLists.txt

@ -28,6 +28,7 @@ endif()
target_link_libraries(${EXECUTABLE} ${JSONCPP_LIBRARIES})
target_link_libraries(${EXECUTABLE} evmcore)
target_link_libraries(${EXECUTABLE} devcore)
target_link_libraries(${EXECUTABLE} devcrypto)
install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )

44
libsolidity/Compiler.cpp

@ -100,7 +100,7 @@ void Compiler::appendConstructorCall(FunctionDefinition const& _constructor)
{
m_context << u256(argumentSize);
m_context.appendProgramSize();
m_context << u256(1); // copy it to byte one as expected for ABI calls
m_context << u256(CompilerUtils::dataStartOffset); // copy it to byte four as expected for ABI calls
m_context << eth::Instruction::CODECOPY;
appendCalldataUnpacker(_constructor, true);
}
@ -118,35 +118,27 @@ set<FunctionDefinition const*> Compiler::getFunctionsNeededByConstructor(Functio
void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
{
vector<FunctionDefinition const*> interfaceFunctions = _contract.getInterfaceFunctions();
vector<eth::AssemblyItem> callDataUnpackerEntryPoints;
if (interfaceFunctions.size() > 255)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("More than 255 public functions for contract."));
// retrieve the first byte of the call data, which determines the called function
// @todo This code had a jump table in a previous version which was more efficient but also
// error prone (due to the optimizer and variable length tag addresses)
m_context << u256(1) << u256(0) // some constants
<< eth::dupInstruction(1) << eth::Instruction::CALLDATALOAD
<< eth::dupInstruction(2) << eth::Instruction::BYTE
<< eth::dupInstruction(2);
// stack here: 1 0 <funid> 0, stack top will be counted up until it matches funid
for (unsigned funid = 0; funid < interfaceFunctions.size(); ++funid)
map<FixedHash<4>, FunctionDefinition const*> interfaceFunctions = _contract.getInterfaceFunctions();
map<FixedHash<4>, const eth::AssemblyItem> callDataUnpackerEntryPoints;
// retrieve the function signature hash from the calldata
m_context << u256(1) << u256(0);
CompilerUtils(m_context).loadFromMemory(0, 4, false, true);
// stack now is: 1 0 <funhash>
// for (auto it = interfaceFunctions.cbegin(); it != interfaceFunctions.cend(); ++it)
for (auto const& it: interfaceFunctions)
{
callDataUnpackerEntryPoints.push_back(m_context.newTag());
m_context << eth::dupInstruction(2) << eth::dupInstruction(2) << eth::Instruction::EQ;
m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.back());
if (funid < interfaceFunctions.size() - 1)
m_context << eth::dupInstruction(4) << eth::Instruction::ADD;
callDataUnpackerEntryPoints.insert(std::make_pair(it.first, m_context.newTag()));
m_context << eth::dupInstruction(1) << u256(FixedHash<4>::Arith(it.first)) << eth::Instruction::EQ;
m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.at(it.first));
}
m_context << eth::Instruction::STOP; // function not found
for (unsigned funid = 0; funid < interfaceFunctions.size(); ++funid)
for (auto const& it: interfaceFunctions)
{
FunctionDefinition const& function = *interfaceFunctions[funid];
m_context << callDataUnpackerEntryPoints[funid];
FunctionDefinition const& function = *it.second;
m_context << callDataUnpackerEntryPoints.at(it.first);
eth::AssemblyItem returnTag = m_context.pushNewTag();
appendCalldataUnpacker(function);
m_context.appendJumpTo(m_context.getFunctionEntryLabel(function));
@ -158,7 +150,7 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
unsigned Compiler::appendCalldataUnpacker(FunctionDefinition const& _function, bool _fromMemory)
{
// We do not check the calldata size, everything is zero-padded.
unsigned dataOffset = 1;
unsigned dataOffset = CompilerUtils::dataStartOffset; // the 4 bytes of the function hash signature
//@todo this can be done more efficiently, saving some CALLDATALOAD calls
for (ASTPointer<VariableDeclaration> const& var: _function.getParameters())
{

13
libsolidity/CompilerStack.cpp

@ -16,6 +16,7 @@
*/
/**
* @author Christian <c@ethdev.com>
* @author Gav Wood <g@ethdev.com>
* @date 2014
* Full-stack compiler that converts a source code string to bytecode.
*/
@ -140,10 +141,15 @@ void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractN
string const& CompilerStack::getInterface(string const& _contractName) const
{
return getJsonDocumentation(_contractName, DocumentationType::ABI_INTERFACE);
return getMetadata(_contractName, DocumentationType::ABI_INTERFACE);
}
string const& CompilerStack::getJsonDocumentation(string const& _contractName, DocumentationType _type) const
string const& CompilerStack::getSolidityInterface(string const& _contractName) const
{
return getMetadata(_contractName, DocumentationType::ABI_SOLIDITY_INTERFACE);
}
string const& CompilerStack::getMetadata(string const& _contractName, DocumentationType _type) const
{
if (!m_parseSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
@ -162,6 +168,9 @@ string const& CompilerStack::getJsonDocumentation(string const& _contractName, D
case DocumentationType::ABI_INTERFACE:
doc = &contract.interface;
break;
case DocumentationType::ABI_SOLIDITY_INTERFACE:
doc = &contract.solidityInterface;
break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type."));
}

12
libsolidity/CompilerStack.h

@ -16,6 +16,7 @@
*/
/**
* @author Christian <c@ethdev.com>
* @author Gav Wood <g@ethdev.com>
* @date 2014
* Full-stack compiler that converts a source code string to bytecode.
*/
@ -43,7 +44,8 @@ enum class DocumentationType: uint8_t
{
NATSPEC_USER = 1,
NATSPEC_DEV,
ABI_INTERFACE
ABI_INTERFACE,
ABI_SOLIDITY_INTERFACE
};
/**
@ -81,11 +83,14 @@ public:
/// Returns a string representing the contract interface in JSON.
/// Prerequisite: Successful call to parse or compile.
std::string const& getInterface(std::string const& _contractName = "") const;
/// Returns a string representing the contract interface in Solidity.
/// Prerequisite: Successful call to parse or compile.
std::string const& getSolidityInterface(std::string const& _contractName = "") const;
/// Returns a string representing the contract's documentation in JSON.
/// Prerequisite: Successful call to parse or compile.
/// @param type The type of the documentation to get.
/// Can be one of 3 types defined at @c DocumentationType
std::string const& getJsonDocumentation(std::string const& _contractName, DocumentationType _type) const;
/// Can be one of 4 types defined at @c DocumentationType
std::string const& getMetadata(std::string const& _contractName, DocumentationType _type) const;
/// @returns the previously used scanner, useful for counting lines during error reporting.
Scanner const& getScanner(std::string const& _sourceName = "") const;
@ -118,6 +123,7 @@ private:
bytes bytecode;
std::shared_ptr<InterfaceHandler> interfaceHandler;
mutable std::unique_ptr<std::string const> interface;
mutable std::unique_ptr<std::string const> solidityInterface;
mutable std::unique_ptr<std::string const> userDocumentation;
mutable std::unique_ptr<std::string const> devDocumentation;

2
libsolidity/CompilerUtils.cpp

@ -31,6 +31,8 @@ namespace dev
namespace solidity
{
const unsigned int CompilerUtils::dataStartOffset = 4;
void CompilerUtils::loadFromMemory(unsigned _offset, unsigned _bytes, bool _leftAligned, bool _fromCalldata)
{
if (_bytes == 0)

4
libsolidity/CompilerUtils.h

@ -58,10 +58,14 @@ public:
static unsigned getSizeOnStack(std::vector<T> const& _variables);
static unsigned getSizeOnStack(std::vector<std::shared_ptr<Type const>> const& _variableTypes);
/// Bytes we need to the start of call data.
/// - The size in bytes of the function (hash) identifier.
static const unsigned int dataStartOffset;
private:
CompilerContext& m_context;
};
template <class T>
unsigned CompilerUtils::getSizeOnStack(std::vector<T> const& _variables)
{

72
libsolidity/ExpressionCompiler.cpp

@ -256,6 +256,61 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
CompilerUtils(m_context).storeInMemory(0);
m_context << u256(32) << u256(0) << eth::Instruction::SHA3;
break;
case Location::LOG0:
arguments.front()->accept(*this);
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
// @todo move this once we actually use memory
CompilerUtils(m_context).storeInMemory(0);
m_context << u256(32) << u256(0) << eth::Instruction::LOG0;
break;
case Location::LOG1:
arguments[1]->accept(*this);
arguments[0]->accept(*this);
appendTypeConversion(*arguments[1]->getType(), *function.getParameterTypes()[1], true);
appendTypeConversion(*arguments[0]->getType(), *function.getParameterTypes()[0], true);
// @todo move this once we actually use memory
CompilerUtils(m_context).storeInMemory(0);
m_context << u256(32) << u256(0) << eth::Instruction::LOG1;
break;
case Location::LOG2:
arguments[2]->accept(*this);
arguments[1]->accept(*this);
arguments[0]->accept(*this);
appendTypeConversion(*arguments[2]->getType(), *function.getParameterTypes()[2], true);
appendTypeConversion(*arguments[1]->getType(), *function.getParameterTypes()[1], true);
appendTypeConversion(*arguments[0]->getType(), *function.getParameterTypes()[0], true);
// @todo move this once we actually use memory
CompilerUtils(m_context).storeInMemory(0);
m_context << u256(32) << u256(0) << eth::Instruction::LOG2;
break;
case Location::LOG3:
arguments[3]->accept(*this);
arguments[2]->accept(*this);
arguments[1]->accept(*this);
arguments[0]->accept(*this);
appendTypeConversion(*arguments[3]->getType(), *function.getParameterTypes()[3], true);
appendTypeConversion(*arguments[2]->getType(), *function.getParameterTypes()[2], true);
appendTypeConversion(*arguments[1]->getType(), *function.getParameterTypes()[1], true);
appendTypeConversion(*arguments[0]->getType(), *function.getParameterTypes()[0], true);
// @todo move this once we actually use memory
CompilerUtils(m_context).storeInMemory(0);
m_context << u256(32) << u256(0) << eth::Instruction::LOG3;
break;
case Location::LOG4:
arguments[4]->accept(*this);
arguments[3]->accept(*this);
arguments[2]->accept(*this);
arguments[1]->accept(*this);
arguments[0]->accept(*this);
appendTypeConversion(*arguments[4]->getType(), *function.getParameterTypes()[4], true);
appendTypeConversion(*arguments[3]->getType(), *function.getParameterTypes()[3], true);
appendTypeConversion(*arguments[2]->getType(), *function.getParameterTypes()[2], true);
appendTypeConversion(*arguments[1]->getType(), *function.getParameterTypes()[1], true);
appendTypeConversion(*arguments[0]->getType(), *function.getParameterTypes()[0], true);
// @todo move this once we actually use memory
CompilerUtils(m_context).storeInMemory(0);
m_context << u256(32) << u256(0) << eth::Instruction::LOG4;
break;
case Location::ECRECOVER:
case Location::SHA256:
case Location::RIPEMD160:
@ -335,7 +390,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
case Type::Category::CONTRACT:
{
ContractType const& type = dynamic_cast<ContractType const&>(*_memberAccess.getExpression().getType());
m_context << type.getFunctionIndex(member);
m_context << type.getFunctionIdentifier(member);
break;
}
case Type::Category::MAGIC:
@ -590,7 +645,11 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio
{
solAssert(_arguments.size() == _functionType.getParameterTypes().size(), "");
unsigned dataOffset = _options.bare ? 0 : 1; // reserve one byte for the function index
_options.obtainAddress();
if (!_options.bare)
CompilerUtils(m_context).storeInMemory(0, CompilerUtils::dataStartOffset);
unsigned dataOffset = _options.bare ? 0 : CompilerUtils::dataStartOffset; // reserve 4 bytes for the function's hash identifier
for (unsigned i = 0; i < _arguments.size(); ++i)
{
_arguments[i]->accept(*this);
@ -617,12 +676,13 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio
_options.obtainValue();
else
m_context << u256(0);
_options.obtainAddress();
if (!_options.bare)
m_context << u256(0) << eth::Instruction::MSTORE8;
m_context << eth::dupInstruction(6); //copy contract address
m_context << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB
<< eth::Instruction::CALL
<< eth::Instruction::POP; // @todo do not ignore failure indicator
<< eth::Instruction::POP // @todo do not ignore failure indicator
<< eth::Instruction::POP; // pop contract address
if (retSize > 0)
{
bool const leftAligned = firstType->getCategory() == Type::Category::STRING;

1
libsolidity/ExpressionCompiler.h

@ -16,6 +16,7 @@
*/
/**
* @author Christian <c@ethdev.com>
* @author Gav Wood <g@ethdev.com>
* @date 2014
* Solidity AST to EVM bytecode compiler for expressions.
*/

56
libsolidity/GlobalContext.cpp

@ -16,6 +16,7 @@
*/
/**
* @author Christian <c@ethdev.com>
* @author Gav Wood <g@ethdev.com>
* @date 2014
* Container of the (implicit and explicit) global objects.
*/
@ -33,33 +34,54 @@ namespace solidity
{
GlobalContext::GlobalContext():
// TODO: make this cleaner.
m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{make_shared<MagicVariableDeclaration>("block", make_shared<MagicType>(MagicType::Kind::BLOCK)),
make_shared<MagicVariableDeclaration>("msg", make_shared<MagicType>(MagicType::Kind::MSG)),
make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::TX)),
make_shared<MagicVariableDeclaration>("suicide",
make_shared<MagicVariableDeclaration>("msg", make_shared<MagicType>(MagicType::Kind::MSG)),
make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::TX)),
make_shared<MagicVariableDeclaration>("suicide",
make_shared<FunctionType>(TypePointers({std::make_shared<IntegerType>(0,
IntegerType::Modifier::ADDRESS)}),
TypePointers(),
FunctionType::Location::SUICIDE)),
make_shared<MagicVariableDeclaration>("sha3",
TypePointers(),
FunctionType::Location::SUICIDE)),
make_shared<MagicVariableDeclaration>("sha3",
make_shared<FunctionType>(TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
FunctionType::Location::SHA3)),
make_shared<MagicVariableDeclaration>("sha256",
TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
FunctionType::Location::SHA3)),
make_shared<MagicVariableDeclaration>("log0",
make_shared<FunctionType>(TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
FunctionType::Location::SHA256)),
make_shared<MagicVariableDeclaration>("ecrecover",
TypePointers(),
FunctionType::Location::LOG0)),
make_shared<MagicVariableDeclaration>("log1",
make_shared<FunctionType>(TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH), std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
TypePointers(),
FunctionType::Location::LOG1)),
make_shared<MagicVariableDeclaration>("log2",
make_shared<FunctionType>(TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH), std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH), std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
TypePointers(),
FunctionType::Location::LOG2)),
make_shared<MagicVariableDeclaration>("log3",
make_shared<FunctionType>(TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH), std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH), std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH), std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
TypePointers(),
FunctionType::Location::LOG3)),
make_shared<MagicVariableDeclaration>("log4",
make_shared<FunctionType>(TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH), std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH), std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH), std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH), std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
TypePointers(),
FunctionType::Location::LOG4)),
make_shared<MagicVariableDeclaration>("sha256",
make_shared<FunctionType>(TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
FunctionType::Location::SHA256)),
make_shared<MagicVariableDeclaration>("ecrecover",
make_shared<FunctionType>(TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH),
std::make_shared<IntegerType>(8, IntegerType::Modifier::HASH),
std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH),
std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
TypePointers({std::make_shared<IntegerType>(0, IntegerType::Modifier::ADDRESS)}),
FunctionType::Location::ECRECOVER)),
make_shared<MagicVariableDeclaration>("ripemd160",
TypePointers({std::make_shared<IntegerType>(0, IntegerType::Modifier::ADDRESS)}),
FunctionType::Location::ECRECOVER)),
make_shared<MagicVariableDeclaration>("ripemd160",
make_shared<FunctionType>(TypePointers({std::make_shared<IntegerType>(256, IntegerType::Modifier::HASH)}),
TypePointers({std::make_shared<IntegerType>(160, IntegerType::Modifier::HASH)}),
FunctionType::Location::RIPEMD160))})
TypePointers({std::make_shared<IntegerType>(160, IntegerType::Modifier::HASH)}),
FunctionType::Location::RIPEMD160))})
{
}

48
libsolidity/InterfaceHandler.cpp

@ -2,6 +2,7 @@
#include <libsolidity/InterfaceHandler.h>
#include <libsolidity/AST.h>
#include <libsolidity/CompilerStack.h>
using namespace std;
namespace dev
{
@ -26,6 +27,8 @@ std::unique_ptr<std::string> InterfaceHandler::getDocumentation(ContractDefiniti
return getDevDocumentation(_contractDef);
case DocumentationType::ABI_INTERFACE:
return getABIInterface(_contractDef);
case DocumentationType::ABI_SOLIDITY_INTERFACE:
return getABISolidityInterface(_contractDef);
}
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation type"));
@ -36,7 +39,7 @@ std::unique_ptr<std::string> InterfaceHandler::getABIInterface(ContractDefinitio
{
Json::Value methods(Json::arrayValue);
for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions())
for (auto const& it: _contractDef.getInterfaceFunctions())
{
Json::Value method;
Json::Value inputs(Json::arrayValue);
@ -55,24 +58,47 @@ std::unique_ptr<std::string> InterfaceHandler::getABIInterface(ContractDefinitio
return params;
};
method["name"] = f->getName();
method["constant"] = f->isDeclaredConst();
method["inputs"] = populateParameters(f->getParameters());
method["outputs"] = populateParameters(f->getReturnParameters());
method["name"] = it.second->getName();
method["constant"] = it.second->isDeclaredConst();
method["inputs"] = populateParameters(it.second->getParameters());
method["outputs"] = populateParameters(it.second->getReturnParameters());
methods.append(method);
}
return std::unique_ptr<std::string>(new std::string(m_writer.write(methods)));
}
unique_ptr<string> InterfaceHandler::getABISolidityInterface(ContractDefinition const& _contractDef)
{
string ret = "contract " + _contractDef.getName() + "{";
for (auto const& it: _contractDef.getInterfaceFunctions())
{
FunctionDefinition const* f = it.second;
auto populateParameters = [](vector<ASTPointer<VariableDeclaration>> const& _vars)
{
string r = "";
for (ASTPointer<VariableDeclaration> const& var: _vars)
r += (r.size() ? "," : "(") + var->getType()->toString() + " " + var->getName();
return r.size() ? r + ")" : "()";
};
ret += "function " + f->getName() + populateParameters(f->getParameters()) + (f->isDeclaredConst() ? "constant " : "");
if (f->getReturnParameters().size())
ret += "returns" + populateParameters(f->getReturnParameters());
else if (ret.back() == ' ')
ret.pop_back();
ret += "{}";
}
return unique_ptr<string>(new string(ret + "}"));
}
std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefinition const& _contractDef)
{
Json::Value doc;
Json::Value methods(Json::objectValue);
for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions())
for (auto const& it: _contractDef.getInterfaceFunctions())
{
Json::Value user;
auto strPtr = f->getDocumentation();
auto strPtr = it.second->getDocumentation();
if (strPtr)
{
resetUser();
@ -80,7 +106,7 @@ std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefi
if (!m_notice.empty())
{// since @notice is the only user tag if missing function should not appear
user["notice"] = Json::Value(m_notice);
methods[f->getName()] = user;
methods[it.second->getName()] = user;
}
}
}
@ -110,10 +136,10 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin
doc["title"] = m_title;
}
for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions())
for (auto const& it: _contractDef.getInterfaceFunctions())
{
Json::Value method;
auto strPtr = f->getDocumentation();
auto strPtr = it.second->getDocumentation();
if (strPtr)
{
resetDev();
@ -136,7 +162,7 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin
method["return"] = m_return;
if (!method.empty()) // add the function, only if we have any documentation to add
methods[f->getName()] = method;
methods[it.second->getName()] = method;
}
}
doc["methods"] = methods;

1
libsolidity/InterfaceHandler.h

@ -74,6 +74,7 @@ public:
/// @return A unique pointer contained string with the json
/// representation of the contract's ABI Interface
std::unique_ptr<std::string> getABIInterface(ContractDefinition const& _contractDef);
std::unique_ptr<std::string> getABISolidityInterface(ContractDefinition const& _contractDef);
/// Get the User documentation of the contract
/// @param _contractDef The contract definition
/// @return A unique pointer contained string with the json

18
libsolidity/Types.cpp

@ -334,8 +334,8 @@ MemberList const& ContractType::getMembers() const
if (!m_members)
{
map<string, shared_ptr<Type const>> members;
for (FunctionDefinition const* function: m_contract.getInterfaceFunctions())
members[function->getName()] = make_shared<FunctionType>(*function, false);
for (auto const& it: m_contract.getInterfaceFunctions())
members[it.second->getName()] = make_shared<FunctionType>(*it.second, false);
m_members.reset(new MemberList(members));
}
return *m_members;
@ -354,15 +354,13 @@ shared_ptr<FunctionType const> const& ContractType::getConstructorType() const
return m_constructorType;
}
unsigned ContractType::getFunctionIndex(string const& _functionName) const
u256 ContractType::getFunctionIdentifier(string const& _functionName) const
{
unsigned index = 0;
for (FunctionDefinition const* function: m_contract.getInterfaceFunctions())
{
if (function->getName() == _functionName)
return index;
++index;
}
auto interfaceFunctions = m_contract.getInterfaceFunctions();
for (auto it = interfaceFunctions.cbegin(); it != interfaceFunctions.cend(); ++it)
if (it->second->getName() == _functionName)
return FixedHash<4>::Arith(it->first);
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Index of non-existing contract function requested."));
}

4
libsolidity/Types.h

@ -267,7 +267,7 @@ public:
/// is not used, as this type cannot be the type of a variable or expression.
std::shared_ptr<FunctionType const> const& getConstructorType() const;
unsigned getFunctionIndex(std::string const& _functionName) const;
u256 getFunctionIdentifier(std::string const& _functionName) const;
private:
ContractDefinition const& m_contract;
@ -318,7 +318,7 @@ public:
/// INTERNAL: jump tag, EXTERNAL: contract address + function index,
/// BARE: contract address (non-abi contract call)
/// OTHERS: special virtual function, nothing on the stack
enum class Location { INTERNAL, EXTERNAL, SEND, SHA3, SUICIDE, ECRECOVER, SHA256, RIPEMD160, BARE };
enum class Location { INTERNAL, EXTERNAL, SEND, SHA3, SUICIDE, ECRECOVER, SHA256, RIPEMD160, LOG0, LOG1, LOG2, LOG3, LOG4, BARE };
virtual Category getCategory() const override { return Category::FUNCTION; }
explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);

10
mix/QContractDefinition.cpp

@ -33,11 +33,9 @@ using namespace dev::mix;
QContractDefinition::QContractDefinition(dev::solidity::ContractDefinition const* _contract): QBasicNodeDefinition(_contract)
{
std::vector<FunctionDefinition const*> functions = _contract->getInterfaceFunctions();
for (unsigned i = 0; i < functions.size(); i++)
{
FunctionDefinition const* func = functions.at(i);
m_functions.append(new QFunctionDefinition(func, i));
}
auto interfaceFunctions = _contract->getInterfaceFunctions();
unsigned i = 0;
for (auto it = interfaceFunctions.cbegin(); it != interfaceFunctions.cend(); ++it, ++i)
m_functions.append(new QFunctionDefinition(it->second, i));
}

41
solc/CommandLineInterface.cpp

@ -16,6 +16,7 @@
*/
/**
* @author Lefteris <lefteris@ethdev.com>
* @author Gav Wood <g@ethdev.com>
* @date 2014
* Solidity command line interface.
*/
@ -50,7 +51,8 @@ namespace solidity
// LTODO: Maybe some argument class pairing names with
// extensions and other attributes would be a better choice here?
static string const g_argAbiStr = "abi";
static string const g_argAbiStr = "json-abi";
static string const g_argSolAbiStr = "sol-abi";
static string const g_argAsmStr = "asm";
static string const g_argAstStr = "ast";
static string const g_argBinaryStr = "binary";
@ -60,7 +62,7 @@ static string const g_argNatspecUserStr = "natspec-user";
static void version()
{
cout << "solc, the solidity complier commandline interface " << dev::Version << endl
cout << "solc, the solidity compiler commandline interface " << dev::Version << endl
<< " by Christian <c@ethdev.com> and Lefteris <lefteris@ethdev.com>, (c) 2014." << endl
<< "Build: " << DEV_QUOTED(ETH_BUILD_PLATFORM) << "/" << DEV_QUOTED(ETH_BUILD_TYPE) << endl;
exit(0);
@ -73,9 +75,11 @@ static inline bool argToStdout(po::variables_map const& _args, string const& _na
static bool needStdout(po::variables_map const& _args)
{
return argToStdout(_args, g_argAbiStr) || argToStdout(_args, g_argNatspecUserStr) ||
argToStdout(_args, g_argNatspecDevStr) || argToStdout(_args, g_argAsmStr) ||
argToStdout(_args, g_argOpcodesStr) || argToStdout(_args, g_argBinaryStr);
return
argToStdout(_args, g_argAbiStr) || argToStdout(_args, g_argSolAbiStr) ||
argToStdout(_args, g_argNatspecUserStr) ||
argToStdout(_args, g_argNatspecDevStr) || argToStdout(_args, g_argAsmStr) ||
argToStdout(_args, g_argOpcodesStr) || argToStdout(_args, g_argBinaryStr);
}
static inline bool outputToFile(OutputType type)
@ -146,8 +150,7 @@ void CommandLineInterface::handleBytecode(string const& _contract)
handleBinary(_contract);
}
void CommandLineInterface::handleJson(DocumentationType _type,
string const& _contract)
void CommandLineInterface::handleMeta(DocumentationType _type, string const& _contract)
{
std::string argName;
std::string suffix;
@ -157,10 +160,15 @@ void CommandLineInterface::handleJson(DocumentationType _type,
case DocumentationType::ABI_INTERFACE:
argName = g_argAbiStr;
suffix = ".abi";
title = "Contract ABI";
title = "Contract JSON ABI";
break;
case DocumentationType::ABI_SOLIDITY_INTERFACE:
argName = g_argSolAbiStr;
suffix = ".sol";
title = "Contract Solidity ABI";
break;
case DocumentationType::NATSPEC_USER:
argName = "g_argNatspecUserStr";
argName = g_argNatspecUserStr;
suffix = ".docuser";
title = "User Documentation";
break;
@ -180,13 +188,13 @@ void CommandLineInterface::handleJson(DocumentationType _type,
if (outputToStdout(choice))
{
cout << title << endl;
cout << m_compiler.getJsonDocumentation(_contract, _type);
cout << m_compiler.getMetadata(_contract, _type) << endl;
}
if (outputToFile(choice))
{
ofstream outFile(_contract + suffix);
outFile << m_compiler.getJsonDocumentation(_contract, _type);
outFile << m_compiler.getMetadata(_contract, _type);
outFile.close();
}
}
@ -214,7 +222,9 @@ bool CommandLineInterface::parseArguments(int argc, char** argv)
(g_argBinaryStr.c_str(), po::value<OutputType>(),
"Request to output the contract in binary (hexadecimal). " OUTPUT_TYPE_STR)
(g_argAbiStr.c_str(), po::value<OutputType>(),
"Request to output the contract's ABI interface. " OUTPUT_TYPE_STR)
"Request to output the contract's JSON ABI interface. " OUTPUT_TYPE_STR)
(g_argSolAbiStr.c_str(), po::value<OutputType>(),
"Request to output the contract's Solidity ABI interface. " OUTPUT_TYPE_STR)
(g_argNatspecUserStr.c_str(), po::value<OutputType>(),
"Request to output the contract's Natspec user documentation. " OUTPUT_TYPE_STR)
(g_argNatspecDevStr.c_str(), po::value<OutputType>(),
@ -384,9 +394,10 @@ void CommandLineInterface::actOnInput()
}
handleBytecode(contract);
handleJson(DocumentationType::ABI_INTERFACE, contract);
handleJson(DocumentationType::NATSPEC_DEV, contract);
handleJson(DocumentationType::NATSPEC_USER, contract);
handleMeta(DocumentationType::ABI_INTERFACE, contract);
handleMeta(DocumentationType::ABI_SOLIDITY_INTERFACE, contract);
handleMeta(DocumentationType::NATSPEC_DEV, contract);
handleMeta(DocumentationType::NATSPEC_USER, contract);
} // end of contracts iteration
}

2
solc/CommandLineInterface.h

@ -56,7 +56,7 @@ private:
void handleBinary(std::string const& _contract);
void handleOpcode(std::string const& _contract);
void handleBytecode(std::string const& _contract);
void handleJson(DocumentationType _type,
void handleMeta(DocumentationType _type,
std::string const& _contract);
/// Compiler arguments variable map

30
test/SolidityABIJSON.cpp

@ -50,7 +50,7 @@ public:
msg += *extra;
BOOST_FAIL(msg);
}
std::string generatedInterfaceString = m_compilerStack.getJsonDocumentation("", DocumentationType::ABI_INTERFACE);
std::string generatedInterfaceString = m_compilerStack.getMetadata("", DocumentationType::ABI_INTERFACE);
Json::Value generatedInterface;
m_reader.parse(generatedInterfaceString, generatedInterface);
Json::Value expectedInterface;
@ -236,20 +236,6 @@ BOOST_AUTO_TEST_CASE(const_function)
"}\n";
char const* interface = R"([
{
"name": "boo",
"constant": true,
"inputs": [{
"name": "a",
"type": "uint32"
}],
"outputs": [
{
"name": "b",
"type": "uint256"
}
]
},
{
"name": "foo",
"constant": false,
@ -269,6 +255,20 @@ BOOST_AUTO_TEST_CASE(const_function)
"type": "uint256"
}
]
},
{
"name": "boo",
"constant": true,
"inputs": [{
"name": "a",
"type": "uint32"
}],
"outputs": [
{
"name": "b",
"type": "uint256"
}
]
}
])";

17
test/SolidityCompiler.cpp

@ -96,7 +96,7 @@ BOOST_AUTO_TEST_CASE(smoke_test)
"}\n";
bytes code = compileContract(sourceCode);
unsigned boilerplateSize = 40;
unsigned boilerplateSize = 73;
bytes expectation({byte(Instruction::JUMPDEST),
byte(Instruction::PUSH1), 0x0, // initialize local variable x
byte(Instruction::PUSH1), 0x2,
@ -115,9 +115,8 @@ BOOST_AUTO_TEST_CASE(different_argument_numbers)
" function g() returns (uint e, uint h) { h = f(1, 2, 3); }\n"
"}\n";
bytes code = compileContract(sourceCode);
unsigned shift = 68;
unsigned boilerplateSize = 81;
unsigned shift = 103;
unsigned boilerplateSize = 116;
bytes expectation({byte(Instruction::JUMPDEST),
byte(Instruction::PUSH1), 0x0, // initialize return variable d
byte(Instruction::DUP3),
@ -166,9 +165,8 @@ BOOST_AUTO_TEST_CASE(ifStatement)
" function f() { bool x; if (x) 77; else if (!x) 78; else 79; }"
"}\n";
bytes code = compileContract(sourceCode);
unsigned shift = 27;
unsigned boilerplateSize = 40;
unsigned shift = 60;
unsigned boilerplateSize = 73;
bytes expectation({byte(Instruction::JUMPDEST),
byte(Instruction::PUSH1), 0x0,
byte(Instruction::DUP1),
@ -208,9 +206,8 @@ BOOST_AUTO_TEST_CASE(loops)
" function f() { while(true){1;break;2;continue;3;return;4;} }"
"}\n";
bytes code = compileContract(sourceCode);
unsigned shift = 27;
unsigned boilerplateSize = 40;
unsigned shift = 60;
unsigned boilerplateSize = 73;
bytes expectation({byte(Instruction::JUMPDEST),
byte(Instruction::JUMPDEST),
byte(Instruction::PUSH1), 0x1,

377
test/SolidityEndToEndTest.cpp

@ -17,6 +17,7 @@
*/
/**
* @author Christian <c@ethdev.com>
* @author Gav Wood <g@ethdev.com>
* @date 2014
* Unit tests for the solidity expression compiler, testing the behaviour of the code.
*/
@ -44,7 +45,7 @@ BOOST_AUTO_TEST_CASE(smoke_test)
" function f(uint a) returns(uint d) { return a * 7; }\n"
"}\n";
compileAndRun(sourceCode);
testSolidityAgainstCppOnRange(0, [](u256 const& a) -> u256 { return a * 7; }, 0, 100);
testSolidityAgainstCppOnRange("f(uint256)", [](u256 const& a) -> u256 { return a * 7; }, 0, 100);
}
BOOST_AUTO_TEST_CASE(empty_contract)
@ -52,7 +53,7 @@ BOOST_AUTO_TEST_CASE(empty_contract)
char const* sourceCode = "contract test {\n"
"}\n";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction(0, bytes()).empty());
BOOST_CHECK(callContractFunction("i_am_not_there()", bytes()).empty());
}
BOOST_AUTO_TEST_CASE(recursive_calls)
@ -72,7 +73,7 @@ BOOST_AUTO_TEST_CASE(recursive_calls)
return n * recursive_calls_cpp(n - 1);
};
testSolidityAgainstCppOnRange(0, recursive_calls_cpp, 0, 5);
testSolidityAgainstCppOnRange("f(uint256)", recursive_calls_cpp, 0, 5);
}
BOOST_AUTO_TEST_CASE(multiple_functions)
@ -84,11 +85,11 @@ BOOST_AUTO_TEST_CASE(multiple_functions)
" function f() returns(uint n) { return 3; }\n"
"}\n";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction(0, bytes()) == toBigEndian(u256(0)));
BOOST_CHECK(callContractFunction(1, bytes()) == toBigEndian(u256(1)));
BOOST_CHECK(callContractFunction(2, bytes()) == toBigEndian(u256(2)));
BOOST_CHECK(callContractFunction(3, bytes()) == toBigEndian(u256(3)));
BOOST_CHECK(callContractFunction(4, bytes()) == bytes());
BOOST_CHECK(callContractFunction("a()", bytes()) == toBigEndian(u256(0)));
BOOST_CHECK(callContractFunction("b()", bytes()) == toBigEndian(u256(1)));
BOOST_CHECK(callContractFunction("c()", bytes()) == toBigEndian(u256(2)));
BOOST_CHECK(callContractFunction("f()", bytes()) == toBigEndian(u256(3)));
BOOST_CHECK(callContractFunction("i_am_not_there()", bytes()) == bytes());
}
BOOST_AUTO_TEST_CASE(while_loop)
@ -112,7 +113,7 @@ BOOST_AUTO_TEST_CASE(while_loop)
return nfac;
};
testSolidityAgainstCppOnRange(0, while_loop_cpp, 0, 5);
testSolidityAgainstCppOnRange("f(uint256)", while_loop_cpp, 0, 5);
}
BOOST_AUTO_TEST_CASE(break_outside_loop)
@ -124,7 +125,7 @@ BOOST_AUTO_TEST_CASE(break_outside_loop)
" }\n"
"}\n";
compileAndRun(sourceCode);
testSolidityAgainstCpp(0, [](u256 const&) -> u256 { return 2; }, u256(0));
testSolidityAgainstCpp("f(uint256)", [](u256 const&) -> u256 { return 2; }, u256(0));
}
BOOST_AUTO_TEST_CASE(nested_loops)
@ -173,7 +174,7 @@ BOOST_AUTO_TEST_CASE(nested_loops)
return n;
};
testSolidityAgainstCppOnRange(0, nested_loops_cpp, 0, 12);
testSolidityAgainstCppOnRange("f(uint256)", nested_loops_cpp, 0, 12);
}
BOOST_AUTO_TEST_CASE(for_loop)
@ -195,7 +196,7 @@ BOOST_AUTO_TEST_CASE(for_loop)
return nfac;
};
testSolidityAgainstCppOnRange(0, for_loop_cpp, 0, 5);
testSolidityAgainstCppOnRange("f(uint256)", for_loop_cpp, 0, 5);
}
BOOST_AUTO_TEST_CASE(for_loop_empty)
@ -223,7 +224,7 @@ BOOST_AUTO_TEST_CASE(for_loop_empty)
return ret;
};
testSolidityAgainstCpp(0, for_loop_empty_cpp);
testSolidityAgainstCpp("f()", for_loop_empty_cpp);
}
BOOST_AUTO_TEST_CASE(for_loop_simple_init_expr)
@ -247,7 +248,7 @@ BOOST_AUTO_TEST_CASE(for_loop_simple_init_expr)
return nfac;
};
testSolidityAgainstCppOnRange(0, for_loop_simple_init_expr_cpp, 0, 5);
testSolidityAgainstCppOnRange("f(uint256)", for_loop_simple_init_expr_cpp, 0, 5);
}
BOOST_AUTO_TEST_CASE(calling_other_functions)
@ -292,11 +293,11 @@ BOOST_AUTO_TEST_CASE(calling_other_functions)
return y;
};
testSolidityAgainstCpp(2, collatz_cpp, u256(0));
testSolidityAgainstCpp(2, collatz_cpp, u256(1));
testSolidityAgainstCpp(2, collatz_cpp, u256(2));
testSolidityAgainstCpp(2, collatz_cpp, u256(8));
testSolidityAgainstCpp(2, collatz_cpp, u256(127));
testSolidityAgainstCpp("run(uint256)", collatz_cpp, u256(0));
testSolidityAgainstCpp("run(uint256)", collatz_cpp, u256(1));
testSolidityAgainstCpp("run(uint256)", collatz_cpp, u256(2));
testSolidityAgainstCpp("run(uint256)", collatz_cpp, u256(8));
testSolidityAgainstCpp("run(uint256)", collatz_cpp, u256(127));
}
BOOST_AUTO_TEST_CASE(many_local_variables)
@ -317,7 +318,7 @@ BOOST_AUTO_TEST_CASE(many_local_variables)
u256 y = a + b + c + x1 + x2 + x3;
return y + b + x2;
};
testSolidityAgainstCpp(0, f, u256(0x1000), u256(0x10000), u256(0x100000));
testSolidityAgainstCpp("run(uint256,uint256,uint256)", f, u256(0x1000), u256(0x10000), u256(0x100000));
}
BOOST_AUTO_TEST_CASE(packing_unpacking_types)
@ -330,7 +331,7 @@ BOOST_AUTO_TEST_CASE(packing_unpacking_types)
" }\n"
"}\n";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction(0, fromHex("01""0f0f0f0f""f0f0f0f0f0f0f0f0"))
BOOST_CHECK(callContractFunction("run(bool,uint32,uint64)", fromHex("01""0f0f0f0f""f0f0f0f0f0f0f0f0"))
== fromHex("00000000000000000000000000000000000000""01""f0f0f0f0""0f0f0f0f0f0f0f0f"));
}
@ -342,7 +343,7 @@ BOOST_AUTO_TEST_CASE(multiple_return_values)
" }\n"
"}\n";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction(0, bytes(1, 1) + toBigEndian(u256(0xcd)))
BOOST_CHECK(callContractFunction("run(bool,uint256)", bytes(1, 1) + toBigEndian(u256(0xcd)))
== toBigEndian(u256(0xcd)) + bytes(1, 1) + toBigEndian(u256(0)));
}
@ -362,7 +363,7 @@ BOOST_AUTO_TEST_CASE(short_circuiting)
return n;
};
testSolidityAgainstCppOnRange(0, short_circuiting_cpp, 0, 2);
testSolidityAgainstCppOnRange("run(uint256)", short_circuiting_cpp, 0, 2);
}
BOOST_AUTO_TEST_CASE(high_bits_cleaning)
@ -382,7 +383,7 @@ BOOST_AUTO_TEST_CASE(high_bits_cleaning)
return 0;
return x;
};
testSolidityAgainstCpp(0, high_bits_cleaning_cpp);
testSolidityAgainstCpp("run()", high_bits_cleaning_cpp);
}
BOOST_AUTO_TEST_CASE(sign_extension)
@ -402,7 +403,7 @@ BOOST_AUTO_TEST_CASE(sign_extension)
return 0;
return u256(x) * -1;
};
testSolidityAgainstCpp(0, sign_extension_cpp);
testSolidityAgainstCpp("run()", sign_extension_cpp);
}
BOOST_AUTO_TEST_CASE(small_unsigned_types)
@ -419,7 +420,7 @@ BOOST_AUTO_TEST_CASE(small_unsigned_types)
uint32_t x = uint32_t(0xffffff) * 0xffffff;
return x / 0x100;
};
testSolidityAgainstCpp(0, small_unsigned_types_cpp);
testSolidityAgainstCpp("run()", small_unsigned_types_cpp);
}
BOOST_AUTO_TEST_CASE(small_signed_types)
@ -434,7 +435,7 @@ BOOST_AUTO_TEST_CASE(small_signed_types)
{
return -int32_t(10) * -int64_t(20);
};
testSolidityAgainstCpp(0, small_signed_types_cpp);
testSolidityAgainstCpp("run()", small_signed_types_cpp);
}
BOOST_AUTO_TEST_CASE(strings)
@ -457,12 +458,12 @@ BOOST_AUTO_TEST_CASE(strings)
expectation[4] = byte(0xff);
expectation[5] = byte('_');
expectation[6] = byte('_');
BOOST_CHECK(callContractFunction(0, bytes()) == expectation);
BOOST_CHECK(callContractFunction("fixed()", bytes()) == expectation);
expectation = bytes(17, 0);
expectation[0] = 0;
expectation[1] = 2;
expectation[16] = 1;
BOOST_CHECK(callContractFunction(1, bytes({0x00, 0x02, 0x01})) == expectation);
BOOST_CHECK(callContractFunction("pipeThrough(string2,bool)", bytes({0x00, 0x02, 0x01})) == expectation);
}
BOOST_AUTO_TEST_CASE(empty_string_on_stack)
@ -476,7 +477,7 @@ BOOST_AUTO_TEST_CASE(empty_string_on_stack)
" }\n"
"}\n";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction(0, bytes(1, 0x02)) == bytes({0x00, 0x02, 0x61/*'a'*/, 0x62/*'b'*/, 0x63/*'c'*/, 0x00}));
BOOST_CHECK(callContractFunction("run(string0,uint8)", bytes(1, 0x02)) == bytes({0x00, 0x02, 0x61/*'a'*/, 0x62/*'b'*/, 0x63/*'c'*/, 0x00}));
}
BOOST_AUTO_TEST_CASE(state_smoke_test)
@ -494,14 +495,14 @@ BOOST_AUTO_TEST_CASE(state_smoke_test)
" }\n"
"}\n";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction(0, bytes(1, 0x00)) == toBigEndian(u256(0)));
BOOST_CHECK(callContractFunction(0, bytes(1, 0x01)) == toBigEndian(u256(0)));
BOOST_CHECK(callContractFunction(1, bytes(1, 0x00) + toBigEndian(u256(0x1234))) == bytes());
BOOST_CHECK(callContractFunction(1, bytes(1, 0x01) + toBigEndian(u256(0x8765))) == bytes());
BOOST_CHECK(callContractFunction(0, bytes(1, 0x00)) == toBigEndian(u256(0x1234)));
BOOST_CHECK(callContractFunction(0, bytes(1, 0x01)) == toBigEndian(u256(0x8765)));
BOOST_CHECK(callContractFunction(1, bytes(1, 0x00) + toBigEndian(u256(0x3))) == bytes());
BOOST_CHECK(callContractFunction(0, bytes(1, 0x00)) == toBigEndian(u256(0x3)));
BOOST_CHECK(callContractFunction("get(uint8)", bytes(1, 0x00)) == toBigEndian(u256(0)));
BOOST_CHECK(callContractFunction("get(uint8)", bytes(1, 0x01)) == toBigEndian(u256(0)));
BOOST_CHECK(callContractFunction("set(uint8,uint256)", bytes(1, 0x00) + toBigEndian(u256(0x1234))) == bytes());
BOOST_CHECK(callContractFunction("set(uint8,uint256)", bytes(1, 0x01) + toBigEndian(u256(0x8765))) == bytes());
BOOST_CHECK(callContractFunction("get(uint8)", bytes(1, 0x00)) == toBigEndian(u256(0x1234)));
BOOST_CHECK(callContractFunction("get(uint8)", bytes(1, 0x01)) == toBigEndian(u256(0x8765)));
BOOST_CHECK(callContractFunction("set(uint8,uint256)", bytes(1, 0x00) + toBigEndian(u256(0x3))) == bytes());
BOOST_CHECK(callContractFunction("get(uint8)", bytes(1, 0x00)) == toBigEndian(u256(0x3)));
}
BOOST_AUTO_TEST_CASE(compound_assign)
@ -529,14 +530,14 @@ BOOST_AUTO_TEST_CASE(compound_assign)
value2 *= value3 + value1;
return value2 += 7;
};
testSolidityAgainstCpp(0, f, u256(0), u256(6));
testSolidityAgainstCpp(0, f, u256(1), u256(3));
testSolidityAgainstCpp(0, f, u256(2), u256(25));
testSolidityAgainstCpp(0, f, u256(3), u256(69));
testSolidityAgainstCpp(0, f, u256(4), u256(84));
testSolidityAgainstCpp(0, f, u256(5), u256(2));
testSolidityAgainstCpp(0, f, u256(6), u256(51));
testSolidityAgainstCpp(0, f, u256(7), u256(48));
testSolidityAgainstCpp("f(uint256,uint256)", f, u256(0), u256(6));
testSolidityAgainstCpp("f(uint256,uint256)", f, u256(1), u256(3));
testSolidityAgainstCpp("f(uint256,uint256)", f, u256(2), u256(25));
testSolidityAgainstCpp("f(uint256,uint256)", f, u256(3), u256(69));
testSolidityAgainstCpp("f(uint256,uint256)", f, u256(4), u256(84));
testSolidityAgainstCpp("f(uint256,uint256)", f, u256(5), u256(2));
testSolidityAgainstCpp("f(uint256,uint256)", f, u256(6), u256(51));
testSolidityAgainstCpp("f(uint256,uint256)", f, u256(7), u256(48));
}
BOOST_AUTO_TEST_CASE(simple_mapping)
@ -553,21 +554,21 @@ BOOST_AUTO_TEST_CASE(simple_mapping)
compileAndRun(sourceCode);
// msvc seems to have problems with initializer-list, when there is only 1 param in the list
BOOST_CHECK(callContractFunction(0, bytes(1, 0x00)) == bytes(1, 0x00));
BOOST_CHECK(callContractFunction(0, bytes(1, 0x01)) == bytes(1, 0x00));
BOOST_CHECK(callContractFunction(0, bytes(1, 0xa7)) == bytes(1, 0x00));
callContractFunction(1, bytes({0x01, 0xa1}));
BOOST_CHECK(callContractFunction(0, bytes(1, 0x00)) == bytes(1, 0x00));
BOOST_CHECK(callContractFunction(0, bytes(1, 0x01)) == bytes(1, 0xa1));
BOOST_CHECK(callContractFunction(0, bytes(1, 0xa7)) == bytes(1, 0x00));
callContractFunction(1, bytes({0x00, 0xef}));
BOOST_CHECK(callContractFunction(0, bytes(1, 0x00)) == bytes(1, 0xef));
BOOST_CHECK(callContractFunction(0, bytes(1, 0x01)) == bytes(1, 0xa1));
BOOST_CHECK(callContractFunction(0, bytes(1, 0xa7)) == bytes(1, 0x00));
callContractFunction(1, bytes({0x01, 0x05}));
BOOST_CHECK(callContractFunction(0, bytes(1, 0x00)) == bytes(1, 0xef));
BOOST_CHECK(callContractFunction(0, bytes(1, 0x01)) == bytes(1, 0x05));
BOOST_CHECK(callContractFunction(0, bytes(1, 0xa7)) == bytes(1, 0x00));
BOOST_CHECK(callContractFunction("get(uint8)", bytes(1, 0x00)) == bytes(1, 0x00));
BOOST_CHECK(callContractFunction("get(uint8)", bytes(1, 0x01)) == bytes(1, 0x00));
BOOST_CHECK(callContractFunction("get(uint8)", bytes(1, 0xa7)) == bytes(1, 0x00));
callContractFunction("set(uint8,uint8)", bytes({0x01, 0xa1}));
BOOST_CHECK(callContractFunction("get(uint8)", bytes(1, 0x00)) == bytes(1, 0x00));
BOOST_CHECK(callContractFunction("get(uint8)", bytes(1, 0x01)) == bytes(1, 0xa1));
BOOST_CHECK(callContractFunction("get(uint8)", bytes(1, 0xa7)) == bytes(1, 0x00));
callContractFunction("set(uint8,uint8)", bytes({0x00, 0xef}));
BOOST_CHECK(callContractFunction("get(uint8)", bytes(1, 0x00)) == bytes(1, 0xef));
BOOST_CHECK(callContractFunction("get(uint8)", bytes(1, 0x01)) == bytes(1, 0xa1));
BOOST_CHECK(callContractFunction("get(uint8)", bytes(1, 0xa7)) == bytes(1, 0x00));
callContractFunction("set(uint8,uint8)", bytes({0x01, 0x05}));
BOOST_CHECK(callContractFunction("get(uint8)", bytes(1, 0x00)) == bytes(1, 0xef));
BOOST_CHECK(callContractFunction("get(uint8)", bytes(1, 0x01)) == bytes(1, 0x05));
BOOST_CHECK(callContractFunction("get(uint8)", bytes(1, 0xa7)) == bytes(1, 0x00));
}
BOOST_AUTO_TEST_CASE(mapping_state)
@ -611,38 +612,38 @@ BOOST_AUTO_TEST_CASE(mapping_state)
auto getVoteCount = bind(&Ballot::getVoteCount, &ballot, _1);
auto grantVoteRight = bind(&Ballot::grantVoteRight, &ballot, _1);
auto vote = bind(&Ballot::vote, &ballot, _1, _2);
testSolidityAgainstCpp(0, getVoteCount, u160(0));
testSolidityAgainstCpp(0, getVoteCount, u160(1));
testSolidityAgainstCpp(0, getVoteCount, u160(2));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(0));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(1));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(2));
// voting without vote right shourd be rejected
testSolidityAgainstCpp(2, vote, u160(0), u160(2));
testSolidityAgainstCpp(0, getVoteCount, u160(0));
testSolidityAgainstCpp(0, getVoteCount, u160(1));
testSolidityAgainstCpp(0, getVoteCount, u160(2));
testSolidityAgainstCpp("vote(address,address)", vote, u160(0), u160(2));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(0));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(1));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(2));
// grant vote rights
testSolidityAgainstCpp(1, grantVoteRight, u160(0));
testSolidityAgainstCpp(1, grantVoteRight, u160(1));
testSolidityAgainstCpp("grantVoteRight(address)", grantVoteRight, u160(0));
testSolidityAgainstCpp("grantVoteRight(address)", grantVoteRight, u160(1));
// vote, should increase 2's vote count
testSolidityAgainstCpp(2, vote, u160(0), u160(2));
testSolidityAgainstCpp(0, getVoteCount, u160(0));
testSolidityAgainstCpp(0, getVoteCount, u160(1));
testSolidityAgainstCpp(0, getVoteCount, u160(2));
testSolidityAgainstCpp("vote(address,address)", vote, u160(0), u160(2));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(0));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(1));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(2));
// vote again, should be rejected
testSolidityAgainstCpp(2, vote, u160(0), u160(1));
testSolidityAgainstCpp(0, getVoteCount, u160(0));
testSolidityAgainstCpp(0, getVoteCount, u160(1));
testSolidityAgainstCpp(0, getVoteCount, u160(2));
testSolidityAgainstCpp("vote(address,address)", vote, u160(0), u160(1));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(0));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(1));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(2));
// vote without right to vote
testSolidityAgainstCpp(2, vote, u160(2), u160(1));
testSolidityAgainstCpp(0, getVoteCount, u160(0));
testSolidityAgainstCpp(0, getVoteCount, u160(1));
testSolidityAgainstCpp(0, getVoteCount, u160(2));
testSolidityAgainstCpp("vote(address,address)", vote, u160(2), u160(1));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(0));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(1));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(2));
// grant vote right and now vote again
testSolidityAgainstCpp(1, grantVoteRight, u160(2));
testSolidityAgainstCpp(2, vote, u160(2), u160(1));
testSolidityAgainstCpp(0, getVoteCount, u160(0));
testSolidityAgainstCpp(0, getVoteCount, u160(1));
testSolidityAgainstCpp(0, getVoteCount, u160(2));
testSolidityAgainstCpp("grantVoteRight(address)", grantVoteRight, u160(2));
testSolidityAgainstCpp("vote(address,address)", vote, u160(2), u160(1));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(0));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(1));
testSolidityAgainstCpp("getVoteCount(address)", getVoteCount, u160(2));
}
BOOST_AUTO_TEST_CASE(mapping_state_inc_dec)
@ -673,7 +674,7 @@ BOOST_AUTO_TEST_CASE(mapping_state_inc_dec)
table[value]++;
return --table[value++];
};
testSolidityAgainstCppOnRange(0, f, 0, 5);
testSolidityAgainstCppOnRange("f(uint256)", f, 0, 5);
}
BOOST_AUTO_TEST_CASE(multi_level_mapping)
@ -693,14 +694,14 @@ BOOST_AUTO_TEST_CASE(multi_level_mapping)
if (_z == 0) return table[_x][_y];
else return table[_x][_y] = _z;
};
testSolidityAgainstCpp(0, f, u256(4), u256(5), u256(0));
testSolidityAgainstCpp(0, f, u256(5), u256(4), u256(0));
testSolidityAgainstCpp(0, f, u256(4), u256(5), u256(9));
testSolidityAgainstCpp(0, f, u256(4), u256(5), u256(0));
testSolidityAgainstCpp(0, f, u256(5), u256(4), u256(0));
testSolidityAgainstCpp(0, f, u256(5), u256(4), u256(7));
testSolidityAgainstCpp(0, f, u256(4), u256(5), u256(0));
testSolidityAgainstCpp(0, f, u256(5), u256(4), u256(0));
testSolidityAgainstCpp("f(uint256,uint256,uint256)", f, u256(4), u256(5), u256(0));
testSolidityAgainstCpp("f(uint256,uint256,uint256)", f, u256(5), u256(4), u256(0));
testSolidityAgainstCpp("f(uint256,uint256,uint256)", f, u256(4), u256(5), u256(9));
testSolidityAgainstCpp("f(uint256,uint256,uint256)", f, u256(4), u256(5), u256(0));
testSolidityAgainstCpp("f(uint256,uint256,uint256)", f, u256(5), u256(4), u256(0));
testSolidityAgainstCpp("f(uint256,uint256,uint256)", f, u256(5), u256(4), u256(7));
testSolidityAgainstCpp("f(uint256,uint256,uint256)", f, u256(4), u256(5), u256(0));
testSolidityAgainstCpp("f(uint256,uint256,uint256)", f, u256(5), u256(4), u256(0));
}
BOOST_AUTO_TEST_CASE(structs)
@ -735,9 +736,9 @@ BOOST_AUTO_TEST_CASE(structs)
" }\n"
"}\n";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction(0) == bytes(1, 0x00));
BOOST_CHECK(callContractFunction(1) == bytes());
BOOST_CHECK(callContractFunction(0) == bytes(1, 0x01));
BOOST_CHECK(callContractFunction("check()") == bytes(1, 0x00));
BOOST_CHECK(callContractFunction("set()") == bytes());
BOOST_CHECK(callContractFunction("check()") == bytes(1, 0x01));
}
BOOST_AUTO_TEST_CASE(struct_reference)
@ -763,9 +764,9 @@ BOOST_AUTO_TEST_CASE(struct_reference)
" }\n"
"}\n";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction(0) == bytes(1, 0x00));
BOOST_CHECK(callContractFunction(1) == bytes());
BOOST_CHECK(callContractFunction(0) == bytes(1, 0x01));
BOOST_CHECK(callContractFunction("check()") == bytes(1, 0x00));
BOOST_CHECK(callContractFunction("set()") == bytes());
BOOST_CHECK(callContractFunction("check()") == bytes(1, 0x01));
}
BOOST_AUTO_TEST_CASE(constructor)
@ -786,8 +787,8 @@ BOOST_AUTO_TEST_CASE(constructor)
{
return data[_x];
};
testSolidityAgainstCpp(0, get, u256(6));
testSolidityAgainstCpp(0, get, u256(7));
testSolidityAgainstCpp("get(uint256)", get, u256(6));
testSolidityAgainstCpp("get(uint256)", get, u256(7));
}
BOOST_AUTO_TEST_CASE(balance)
@ -798,7 +799,7 @@ BOOST_AUTO_TEST_CASE(balance)
" }\n"
"}\n";
compileAndRun(sourceCode, 23);
BOOST_CHECK(callContractFunction(0) == toBigEndian(u256(23)));
BOOST_CHECK(callContractFunction("getBalance()") == toBigEndian(u256(23)));
}
BOOST_AUTO_TEST_CASE(blockchain)
@ -811,7 +812,7 @@ BOOST_AUTO_TEST_CASE(blockchain)
" }\n"
"}\n";
compileAndRun(sourceCode, 27);
BOOST_CHECK(callContractFunction(0, bytes{0}, u256(28)) == toBigEndian(u256(28)) + bytes(20, 0) + toBigEndian(u256(1)));
BOOST_CHECK(callContractFunction("someInfo()", bytes{0}, u256(28)) == toBigEndian(u256(28)) + bytes(20, 0) + toBigEndian(u256(1)));
}
BOOST_AUTO_TEST_CASE(function_types)
@ -830,8 +831,8 @@ BOOST_AUTO_TEST_CASE(function_types)
" }\n"
"}\n";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction(0, bytes{0}) == toBigEndian(u256(11)));
BOOST_CHECK(callContractFunction(0, bytes{1}) == toBigEndian(u256(12)));
BOOST_CHECK(callContractFunction("a(bool)", bytes{0}) == toBigEndian(u256(11)));
BOOST_CHECK(callContractFunction("a(bool)", bytes{1}) == toBigEndian(u256(12)));
}
BOOST_AUTO_TEST_CASE(send_ether)
@ -845,10 +846,102 @@ BOOST_AUTO_TEST_CASE(send_ether)
u256 amount(130);
compileAndRun(sourceCode, amount + 1);
u160 address(23);
BOOST_CHECK(callContractFunction(0, address, amount) == toBigEndian(u256(1)));
BOOST_CHECK(callContractFunction("a(address,uint256)", address, amount) == toBigEndian(u256(1)));
BOOST_CHECK_EQUAL(m_state.balance(address), amount);
}
BOOST_AUTO_TEST_CASE(log0)
{
char const* sourceCode = "contract test {\n"
" function a() {\n"
" log0(1);\n"
" }\n"
"}\n";
u256 amount(130);
compileAndRun(sourceCode, amount + 1);
u160 address(23);
callContractFunction("a()", address, amount);
BOOST_CHECK_EQUAL(m_logs.size(), 1);
BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
BOOST_CHECK_EQUAL(h256(m_logs[0].data), h256(u256(1)));
BOOST_CHECK_EQUAL(m_logs[0].topics.size(), 0);
}
BOOST_AUTO_TEST_CASE(log1)
{
char const* sourceCode = "contract test {\n"
" function a() {\n"
" log1(1, 2);\n"
" }\n"
"}\n";
u256 amount(130);
compileAndRun(sourceCode, amount + 1);
u160 address(23);
callContractFunction("a()", address, amount);
BOOST_CHECK_EQUAL(m_logs.size(), 1);
BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
BOOST_CHECK_EQUAL(h256(m_logs[0].data), h256(u256(1)));
BOOST_CHECK_EQUAL(m_logs[0].topics.size(), 1);
BOOST_CHECK_EQUAL(m_logs[0].topics[0], h256(u256(2)));
}
BOOST_AUTO_TEST_CASE(log2)
{
char const* sourceCode = "contract test {\n"
" function a() {\n"
" log2(1, 2, 3);\n"
" }\n"
"}\n";
u256 amount(130);
compileAndRun(sourceCode, amount + 1);
u160 address(23);
callContractFunction("a()", address, amount);
BOOST_CHECK_EQUAL(m_logs.size(), 1);
BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
BOOST_CHECK_EQUAL(h256(m_logs[0].data), h256(u256(1)));
BOOST_CHECK_EQUAL(m_logs[0].topics.size(), 2);
for (unsigned i = 0; i < 2; ++i)
BOOST_CHECK_EQUAL(m_logs[0].topics[i], h256(u256(i + 2)));
}
BOOST_AUTO_TEST_CASE(log3)
{
char const* sourceCode = "contract test {\n"
" function a() {\n"
" log3(1, 2, 3, 4);\n"
" }\n"
"}\n";
u256 amount(130);
compileAndRun(sourceCode, amount + 1);
u160 address(23);
callContractFunction("a()", address, amount);
BOOST_CHECK_EQUAL(m_logs.size(), 1);
BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
BOOST_CHECK_EQUAL(h256(m_logs[0].data), h256(u256(1)));
BOOST_CHECK_EQUAL(m_logs[0].topics.size(), 3);
for (unsigned i = 0; i < 3; ++i)
BOOST_CHECK_EQUAL(m_logs[0].topics[i], h256(u256(i + 2)));
}
BOOST_AUTO_TEST_CASE(log4)
{
char const* sourceCode = "contract test {\n"
" function a() {\n"
" log4(1, 2, 3, 4, 5);\n"
" }\n"
"}\n";
u256 amount(130);
compileAndRun(sourceCode, amount + 1);
u160 address(23);
callContractFunction("a()", address, amount);
BOOST_CHECK_EQUAL(m_logs.size(), 1);
BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress);
BOOST_CHECK_EQUAL(h256(m_logs[0].data), h256(u256(1)));
BOOST_CHECK_EQUAL(m_logs[0].topics.size(), 4);
for (unsigned i = 0; i < 4; ++i)
BOOST_CHECK_EQUAL(m_logs[0].topics[i], h256(u256(i + 2)));
}
BOOST_AUTO_TEST_CASE(suicide)
{
char const* sourceCode = "contract test {\n"
@ -860,7 +953,7 @@ BOOST_AUTO_TEST_CASE(suicide)
u256 amount(130);
compileAndRun(sourceCode, amount);
u160 address(23);
BOOST_CHECK(callContractFunction(0, address) == bytes());
BOOST_CHECK(callContractFunction("a(address)", address) == bytes());
BOOST_CHECK(!m_state.addressHasCode(m_contractAddress));
BOOST_CHECK_EQUAL(m_state.balance(address), amount);
}
@ -877,9 +970,9 @@ BOOST_AUTO_TEST_CASE(sha3)
{
return dev::sha3(toBigEndian(_x));
};
testSolidityAgainstCpp(0, f, u256(4));
testSolidityAgainstCpp(0, f, u256(5));
testSolidityAgainstCpp(0, f, u256(-1));
testSolidityAgainstCpp("a(hash256)", f, u256(4));
testSolidityAgainstCpp("a(hash256)", f, u256(5));
testSolidityAgainstCpp("a(hash256)", f, u256(-1));
}
BOOST_AUTO_TEST_CASE(sha256)
@ -896,9 +989,9 @@ BOOST_AUTO_TEST_CASE(sha256)
dev::sha256(dev::ref(toBigEndian(_input)), bytesRef(&ret[0], 32));
return ret;
};
testSolidityAgainstCpp(0, f, u256(4));
testSolidityAgainstCpp(0, f, u256(5));
testSolidityAgainstCpp(0, f, u256(-1));
testSolidityAgainstCpp("a(hash256)", f, u256(4));
testSolidityAgainstCpp("a(hash256)", f, u256(5));
testSolidityAgainstCpp("a(hash256)", f, u256(-1));
}
BOOST_AUTO_TEST_CASE(ripemd)
@ -915,9 +1008,9 @@ BOOST_AUTO_TEST_CASE(ripemd)
dev::ripemd160(dev::ref(toBigEndian(_input)), bytesRef(&ret[0], 32));
return u256(ret) >> (256 - 160);
};
testSolidityAgainstCpp(0, f, u256(4));
testSolidityAgainstCpp(0, f, u256(5));
testSolidityAgainstCpp(0, f, u256(-1));
testSolidityAgainstCpp("a(hash256)", f, u256(4));
testSolidityAgainstCpp("a(hash256)", f, u256(5));
testSolidityAgainstCpp("a(hash256)", f, u256(-1));
}
BOOST_AUTO_TEST_CASE(ecrecover)
@ -933,7 +1026,7 @@ BOOST_AUTO_TEST_CASE(ecrecover)
u256 r("0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f");
u256 s("0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549");
u160 addr("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b");
BOOST_CHECK(callContractFunction(0, h, v, r, s) == toBigEndian(addr));
BOOST_CHECK(callContractFunction("a(hash256,uint8,hash256,hash256)", h, v, r, s) == toBigEndian(addr));
}
BOOST_AUTO_TEST_CASE(inter_contract_calls)
@ -959,11 +1052,11 @@ BOOST_AUTO_TEST_CASE(inter_contract_calls)
compileAndRun(sourceCode, 0, "Helper");
u160 const helperAddress = m_contractAddress;
compileAndRun(sourceCode, 0, "Main");
BOOST_REQUIRE(callContractFunction(2, helperAddress) == bytes());
BOOST_REQUIRE(callContractFunction(1, helperAddress) == toBigEndian(helperAddress));
BOOST_REQUIRE(callContractFunction("setHelper(address)", helperAddress) == bytes());
BOOST_REQUIRE(callContractFunction("getHelper()", helperAddress) == toBigEndian(helperAddress));
u256 a(3456789);
u256 b("0x282837623374623234aa74");
BOOST_REQUIRE(callContractFunction(0, a, b) == toBigEndian(a * b));
BOOST_REQUIRE(callContractFunction("callHelper(uint256,uint256)", a, b) == toBigEndian(a * b));
}
BOOST_AUTO_TEST_CASE(inter_contract_calls_with_complex_parameters)
@ -989,12 +1082,12 @@ BOOST_AUTO_TEST_CASE(inter_contract_calls_with_complex_parameters)
compileAndRun(sourceCode, 0, "Helper");
u160 const helperAddress = m_contractAddress;
compileAndRun(sourceCode, 0, "Main");
BOOST_REQUIRE(callContractFunction(2, helperAddress) == bytes());
BOOST_REQUIRE(callContractFunction(1, helperAddress) == toBigEndian(helperAddress));
BOOST_REQUIRE(callContractFunction("setHelper(address)", helperAddress) == bytes());
BOOST_REQUIRE(callContractFunction("getHelper()", helperAddress) == toBigEndian(helperAddress));
u256 a(3456789);
u256 b("0x282837623374623234aa74");
BOOST_REQUIRE(callContractFunction(0, a, true, b) == toBigEndian(a * 3));
BOOST_REQUIRE(callContractFunction(0, a, false, b) == toBigEndian(b * 3));
BOOST_REQUIRE(callContractFunction("callHelper(uint256,bool,uint256)", a, true, b) == toBigEndian(a * 3));
BOOST_REQUIRE(callContractFunction("callHelper(uint256,bool,uint256)", a, false, b) == toBigEndian(b * 3));
}
BOOST_AUTO_TEST_CASE(inter_contract_calls_accessing_this)
@ -1020,9 +1113,9 @@ BOOST_AUTO_TEST_CASE(inter_contract_calls_accessing_this)
compileAndRun(sourceCode, 0, "Helper");
u160 const helperAddress = m_contractAddress;
compileAndRun(sourceCode, 0, "Main");
BOOST_REQUIRE(callContractFunction(2, helperAddress) == bytes());
BOOST_REQUIRE(callContractFunction(1, helperAddress) == toBigEndian(helperAddress));
BOOST_REQUIRE(callContractFunction(0) == toBigEndian(helperAddress));
BOOST_REQUIRE(callContractFunction("setHelper(address)", helperAddress) == bytes());
BOOST_REQUIRE(callContractFunction("getHelper()", helperAddress) == toBigEndian(helperAddress));
BOOST_REQUIRE(callContractFunction("callHelper()") == toBigEndian(helperAddress));
}
BOOST_AUTO_TEST_CASE(calls_to_this)
@ -1051,11 +1144,11 @@ BOOST_AUTO_TEST_CASE(calls_to_this)
compileAndRun(sourceCode, 0, "Helper");
u160 const helperAddress = m_contractAddress;
compileAndRun(sourceCode, 0, "Main");
BOOST_REQUIRE(callContractFunction(2, helperAddress) == bytes());
BOOST_REQUIRE(callContractFunction(1, helperAddress) == toBigEndian(helperAddress));
BOOST_REQUIRE(callContractFunction("setHelper(address)", helperAddress) == bytes());
BOOST_REQUIRE(callContractFunction("getHelper()", helperAddress) == toBigEndian(helperAddress));
u256 a(3456789);
u256 b("0x282837623374623234aa74");
BOOST_REQUIRE(callContractFunction(0, a, b) == toBigEndian(a * b + 10));
BOOST_REQUIRE(callContractFunction("callHelper(uint256,uint256)", a, b) == toBigEndian(a * b + 10));
}
BOOST_AUTO_TEST_CASE(inter_contract_calls_with_local_vars)
@ -1086,11 +1179,11 @@ BOOST_AUTO_TEST_CASE(inter_contract_calls_with_local_vars)
compileAndRun(sourceCode, 0, "Helper");
u160 const helperAddress = m_contractAddress;
compileAndRun(sourceCode, 0, "Main");
BOOST_REQUIRE(callContractFunction(2, helperAddress) == bytes());
BOOST_REQUIRE(callContractFunction(1, helperAddress) == toBigEndian(helperAddress));
BOOST_REQUIRE(callContractFunction("setHelper(address)", helperAddress) == bytes());
BOOST_REQUIRE(callContractFunction("getHelper()", helperAddress) == toBigEndian(helperAddress));
u256 a(3456789);
u256 b("0x282837623374623234aa74");
BOOST_REQUIRE(callContractFunction(0, a, b) == toBigEndian(a * b + 9));
BOOST_REQUIRE(callContractFunction("callHelper(uint256,uint256)", a, b) == toBigEndian(a * b + 9));
}
BOOST_AUTO_TEST_CASE(strings_in_calls)
@ -1116,9 +1209,9 @@ BOOST_AUTO_TEST_CASE(strings_in_calls)
compileAndRun(sourceCode, 0, "Helper");
u160 const helperAddress = m_contractAddress;
compileAndRun(sourceCode, 0, "Main");
BOOST_REQUIRE(callContractFunction(2, helperAddress) == bytes());
BOOST_REQUIRE(callContractFunction(1, helperAddress) == toBigEndian(helperAddress));
BOOST_CHECK(callContractFunction(0, bytes({0, 'a', 1})) == bytes({0, 'a', 0, 0, 0}));
BOOST_REQUIRE(callContractFunction("setHelper(address)", helperAddress) == bytes());
BOOST_REQUIRE(callContractFunction("getHelper()", helperAddress) == toBigEndian(helperAddress));
BOOST_CHECK(callContractFunction("callHelper(string2,bool)", bytes({0, 'a', 1})) == bytes({0, 'a', 0, 0, 0}));
}
BOOST_AUTO_TEST_CASE(constructor_arguments)
@ -1143,8 +1236,8 @@ BOOST_AUTO_TEST_CASE(constructor_arguments)
function getName() returns (string3 ret) { return h.getName(); }
})";
compileAndRun(sourceCode, 0, "Main");
BOOST_REQUIRE(callContractFunction(0) == bytes({byte(0x01)}));
BOOST_REQUIRE(callContractFunction(1) == bytes({'a', 'b', 'c'}));
BOOST_REQUIRE(callContractFunction("getFlag()") == bytes({byte(0x01)}));
BOOST_REQUIRE(callContractFunction("getName()") == bytes({'a', 'b', 'c'}));
}
BOOST_AUTO_TEST_CASE(functions_called_by_constructor)
@ -1161,7 +1254,7 @@ BOOST_AUTO_TEST_CASE(functions_called_by_constructor)
function setName(string3 _name) { name = _name; }
})";
compileAndRun(sourceCode);
BOOST_REQUIRE(callContractFunction(0) == bytes({'a', 'b', 'c'}));
BOOST_REQUIRE(callContractFunction("getName()") == bytes({'a', 'b', 'c'}));
}
BOOST_AUTO_TEST_SUITE_END()

4
test/SolidityNatspecJSON.cpp

@ -56,9 +56,9 @@ public:
}
if (_userDocumentation)
generatedDocumentationString = m_compilerStack.getJsonDocumentation("", DocumentationType::NATSPEC_USER);
generatedDocumentationString = m_compilerStack.getMetadata("", DocumentationType::NATSPEC_USER);
else
generatedDocumentationString = m_compilerStack.getJsonDocumentation("", DocumentationType::NATSPEC_DEV);
generatedDocumentationString = m_compilerStack.getMetadata("", DocumentationType::NATSPEC_DEV);
Json::Value generatedDocumentation;
m_reader.parse(generatedDocumentationString, generatedDocumentation);
Json::Value expectedDocumentation;

26
test/SolidityOptimizer.cpp

@ -55,12 +55,12 @@ public:
}
template <class... Args>
void compareVersions(byte _index, Args const&... _arguments)
void compareVersions(std::string _sig, Args const&... _arguments)
{
m_contractAddress = m_nonOptimizedContract;
bytes nonOptimizedOutput = callContractFunction(_index, _arguments...);
bytes nonOptimizedOutput = callContractFunction(_sig, _arguments...);
m_contractAddress = m_optimizedContract;
bytes optimizedOutput = callContractFunction(_index, _arguments...);
bytes optimizedOutput = callContractFunction(_sig, _arguments...);
BOOST_CHECK_MESSAGE(nonOptimizedOutput == optimizedOutput, "Computed values do not match."
"\nNon-Optimized: " + toHex(nonOptimizedOutput) +
"\nOptimized: " + toHex(optimizedOutput));
@ -81,8 +81,8 @@ BOOST_AUTO_TEST_CASE(smoke_test)
return a;
}
})";
compileBothVersions(4, sourceCode);
compareVersions(0, u256(7));
compileBothVersions(29, sourceCode);
compareVersions("f(uint256)", u256(7));
}
BOOST_AUTO_TEST_CASE(large_integers)
@ -94,8 +94,8 @@ BOOST_AUTO_TEST_CASE(large_integers)
b = 0x10000000000000000000000002;
}
})";
compileBothVersions(33, sourceCode);
compareVersions(0);
compileBothVersions(58, sourceCode);
compareVersions("f()");
}
BOOST_AUTO_TEST_CASE(invariants)
@ -106,8 +106,8 @@ BOOST_AUTO_TEST_CASE(invariants)
return int(0) | (int(1) * (int(0) ^ (0 + a)));
}
})";
compileBothVersions(28, sourceCode);
compareVersions(0, u256(0x12334664));
compileBothVersions(53, sourceCode);
compareVersions("f(uint256)", u256(0x12334664));
}
BOOST_AUTO_TEST_CASE(unused_expressions)
@ -120,8 +120,8 @@ BOOST_AUTO_TEST_CASE(unused_expressions)
data;
}
})";
compileBothVersions(11, sourceCode);
compareVersions(0);
compileBothVersions(36, sourceCode);
compareVersions("f()");
}
BOOST_AUTO_TEST_CASE(constant_folding_both_sides)
@ -135,8 +135,8 @@ BOOST_AUTO_TEST_CASE(constant_folding_both_sides)
return 98 ^ (7 * ((1 | (x | 1000)) * 40) ^ 102);
}
})";
compileBothVersions(31, sourceCode);
compareVersions(0);
compileBothVersions(56, sourceCode);
compareVersions("f(uint256)");
}
BOOST_AUTO_TEST_SUITE_END()

20
test/solidityExecutionFramework.h

@ -56,34 +56,36 @@ public:
return m_output;
}
bytes const& callContractFunction(byte _index, bytes const& _data = bytes(), u256 const& _value = 0)
bytes const& callContractFunction(std::string _sig, bytes const& _data = bytes(),
u256 const& _value = 0)
{
sendMessage(bytes(1, _index) + _data, false, _value);
FixedHash<4> hash(dev::sha3(_sig));
sendMessage(hash.asBytes() + _data, false, _value);
return m_output;
}
template <class... Args>
bytes const& callContractFunction(byte _index, Args const&... _arguments)
bytes const& callContractFunction(std::string _sig, Args const&... _arguments)
{
return callContractFunction(_index, argsToBigEndian(_arguments...));
return callContractFunction(_sig, argsToBigEndian(_arguments...));
}
template <class CppFunction, class... Args>
void testSolidityAgainstCpp(byte _index, CppFunction const& _cppFunction, Args const&... _arguments)
void testSolidityAgainstCpp(std::string _sig, CppFunction const& _cppFunction, Args const&... _arguments)
{
bytes solidityResult = callContractFunction(_index, _arguments...);
bytes solidityResult = callContractFunction(_sig, _arguments...);
bytes cppResult = callCppAndEncodeResult(_cppFunction, _arguments...);
BOOST_CHECK_MESSAGE(solidityResult == cppResult, "Computed values do not match."
"\nSolidity: " + toHex(solidityResult) + "\nC++: " + toHex(cppResult));
}
template <class CppFunction, class... Args>
void testSolidityAgainstCppOnRange(byte _index, CppFunction const& _cppFunction,
void testSolidityAgainstCppOnRange(std::string _sig, CppFunction const& _cppFunction,
u256 const& _rangeStart, u256 const& _rangeEnd)
{
for (u256 argument = _rangeStart; argument < _rangeEnd; ++argument)
{
bytes solidityResult = callContractFunction(_index, argument);
bytes solidityResult = callContractFunction(_sig, argument);
bytes cppResult = callCppAndEncodeResult(_cppFunction, argument);
BOOST_CHECK_MESSAGE(solidityResult == cppResult, "Computed values do not match."
"\nSolidity: " + toHex(solidityResult) + "\nC++: " + toHex(cppResult) +
@ -143,6 +145,7 @@ private:
m_state.noteSending(m_sender);
executive.finalize();
m_output = executive.out().toVector();
m_logs = executive.logs();
}
protected:
@ -153,6 +156,7 @@ protected:
u256 const m_gasPrice = 100 * eth::szabo;
u256 const m_gas = 1000000;
bytes m_output;
eth::LogEntries m_logs;
};
}

Loading…
Cancel
Save