Browse Source

Merge branch 'develop' of github.com:ethereum/cpp-ethereum into develop

cl-refactor
Gav Wood 10 years ago
parent
commit
736a3d0b6c
  1. 10
      libevmasm/Assembly.cpp
  2. 2
      libevmasm/AssemblyItem.h
  3. 91
      libevmasm/BlockDeduplicator.cpp
  4. 69
      libevmasm/BlockDeduplicator.h
  5. 12
      libsolidity/Compiler.cpp
  6. 74
      mix/ClientModel.cpp
  7. 17
      mix/ClientModel.h
  8. 62
      mix/qml/QAddressView.qml
  9. 4
      mix/qml/StateDialog.qml
  10. 22
      mix/qml/StateListModel.qml
  11. 29
      mix/qml/StructView.qml
  12. 128
      mix/qml/TransactionDialog.qml
  13. 5
      mix/qml/js/TransactionHelper.js
  14. 102
      test/TestHelper.cpp
  15. 3
      test/TestHelper.h
  16. 2
      test/libethereum/blockchain.cpp
  17. 2
      test/libethereum/state.cpp
  18. 2
      test/libethereum/transaction.cpp
  19. 4
      test/libevm/vm.cpp
  20. 150
      test/libp2p/capability.cpp
  21. 59
      test/libsolidity/SolidityCompiler.cpp
  22. 38
      test/libsolidity/SolidityOptimizer.cpp

10
libevmasm/Assembly.cpp

@ -24,6 +24,7 @@
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libevmasm/CommonSubexpressionEliminator.h> #include <libevmasm/CommonSubexpressionEliminator.h>
#include <libevmasm/ControlFlowGraph.h> #include <libevmasm/ControlFlowGraph.h>
#include <libevmasm/BlockDeduplicator.h>
#include <json/json.h> #include <json/json.h>
using namespace std; using namespace std;
using namespace dev; using namespace dev;
@ -348,8 +349,17 @@ Assembly& Assembly::optimise(bool _enable)
copy(orig, iter, back_inserter(optimisedItems)); copy(orig, iter, back_inserter(optimisedItems));
} }
} }
if (optimisedItems.size() < m_items.size()) if (optimisedItems.size() < m_items.size())
{
m_items = move(optimisedItems); m_items = move(optimisedItems);
count++;
}
// This only modifies PushTags, we have to run again to actually remove code.
BlockDeduplicator dedup(m_items);
if (dedup.deduplicate())
count++;
} }
} }

2
libevmasm/AssemblyItem.h

@ -68,6 +68,8 @@ public:
/// @returns true iff the type and data of the items are equal. /// @returns true iff the type and data of the items are equal.
bool operator==(AssemblyItem const& _other) const { return m_type == _other.m_type && m_data == _other.m_data; } bool operator==(AssemblyItem const& _other) const { return m_type == _other.m_type && m_data == _other.m_data; }
bool operator!=(AssemblyItem const& _other) const { return !operator==(_other); } bool operator!=(AssemblyItem const& _other) const { return !operator==(_other); }
/// Less-than operator compatible with operator==.
bool operator<(AssemblyItem const& _other) const { return std::tie(m_type, m_data) < std::tie(_other.m_type, _other.m_data); }
/// @returns an upper bound for the number of bytes required by this item, assuming that /// @returns an upper bound for the number of bytes required by this item, assuming that
/// the value of a jump tag takes @a _addressLength bytes. /// the value of a jump tag takes @a _addressLength bytes.

91
libevmasm/BlockDeduplicator.cpp

@ -0,0 +1,91 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file BlockDeduplicator.cpp
* @author Christian <c@ethdev.com>
* @date 2015
* Unifies basic blocks that share content.
*/
#include <libevmasm/BlockDeduplicator.h>
#include <functional>
#include <libevmasm/AssemblyItem.h>
#include <libevmasm/SemanticInformation.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
bool BlockDeduplicator::deduplicate()
{
// Compares indices based on the suffix that starts there, ignoring tags and stopping at
// opcodes that stop the control flow.
function<bool(size_t, size_t)> comparator = [&](size_t _i, size_t _j)
{
if (_i == _j)
return false;
BlockIterator first(m_items.begin() + _i, m_items.end());
BlockIterator second(m_items.begin() + _j, m_items.end());
BlockIterator end(m_items.end(), m_items.end());
if (first != end && (*first).type() == Tag)
++first;
if (second != end && (*second).type() == Tag)
++second;
return std::lexicographical_compare(first, end, second, end);
};
set<size_t, function<bool(size_t, size_t)>> blocksSeen(comparator);
map<u256, u256> tagReplacement;
for (size_t i = 0; i < m_items.size(); ++i)
{
if (m_items.at(i).type() != Tag)
continue;
auto it = blocksSeen.find(i);
if (it == blocksSeen.end())
blocksSeen.insert(i);
else
tagReplacement[m_items.at(i).data()] = m_items.at(*it).data();
}
bool ret = false;
for (AssemblyItem& item: m_items)
if (item.type() == PushTag && tagReplacement.count(item.data()))
{
ret = true;
item.setData(tagReplacement.at(item.data()));
}
return ret;
}
BlockDeduplicator::BlockIterator& BlockDeduplicator::BlockIterator::operator++()
{
if (it == end)
return *this;
if (SemanticInformation::altersControlFlow(*it) && *it != AssemblyItem(eth::Instruction::JUMPI))
it = end;
else
{
++it;
while (it != end && it->type() == Tag)
++it;
}
return *this;
}

69
libevmasm/BlockDeduplicator.h

@ -0,0 +1,69 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file BlockDeduplicator.h
* @author Christian <c@ethdev.com>
* @date 2015
* Unifies basic blocks that share content.
*/
#pragma once
#include <cstddef>
#include <vector>
#include <functional>
namespace dev
{
namespace eth
{
class AssemblyItem;
using AssemblyItems = std::vector<AssemblyItem>;
/**
* Optimizer class to be used to unify blocks that share content.
* Modifies the passed vector in place.
*/
class BlockDeduplicator
{
public:
BlockDeduplicator(AssemblyItems& _items): m_items(_items) {}
/// @returns true if something was changed
bool deduplicate();
private:
/// Iterator that skips tags skips to the end if (all branches of) the control
/// flow does not continue to the next instruction.
struct BlockIterator: std::iterator<std::forward_iterator_tag, AssemblyItem const>
{
public:
BlockIterator(AssemblyItems::const_iterator _it, AssemblyItems::const_iterator _end):
it(_it), end(_end) { }
BlockIterator& operator++();
bool operator==(BlockIterator const& _other) const { return it == _other.it; }
bool operator!=(BlockIterator const& _other) const { return it != _other.it; }
AssemblyItem const& operator*() const { return *it; }
AssemblyItems::const_iterator it;
AssemblyItems::const_iterator end;
};
AssemblyItems& m_items;
};
}
}

12
libsolidity/Compiler.cpp

@ -377,12 +377,16 @@ bool Compiler::visit(IfStatement const& _ifStatement)
StackHeightChecker checker(m_context); StackHeightChecker checker(m_context);
CompilerContext::LocationSetter locationSetter(m_context, _ifStatement); CompilerContext::LocationSetter locationSetter(m_context, _ifStatement);
compileExpression(_ifStatement.getCondition()); compileExpression(_ifStatement.getCondition());
eth::AssemblyItem trueTag = m_context.appendConditionalJump(); m_context << eth::Instruction::ISZERO;
eth::AssemblyItem falseTag = m_context.appendConditionalJump();
eth::AssemblyItem endTag = falseTag;
_ifStatement.getTrueStatement().accept(*this);
if (_ifStatement.getFalseStatement()) if (_ifStatement.getFalseStatement())
{
endTag = m_context.appendJumpToNew();
m_context << falseTag;
_ifStatement.getFalseStatement()->accept(*this); _ifStatement.getFalseStatement()->accept(*this);
eth::AssemblyItem endTag = m_context.appendJumpToNew(); }
m_context << trueTag;
_ifStatement.getTrueStatement().accept(*this);
m_context << endTag; m_context << endTag;
checker.check(); checker.check();

74
mix/ClientModel.cpp

@ -194,7 +194,7 @@ QVariantMap ClientModel::contractAddresses() const
{ {
QVariantMap res; QVariantMap res;
for (auto const& c: m_contractAddresses) for (auto const& c: m_contractAddresses)
res.insert(c.first, QString::fromStdString(toJS(c.second))); res.insert(c.first.first, QString::fromStdString(toJS(c.second)));
return res; return res;
} }
@ -256,7 +256,9 @@ void ClientModel::setupState(QVariantMap _state)
u256 value = (qvariant_cast<QEther*>(transaction.value("value")))->toU256Wei(); u256 value = (qvariant_cast<QEther*>(transaction.value("value")))->toU256Wei();
u256 gasPrice = (qvariant_cast<QEther*>(transaction.value("gasPrice")))->toU256Wei(); u256 gasPrice = (qvariant_cast<QEther*>(transaction.value("gasPrice")))->toU256Wei();
QString sender = transaction.value("sender").toString(); QString sender = transaction.value("sender").toString();
bool isStdContract = (transaction.value("stdContract").toBool()); bool isStdContract = transaction.value("stdContract").toBool();
bool isContractCreation = transaction.value("isContractCreation").toBool();
bool isFunctionCall = transaction.value("isFunctionCall").toBool();
if (isStdContract) if (isStdContract)
{ {
if (contractId.isEmpty()) //TODO: This is to support old project files, remove later if (contractId.isEmpty()) //TODO: This is to support old project files, remove later
@ -272,7 +274,7 @@ void ClientModel::setupState(QVariantMap _state)
{ {
if (contractId.isEmpty() && m_codeModel->hasContract()) //TODO: This is to support old project files, remove later if (contractId.isEmpty() && m_codeModel->hasContract()) //TODO: This is to support old project files, remove later
contractId = m_codeModel->contracts().keys()[0]; contractId = m_codeModel->contracts().keys()[0];
TransactionSettings transactionSettings(contractId, functionId, value, gas, gasAuto, gasPrice, Secret(sender.toStdString())); TransactionSettings transactionSettings(contractId, functionId, value, gas, gasAuto, gasPrice, Secret(sender.toStdString()), isContractCreation, isFunctionCall);
transactionSettings.parameterValues = transaction.value("parameters").toMap(); transactionSettings.parameterValues = transaction.value("parameters").toMap();
if (contractId == functionId || functionId == "Constructor") if (contractId == functionId || functionId == "Constructor")
@ -308,6 +310,14 @@ void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence,
m_gasCosts.clear(); m_gasCosts.clear();
for (TransactionSettings const& transaction: _sequence) for (TransactionSettings const& transaction: _sequence)
{ {
std::pair<QString, int> ctrInstance = resolvePair(transaction.contractId);
QString address = resolveToken(ctrInstance, deployedContracts);
if (!transaction.isFunctionCall)
{
callAddress(Address(address.toStdString()), bytes(), transaction);
onNewTransaction();
continue;
}
ContractCallDataEncoder encoder; ContractCallDataEncoder encoder;
if (!transaction.stdContractUrl.isEmpty()) if (!transaction.stdContractUrl.isEmpty())
{ {
@ -322,7 +332,7 @@ void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence,
else else
{ {
//encode data //encode data
CompiledContract const& compilerRes = m_codeModel->contract(transaction.contractId); CompiledContract const& compilerRes = m_codeModel->contract(ctrInstance.first);
QFunctionDefinition const* f = nullptr; QFunctionDefinition const* f = nullptr;
bytes contractCode = compilerRes.bytes(); bytes contractCode = compilerRes.bytes();
shared_ptr<QContractDefinition> contractDef = compilerRes.sharedContract(); shared_ptr<QContractDefinition> contractDef = compilerRes.sharedContract();
@ -348,32 +358,29 @@ void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence,
{ {
QSolidityType const* type = p->type(); QSolidityType const* type = p->type();
QVariant value = transaction.parameterValues.value(p->name()); QVariant value = transaction.parameterValues.value(p->name());
if (type->type().type == SolidityType::Type::Address && value.toString().startsWith("<") && value.toString().endsWith(">")) if (type->type().type == SolidityType::Type::Address)
{ {
QStringList nb = value.toString().remove("<").remove(">").split(" - "); std::pair<QString, int> ctrParamInstance = resolvePair(value.toString());
value = QVariant(QString::fromStdString("0x" + dev::toHex(deployedContracts.at(nb.back().toInt()).ref()))); value = QVariant(resolveToken(ctrParamInstance, deployedContracts));
} }
encoder.encode(value, type->type()); encoder.encode(value, type->type());
} }
if (transaction.functionId.isEmpty() || transaction.functionId == transaction.contractId) if (transaction.functionId.isEmpty() || transaction.functionId == ctrInstance.first)
{ {
bytes param = encoder.encodedData(); bytes param = encoder.encodedData();
contractCode.insert(contractCode.end(), param.begin(), param.end()); contractCode.insert(contractCode.end(), param.begin(), param.end());
Address newAddress = deployContract(contractCode, transaction); Address newAddress = deployContract(contractCode, transaction);
deployedContracts.push_back(newAddress); deployedContracts.push_back(newAddress);
auto contractAddressIter = m_contractAddresses.find(transaction.contractId); std::pair<QString, int> contractToken = retrieveToken(transaction.contractId, deployedContracts);
if (contractAddressIter == m_contractAddresses.end() || newAddress != contractAddressIter->second) m_contractAddresses[contractToken] = newAddress;
{ m_contractNames[newAddress] = contractToken.first;
m_contractAddresses[transaction.contractId] = newAddress; contractAddressesChanged();
m_contractNames[newAddress] = transaction.contractId;
contractAddressesChanged();
}
gasCostsChanged(); gasCostsChanged();
} }
else else
{ {
auto contractAddressIter = m_contractAddresses.find(transaction.contractId); auto contractAddressIter = m_contractAddresses.find(ctrInstance);
if (contractAddressIter == m_contractAddresses.end()) if (contractAddressIter == m_contractAddresses.end())
{ {
emit runFailed("Contract '" + transaction.contractId + tr(" not deployed.") + "' " + tr(" Cannot call ") + transaction.functionId); emit runFailed("Contract '" + transaction.contractId + tr(" not deployed.") + "' " + tr(" Cannot call ") + transaction.functionId);
@ -381,7 +388,7 @@ void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence,
emit runStateChanged(); emit runStateChanged();
return; return;
} }
callContract(contractAddressIter->second, encoder.encodedData(), transaction); callAddress(contractAddressIter->second, encoder.encodedData(), transaction);
} }
m_gasCosts.append(m_client->lastExecution().gasUsed); m_gasCosts.append(m_client->lastExecution().gasUsed);
} }
@ -405,6 +412,37 @@ void ClientModel::executeSequence(vector<TransactionSettings> const& _sequence,
}); });
} }
std::pair<QString, int> ClientModel::resolvePair(QString const& _contractId)
{
std::pair<QString, int> ret;
ret.first = _contractId;
ret.second = -1;
if (_contractId.startsWith("<") && _contractId.endsWith(">"))
{
QStringList values = ret.first.remove("<").remove(">").split(" - ");
ret.first = values[0];
ret.second = values[1].toUInt();
}
return ret;
}
QString ClientModel::resolveToken(std::pair<QString, int> const& _value, vector<Address> const& _contracts)
{
if (_value.second != -1)
return QString::fromStdString("0x" + dev::toHex(_contracts.at(_value.second).ref()));
else
return _value.first;
}
std::pair<QString, int> ClientModel::retrieveToken(QString const& _value, vector<Address> const& _contracts)
{
std::pair<QString, int> ret;
ret.first = _value;
ret.second = _contracts.size() - 1;
return ret;
}
void ClientModel::showDebugger() void ClientModel::showDebugger()
{ {
ExecutionResult last = m_client->lastExecution(); ExecutionResult last = m_client->lastExecution();
@ -609,7 +647,7 @@ Address ClientModel::deployContract(bytes const& _code, TransactionSettings cons
return newAddress; return newAddress;
} }
void ClientModel::callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr) void ClientModel::callAddress(Address const& _contract, bytes const& _data, TransactionSettings const& _tr)
{ {
m_client->submitTransaction(_tr.sender, _tr.value, _contract, _data, _tr.gas, _tr.gasPrice, _tr.gasAuto); m_client->submitTransaction(_tr.sender, _tr.value, _contract, _data, _tr.gas, _tr.gasPrice, _tr.gasAuto);
} }

17
mix/ClientModel.h

@ -53,10 +53,10 @@ struct SolidityType;
struct TransactionSettings struct TransactionSettings
{ {
TransactionSettings() {} TransactionSettings() {}
TransactionSettings(QString const& _contractId, QString const& _functionId, u256 _value, u256 _gas, bool _gasAuto, u256 _gasPrice, Secret _sender): TransactionSettings(QString const& _contractId, QString const& _functionId, u256 _value, u256 _gas, bool _gasAuto, u256 _gasPrice, Secret _sender, bool _isContractCreation, bool _isFunctionCall):
contractId(_contractId), functionId(_functionId), value(_value), gas(_gas), gasAuto(_gasAuto), gasPrice(_gasPrice), sender(_sender) {} contractId(_contractId), functionId(_functionId), value(_value), gas(_gas), gasAuto(_gasAuto), gasPrice(_gasPrice), sender(_sender), isContractCreation(_isContractCreation), isFunctionCall(_isFunctionCall) {}
TransactionSettings(QString const& _stdContractName, QString const& _stdContractUrl): TransactionSettings(QString const& _stdContractName, QString const& _stdContractUrl):
contractId(_stdContractName), gasAuto(true), stdContractUrl(_stdContractUrl) {} contractId(_stdContractName), gasAuto(true), stdContractUrl(_stdContractUrl), isContractCreation(true), isFunctionCall(true) {}
/// Contract name /// Contract name
QString contractId; QString contractId;
@ -76,6 +76,10 @@ struct TransactionSettings
QString stdContractUrl; QString stdContractUrl;
/// Sender /// Sender
Secret sender; Secret sender;
/// Tr deploys a contract
bool isContractCreation;
/// Tr call a ctr function
bool isFunctionCall;
}; };
@ -222,11 +226,14 @@ private:
QVariantList gasCosts() const; QVariantList gasCosts() const;
void executeSequence(std::vector<TransactionSettings> const& _sequence, std::unordered_map<Address, dev::eth::Account> const& _accounts, Secret const& _miner); void executeSequence(std::vector<TransactionSettings> const& _sequence, std::unordered_map<Address, dev::eth::Account> const& _accounts, Secret const& _miner);
dev::Address deployContract(bytes const& _code, TransactionSettings const& _tr = TransactionSettings()); dev::Address deployContract(bytes const& _code, TransactionSettings const& _tr = TransactionSettings());
void callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr); void callAddress(Address const& _contract, bytes const& _data, TransactionSettings const& _tr);
void onNewTransaction(); void onNewTransaction();
void onStateReset(); void onStateReset();
void showDebuggerForTransaction(ExecutionResult const& _t); void showDebuggerForTransaction(ExecutionResult const& _t);
QVariant formatValue(SolidityType const& _type, dev::u256 const& _value); QVariant formatValue(SolidityType const& _type, dev::u256 const& _value);
QString resolveToken(std::pair<QString, int> const& _value, std::vector<Address> const& _contracts);
std::pair<QString, int> retrieveToken(QString const& _value, std::vector<Address> const& _contracts);
std::pair<QString, int> resolvePair(QString const& _contractId);
QVariant formatStorageValue(SolidityType const& _type, std::unordered_map<dev::u256, dev::u256> const& _storage, unsigned _offset, dev::u256 const& _slot); QVariant formatStorageValue(SolidityType const& _type, std::unordered_map<dev::u256, dev::u256> const& _storage, unsigned _offset, dev::u256 const& _slot);
std::atomic<bool> m_running; std::atomic<bool> m_running;
@ -237,7 +244,7 @@ private:
std::unique_ptr<Web3Server> m_web3Server; std::unique_ptr<Web3Server> m_web3Server;
std::shared_ptr<eth::FixedAccountHolder> m_ethAccounts; std::shared_ptr<eth::FixedAccountHolder> m_ethAccounts;
QList<u256> m_gasCosts; QList<u256> m_gasCosts;
std::map<QString, Address> m_contractAddresses; std::map<std::pair<QString, int>, Address> m_contractAddresses;
std::map<Address, QString> m_contractNames; std::map<Address, QString> m_contractNames;
std::map<QString, Address> m_stdContractAddresses; std::map<QString, Address> m_stdContractAddresses;
std::map<Address, QString> m_stdContractNames; std::map<Address, QString> m_stdContractNames;

62
mix/qml/QAddressView.qml

@ -8,6 +8,10 @@ Item
property alias accountRef: ctrModel property alias accountRef: ctrModel
property string subType property string subType
property bool readOnly property bool readOnly
property alias currentIndex: trCombobox.currentIndex
property alias currentText: textinput.text
property variant accounts
signal indexChanged()
id: editRoot id: editRoot
height: 20 height: 20
width: 320 width: 320
@ -17,6 +21,51 @@ Item
id: boldFont id: boldFont
} }
function currentValue() {
return currentText;
}
function currentType()
{
return accountRef.get(trCombobox.currentIndex).type;
}
function current()
{
return accountRef.get(trCombobox.currentIndex);
}
function load()
{
accountRef.clear();
accountRef.append({"itemid": " - "});
if (subType === "contract" || subType === "address")
{
var trCr = 0;
for (var k = 0; k < transactionsModel.count; k++)
{
if (k >= transactionIndex)
break;
var tr = transactionsModel.get(k);
if (tr.functionId === tr.contractId /*&& (dec[1] === tr.contractId || item.subType === "address")*/)
{
accountRef.append({ "itemid": tr.contractId + " - " + trCr, "value": "<" + tr.contractId + " - " + trCr + ">", "type": "contract" });
trCr++;
}
}
}
if (subType === "address")
{
for (k = 0; k < accounts.length; k++)
{
if (accounts[k].address === undefined)
accounts[k].address = clientModel.address(accounts[k].secret);
accountRef.append({ "itemid": accounts[k].name, "value": "0x" + accounts[k].address, "type": "address" });
}
}
}
function init() function init()
{ {
trCombobox.visible = !readOnly trCombobox.visible = !readOnly
@ -35,6 +84,18 @@ Item
} }
} }
function select(address)
{
for (var k = 0; k < accountRef.count; k++)
{
if (accountRef.get(k).value === address)
{
trCombobox.currentIndex = k;
break;
}
}
}
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
radius: 4 radius: 4
@ -96,6 +157,7 @@ Item
{ {
textinput.text = ""; textinput.text = "";
} }
indexChanged();
} }
} }
} }

4
mix/qml/StateDialog.qml

@ -436,7 +436,7 @@ Dialog {
model: transactionsModel model: transactionsModel
headerVisible: false headerVisible: false
TableViewColumn { TableViewColumn {
role: "name" role: "label"
title: qsTr("Name") title: qsTr("Name")
width: 150 width: 150
delegate: Item { delegate: Item {
@ -476,7 +476,7 @@ Dialog {
text: { text: {
if (styleData.row >= 0) if (styleData.row >= 0)
return transactionsModel.get( return transactionsModel.get(
styleData.row).functionId styleData.row).label
else else
return "" return ""
} }

22
mix/qml/StateListModel.qml

@ -46,6 +46,7 @@ Item {
t.sender = defaultAccount; //support for old project t.sender = defaultAccount; //support for old project
var r = { var r = {
type: t.type,
contractId: t.contractId, contractId: t.contractId,
functionId: t.functionId, functionId: t.functionId,
url: t.url, url: t.url,
@ -55,8 +56,21 @@ Item {
gasAuto: t.gasAuto, gasAuto: t.gasAuto,
stdContract: t.stdContract ? true : false, stdContract: t.stdContract ? true : false,
parameters: {}, parameters: {},
sender: t.sender sender: t.sender,
isContractCreation: t.isContractCreation,
label: t.label,
isFunctionCall: t.isFunctionCall
}; };
if (r.isFunctionCall === undefined)
r.isFunctionCall = true;
if (!r.label)
r.label = r.contractId + " - " + r.functionId;
if (r.isContractCreation === undefined)
r.isContractCreation = r.functionId === r.contractId;
for (var key in t.parameters) for (var key in t.parameters)
r.parameters[key] = t.parameters[key]; r.parameters[key] = t.parameters[key];
@ -100,6 +114,7 @@ Item {
function toPlainTransactionItem(t) { function toPlainTransactionItem(t) {
var r = { var r = {
type: t.type,
contractId: t.contractId, contractId: t.contractId,
functionId: t.functionId, functionId: t.functionId,
url: t.url, url: t.url,
@ -109,7 +124,10 @@ Item {
gasPrice: { value: t.gasPrice.value, unit: t.gasPrice.unit }, gasPrice: { value: t.gasPrice.value, unit: t.gasPrice.unit },
stdContract: t.stdContract, stdContract: t.stdContract,
sender: t.sender, sender: t.sender,
parameters: {} parameters: {},
isContractCreation: t.isContractCreation,
label: t.label,
isFunctionCall: t.isFunctionCall
}; };
for (var key in t.parameters) for (var key in t.parameters)
r.parameters[key] = t.parameters[key]; r.parameters[key] = t.parameters[key];

29
mix/qml/StructView.qml

@ -75,38 +75,13 @@ Column
item.readOnly = context === "variable"; item.readOnly = context === "variable";
if (ptype.category === QSolidityType.Address) if (ptype.category === QSolidityType.Address)
{ {
item.accounts = accounts
item.value = getValue(); item.value = getValue();
if (context === "parameter") if (context === "parameter")
{ {
var dec = modelData.type.name.split(" "); var dec = modelData.type.name.split(" ");
item.subType = dec[0]; item.subType = dec[0];
item.accountRef.append({"itemid": " - "}); item.load();
if (item.subType === "contract" || item.subType === "address")
{
var trCr = 0;
for (var k = 0; k < transactionsModel.count; k++)
{
if (k >= transactionIndex)
break;
var tr = transactionsModel.get(k);
if (tr.functionId === tr.contractId && (dec[1] === tr.contractId || item.subType === "address"))
{
item.accountRef.append({ "itemid": tr.contractId + " - " + trCr, "value": "<" + tr.contractId + " - " + trCr + ">", "type": "contract" });
trCr++;
}
}
}
if (item.subType === "address")
{
for (k = 0; k < accounts.length; k++)
{
if (accounts[k].address === undefined)
accounts[k].address = clientModel.address(accounts[k].secret);
item.accountRef.append({ "itemid": accounts[k].name, "value": "0x" + accounts[k].address, "type": "address" });
}
}
} }
item.init(); item.init();
} }

128
mix/qml/TransactionDialog.qml

@ -66,29 +66,48 @@ Dialog {
contractIndex = 0; //@todo suggest unused contract contractIndex = 0; //@todo suggest unused contract
contractComboBox.currentIndex = contractIndex; contractComboBox.currentIndex = contractIndex;
loadFunctions(contractComboBox.currentValue()); recipients.accounts = senderComboBox.model;
recipients.subType = "address";
recipients.load();
recipients.init();
recipients.select(contractId);
if (item.isContractCreation)
loadFunctions(contractComboBox.currentValue());
else
loadFunctions(contractFromToken(recipients.currentValue()))
selectFunction(functionId); selectFunction(functionId);
trType.checked = item.isContractCreation
trType.init();
paramsModel = []; paramsModel = [];
if (functionId !== contractComboBox.currentValue()) if (item.isContractCreation)
loadCtorParameters();
else
loadParameters(); loadParameters();
else {
var contract = codeModel.contracts[contractId];
if (contract) {
var params = contract.contract.constructor.parameters;
for (var p = 0; p < params.length; p++)
loadParameter(params[p]);
}
}
initTypeLoader();
visible = true; visible = true;
valueField.focus = true; valueField.focus = true;
} }
function loadCtorParameters(contractId)
{
paramsModel = [];
console.log(contractId);
var contract = codeModel.contracts[contractId];
if (contract) {
var params = contract.contract.constructor.parameters;
for (var p = 0; p < params.length; p++)
loadParameter(params[p]);
}
initTypeLoader();
}
function loadFunctions(contractId) function loadFunctions(contractId)
{ {
functionsModel.clear(); functionsModel.clear();
functionsModel.append({ text: " - " });
var contract = codeModel.contracts[contractId]; var contract = codeModel.contracts[contractId];
if (contract) { if (contract) {
var functions = codeModel.contracts[contractId].contract.functions; var functions = codeModel.contracts[contractId].contract.functions;
@ -96,9 +115,6 @@ Dialog {
functionsModel.append({ text: functions[f].name }); functionsModel.append({ text: functions[f].name });
} }
} }
//append constructor
functionsModel.append({ text: contractId });
} }
function selectContract(contractName) function selectContract(contractName)
@ -136,7 +152,7 @@ Dialog {
function loadParameters() { function loadParameters() {
paramsModel = [] paramsModel = []
if (functionComboBox.currentIndex >= 0 && functionComboBox.currentIndex < functionsModel.count) { if (functionComboBox.currentIndex >= 0 && functionComboBox.currentIndex < functionsModel.count) {
var contract = codeModel.contracts[contractComboBox.currentValue()]; var contract = codeModel.contracts[contractFromToken(recipients.currentValue())];
if (contract) { if (contract) {
var func = contract.contract.functions[functionComboBox.currentIndex]; var func = contract.contract.functions[functionComboBox.currentIndex];
if (func) { if (func) {
@ -193,10 +209,37 @@ Dialog {
item.functionId = transactionDialog.functionId; item.functionId = transactionDialog.functionId;
} }
item.isContractCreation = trType.checked;
item.isFunctionCall = item.functionId !== " - ";
if (!item.isContractCreation)
{
item.contractId = recipients.currentText;
item.label = item.contractId + " " + item.functionId;
if (recipients.current().type === "address")
{
item.functionId = "";
item.isFunctionCall = false;
}
}
else
{
item.functionId = item.contractId;
item.label = qsTr("Deploy") + " " + item.contractId;
}
item.sender = senderComboBox.model[senderComboBox.currentIndex].secret; item.sender = senderComboBox.model[senderComboBox.currentIndex].secret;
item.parameters = paramValues; item.parameters = paramValues;
return item; return item;
} }
function contractFromToken(token)
{
if (token.indexOf('<') === 0)
return token.replace("<", "").replace(">", "").split(" - ")[0];
return token;
}
contentItem: Rectangle { contentItem: Rectangle {
color: transactionDialogStyle.generic.backgroundColor color: transactionDialogStyle.generic.backgroundColor
ColumnLayout { ColumnLayout {
@ -238,6 +281,59 @@ Dialog {
} }
} }
RowLayout
{
id: rowIsContract
Layout.fillWidth: true
height: 150
CheckBox {
id: trType
onCheckedChanged:
{
init();
}
function init()
{
rowFunction.visible = !checked;
rowContract.visible = checked;
rowRecipient.visible = !checked;
paramLabel.visible = checked;
paramScroll.visible = checked;
functionComboBox.enabled = !checked;
if (checked)
loadCtorParameters(contractComboBox.currentValue());
}
text: qsTr("is contract creation")
checked: true
}
}
RowLayout
{
id: rowRecipient
Layout.fillWidth: true
height: 150
DefaultLabel {
Layout.preferredWidth: 75
text: qsTr("Recipient")
}
QAddressView
{
id: recipients
onIndexChanged:
{
rowFunction.visible = current().type === "contract";
paramLabel.visible = current().type === "contract";
paramScroll.visible = current().type === "contract";
if (!rowIsContract.checked)
loadFunctions(contractFromToken(recipients.currentValue()))
}
}
}
RowLayout RowLayout
{ {
id: rowContract id: rowContract
@ -260,7 +356,7 @@ Dialog {
id: contractsModel id: contractsModel
} }
onCurrentIndexChanged: { onCurrentIndexChanged: {
loadFunctions(currentValue()); loadCtorParameters(currentValue());
} }
} }
} }

5
mix/qml/js/TransactionHelper.js

@ -9,7 +9,10 @@ function defaultTransaction()
gasAuto: true, gasAuto: true,
gasPrice: createEther("100000", QEther.Wei), gasPrice: createEther("100000", QEther.Wei),
parameters: {}, parameters: {},
stdContract: false stdContract: false,
isContractCreation: true,
label: "",
isFunctionCall: true
}; };
} }

102
test/TestHelper.cpp

@ -549,58 +549,53 @@ void checkCallCreates(eth::Transactions _resultCallCreates, eth::Transactions _e
} }
} }
void userDefinedTest(string testTypeFlag, std::function<void(json_spirit::mValue&, bool)> doTests) void userDefinedTest(std::function<void(json_spirit::mValue&, bool)> doTests)
{ {
Options::get(); // parse command line options, e.g. to enable JIT if (!Options::get().singleTest)
return;
for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i) if (Options::get().singleTestFile.empty() || Options::get().singleTestName.empty())
{ {
string arg = boost::unit_test::framework::master_test_suite().argv[i]; cnote << "Missing user test specification\nUsage: testeth --singletest <filename> <testname>\n";
if (arg == testTypeFlag) return;
{ }
if (boost::unit_test::framework::master_test_suite().argc <= i + 2)
{
cnote << "Missing filename\nUsage: testeth " << testTypeFlag << " <filename> <testname>\n";
return;
}
string filename = boost::unit_test::framework::master_test_suite().argv[i + 1];
string testname = boost::unit_test::framework::master_test_suite().argv[i + 2];
int currentVerbosity = g_logVerbosity;
g_logVerbosity = 12;
try
{
cnote << "Testing user defined test: " << filename;
json_spirit::mValue v;
string s = asString(contents(filename));
BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of " + filename + " is empty. ");
json_spirit::read_string(s, v);
json_spirit::mObject oSingleTest;
json_spirit::mObject::const_iterator pos = v.get_obj().find(testname);
if (pos == v.get_obj().end())
{
cnote << "Could not find test: " << testname << " in " << filename << "\n";
return;
}
else
oSingleTest[pos->first] = pos->second;
json_spirit::mValue v_singleTest(oSingleTest); auto& filename = Options::get().singleTestFile;
doTests(v_singleTest, false); auto& testname = Options::get().singleTestName;
} int currentVerbosity = g_logVerbosity;
catch (Exception const& _e) g_logVerbosity = 12;
{ try
BOOST_ERROR("Failed Test with Exception: " << diagnostic_information(_e)); {
g_logVerbosity = currentVerbosity; cnote << "Testing user defined test: " << filename;
} json_spirit::mValue v;
catch (std::exception const& _e) string s = asString(contents(filename));
{ BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of " + filename + " is empty. ");
BOOST_ERROR("Failed Test with Exception: " << _e.what()); json_spirit::read_string(s, v);
g_logVerbosity = currentVerbosity; json_spirit::mObject oSingleTest;
}
g_logVerbosity = currentVerbosity; json_spirit::mObject::const_iterator pos = v.get_obj().find(testname);
if (pos == v.get_obj().end())
{
cnote << "Could not find test: " << testname << " in " << filename << "\n";
return;
} }
else
oSingleTest[pos->first] = pos->second;
json_spirit::mValue v_singleTest(oSingleTest);
doTests(v_singleTest, false);
}
catch (Exception const& _e)
{
BOOST_ERROR("Failed Test with Exception: " << diagnostic_information(_e));
g_logVerbosity = currentVerbosity;
} }
catch (std::exception const& _e)
{
BOOST_ERROR("Failed Test with Exception: " << _e.what());
g_logVerbosity = currentVerbosity;
}
g_logVerbosity = currentVerbosity;
} }
void executeTests(const string& _name, const string& _testPathAppendix, const boost::filesystem::path _pathToFiller, std::function<void(json_spirit::mValue&, bool)> doTests) void executeTests(const string& _name, const string& _testPathAppendix, const boost::filesystem::path _pathToFiller, std::function<void(json_spirit::mValue&, bool)> doTests)
@ -742,7 +737,20 @@ Options::Options()
else if (arg == "--singletest" && i + 1 < argc) else if (arg == "--singletest" && i + 1 < argc)
{ {
singleTest = true; singleTest = true;
singleTestName = argv[i + 1]; auto name1 = std::string{argv[i + 1]};
if (i + 1 < argc) // two params
{
auto name2 = std::string{argv[i + 2]};
if (name2[0] == '-') // not param, another option
singleTestName = std::move(name1);
else
{
singleTestFile = std::move(name1);
singleTestName = std::move(name2);
}
}
else
singleTestName = std::move(name1);
} }
} }
} }

3
test/TestHelper.h

@ -157,7 +157,7 @@ void checkLog(eth::LogEntries _resultLogs, eth::LogEntries _expectedLogs);
void checkCallCreates(eth::Transactions _resultCallCreates, eth::Transactions _expectedCallCreates); void checkCallCreates(eth::Transactions _resultCallCreates, eth::Transactions _expectedCallCreates);
void executeTests(const std::string& _name, const std::string& _testPathAppendix, const boost::filesystem::path _pathToFiller, std::function<void(json_spirit::mValue&, bool)> doTests); void executeTests(const std::string& _name, const std::string& _testPathAppendix, const boost::filesystem::path _pathToFiller, std::function<void(json_spirit::mValue&, bool)> doTests);
void userDefinedTest(std::string testTypeFlag, std::function<void(json_spirit::mValue&, bool)> doTests); void userDefinedTest(std::function<void(json_spirit::mValue&, bool)> doTests);
RLPStream createRLPStreamFromTransactionFields(json_spirit::mObject& _tObj); RLPStream createRLPStreamFromTransactionFields(json_spirit::mObject& _tObj);
eth::LastHashes lastHashes(u256 _currentBlockNumber); eth::LastHashes lastHashes(u256 _currentBlockNumber);
json_spirit::mObject fillJsonWithState(eth::State _state); json_spirit::mObject fillJsonWithState(eth::State _state);
@ -188,6 +188,7 @@ public:
/// Test selection /// Test selection
/// @{ /// @{
bool singleTest = false; bool singleTest = false;
std::string singleTestFile;
std::string singleTestName; std::string singleTestName;
bool performance = false; bool performance = false;
bool quadratic = false; bool quadratic = false;

2
test/libethereum/blockchain.cpp

@ -792,7 +792,7 @@ BOOST_AUTO_TEST_CASE(bcWalletTest)
BOOST_AUTO_TEST_CASE(userDefinedFile) BOOST_AUTO_TEST_CASE(userDefinedFile)
{ {
dev::test::userDefinedTest("--singletest", dev::test::doBlockchainTests); dev::test::userDefinedTest(dev::test::doBlockchainTests);
} }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

2
test/libethereum/state.cpp

@ -265,7 +265,7 @@ BOOST_AUTO_TEST_CASE(stRandom)
BOOST_AUTO_TEST_CASE(userDefinedFileState) BOOST_AUTO_TEST_CASE(userDefinedFileState)
{ {
dev::test::userDefinedTest("--singletest", dev::test::doStateTests); dev::test::userDefinedTest(dev::test::doStateTests);
} }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

2
test/libethereum/transaction.cpp

@ -195,7 +195,7 @@ BOOST_AUTO_TEST_CASE(ttCreateTest)
BOOST_AUTO_TEST_CASE(userDefinedFile) BOOST_AUTO_TEST_CASE(userDefinedFile)
{ {
dev::test::userDefinedTest("--singletest", dev::test::doTransactionTests); dev::test::userDefinedTest(dev::test::doTransactionTests);
} }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

4
test/libevm/vm.cpp

@ -432,7 +432,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
} }
} }
} } // Namespace Close } } // namespace close
BOOST_AUTO_TEST_SUITE(VMTests) BOOST_AUTO_TEST_SUITE(VMTests)
@ -542,7 +542,7 @@ BOOST_AUTO_TEST_CASE(vmRandom)
BOOST_AUTO_TEST_CASE(userDefinedFile) BOOST_AUTO_TEST_CASE(userDefinedFile)
{ {
dev::test::userDefinedTest("--singletest", dev::test::doVMTests); dev::test::userDefinedTest(dev::test::doVMTests);
} }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

150
test/libp2p/capability.cpp

@ -0,0 +1,150 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file capability.cpp
* @author Vladislav Gluhovsky <vlad@ethdev.com>
* @date May 2015
*/
#include <boost/test/unit_test.hpp>
#include <chrono>
#include <thread>
#include <libp2p/Common.h>
#include <libp2p/Host.h>
#include <libp2p/Session.h>
#include <libp2p/Capability.h>
#include <libp2p/HostCapability.h>
using namespace std;
using namespace dev;
using namespace dev::p2p;
struct P2PFixture
{
P2PFixture() { dev::p2p::NodeIPEndpoint::test_allowLocal = true; }
~P2PFixture() { dev::p2p::NodeIPEndpoint::test_allowLocal = false; }
};
struct VerbosityHolder
{
VerbosityHolder(): oldLogVerbosity(g_logVerbosity) { g_logVerbosity = 10; }
~VerbosityHolder() { g_logVerbosity = oldLogVerbosity; }
int oldLogVerbosity;
};
class TestCapability: public Capability
{
public:
TestCapability(Session* _s, HostCapabilityFace* _h, unsigned _idOffset): Capability(_s, _h, _idOffset), m_cntReceivedMessages(0), m_testSum(0) {}
virtual ~TestCapability() {}
int countReceivedMessages() { return m_cntReceivedMessages; }
int testSum() { return m_testSum; }
static std::string name() { return "test"; }
static u256 version() { return 2; }
static unsigned messageCount() { return UserPacket + 1; }
void sendTestMessage(int _i) { RLPStream s; sealAndSend(prep(s, UserPacket, 1) << _i); }
protected:
virtual bool interpret(unsigned _id, RLP const& _r) override;
int m_cntReceivedMessages;
int m_testSum;
};
bool TestCapability::interpret(unsigned _id, RLP const& _r)
{
cnote << "Capability::interpret(): custom message received";
++m_cntReceivedMessages;
m_testSum += _r[0].toInt();
BOOST_ASSERT(_id == UserPacket);
return (_id == UserPacket);
}
class TestHostCapability: public HostCapability<TestCapability>, public Worker
{
public:
TestHostCapability(): Worker("test") {}
virtual ~TestHostCapability() {}
void sendTestMessage(NodeId const& _id, int _x)
{
for (auto i: peerSessions())
if (_id == i.second->id)
i.first->cap<TestCapability>().get()->sendTestMessage(_x);
}
std::pair<int, int> retrieveTestData(NodeId const& _id)
{
int cnt = 0;
int checksum = 0;
for (auto i: peerSessions())
if (_id == i.second->id)
{
cnt += i.first->cap<TestCapability>().get()->countReceivedMessages();
checksum += i.first->cap<TestCapability>().get()->testSum();
}
return std::pair<int, int>(cnt, checksum);
}
};
BOOST_FIXTURE_TEST_SUITE(p2pCapability, P2PFixture)
BOOST_AUTO_TEST_CASE(capability)
{
VerbosityHolder verbosityHolder;
cnote << "Testing Capability...";
const char* const localhost = "127.0.0.1";
NetworkPreferences prefs1(localhost, 30301, false);
NetworkPreferences prefs2(localhost, 30302, false);
Host host1("Test", prefs1);
auto thc1 = host1.registerCapability(new TestHostCapability());
host1.start();
Host host2("Test", prefs2);
auto thc2 = host2.registerCapability(new TestHostCapability());
host2.start();
int const step = 10;
for (int i = 0; i < 3000 && (!host1.isStarted() || !host2.isStarted()); i += step)
this_thread::sleep_for(chrono::milliseconds(step));
BOOST_REQUIRE(host1.isStarted() && host2.isStarted());
host1.requirePeer(host2.id(), NodeIPEndpoint(bi::address::from_string(localhost), prefs2.listenPort, prefs2.listenPort));
for (int i = 0; i < 3000 && (!host1.peerCount() || !host2.peerCount()); i += step)
this_thread::sleep_for(chrono::milliseconds(step));
BOOST_REQUIRE(host1.peerCount() > 0 && host2.peerCount() > 0);
int const target = 7;
int checksum = 0;
for (int i = 0; i < target; checksum += i++)
thc2->sendTestMessage(host1.id(), i);
this_thread::sleep_for(chrono::seconds(1));
std::pair<int, int> testData = thc1->retrieveTestData(host2.id());
BOOST_REQUIRE_EQUAL(target, testData.first);
BOOST_REQUIRE_EQUAL(checksum, testData.second);
}
BOOST_AUTO_TEST_SUITE_END()

59
test/libsolidity/SolidityCompiler.cpp

@ -116,36 +116,35 @@ BOOST_AUTO_TEST_CASE(ifStatement)
bytes code = compileContract(sourceCode); bytes code = compileContract(sourceCode);
unsigned shift = 60; unsigned shift = 60;
unsigned boilerplateSize = 73; unsigned boilerplateSize = 73;
bytes expectation({byte(Instruction::JUMPDEST), bytes expectation({
byte(Instruction::PUSH1), 0x0, byte(Instruction::JUMPDEST),
byte(Instruction::DUP1), byte(Instruction::PUSH1), 0x0,
byte(Instruction::PUSH1), byte(0x1b + shift), // "true" target byte(Instruction::DUP1),
byte(Instruction::JUMPI), byte(Instruction::ISZERO),
// new check "else if" condition byte(Instruction::PUSH1), byte(0x0f + shift), // "false" target
byte(Instruction::DUP1), byte(Instruction::JUMPI),
byte(Instruction::ISZERO), // "if" body
byte(Instruction::PUSH1), byte(0x13 + shift), byte(Instruction::PUSH1), 0x4d,
byte(Instruction::JUMPI), byte(Instruction::POP),
// "else" body byte(Instruction::PUSH1), byte(0x21 + shift),
byte(Instruction::PUSH1), 0x4f, byte(Instruction::JUMP),
byte(Instruction::POP), // new check "else if" condition
byte(Instruction::PUSH1), byte(0x17 + shift), // exit path of second part byte(Instruction::JUMPDEST),
byte(Instruction::JUMP), byte(Instruction::DUP1),
// "else if" body byte(Instruction::ISZERO),
byte(Instruction::JUMPDEST), byte(Instruction::ISZERO),
byte(Instruction::PUSH1), 0x4e, byte(Instruction::PUSH1), byte(0x1c + shift),
byte(Instruction::POP), byte(Instruction::JUMPI),
byte(Instruction::JUMPDEST), // "else if" body
byte(Instruction::PUSH1), byte(0x1f + shift), byte(Instruction::PUSH1), 0x4e,
byte(Instruction::JUMP), byte(Instruction::POP),
// "if" body byte(Instruction::PUSH1), byte(0x20 + shift),
byte(Instruction::JUMPDEST), byte(Instruction::JUMP),
byte(Instruction::PUSH1), 0x4d, // "else" body
byte(Instruction::POP), byte(Instruction::JUMPDEST),
byte(Instruction::JUMPDEST), byte(Instruction::PUSH1), 0x4f,
byte(Instruction::JUMPDEST), byte(Instruction::POP),
byte(Instruction::POP), });
byte(Instruction::JUMP)});
checkCodePresentAt(code, expectation, boilerplateSize); checkCodePresentAt(code, expectation, boilerplateSize);
} }

38
test/libsolidity/SolidityOptimizer.cpp

@ -29,6 +29,7 @@
#include <libevmasm/CommonSubexpressionEliminator.h> #include <libevmasm/CommonSubexpressionEliminator.h>
#include <libevmasm/ControlFlowGraph.h> #include <libevmasm/ControlFlowGraph.h>
#include <libevmasm/Assembly.h> #include <libevmasm/Assembly.h>
#include <libevmasm/BlockDeduplicator.h>
using namespace std; using namespace std;
using namespace dev::eth; using namespace dev::eth;
@ -125,7 +126,7 @@ public:
BOOST_CHECK_EQUAL_COLLECTIONS(_expectation.begin(), _expectation.end(), output.begin(), output.end()); BOOST_CHECK_EQUAL_COLLECTIONS(_expectation.begin(), _expectation.end(), output.begin(), output.end());
} }
void checkCFG(AssemblyItems const& _input, AssemblyItems const& _expectation) AssemblyItems getCFG(AssemblyItems const& _input)
{ {
AssemblyItems output = _input; AssemblyItems output = _input;
// Running it four times should be enough for these tests. // Running it four times should be enough for these tests.
@ -138,6 +139,12 @@ public:
back_inserter(optItems)); back_inserter(optItems));
output = move(optItems); output = move(optItems);
} }
return output;
}
void checkCFG(AssemblyItems const& _input, AssemblyItems const& _expectation)
{
AssemblyItems output = getCFG(_input);
BOOST_CHECK_EQUAL_COLLECTIONS(_expectation.begin(), _expectation.end(), output.begin(), output.end()); BOOST_CHECK_EQUAL_COLLECTIONS(_expectation.begin(), _expectation.end(), output.begin(), output.end());
} }
@ -925,6 +932,35 @@ BOOST_AUTO_TEST_CASE(control_flow_graph_do_not_remove_returned_to)
checkCFG(input, {u256(2)}); checkCFG(input, {u256(2)});
} }
BOOST_AUTO_TEST_CASE(block_deduplicator)
{
AssemblyItems input{
AssemblyItem(PushTag, 2),
AssemblyItem(PushTag, 1),
AssemblyItem(PushTag, 3),
u256(6),
eth::Instruction::SWAP3,
eth::Instruction::JUMP,
AssemblyItem(Tag, 1),
u256(6),
eth::Instruction::SWAP3,
eth::Instruction::JUMP,
AssemblyItem(Tag, 2),
u256(6),
eth::Instruction::SWAP3,
eth::Instruction::JUMP,
AssemblyItem(Tag, 3)
};
BlockDeduplicator dedup(input);
dedup.deduplicate();
set<u256> pushTags;
for (AssemblyItem const& item: input)
if (item.type() == PushTag)
pushTags.insert(item.data());
BOOST_CHECK_EQUAL(pushTags.size(), 2);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

Loading…
Cancel
Save